186 lines
4.1 KiB
TypeScript
186 lines
4.1 KiB
TypeScript
import { LitElement, html, css } from './plugins.js';
|
|
import type { CSSResult, TemplateResult } from './plugins.js';
|
|
|
|
export class SmartchatMessage extends LitElement {
|
|
declare role: 'user' | 'assistant' | 'tool';
|
|
declare content: string;
|
|
declare toolName: string;
|
|
declare timestamp: number;
|
|
|
|
static properties = {
|
|
role: { type: String },
|
|
content: { type: String },
|
|
toolName: { type: String },
|
|
timestamp: { type: Number },
|
|
};
|
|
|
|
static styles: CSSResult = css`
|
|
:host {
|
|
display: block;
|
|
animation: msg-in 0.3s ease-out both;
|
|
}
|
|
|
|
@keyframes msg-in {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateY(6px);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
|
|
/* ── Message Row ── */
|
|
.row {
|
|
display: flex;
|
|
gap: 10px;
|
|
align-items: flex-end;
|
|
margin-bottom: 4px;
|
|
}
|
|
|
|
.row.user {
|
|
justify-content: flex-end;
|
|
}
|
|
|
|
.row.assistant {
|
|
justify-content: flex-start;
|
|
}
|
|
|
|
/* ── Avatar ── */
|
|
.avatar {
|
|
width: 28px;
|
|
height: 28px;
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 12px;
|
|
flex-shrink: 0;
|
|
line-height: 1;
|
|
}
|
|
|
|
.avatar.user {
|
|
background: linear-gradient(135deg, #3b82f6, #2563eb);
|
|
color: #fff;
|
|
order: 2;
|
|
}
|
|
|
|
.avatar.assistant {
|
|
background: linear-gradient(135deg, #6366f1, #7c3aed);
|
|
color: #fff;
|
|
}
|
|
|
|
/* ── Bubble ── */
|
|
.bubble {
|
|
padding: 10px 14px;
|
|
border-radius: 18px;
|
|
word-break: break-word;
|
|
white-space: pre-wrap;
|
|
font-size: 14px;
|
|
line-height: 1.55;
|
|
max-width: min(75%, 480px);
|
|
}
|
|
|
|
.bubble.user {
|
|
background: linear-gradient(135deg, #3b82f6, #2563eb);
|
|
color: #fff;
|
|
border-bottom-right-radius: 6px;
|
|
}
|
|
|
|
.bubble.assistant {
|
|
background: var(--smartchat-assistant-bg, #1e1e2e);
|
|
border: 1px solid var(--smartchat-assistant-border, rgba(255, 255, 255, 0.08));
|
|
color: var(--smartchat-assistant-text, #d1d5db);
|
|
border-bottom-left-radius: 6px;
|
|
}
|
|
|
|
/* ── Tool ── */
|
|
.tool-row {
|
|
display: flex;
|
|
justify-content: flex-start;
|
|
padding: 2px 0 2px 38px;
|
|
}
|
|
|
|
.tool-pill {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
padding: 4px 10px;
|
|
background: rgba(99, 102, 241, 0.1);
|
|
border: 1px solid rgba(99, 102, 241, 0.15);
|
|
border-radius: 20px;
|
|
font-size: 11px;
|
|
font-family: 'SF Mono', 'Fira Code', ui-monospace, monospace;
|
|
color: #a5b4fc;
|
|
}
|
|
|
|
.tool-icon {
|
|
font-size: 10px;
|
|
}
|
|
|
|
.tool-name {
|
|
font-weight: 600;
|
|
}
|
|
|
|
/* ── Timestamp ── */
|
|
.time-row {
|
|
padding: 1px 38px 6px;
|
|
font-size: 10px;
|
|
color: rgba(255, 255, 255, 0.2);
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
|
|
.time-row.user {
|
|
text-align: right;
|
|
}
|
|
|
|
.time-row.assistant {
|
|
text-align: left;
|
|
}
|
|
`;
|
|
|
|
constructor() {
|
|
super();
|
|
this.role = 'user';
|
|
this.content = '';
|
|
this.toolName = '';
|
|
this.timestamp = 0;
|
|
}
|
|
|
|
private formatTime(ts: number): string {
|
|
if (!ts) return '';
|
|
const d = new Date(ts);
|
|
return d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
|
|
}
|
|
|
|
render(): TemplateResult {
|
|
if (this.role === 'tool') {
|
|
return html`
|
|
<div class="tool-row">
|
|
<div class="tool-pill">
|
|
<span class="tool-icon">⚡</span>
|
|
<span class="tool-name">${this.toolName || 'tool'}</span>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
return html`
|
|
<div class="row ${this.role}">
|
|
<div class="avatar ${this.role}">
|
|
${this.role === 'user' ? '⬆' : '✦'}
|
|
</div>
|
|
<div class="bubble ${this.role}">
|
|
${this.content}
|
|
</div>
|
|
</div>
|
|
${this.timestamp
|
|
? html`<div class="time-row ${this.role}">${this.formatTime(this.timestamp)}</div>`
|
|
: ''}
|
|
`;
|
|
}
|
|
}
|
|
|
|
customElements.define('smartchat-message', SmartchatMessage);
|