129 lines
5.2 KiB
TypeScript
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>
|
|
`;
|
|
}
|
|
}
|