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