Agent-JAE/packages/web-ui/example/src/components/utility-toggle.ts
2026-03-26 21:27:24 +00:00

129 lines
5.2 KiB
TypeScript

import { html, LitElement } from "lit";
import { customElement, property } from "lit/decorators.js";
export interface UtilityVisibility {
showToolCalls: boolean;
showThinking: boolean;
showSystemMessages: boolean;
showTimestamps: boolean;
}
@customElement("jae-utility-toggle")
export class JaeUtilityToggle extends LitElement {
@property({ type: Object }) visibility: UtilityVisibility = {
showToolCalls: true,
showThinking: false,
showSystemMessages: false,
showTimestamps: true,
};
@property({ type: Boolean }) open = false;
protected override createRenderRoot() {
return this;
}
private _toggle(key: keyof UtilityVisibility) {
this.visibility = { ...this.visibility, [key]: !this.visibility[key] };
this.dispatchEvent(
new CustomEvent("visibility-change", {
detail: this.visibility,
bubbles: true,
composed: true,
}),
);
}
private _items: { key: keyof UtilityVisibility; label: string; icon: string; desc: string }[] = [
{
key: "showToolCalls",
label: "Tool Calls",
icon: "🔧",
desc: "Show web search, image gen & other tool results",
},
{ key: "showThinking", label: "Thinking", icon: "🧠", desc: "Show model reasoning / thinking blocks" },
{ key: "showSystemMessages", label: "System Messages", icon: "⚙️", desc: "Show system notifications and prompts" },
{ key: "showTimestamps", label: "Timestamps", icon: "🕐", desc: "Show message timestamps" },
];
override render() {
const activeCount = Object.values(this.visibility).filter(Boolean).length;
return html`
<div class="relative">
<!-- Toggle button -->
<button
title="Message filters"
@click=${() => {
this.open = !this.open;
this.requestUpdate();
}}
class="flex items-center gap-1.5 px-2.5 py-1.5 rounded-lg text-xs border border-border bg-secondary/60 hover:bg-secondary hover:border-primary/40 transition-all text-muted-foreground hover:text-foreground"
>
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<line x1="4" y1="6" x2="20" y2="6"/>
<line x1="8" y1="12" x2="20" y2="12"/>
<line x1="12" y1="18" x2="20" y2="18"/>
</svg>
<span>View</span>
${activeCount < 4 ? html`<span class="bg-primary text-primary-foreground rounded-full w-4 h-4 flex items-center justify-center text-[10px] font-bold">${activeCount}</span>` : html``}
</button>
<!-- Dropdown panel -->
${
this.open
? html`
<div class="absolute top-full right-0 mt-2 w-72 rounded-xl border border-border bg-background shadow-2xl z-50 overflow-hidden">
<!-- Header -->
<div class="flex items-center gap-2 px-4 py-3 border-b border-border bg-secondary/30">
<img src="/mascot/jae-default.png" alt="JAE" class="w-7 h-auto" />
<div>
<div class="text-sm font-semibold text-foreground">Message Filters</div>
<div class="text-xs text-muted-foreground">Control what JAE shows you</div>
</div>
<button @click=${() => {
this.open = false;
this.requestUpdate();
}} class="ml-auto text-muted-foreground hover:text-foreground transition-colors">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
</button>
</div>
<!-- Items -->
<div class="p-2">
${this._items.map(
(item) => html`
<button
class="w-full flex items-start gap-3 px-3 py-2.5 rounded-lg hover:bg-secondary/60 transition-colors text-left"
@click=${() => this._toggle(item.key)}
>
<span class="text-base mt-0.5 shrink-0">${item.icon}</span>
<div class="flex-1 min-w-0">
<div class="text-sm font-medium text-foreground">${item.label}</div>
<div class="text-xs text-muted-foreground leading-tight">${item.desc}</div>
</div>
<!-- Toggle switch -->
<div class="shrink-0 mt-0.5 w-9 h-5 rounded-full transition-colors duration-200 flex items-center px-0.5 ${this.visibility[item.key] ? "bg-primary" : "bg-muted"}">
<div class="w-4 h-4 rounded-full bg-white shadow transition-transform duration-200 ${this.visibility[item.key] ? "translate-x-4" : "translate-x-0"}"></div>
</div>
</button>
`,
)}
</div>
<!-- Footer -->
<div class="px-4 py-2 border-t border-border bg-secondary/20 text-xs text-muted-foreground text-center">
Settings auto-save per session
</div>
</div>
<!-- Click-outside overlay -->
<div class="fixed inset-0 z-40" @click=${() => {
this.open = false;
this.requestUpdate();
}}></div>
`
: html``
}
</div>
`;
}
}