import { LitElement, html, css } from './plugins.js'; import type { CSSResult, TemplateResult, ChatSession, IAgentRunResult } from './plugins.js'; import './smartchat-message.js'; import './smartchat-input.js'; interface IDisplayMessage { role: 'user' | 'assistant' | 'tool'; content: string; toolName?: string; } export class SmartchatWindow extends LitElement { declare chatSession: ChatSession; declare placeholder: string; private messages: IDisplayMessage[] = []; private busy = false; private streamingText = ''; static properties = { chatSession: { attribute: false }, placeholder: { type: String }, }; static styles: CSSResult = css` :host { display: flex; flex-direction: column; height: 100%; background: var(--smartchat-bg, #111827); color: var(--smartchat-text, #e5e7eb); font-family: var(--smartchat-font, system-ui, -apple-system, sans-serif); border-radius: var(--smartchat-radius, 12px); overflow: hidden; } .message-list { flex: 1; overflow-y: auto; padding: 16px; display: flex; flex-direction: column; gap: 4px; } .empty-state { display: flex; align-items: center; justify-content: center; flex: 1; color: var(--smartchat-muted, #6b7280); font-size: 14px; } .streaming { padding: 8px 12px; background: var(--smartchat-assistant-bubble, #374151); color: var(--smartchat-assistant-text, #e5e7eb); border-radius: 8px; border-bottom-left-radius: 2px; max-width: 85%; white-space: pre-wrap; } .cursor { display: inline-block; width: 2px; height: 1em; background: var(--smartchat-cursor, #6366f1); animation: blink 1s step-end infinite; vertical-align: text-bottom; } @keyframes blink { 50% { opacity: 0; } } .input-area { padding: 12px 16px; border-top: 1px solid var(--smartchat-border, #1f2937); } `; constructor() { super(); this.placeholder = 'Type a message...'; } connectedCallback() { super.connectedCallback(); if (this.chatSession) { this.chatSession.updateCallbacks({ onToken: (delta: string) => { this.streamingText += delta; this.requestUpdate(); this.scrollToBottom(); }, onToolCall: (name: string) => { this.messages = [ ...this.messages, { role: 'tool', content: '', toolName: name }, ]; this.requestUpdate(); this.scrollToBottom(); }, }); } } private scrollToBottom() { requestAnimationFrame(() => { const list = this.shadowRoot?.querySelector('.message-list'); if (list) { list.scrollTop = list.scrollHeight; } }); } private async handleSend(e: CustomEvent<{ text: string }>) { if (this.busy) return; const text = e.detail.text; this.messages = [...this.messages, { role: 'user', content: text }]; this.busy = true; this.streamingText = ''; this.requestUpdate(); this.scrollToBottom(); try { const result = await this.chatSession.send(text); this.streamingText = ''; this.messages = [...this.messages, { role: 'assistant', content: result.text }]; } catch (error) { const errMsg = error instanceof Error ? error.message : String(error); this.messages = [ ...this.messages, { role: 'assistant', content: `Error: ${errMsg}` }, ]; } finally { this.busy = false; this.requestUpdate(); this.scrollToBottom(); } } render(): TemplateResult { return html`