blob: 989a2e66f0a959d0e10a0528f00d0b1f6e47e5c4 [file] [log] [blame]
Sean McCullough86b56862025-04-18 13:04:03 -07001import { css, html, LitElement, PropertyValues } from "lit";
2import { customElement, property, query } from "lit/decorators.js";
3import { DataManager, ConnectionStatus } from "../data";
4import { State, TimelineMessage } from "../types";
5import "./sketch-container-status";
6
7@customElement("sketch-chat-input")
8export class SketchChatInput extends LitElement {
9 @property()
10 content: string = "";
11
12 // See https://lit.dev/docs/components/styles/ for how lit-element handles CSS.
13 // Note that these styles only apply to the scope of this web component's
14 // shadow DOM node, so they won't leak out or collide with CSS declared in
15 // other components or the containing web page (...unless you want it to do that).
16 static styles = css`
17 /* Chat styles - exactly matching timeline.css */
18 .chat-container {
19 position: fixed;
20 bottom: 0;
21 left: 0;
22 width: 100%;
23 background: #f0f0f0;
24 padding: 15px;
25 box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
26 z-index: 1000;
27 min-height: 40px; /* Ensure minimum height */
28 }
29
30 .chat-input-wrapper {
31 display: flex;
32 max-width: 1200px;
33 margin: 0 auto;
34 gap: 10px;
35 }
36
37 #chatInput {
38 flex: 1;
39 padding: 12px;
40 border: 1px solid #ddd;
41 border-radius: 4px;
42 resize: none;
43 font-family: monospace;
44 font-size: 12px;
45 min-height: 40px;
46 max-height: 120px;
47 background: #f7f7f7;
48 }
49
50 #sendChatButton {
51 background-color: #2196f3;
52 color: white;
53 border: none;
54 border-radius: 4px;
55 padding: 0 20px;
56 cursor: pointer;
57 font-weight: 600;
58 }
59
60 #sendChatButton:hover {
61 background-color: #0d8bf2;
62 }
63 `;
64
65 constructor() {
66 super();
67
68 // Binding methods
69 this._handleUpdateContent = this._handleUpdateContent.bind(this);
70 }
71
72 /**
73 * Handle update-content event
74 */
75 private _handleUpdateContent(event: CustomEvent) {
76 const { content } = event.detail;
77 if (content !== undefined) {
78 this.content = content;
79
80 // Update the textarea value directly, otherwise it won't update until next render
81 const textarea = this.shadowRoot?.querySelector(
Sean McCullough71941bd2025-04-18 13:31:48 -070082 "#chatInput",
Sean McCullough86b56862025-04-18 13:04:03 -070083 ) as HTMLTextAreaElement;
84 if (textarea) {
85 textarea.value = content;
86 }
87 }
88 }
89
90 // See https://lit.dev/docs/components/lifecycle/
91 connectedCallback() {
92 super.connectedCallback();
93
94 // Listen for update-content events
95 this.addEventListener(
96 "update-content",
Sean McCullough71941bd2025-04-18 13:31:48 -070097 this._handleUpdateContent as EventListener,
Sean McCullough86b56862025-04-18 13:04:03 -070098 );
99 }
100
101 // See https://lit.dev/docs/components/lifecycle/
102 disconnectedCallback() {
103 super.disconnectedCallback();
104
105 // Remove event listeners
106 this.removeEventListener(
107 "update-content",
Sean McCullough71941bd2025-04-18 13:31:48 -0700108 this._handleUpdateContent as EventListener,
Sean McCullough86b56862025-04-18 13:04:03 -0700109 );
110 }
111
112 sendChatMessage() {
113 const event = new CustomEvent("send-chat", {
114 detail: { message: this.content },
115 bubbles: true,
116 composed: true,
117 });
118 this.dispatchEvent(event);
119 this.content = ""; // Clear the input after sending
120 }
121
122 adjustChatSpacing() {
123 console.log("TODO: adjustChatSpacing");
124 }
125
126 _sendChatClicked() {
127 this.sendChatMessage();
128 this.chatInput.focus(); // Refocus the input after sending
129 }
130
131 _chatInputKeyDown(event: KeyboardEvent) {
132 // Send message if Enter is pressed without Shift key
133 if (event.key === "Enter" && !event.shiftKey) {
134 event.preventDefault(); // Prevent default newline
135 this.sendChatMessage();
136 }
137 }
138
139 _chatInputChanged(event) {
140 this.content = event.target.value;
141 requestAnimationFrame(() => this.adjustChatSpacing());
142 }
143
144 @query("#chatInput")
145 private chatInput: HTMLTextAreaElement;
146
147 protected firstUpdated(): void {
148 if (this.chatInput) {
149 this.chatInput.focus();
150 }
151 }
152
153 render() {
154 return html`
155 <div class="chat-container">
156 <div class="chat-input-wrapper">
157 <textarea
158 id="chatInput"
159 placeholder="Type your message here and press Enter to send..."
160 autofocus
161 @keydown="${this._chatInputKeyDown}"
162 @input="${this._chatInputChanged}"
163 .value=${this.content || ""}
164 ></textarea>
165 <button @click="${this._sendChatClicked}" id="sendChatButton">
166 Send
167 </button>
168 </div>
169 </div>
170 `;
171 }
172}
173
174declare global {
175 interface HTMLElementTagNameMap {
176 "sketch-chat-input": SketchChatInput;
177 }
178}