From 93b4ef209e43b599d9a3caffac09b3808cee2a30 Mon Sep 17 00:00:00 2001 From: Waishnav Date: Thu, 6 Aug 2026 16:18:49 +0530 Subject: [PATCH 1/8] fix(ui): open workspace cards by default --- src/ui/card-types.test.ts | 10 ++++++++++ src/ui/card-types.ts | 1 + 2 files changed, 11 insertions(+) diff --git a/src/ui/card-types.test.ts b/src/ui/card-types.test.ts index 3905ecd9..1648f025 100644 --- a/src/ui/card-types.test.ts +++ b/src/ui/card-types.test.ts @@ -78,6 +78,16 @@ test("a workspace card expands when it contains provider metadata", () => { ); }); +test("a workspace card with details opens immediately", () => { + assert.equal( + isInitiallyExpandedCard({ + tool: "open_workspace", + skills: [{ name: "research" }], + }), + true, + ); +}); + test("a workspace card expands when it contains agent metadata", () => { assert.equal( isExpandableCard({ diff --git a/src/ui/card-types.ts b/src/ui/card-types.ts index 107f3063..3d238083 100644 --- a/src/ui/card-types.ts +++ b/src/ui/card-types.ts @@ -187,6 +187,7 @@ export function isExpandableCard(card: ToolResultCard): boolean { } export function isInitiallyExpandedCard(card: ToolResultCard): boolean { + if (card.tool === "open_workspace") return isExpandableCard(card); if (isReviewTool(card.tool)) return isExpandableCard(card); if (isPatchTool(card.tool)) { return card.files?.length === 1 && isExpandableCard(card); From 0fff9f74e3369f87e4cfe7a35bc3e180add9e052 Mon Sep 17 00:00:00 2001 From: Waishnav Date: Thu, 6 Aug 2026 16:21:02 +0530 Subject: [PATCH 2/8] feat(ui): add provider logo assets --- src/ui/assets/provider-logos/claude.svg | 1 + src/ui/assets/provider-logos/copilot-dark.svg | 1 + src/ui/assets/provider-logos/cursor-dark.svg | 1 + src/ui/assets/provider-logos/openai-dark.svg | 1 + src/ui/assets/provider-logos/opencode-dark.svg | 1 + src/ui/assets/provider-logos/pi-on-dark.svg | 1 + src/ui/icons.ts | 16 +++++++++++++++- 7 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 src/ui/assets/provider-logos/claude.svg create mode 100644 src/ui/assets/provider-logos/copilot-dark.svg create mode 100644 src/ui/assets/provider-logos/cursor-dark.svg create mode 100644 src/ui/assets/provider-logos/openai-dark.svg create mode 100644 src/ui/assets/provider-logos/opencode-dark.svg create mode 100644 src/ui/assets/provider-logos/pi-on-dark.svg diff --git a/src/ui/assets/provider-logos/claude.svg b/src/ui/assets/provider-logos/claude.svg new file mode 100644 index 00000000..a85b80fa --- /dev/null +++ b/src/ui/assets/provider-logos/claude.svg @@ -0,0 +1 @@ + diff --git a/src/ui/assets/provider-logos/copilot-dark.svg b/src/ui/assets/provider-logos/copilot-dark.svg new file mode 100644 index 00000000..d09df805 --- /dev/null +++ b/src/ui/assets/provider-logos/copilot-dark.svg @@ -0,0 +1 @@ + diff --git a/src/ui/assets/provider-logos/cursor-dark.svg b/src/ui/assets/provider-logos/cursor-dark.svg new file mode 100644 index 00000000..d50421b5 --- /dev/null +++ b/src/ui/assets/provider-logos/cursor-dark.svg @@ -0,0 +1 @@ + diff --git a/src/ui/assets/provider-logos/openai-dark.svg b/src/ui/assets/provider-logos/openai-dark.svg new file mode 100644 index 00000000..7e19c92d --- /dev/null +++ b/src/ui/assets/provider-logos/openai-dark.svg @@ -0,0 +1 @@ + diff --git a/src/ui/assets/provider-logos/opencode-dark.svg b/src/ui/assets/provider-logos/opencode-dark.svg new file mode 100644 index 00000000..62e10df4 --- /dev/null +++ b/src/ui/assets/provider-logos/opencode-dark.svg @@ -0,0 +1 @@ + diff --git a/src/ui/assets/provider-logos/pi-on-dark.svg b/src/ui/assets/provider-logos/pi-on-dark.svg new file mode 100644 index 00000000..7b55eca7 --- /dev/null +++ b/src/ui/assets/provider-logos/pi-on-dark.svg @@ -0,0 +1 @@ + diff --git a/src/ui/icons.ts b/src/ui/icons.ts index 1713e0f8..022105d0 100644 --- a/src/ui/icons.ts +++ b/src/ui/icons.ts @@ -1,6 +1,6 @@ import { - Blocks, Bot, + Blocks, ChevronDown, CircleAlert, Cpu, @@ -52,6 +52,20 @@ export const toolIcons = { export type ToolIcon = IconNode; +const providerLogos = { + claude: new URL("./assets/provider-logos/claude.svg", import.meta.url).href, + codex: new URL("./assets/provider-logos/openai-dark.svg", import.meta.url).href, + copilot: new URL("./assets/provider-logos/copilot-dark.svg", import.meta.url).href, + cursor: new URL("./assets/provider-logos/cursor-dark.svg", import.meta.url).href, + opencode: new URL("./assets/provider-logos/opencode-dark.svg", import.meta.url).href, + pi: new URL("./assets/provider-logos/pi-on-dark.svg", import.meta.url).href, +} as const; + +export function getProviderLogo(name: string): string | undefined { + const normalizedName = name.trim().toLowerCase() as keyof typeof providerLogos; + return providerLogos[normalizedName]; +} + export function renderIcon(icon: ToolIcon, className = "icon-svg"): SVGElement { return createElement(icon, { class: className, From 7eae23ee184992d05914d319688521c061f1ba90 Mon Sep 17 00:00:00 2001 From: Waishnav Date: Thu, 6 Aug 2026 16:23:47 +0530 Subject: [PATCH 3/8] refactor(ui): simplify workspace context details --- src/ui/workspace-app.css | 173 ++++++++++---------------- src/ui/workspace-app.tsx | 254 ++++++++++----------------------------- 2 files changed, 130 insertions(+), 297 deletions(-) diff --git a/src/ui/workspace-app.css b/src/ui/workspace-app.css index 8a6ef00b..2211c07c 100644 --- a/src/ui/workspace-app.css +++ b/src/ui/workspace-app.css @@ -374,6 +374,7 @@ body { max-width: 100%; min-height: 24px; align-items: center; + gap: 5px; overflow: hidden; padding: 3px 8px; border: 1px solid color-mix(in srgb, var(--tool-accent) 16%, var(--tool-card-divider)); @@ -386,127 +387,61 @@ body { white-space: nowrap; } -.workspace-chip.muted { - border-style: dashed; - color: var(--color-text-tertiary, #a3a3aa); - opacity: 0.72; -} - -.workspace-row-disclosure { - align-items: start; +.workspace-chip-logo { + display: block; + width: 13px; + height: 13px; + flex: 0 0 auto; + object-fit: contain; } -.workspace-disclosure { - display: grid; +.workspace-chip-label { min-width: 0; - grid-template-columns: minmax(0, 1fr) auto; - gap: 8px; - align-items: start; -} - -.workspace-skills-list { - max-height: 24px; -} - -.workspace-disclosure.expanded { - grid-template-columns: minmax(0, 1fr); -} - -.workspace-skills-disclosure.expanded .workspace-skills-list { - max-height: none; - flex-wrap: wrap; - overflow: visible; -} - -.workspace-disclosure-toggle { - min-height: 24px; - padding: 3px 9px; - border: 1px solid color-mix(in srgb, var(--tool-accent) 28%, var(--tool-card-divider)); - border-radius: 9999px; - background: transparent; - color: var(--tool-accent); - cursor: pointer; - font: inherit; - font-size: var(--font-text-sm-size, 11px); - line-height: 1.25; + overflow: hidden; + text-overflow: ellipsis; white-space: nowrap; } -.workspace-disclosure-toggle:hover { - background: color-mix(in srgb, var(--tool-accent) 10%, transparent); -} - -.workspace-disclosure-toggle:focus-visible { - outline: 2px solid color-mix(in srgb, var(--tool-accent) 72%, transparent); - outline-offset: 2px; +.workspace-provider-logo { + display: inline-grid; + width: 20px; + height: 24px; + flex: 0 0 auto; + place-items: center; + cursor: help; } -.workspace-disclosure.expanded .workspace-disclosure-toggle { - justify-self: start; +.workspace-provider-logo-image { + display: block; + width: 16px; + height: 16px; + object-fit: contain; } -.workspace-value-list { - display: grid; - min-width: 0; - max-height: calc(1.45em); - overflow: hidden; - color: var(--color-text-secondary, #c7c7ce); - font-size: var(--font-text-sm-size, 12px); - line-height: 1.45; +.workspace-provider-logo.muted { + opacity: 0.62; } -.workspace-value-list.mono { - font-family: var(--font-mono, ui-monospace, SFMono-Regular, monospace); +.workspace-chip.muted { + border-style: dashed; + color: var(--color-text-tertiary, #a3a3aa); + opacity: 0.72; } -.workspace-value-item { - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; +.workspace-skills-row, +.workspace-instructions-row, +.workspace-agents-row { + align-items: start; } -.workspace-disclosure.expanded .workspace-value-list { - max-height: none; - gap: 5px; +.workspace-skills-list { + flex-wrap: wrap; overflow: visible; } -.workspace-instruction-summary { - display: flex; - min-width: 0; - height: 24px; - align-items: center; - gap: 6px; - overflow: hidden; -} - -.workspace-instruction-summary-item { - display: inline-flex; - min-width: 0; - flex: 0 1 auto; - align-items: center; - gap: 5px; - overflow: hidden; - padding: 2px 7px 2px 5px; - border: 1px solid color-mix(in srgb, var(--tool-accent) 14%, var(--tool-card-divider)); - border-radius: 9999px; - background: color-mix(in srgb, var(--tool-accent) 6%, transparent); - color: var(--color-text-secondary, #c7c7ce); - font-size: var(--font-text-sm-size, 11px); - line-height: 1.25; -} - -.workspace-instruction-summary-item.available { - border-style: dashed; - color: var(--color-text-tertiary, #a3a3aa); -} - -.workspace-instruction-summary-name { - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; +.workspace-agents-list { + flex-wrap: wrap; + overflow: visible; } .workspace-instruction-status { @@ -535,7 +470,7 @@ body { } .workspace-instruction-list { - display: none; + display: grid; min-width: 0; overflow: hidden; border: 1px solid var(--tool-card-divider); @@ -543,12 +478,34 @@ body { background: color-mix(in srgb, var(--tool-card-body-bg) 88%, transparent); } -.workspace-instructions-disclosure.expanded .workspace-instruction-summary { - display: none; +.workspace-instructions-content { + display: grid; + min-width: 0; + gap: 6px; } -.workspace-instructions-disclosure.expanded .workspace-instruction-list { - display: grid; +.workspace-instructions-view-all { + justify-self: start; + min-height: 24px; + padding: 3px 9px; + border: 1px solid color-mix(in srgb, var(--tool-accent) 28%, var(--tool-card-divider)); + border-radius: 9999px; + background: transparent; + color: var(--tool-accent); + cursor: pointer; + font: inherit; + font-size: var(--font-text-sm-size, 11px); + line-height: 1.25; + white-space: nowrap; +} + +.workspace-instructions-view-all:hover { + background: color-mix(in srgb, var(--tool-accent) 10%, transparent); +} + +.workspace-instructions-view-all:focus-visible { + outline: 2px solid color-mix(in srgb, var(--tool-accent) 72%, transparent); + outline-offset: 2px; } .workspace-instruction-item + .workspace-instruction-item { @@ -906,7 +863,7 @@ body { .workspace-row > .workspace-key, .workspace-row > .workspace-value, .workspace-row > .workspace-chip-list, - .workspace-row > .workspace-disclosure { + .workspace-row > .workspace-instructions-content { grid-column: 2; } } diff --git a/src/ui/workspace-app.tsx b/src/ui/workspace-app.tsx index b5a69de0..16a95b43 100644 --- a/src/ui/workspace-app.tsx +++ b/src/ui/workspace-app.tsx @@ -49,9 +49,7 @@ let errorMessage: string | null = null; let currentPayload: MountedPayload | null = null; let currentPayloadContainer: HTMLElement | null = null; let openWorkspaceInstructionKey: string | null = null; - -type WorkspaceDisclosureKey = "instructions" | "skills" | "agents"; -const expandedWorkspaceDisclosures = new Set(); +let showAvailableWorkspaceInstructions = false; const maybeAppRoot = document.querySelector("#app"); @@ -83,8 +81,8 @@ async function boot(): Promise { card = null; expanded = false; reviewFilesExpanded = false; - expandedWorkspaceDisclosures.clear(); openWorkspaceInstructionKey = null; + showAvailableWorkspaceInstructions = false; errorMessage = "No result card is available for this tool result."; render(); return; @@ -94,8 +92,8 @@ async function boot(): Promise { card = nextCard; expanded = isInitiallyExpandedCard(nextCard); reviewFilesExpanded = false; - expandedWorkspaceDisclosures.clear(); openWorkspaceInstructionKey = null; + showAvailableWorkspaceInstructions = false; errorMessage = null; render(); }; @@ -107,7 +105,7 @@ async function boot(): Promise { }; applyHostContext(); // Workspace details inherit host variables directly. Rebuilding their DOM on - // iframe resize would reset an in-progress disclosure interaction. + // iframe resize would reset an in-progress instruction preview interaction. if (card?.tool !== "open_workspace") renderPayloadIfNeeded(); }; @@ -563,6 +561,9 @@ function renderWorkspacePayload(container: HTMLElement, card: ToolResultCard): v interface WorkspaceChip { label: string; + logo?: string; + bareLogo?: boolean; + ariaLabel?: string; title?: string; tone?: "muted"; } @@ -575,18 +576,6 @@ interface WorkspaceInstruction { status: "loaded" | "available"; } -interface WorkspaceDisclosureRowOptions { - label: string; - icon: ToolIcon; - disclosureKey: WorkspaceDisclosureKey; - content: HTMLElement[]; - expandable?: boolean; - rowClassName?: string; - disclosureClassName?: string; - collapsedLabel?: string; - onCollapse?: () => void; -} - function appendWorkspaceInstructions( container: HTMLElement, loadedFiles: NonNullable, @@ -615,62 +604,37 @@ function appendWorkspaceInstructions( status: "available", }); } - const instructions: WorkspaceInstruction[] = [...loaded, ...available]; - - if (instructions.length === 0) return; - - const canPreview = loaded.some((file) => file.content !== undefined); - const canExpand = instructions.length > 1 || canPreview; - const summary = renderWorkspaceInstructionSummary(instructions); - const list = canExpand ? renderWorkspaceInstructionList(instructions) : undefined; - appendWorkspaceDisclosureRow(container, { - label: "Instructions", - icon: toolIcons.instructions, - disclosureKey: "instructions", - content: list ? [summary, list] : [summary], - expandable: canExpand, - rowClassName: "workspace-instructions-row", - disclosureClassName: "workspace-instructions-disclosure", - collapsedLabel: instructions.length === 1 ? "View" : undefined, - onCollapse: list - ? () => { - openWorkspaceInstructionKey = null; - syncWorkspaceInstructionPreviews(list); - } - : undefined, - }); -} + if (loaded.length === 0 && available.length === 0) return; -function renderWorkspaceInstructionSummary( - instructions: WorkspaceInstruction[], -): HTMLElement { - const summary = element("span", { className: "workspace-instruction-summary" }); - const basenameCounts = new Map(); - for (const instruction of instructions) { - const basename = workspacePathBasename(instruction.label); - basenameCounts.set(basename, (basenameCounts.get(basename) ?? 0) + 1); + const instructions = showAvailableWorkspaceInstructions + ? [...loaded, ...available] + : loaded; + const content = element("div", { className: "workspace-instructions-content" }); + if (instructions.length > 0) { + content.append(renderWorkspaceInstructionList(instructions)); } - for (const instruction of instructions) { - const basename = workspacePathBasename(instruction.label); - const item = element("span", { - className: `workspace-instruction-summary-item ${instruction.status}`, - title: instruction.path - ? `${instructionStatusLabel(instruction.status)}: ${instruction.path}` - : instructionStatusLabel(instruction.status), + if (available.length > 0 && !showAvailableWorkspaceInstructions) { + const viewAll = element("button", { + className: "workspace-instructions-view-all", + type: "button", + text: "View all", + ariaLabel: `View all ${available.length} available instruction files`, }); - item.append( - renderWorkspaceInstructionStatus(instruction.status), - element("span", { - className: "workspace-instruction-summary-name", - text: basenameCounts.get(basename) === 1 - ? basename - : workspaceCompactPath(instruction.label), - }), - ); - summary.append(item); + viewAll.addEventListener("click", () => { + showAvailableWorkspaceInstructions = true; + render(); + }); + content.append(viewAll); } - return summary; + + appendWorkspaceRow( + container, + "Instructions", + content, + toolIcons.instructions, + "workspace-instructions-row", + ); } function renderWorkspaceInstructionList( @@ -777,9 +741,9 @@ function workspacePathBasename(path: string): string { return parts.at(-1) ?? path; } -function workspaceCompactPath(path: string): string { - const parts = path.replaceAll("\\", "/").split("/").filter(Boolean); - return parts.slice(-2).join("/") || path; +function normalizeProviderName(name: string | undefined): string | undefined { + const normalized = name?.trim().toLowerCase(); + return normalized || undefined; } function appendWorkspaceTextRow( @@ -797,37 +761,6 @@ function appendWorkspaceTextRow( appendWorkspaceRow(container, label, content, icon); } -function appendWorkspaceTextListRow( - container: HTMLElement, - label: string, - values: string[], - icon: ToolIcon, - disclosureKey: WorkspaceDisclosureKey, - mono = false, -): void { - if (values.length === 1) { - appendWorkspaceTextRow(container, label, values[0], icon, mono); - return; - } - - const list = element("span", { - className: `workspace-value-list${mono ? " mono" : ""}`, - }); - for (const value of values) { - list.append(element("span", { - className: "workspace-value-item", - text: value, - title: value, - })); - } - appendWorkspaceDisclosureRow(container, { - label, - icon, - disclosureKey, - content: [list], - }); -} - function appendWorkspaceChipRow( container: HTMLElement, label: string, @@ -842,8 +775,11 @@ function appendWorkspaceRow( label: string, content: HTMLElement, icon: ToolIcon, + rowClassName?: string, ): void { - const row = element("div", { className: "workspace-row" }); + const row = element("div", { + className: ["workspace-row", rowClassName].filter(Boolean).join(" "), + }); row.append( renderWorkspaceRowIcon(icon), element("span", { className: "workspace-key", text: label }), @@ -858,91 +794,12 @@ function appendWorkspaceSkills( ): void { const skillChips = skills.map((skill) => ({ label: skill.name ?? skill.path ?? "Unnamed skill", - title: skill.path, + title: [skill.path, skill.description].filter(Boolean).join("\n\n") || undefined, })); - if (skillChips.length === 1) { - appendWorkspaceChipRow(container, "Skills", skillChips, toolIcons.skills); - return; - } const chipList = renderWorkspaceChips(skillChips); chipList.classList.add("workspace-skills-list"); - appendWorkspaceDisclosureRow(container, { - label: "Skills", - icon: toolIcons.skills, - disclosureKey: "skills", - content: [chipList], - disclosureClassName: "workspace-skills-disclosure", - }); -} - -function appendWorkspaceDisclosureRow( - container: HTMLElement, - options: WorkspaceDisclosureRowOptions, -): void { - const expandable = options.expandable ?? true; - const initiallyExpanded = expandable && expandedWorkspaceDisclosures.has(options.disclosureKey); - const row = element("div", { - className: [ - "workspace-row", - "workspace-row-disclosure", - options.rowClassName, - initiallyExpanded ? "expanded" : undefined, - ].filter(Boolean).join(" "), - }); - const disclosure = element("span", { - className: [ - "workspace-disclosure", - options.disclosureClassName, - initiallyExpanded ? "expanded" : undefined, - ].filter(Boolean).join(" "), - }); - disclosure.append(...options.content); - - if (expandable) { - const toggle = renderWorkspaceDisclosureToggle( - initiallyExpanded, - (nextExpanded) => { - disclosure.classList.toggle("expanded", nextExpanded); - row.classList.toggle("expanded", nextExpanded); - if (nextExpanded) { - expandedWorkspaceDisclosures.add(options.disclosureKey); - } else { - expandedWorkspaceDisclosures.delete(options.disclosureKey); - options.onCollapse?.(); - } - }, - options.collapsedLabel, - ); - disclosure.append(toggle); - } - - row.append( - renderWorkspaceRowIcon(options.icon), - element("span", { className: "workspace-key", text: options.label }), - disclosure, - ); - container.append(row); -} - -function renderWorkspaceDisclosureToggle( - expanded: boolean, - onToggle: (expanded: boolean) => void, - collapsedLabel = "View all", -): HTMLButtonElement { - const toggle = element("button", { - className: "workspace-disclosure-toggle", - type: "button", - text: expanded ? "Show less" : collapsedLabel, - ariaExpanded: String(expanded), - }); - toggle.addEventListener("click", () => { - const nextExpanded = toggle.getAttribute("aria-expanded") !== "true"; - toggle.setAttribute("aria-expanded", String(nextExpanded)); - toggle.textContent = nextExpanded ? "Show less" : collapsedLabel; - onToggle(nextExpanded); - }); - return toggle; + appendWorkspaceRow(container, "Skills", chipList, toolIcons.skills, "workspace-skills-row"); } function renderWorkspaceRowIcon(icon: ToolIcon): HTMLElement { @@ -957,11 +814,30 @@ function renderWorkspaceRowIcon(icon: ToolIcon): HTMLElement { function renderWorkspaceChips(chips: WorkspaceChip[]): HTMLElement { const list = element("span", { className: "workspace-chip-list" }); for (const chip of chips) { - list.append(element("span", { - className: `workspace-chip${chip.tone ? ` ${chip.tone}` : ""}`, - text: chip.label, + const bareLogo = Boolean(chip.bareLogo && chip.logo); + const item = element("span", { + className: [ + bareLogo ? "workspace-provider-logo" : "workspace-chip", + chip.tone, + ].filter(Boolean).join(" "), title: chip.title, - })); + }); + if (bareLogo) { + item.setAttribute("role", "img"); + item.setAttribute("aria-label", chip.ariaLabel ?? chip.label); + } + if (chip.logo) { + const logo = document.createElement("img"); + logo.className = bareLogo ? "workspace-provider-logo-image" : "workspace-chip-logo"; + logo.src = chip.logo; + logo.alt = ""; + logo.setAttribute("aria-hidden", "true"); + item.append(logo); + } + if (!bareLogo) { + item.append(element("span", { className: "workspace-chip-label", text: chip.label })); + } + list.append(item); } return list; } From 40236695b32aeb6574e0d6d3ada679aff146f86e Mon Sep 17 00:00:00 2001 From: Waishnav Date: Thu, 6 Aug 2026 16:23:58 +0530 Subject: [PATCH 4/8] feat(ui): show provider icons on agent profiles --- src/ui/workspace-app.tsx | 66 +++++++++++++++++++++++++++------------- 1 file changed, 45 insertions(+), 21 deletions(-) diff --git a/src/ui/workspace-app.tsx b/src/ui/workspace-app.tsx index 16a95b43..6b940358 100644 --- a/src/ui/workspace-app.tsx +++ b/src/ui/workspace-app.tsx @@ -20,7 +20,7 @@ import { type ToolName, type ToolResultCard, } from "./card-types.js"; -import { renderIcon, toolIcons, type ToolIcon } from "./icons.js"; +import { getProviderLogo, renderIcon, toolIcons, type ToolIcon } from "./icons.js"; import { getToolDisplay, getToolHeaderSummary, @@ -522,32 +522,57 @@ function renderWorkspacePayload(container: HTMLElement, card: ToolResultCard): v } const providers = card.agentProviders ?? []; - if (providers.length > 0) { - const providerChips: WorkspaceChip[] = []; - for (const provider of providers) { - const unavailable = provider.available === false; - providerChips.push({ - label: provider.name ?? "Unknown provider", - tone: unavailable ? "muted" : undefined, - title: unavailable ? provider.reason ?? "Provider unavailable" : undefined, - }); - } - appendWorkspaceChipRow(rows, "Providers", providerChips, toolIcons.providers); - } - - const agentChips = (card.agents ?? []).map((agent) => { + const agents = card.agents ?? []; + const representedProviderNames = new Set( + agents + .map((agent) => normalizeProviderName(agent.provider)) + .filter((name): name is string => Boolean(name)), + ); + const agentChips: WorkspaceChip[] = agents.map((agent) => { const name = agent.name ?? "Unnamed agent"; + const providerName = agent.provider?.trim(); const unavailable = agent.providerAvailable === false; - return { - label: agent.provider ? `${name} ยท ${agent.provider}` : name, - tone: unavailable ? "muted" as const : undefined, - title: unavailable + const title = [ + agent.description, + providerName ? `Provider: ${providerName}` : undefined, + agent.model ? `Model: ${agent.model}` : undefined, + agent.thinking ? `Thinking: ${agent.thinking}` : undefined, + unavailable ? agent.providerUnavailableReason ?? "Provider unavailable" : undefined, + ].filter((value): value is string => Boolean(value)).join("\n"); + return { + label: name, + logo: providerName ? getProviderLogo(providerName) : undefined, + tone: unavailable ? "muted" as const : undefined, + title: title || undefined, }; }); + const providerOnlyChips: WorkspaceChip[] = providers + .filter((provider) => { + const name = normalizeProviderName(provider.name); + return name === undefined || !representedProviderNames.has(name); + }) + .map((provider) => { + const name = provider.name?.trim() || "Unknown provider"; + const unavailable = provider.available === false; + const logo = getProviderLogo(name); + return { + label: name, + logo, + bareLogo: Boolean(logo), + ariaLabel: name, + tone: unavailable ? "muted" as const : undefined, + title: unavailable ? provider.reason ?? "Provider unavailable" : name, + }; + }); + if (agentChips.length > 0) { - appendWorkspaceChipRow(rows, "Agents", agentChips, toolIcons.agents); + const chipList = renderWorkspaceChips([...agentChips, ...providerOnlyChips]); + chipList.classList.add("workspace-agents-list"); + appendWorkspaceRow(rows, "Agents", chipList, toolIcons.agents, "workspace-agents-row"); + } else if (providerOnlyChips.length > 0) { + appendWorkspaceChipRow(rows, "Providers", providerOnlyChips, toolIcons.providers); } if (rows.childElementCount > 0) details.append(rows); @@ -884,4 +909,3 @@ function element( } return node; } - From 3d6d276607e949bd91eb480e1214cbc49da406be Mon Sep 17 00:00:00 2001 From: Waishnav Date: Thu, 6 Aug 2026 16:31:59 +0530 Subject: [PATCH 5/8] fix(ui): attach instruction visibility toggle --- src/ui/workspace-app.css | 28 ++++++++++++++++------------ src/ui/workspace-app.tsx | 29 +++++++++++++++++------------ 2 files changed, 33 insertions(+), 24 deletions(-) diff --git a/src/ui/workspace-app.css b/src/ui/workspace-app.css index 2211c07c..f1a2d2e5 100644 --- a/src/ui/workspace-app.css +++ b/src/ui/workspace-app.css @@ -479,31 +479,35 @@ body { } .workspace-instructions-content { - display: grid; + display: block; min-width: 0; - gap: 6px; } -.workspace-instructions-view-all { - justify-self: start; - min-height: 24px; - padding: 3px 9px; - border: 1px solid color-mix(in srgb, var(--tool-accent) 28%, var(--tool-card-divider)); - border-radius: 9999px; - background: transparent; +.workspace-instructions-toggle { + display: flex; + width: 100%; + min-height: 32px; + align-items: center; + justify-content: center; + padding: 6px 10px; + border: 0; + border-top: 1px solid var(--tool-card-divider); + border-radius: 0 0 9px 9px; + background: color-mix(in srgb, var(--tool-card-body-bg) 72%, transparent); color: var(--tool-accent); cursor: pointer; font: inherit; font-size: var(--font-text-sm-size, 11px); + font-weight: 550; line-height: 1.25; white-space: nowrap; } -.workspace-instructions-view-all:hover { - background: color-mix(in srgb, var(--tool-accent) 10%, transparent); +.workspace-instructions-toggle:hover { + background: var(--tool-card-hover-bg); } -.workspace-instructions-view-all:focus-visible { +.workspace-instructions-toggle:focus-visible { outline: 2px solid color-mix(in srgb, var(--tool-accent) 72%, transparent); outline-offset: 2px; } diff --git a/src/ui/workspace-app.tsx b/src/ui/workspace-app.tsx index 6b940358..e96afff5 100644 --- a/src/ui/workspace-app.tsx +++ b/src/ui/workspace-app.tsx @@ -634,25 +634,30 @@ function appendWorkspaceInstructions( const instructions = showAvailableWorkspaceInstructions ? [...loaded, ...available] : loaded; - const content = element("div", { className: "workspace-instructions-content" }); - if (instructions.length > 0) { - content.append(renderWorkspaceInstructionList(instructions)); - } + const list = renderWorkspaceInstructionList(instructions); - if (available.length > 0 && !showAvailableWorkspaceInstructions) { - const viewAll = element("button", { - className: "workspace-instructions-view-all", + if (available.length > 0) { + const showAll = showAvailableWorkspaceInstructions; + const toggle = element("button", { + className: "workspace-instructions-toggle", type: "button", - text: "View all", - ariaLabel: `View all ${available.length} available instruction files`, + text: showAll ? "Show less" : "View all", + ariaLabel: showAll + ? "Show only loaded instruction files" + : `View all ${available.length} available instruction files`, + ariaExpanded: String(showAll), }); - viewAll.addEventListener("click", () => { - showAvailableWorkspaceInstructions = true; + toggle.addEventListener("click", () => { + showAvailableWorkspaceInstructions = !showAvailableWorkspaceInstructions; + if (!showAvailableWorkspaceInstructions) openWorkspaceInstructionKey = null; render(); }); - content.append(viewAll); + list.append(toggle); } + const content = element("div", { className: "workspace-instructions-content" }); + content.append(list); + appendWorkspaceRow( container, "Instructions", From e04a637cc5595e26fb69b9ec2df80fec1a13144f Mon Sep 17 00:00:00 2001 From: Waishnav Date: Thu, 6 Aug 2026 16:37:53 +0530 Subject: [PATCH 6/8] fix(ui): show raw providers after profiles --- src/ui/workspace-app.css | 27 ++++++++++++++++++ src/ui/workspace-app.tsx | 61 ++++++++++++++++++---------------------- 2 files changed, 55 insertions(+), 33 deletions(-) diff --git a/src/ui/workspace-app.css b/src/ui/workspace-app.css index f1a2d2e5..db42825a 100644 --- a/src/ui/workspace-app.css +++ b/src/ui/workspace-app.css @@ -402,6 +402,33 @@ body { white-space: nowrap; } +.workspace-agent-profile { + padding: 3px 2px 4px; + border: 0; + border-bottom: 1px solid color-mix(in srgb, var(--tool-accent) 34%, var(--tool-card-divider)); + border-radius: 0; + background: transparent; +} + +.workspace-agent-profile-logo { + display: block; + width: 14px; + height: 14px; + flex: 0 0 auto; + object-fit: contain; +} + +.workspace-agent-profile:hover { + border-bottom-color: var(--tool-accent); + color: var(--color-text-primary, #f5f5f6); +} + +.workspace-agent-profile.muted { + border-bottom-style: dashed; + color: var(--color-text-tertiary, #a3a3aa); + opacity: 0.72; +} + .workspace-provider-logo { display: inline-grid; width: 20px; diff --git a/src/ui/workspace-app.tsx b/src/ui/workspace-app.tsx index e96afff5..eab9c501 100644 --- a/src/ui/workspace-app.tsx +++ b/src/ui/workspace-app.tsx @@ -523,11 +523,6 @@ function renderWorkspacePayload(container: HTMLElement, card: ToolResultCard): v const providers = card.agentProviders ?? []; const agents = card.agents ?? []; - const representedProviderNames = new Set( - agents - .map((agent) => normalizeProviderName(agent.provider)) - .filter((name): name is string => Boolean(name)), - ); const agentChips: WorkspaceChip[] = agents.map((agent) => { const name = agent.name ?? "Unnamed agent"; const providerName = agent.provider?.trim(); @@ -544,35 +539,31 @@ function renderWorkspacePayload(container: HTMLElement, card: ToolResultCard): v return { label: name, logo: providerName ? getProviderLogo(providerName) : undefined, + profile: true, tone: unavailable ? "muted" as const : undefined, title: title || undefined, }; }); - const providerOnlyChips: WorkspaceChip[] = providers - .filter((provider) => { - const name = normalizeProviderName(provider.name); - return name === undefined || !representedProviderNames.has(name); - }) - .map((provider) => { - const name = provider.name?.trim() || "Unknown provider"; - const unavailable = provider.available === false; - const logo = getProviderLogo(name); - return { - label: name, - logo, - bareLogo: Boolean(logo), - ariaLabel: name, - tone: unavailable ? "muted" as const : undefined, - title: unavailable ? provider.reason ?? "Provider unavailable" : name, - }; - }); + const providerChips: WorkspaceChip[] = providers.map((provider) => { + const name = provider.name?.trim() || "Unknown provider"; + const unavailable = provider.available === false; + const logo = getProviderLogo(name); + return { + label: name, + logo, + bareLogo: Boolean(logo), + ariaLabel: name, + tone: unavailable ? "muted" as const : undefined, + title: unavailable ? provider.reason ?? "Provider unavailable" : name, + }; + }); if (agentChips.length > 0) { - const chipList = renderWorkspaceChips([...agentChips, ...providerOnlyChips]); + const chipList = renderWorkspaceChips([...agentChips, ...providerChips]); chipList.classList.add("workspace-agents-list"); appendWorkspaceRow(rows, "Agents", chipList, toolIcons.agents, "workspace-agents-row"); - } else if (providerOnlyChips.length > 0) { - appendWorkspaceChipRow(rows, "Providers", providerOnlyChips, toolIcons.providers); + } else if (providerChips.length > 0) { + appendWorkspaceChipRow(rows, "Providers", providerChips, toolIcons.providers); } if (rows.childElementCount > 0) details.append(rows); @@ -587,6 +578,7 @@ function renderWorkspacePayload(container: HTMLElement, card: ToolResultCard): v interface WorkspaceChip { label: string; logo?: string; + profile?: boolean; bareLogo?: boolean; ariaLabel?: string; title?: string; @@ -771,11 +763,6 @@ function workspacePathBasename(path: string): string { return parts.at(-1) ?? path; } -function normalizeProviderName(name: string | undefined): string | undefined { - const normalized = name?.trim().toLowerCase(); - return normalized || undefined; -} - function appendWorkspaceTextRow( container: HTMLElement, label: string, @@ -847,7 +834,11 @@ function renderWorkspaceChips(chips: WorkspaceChip[]): HTMLElement { const bareLogo = Boolean(chip.bareLogo && chip.logo); const item = element("span", { className: [ - bareLogo ? "workspace-provider-logo" : "workspace-chip", + bareLogo + ? "workspace-provider-logo" + : chip.profile + ? "workspace-agent-profile" + : "workspace-chip", chip.tone, ].filter(Boolean).join(" "), title: chip.title, @@ -858,7 +849,11 @@ function renderWorkspaceChips(chips: WorkspaceChip[]): HTMLElement { } if (chip.logo) { const logo = document.createElement("img"); - logo.className = bareLogo ? "workspace-provider-logo-image" : "workspace-chip-logo"; + logo.className = bareLogo + ? "workspace-provider-logo-image" + : chip.profile + ? "workspace-agent-profile-logo" + : "workspace-chip-logo"; logo.src = chip.logo; logo.alt = ""; logo.setAttribute("aria-hidden", "true"); From f9f450fa534d9ffeb25294e0436ef886fbc9588b Mon Sep 17 00:00:00 2001 From: Waishnav Date: Thu, 6 Aug 2026 16:43:20 +0530 Subject: [PATCH 7/8] fix(ui): align workspace row labels --- src/ui/workspace-app.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ui/workspace-app.css b/src/ui/workspace-app.css index db42825a..697479be 100644 --- a/src/ui/workspace-app.css +++ b/src/ui/workspace-app.css @@ -313,6 +313,9 @@ body { } .workspace-key { + display: flex; + min-height: 22px; + align-items: center; color: var(--color-text-tertiary, #a3a3aa); font-size: var(--font-text-sm-size, 12px); font-weight: 500; From b477db634f08057e4a1244be533c6b039247e970 Mon Sep 17 00:00:00 2001 From: Waishnav Date: Thu, 6 Aug 2026 16:48:54 +0530 Subject: [PATCH 8/8] fix(ui): keep agent profiles compact --- src/ui/workspace-app.css | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/ui/workspace-app.css b/src/ui/workspace-app.css index 697479be..bee72228 100644 --- a/src/ui/workspace-app.css +++ b/src/ui/workspace-app.css @@ -406,11 +406,21 @@ body { } .workspace-agent-profile { + display: inline-flex; + max-width: 100%; + min-height: 24px; + align-items: center; + gap: 5px; + overflow: hidden; padding: 3px 2px 4px; border: 0; border-bottom: 1px solid color-mix(in srgb, var(--tool-accent) 34%, var(--tool-card-divider)); border-radius: 0; background: transparent; + color: var(--color-text-secondary, #c7c7ce); + font-size: 11px; + line-height: 1.25; + white-space: nowrap; } .workspace-agent-profile-logo {