refactor(ui): simplify agent overview component by removing unused identity fields and enhancing fallback display

This commit is contained in:
Val Alexander
2026-02-22 06:18:55 -06:00
parent eec3182cbb
commit 1c86a1b337
5 changed files with 131 additions and 83 deletions

View File

@@ -2005,8 +2005,8 @@
.agents-overview-grid {
display: grid;
gap: 14px;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 10px;
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
}
.agent-kv {
@@ -2658,6 +2658,67 @@
text-decoration: underline;
}
/* ===========================================
Debug Event Log
=========================================== */
.debug-event-log-scroll {
margin-top: 12px;
max-height: 480px;
overflow-y: auto;
}
.debug-event-entry {
border-bottom: 1px solid var(--border);
}
.debug-event-entry:last-child {
border-bottom: none;
}
.debug-event-summary {
display: flex;
align-items: center;
gap: 10px;
padding: 8px 0;
cursor: pointer;
font-family: var(--mono);
font-size: 13px;
list-style: none;
}
.debug-event-summary::-webkit-details-marker {
display: none;
}
.debug-event-summary::before {
content: "▸";
flex-shrink: 0;
width: 12px;
color: var(--muted);
transition: transform 0.15s ease;
}
.debug-event-entry[open] > .debug-event-summary::before {
transform: rotate(90deg);
}
.debug-event-name {
font-weight: 600;
}
.debug-event-ts {
margin-left: auto;
flex-shrink: 0;
font-size: 12px;
}
.debug-event-payload {
margin: 0 0 8px 22px;
max-height: 300px;
overflow-y: auto;
}
/* ===========================================
Overview Event Log
=========================================== */
@@ -2838,8 +2899,10 @@
.ov-bottom-grid {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-columns: 1fr;
gap: 14px;
max-height: 420px;
overflow-y: auto;
}
@media (max-width: 768px) {

View File

@@ -8,7 +8,7 @@
grid-template-columns: 260px minmax(0, 1fr);
gap: 0;
height: calc(100vh - 160px);
margin: -16px;
margin: 0 -16px -16px;
border-radius: var(--radius-xl);
border: 1px solid var(--border);
background: var(--panel);

View File

@@ -1,14 +1,10 @@
import { html, nothing } from "lit";
import type { AgentIdentityResult, AgentsFilesListResult, AgentsListResult } from "../types.ts";
import type { AgentsFilesListResult, AgentsListResult } from "../types.ts";
import {
agentAvatarHue,
agentBadgeText,
buildModelOptions,
normalizeAgentLabel,
normalizeModelValue,
parseFallbackList,
resolveAgentConfig,
resolveAgentEmoji,
resolveModelFallbacks,
resolveModelLabel,
resolveModelPrimary,
@@ -20,9 +16,6 @@ export function renderAgentOverview(params: {
defaultId: string | null;
configForm: Record<string, unknown> | null;
agentFilesList: AgentsFilesListResult | null;
agentIdentity: AgentIdentityResult | null;
agentIdentityLoading: boolean;
agentIdentityError: string | null;
configLoading: boolean;
configSaving: boolean;
configDirty: boolean;
@@ -36,9 +29,6 @@ export function renderAgentOverview(params: {
agent,
configForm,
agentFilesList,
agentIdentity,
agentIdentityLoading,
agentIdentityError,
configLoading,
configSaving,
configDirty,
@@ -65,26 +55,9 @@ export function renderAgentOverview(params: {
const effectivePrimary = modelPrimary ?? defaultPrimary ?? null;
const modelFallbacks = resolveModelFallbacks(config.entry?.model);
const fallbackChips = modelFallbacks ?? [];
const identityName =
agentIdentity?.name?.trim() ||
agent.identity?.name?.trim() ||
agent.name?.trim() ||
config.entry?.name ||
"-";
const resolvedEmoji = resolveAgentEmoji(agent, agentIdentity);
const identityEmoji = resolvedEmoji || "-";
const skillFilter = Array.isArray(config.entry?.skills) ? config.entry?.skills : null;
const skillCount = skillFilter?.length ?? null;
const identityStatus = agentIdentityLoading
? "Loading…"
: agentIdentityError
? "Unavailable"
: "";
const isDefault = Boolean(params.defaultId && agent.id === params.defaultId);
const badge = agentBadgeText(agent.id, params.defaultId);
const hue = agentAvatarHue(agent.id);
const displayName = normalizeAgentLabel(agent);
const subtitle = agent.identity?.theme?.trim() || "";
const disabled = !configForm || configLoading || configSaving;
const removeChip = (index: number) => {
@@ -104,27 +77,11 @@ export function renderAgentOverview(params: {
}
};
const fallbackSummary = fallbackChips.length > 0 ? `${fallbackChips.length} configured` : "none";
return html`
<section class="card">
<div class="card-title">Overview</div>
<div class="card-sub">Workspace paths and identity metadata.</div>
<div class="agent-identity-card" style="margin-top: 16px;">
<div class="agent-avatar" style="--agent-hue: ${hue}">
${resolvedEmoji || displayName.slice(0, 1)}
</div>
<div class="agent-identity-details">
<div class="agent-identity-name">${identityName}</div>
<div class="agent-identity-meta">
${identityEmoji !== "-" ? html`<span>${identityEmoji}</span>` : nothing}
${subtitle ? html`<span>${subtitle}</span>` : nothing}
${badge ? html`<span class="agent-pill">${badge}</span>` : nothing}
${identityStatus ? html`<span class="muted">${identityStatus}</span>` : nothing}
</div>
</div>
</div>
<div class="agents-overview-grid" style="margin-top: 16px;">
<div class="agents-overview-grid">
<div class="agent-kv">
<div class="label">Workspace</div>
<div>
@@ -144,23 +101,25 @@ export function renderAgentOverview(params: {
<div class="label">Skills Filter</div>
<div>${skillFilter ? `${skillCount} selected` : "all skills"}</div>
</div>
<div class="agent-kv">
<div class="label">Fallbacks</div>
<div class="mono">${fallbackSummary}</div>
</div>
</div>
${
configDirty
? html`
<div class="callout warn" style="margin-top: 16px">You have unsaved config changes.</div>
<div class="callout warn" style="margin-top: 12px">You have unsaved config changes.</div>
`
: nothing
}
<div class="agent-model-select" style="margin-top: 20px;">
<div class="label">Model Selection</div>
<div class="row" style="gap: 12px; flex-wrap: wrap;">
<label class="field" style="min-width: 260px; flex: 1;">
<div class="agent-model-select">
<div class="row" style="gap: 12px; flex-wrap: wrap; align-items: flex-end;">
<label class="field" style="min-width: 240px; flex: 1;">
<span>Primary model${isDefault ? " (default)" : ""}</span>
<select
.value=${effectivePrimary ?? ""}
?disabled=${disabled}
@change=${(e: Event) =>
onModelChange(agent.id, (e.target as HTMLSelectElement).value || null)}
@@ -177,7 +136,7 @@ export function renderAgentOverview(params: {
${buildModelOptions(configForm, effectivePrimary ?? undefined)}
</select>
</label>
<div class="field" style="min-width: 260px; flex: 1;">
<div class="field" style="min-width: 240px; flex: 1;">
<span>Fallbacks</span>
<div class="agent-chip-input" @click=${(e: Event) => {
const container = e.currentTarget as HTMLElement;
@@ -214,18 +173,18 @@ export function renderAgentOverview(params: {
/>
</div>
</div>
</div>
<div class="row" style="justify-content: flex-end; gap: 8px;">
<button class="btn btn--sm" ?disabled=${configLoading} @click=${onConfigReload}>
Reload Config
</button>
<button
class="btn btn--sm primary"
?disabled=${configSaving || !configDirty}
@click=${onConfigSave}
>
${configSaving ? "Saving…" : "Save"}
</button>
<div class="agent-model-actions">
<button class="btn btn--sm" ?disabled=${configLoading} @click=${onConfigReload}>
Reload Config
</button>
<button
class="btn btn--sm primary"
?disabled=${configSaving || !configDirty}
@click=${onConfigSave}
>
${configSaving ? "Saving…" : "Save"}
</button>
</div>
</div>
</div>
</section>

View File

@@ -387,7 +387,10 @@ export function buildModelOptions(
<option value="" disabled>No configured models</option>
`;
}
return options.map((option) => html`<option value=${option.value}>${option.label}</option>`);
return options.map(
(option) =>
html`<option value=${option.value} ?selected=${current === option.value}>${option.label}</option>`,
);
}
type CompiledPattern =

View File

@@ -120,26 +120,49 @@ export function renderDebug(props: DebugProps) {
</section>
<section class="card" style="margin-top: 18px;">
<div class="card-title">Event Log</div>
<div class="card-sub">Latest gateway events.</div>
<div class="row" style="justify-content: space-between; align-items: baseline;">
<div>
<div class="card-title">Event Log</div>
<div class="card-sub">Latest gateway events.</div>
</div>
${
props.eventLog.length > 0
? html`<button
class="btn btn-sm"
@click=${(e: Event) => {
const section = (e.target as HTMLElement).closest("section")!;
const details = section.querySelectorAll<HTMLDetailsElement>(
"details.debug-event-entry",
);
const allOpen = Array.from(details).every((d) => d.open);
details.forEach((d) => (d.open = !allOpen));
}}
>${"Expand All / Collapse All"}</button>`
: nothing
}
</div>
${
props.eventLog.length === 0
? html`
<div class="muted" style="margin-top: 12px">No events yet.</div>
`
: html`
<div class="list" style="margin-top: 12px;">
<div class="debug-event-log-scroll">
${props.eventLog.map(
(evt) => html`
<div class="list-item">
<div class="list-main">
<div class="list-title">${evt.event}</div>
<div class="list-sub">${new Date(evt.ts).toLocaleTimeString()}</div>
</div>
<div class="list-meta">
<pre class="code-block">${formatEventPayload(evt.payload)}</pre>
</div>
</div>
<details class="debug-event-entry">
<summary class="debug-event-summary">
<span class="debug-event-name">${evt.event}</span>
<span class="debug-event-ts muted">${new Date(evt.ts).toLocaleTimeString()}</span>
</summary>
${
evt.payload
? html`<pre class="code-block debug-event-payload">${formatEventPayload(evt.payload)}</pre>`
: html`
<div class="muted" style="padding: 8px 0 4px">No payload.</div>
`
}
</details>
`,
)}
</div>