blob: 74570cc1c9641c08c80f5c966d0b58c05c92a769 [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;
Sean McCullough07b3e392025-04-21 22:51:14 +000039 resize: vertical;
Sean McCullough86b56862025-04-18 13:04:03 -070040 font-family: monospace;
41 font-size: 12px;
42 min-height: 40px;
Sean McCullough07b3e392025-04-21 22:51:14 +000043 max-height: 300px;
Sean McCullough86b56862025-04-18 13:04:03 -070044 background: #f7f7f7;
Sean McCullough5164eee2025-04-21 18:20:23 -070045 overflow-y: auto;
Sean McCullough07b3e392025-04-21 22:51:14 +000046 box-sizing: border-box; /* Ensure padding is included in height calculation */
47 line-height: 1.4; /* Consistent line height for better height calculation */
Sean McCullough86b56862025-04-18 13:04:03 -070048 }
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;
Pokey Rule97188fc2025-04-23 15:50:04 +010058 align-self: center;
59 height: 40px;
Sean McCullough86b56862025-04-18 13:04:03 -070060 }
61
62 #sendChatButton:hover {
63 background-color: #0d8bf2;
64 }
65 `;
66
67 constructor() {
68 super();
Sean McCullough86b56862025-04-18 13:04:03 -070069 }
70
Sean McCullough86b56862025-04-18 13:04:03 -070071 connectedCallback() {
72 super.connectedCallback();
Sean McCullough86b56862025-04-18 13:04:03 -070073 }
74
75 // See https://lit.dev/docs/components/lifecycle/
76 disconnectedCallback() {
77 super.disconnectedCallback();
Sean McCullough86b56862025-04-18 13:04:03 -070078 }
79
80 sendChatMessage() {
81 const event = new CustomEvent("send-chat", {
82 detail: { message: this.content },
83 bubbles: true,
84 composed: true,
85 });
86 this.dispatchEvent(event);
Sean McCullough86b56862025-04-18 13:04:03 -070087 }
88
89 adjustChatSpacing() {
Sean McCullough07b3e392025-04-21 22:51:14 +000090 if (!this.chatInput) return;
Sean McCullough5164eee2025-04-21 18:20:23 -070091
Sean McCullough07b3e392025-04-21 22:51:14 +000092 // Reset height to minimal value to correctly calculate scrollHeight
Sean McCullough5164eee2025-04-21 18:20:23 -070093 this.chatInput.style.height = "auto";
94
Sean McCullough07b3e392025-04-21 22:51:14 +000095 // Get the scroll height (content height)
96 const scrollHeight = this.chatInput.scrollHeight;
Sean McCullough5164eee2025-04-21 18:20:23 -070097
Sean McCullough07b3e392025-04-21 22:51:14 +000098 // Set the height to match content (up to max-height which is handled by CSS)
99 this.chatInput.style.height = `${scrollHeight}px`;
Sean McCullough86b56862025-04-18 13:04:03 -0700100 }
101
102 _sendChatClicked() {
103 this.sendChatMessage();
104 this.chatInput.focus(); // Refocus the input after sending
Sean McCullough07b3e392025-04-21 22:51:14 +0000105 // Reset height after sending a message
106 requestAnimationFrame(() => this.adjustChatSpacing());
Sean McCullough86b56862025-04-18 13:04:03 -0700107 }
108
109 _chatInputKeyDown(event: KeyboardEvent) {
110 // Send message if Enter is pressed without Shift key
111 if (event.key === "Enter" && !event.shiftKey) {
112 event.preventDefault(); // Prevent default newline
113 this.sendChatMessage();
114 }
115 }
116
117 _chatInputChanged(event) {
118 this.content = event.target.value;
Sean McCullough07b3e392025-04-21 22:51:14 +0000119 // Use requestAnimationFrame to ensure DOM updates have completed
Sean McCullough86b56862025-04-18 13:04:03 -0700120 requestAnimationFrame(() => this.adjustChatSpacing());
121 }
122
123 @query("#chatInput")
Sean McCullough07b3e392025-04-21 22:51:14 +0000124 chatInput: HTMLTextAreaElement;
Sean McCullough86b56862025-04-18 13:04:03 -0700125
126 protected firstUpdated(): void {
127 if (this.chatInput) {
128 this.chatInput.focus();
Sean McCullough07b3e392025-04-21 22:51:14 +0000129 // Initialize the input height
130 this.adjustChatSpacing();
Sean McCullough86b56862025-04-18 13:04:03 -0700131 }
132 }
133
134 render() {
135 return html`
136 <div class="chat-container">
137 <div class="chat-input-wrapper">
138 <textarea
139 id="chatInput"
140 placeholder="Type your message here and press Enter to send..."
141 autofocus
142 @keydown="${this._chatInputKeyDown}"
143 @input="${this._chatInputChanged}"
144 .value=${this.content || ""}
145 ></textarea>
146 <button @click="${this._sendChatClicked}" id="sendChatButton">
147 Send
148 </button>
149 </div>
150 </div>
151 `;
152 }
153}
154
155declare global {
156 interface HTMLElementTagNameMap {
157 "sketch-chat-input": SketchChatInput;
158 }
159}