feat(web): add web demo and polish Lit web components UI and demo tooling

This commit is contained in:
2026-03-07 08:15:23 +00:00
parent dd04edb420
commit 54f9cea7f9
15 changed files with 994 additions and 100 deletions
+101 -33
View File
@@ -16,51 +16,100 @@ export class SmartchatInput extends LitElement {
display: block;
}
.input-row {
.input-wrap {
display: flex;
align-items: center;
align-items: flex-end;
gap: 8px;
padding: 8px;
background: var(--smartchat-input-bg, #1f2937);
border-radius: 8px;
border: 1px solid var(--smartchat-input-border, #374151);
padding: 10px 12px;
background: rgba(255, 255, 255, 0.04);
border-radius: 16px;
border: 1px solid rgba(255, 255, 255, 0.07);
transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.input-row:focus-within {
border-color: var(--smartchat-input-focus, #6366f1);
.input-wrap:focus-within {
border-color: rgba(99, 102, 241, 0.45);
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.08);
}
input {
.input-wrap.disabled {
opacity: 0.45;
pointer-events: none;
}
textarea {
flex: 1;
background: transparent;
border: none;
outline: none;
color: var(--smartchat-input-text, #e5e7eb);
color: #e4e4e7;
font-size: 14px;
font-family: inherit;
line-height: 1.5;
resize: none;
min-height: 22px;
max-height: 110px;
padding: 2px 0;
}
input::placeholder {
color: var(--smartchat-input-placeholder, #6b7280);
textarea::placeholder {
color: rgba(255, 255, 255, 0.22);
}
button {
background: var(--smartchat-send-bg, #6366f1);
color: var(--smartchat-send-text, #fff);
.send-btn {
width: 32px;
height: 32px;
border-radius: 50%;
border: none;
border-radius: 6px;
padding: 6px 16px;
background: linear-gradient(135deg, #6366f1, #7c3aed);
color: #fff;
cursor: pointer;
font-size: 14px;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
transition: transform 0.12s ease, opacity 0.12s ease, box-shadow 0.12s ease;
box-shadow: 0 2px 8px rgba(99, 102, 241, 0.3);
}
button:hover {
opacity: 0.9;
.send-btn:hover:not(:disabled) {
transform: scale(1.08);
box-shadow: 0 3px 12px rgba(99, 102, 241, 0.45);
}
button:disabled {
opacity: 0.5;
cursor: not-allowed;
.send-btn:active:not(:disabled) {
transform: scale(0.94);
}
.send-btn:disabled {
opacity: 0.25;
cursor: default;
box-shadow: none;
}
.send-btn svg {
width: 15px;
height: 15px;
}
.hint {
display: flex;
justify-content: flex-end;
padding: 5px 2px 0;
}
.hint-text {
font-size: 10.5px;
color: rgba(255, 255, 255, 0.13);
}
kbd {
padding: 0 3px;
border-radius: 3px;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.07);
font-family: inherit;
font-size: 10px;
}
`;
@@ -71,13 +120,17 @@ export class SmartchatInput extends LitElement {
}
private handleKeydown(e: KeyboardEvent) {
if (e.key === 'Enter' && !e.shiftKey && this.value.trim()) {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
this.submit();
}
}
private handleInput(e: Event) {
this.value = (e.target as HTMLInputElement).value;
const textarea = e.target as HTMLTextAreaElement;
this.value = textarea.value;
textarea.style.height = 'auto';
textarea.style.height = Math.min(textarea.scrollHeight, 110) + 'px';
}
private submit() {
@@ -90,25 +143,40 @@ export class SmartchatInput extends LitElement {
}),
);
this.value = '';
const input = this.shadowRoot?.querySelector('input');
if (input) input.value = '';
const textarea = this.shadowRoot?.querySelector('textarea');
if (textarea) {
textarea.value = '';
textarea.style.height = 'auto';
}
}
render(): TemplateResult {
const canSend = !this.disabled && !!this.value.trim();
return html`
<div class="input-row">
<input
type="text"
<div class="input-wrap ${this.disabled ? 'disabled' : ''}">
<textarea
rows="1"
.value=${this.value}
@input=${this.handleInput}
@keydown=${this.handleKeydown}
placeholder=${this.disabled ? 'Waiting for response...' : this.placeholder}
?disabled=${this.disabled}
/>
<button @click=${this.submit} ?disabled=${this.disabled}>
Send
></textarea>
<button
class="send-btn"
@click=${this.submit}
?disabled=${!canSend}
aria-label="Send message"
>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<line x1="12" y1="19" x2="12" y2="5"></line>
<polyline points="5 12 12 5 19 12"></polyline>
</svg>
</button>
</div>
<div class="hint">
<span class="hint-text"><kbd>Enter</kbd> send · <kbd>Shift+Enter</kbd> new line</span>
</div>
`;
}
}