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/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); 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, diff --git a/src/ui/workspace-app.css b/src/ui/workspace-app.css index 8a6ef00b..bee72228 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; @@ -374,6 +377,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 +390,98 @@ 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; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } -.workspace-disclosure-toggle { +.workspace-agent-profile { + display: inline-flex; + max-width: 100%; min-height: 24px; - padding: 3px 9px; - border: 1px solid color-mix(in srgb, var(--tool-accent) 28%, var(--tool-card-divider)); - border-radius: 9999px; + 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(--tool-accent); - cursor: pointer; - font: inherit; - font-size: var(--font-text-sm-size, 11px); + color: var(--color-text-secondary, #c7c7ce); + font-size: 11px; line-height: 1.25; 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-agent-profile-logo { + display: block; + width: 14px; + height: 14px; + flex: 0 0 auto; + object-fit: contain; } -.workspace-disclosure.expanded .workspace-disclosure-toggle { - justify-self: start; +.workspace-agent-profile:hover { + border-bottom-color: var(--tool-accent); + color: var(--color-text-primary, #f5f5f6); } -.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-agent-profile.muted { + border-bottom-style: dashed; + color: var(--color-text-tertiary, #a3a3aa); + opacity: 0.72; } -.workspace-value-list.mono { - font-family: var(--font-mono, ui-monospace, SFMono-Regular, monospace); +.workspace-provider-logo { + display: inline-grid; + width: 20px; + height: 24px; + flex: 0 0 auto; + place-items: center; + cursor: help; } -.workspace-value-item { - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; +.workspace-provider-logo-image { + display: block; + width: 16px; + height: 16px; + object-fit: contain; } -.workspace-disclosure.expanded .workspace-value-list { - max-height: none; - gap: 5px; - overflow: visible; +.workspace-provider-logo.muted { + opacity: 0.62; } -.workspace-instruction-summary { - display: flex; - min-width: 0; - height: 24px; - align-items: center; - gap: 6px; - overflow: hidden; +.workspace-chip.muted { + border-style: dashed; + color: var(--color-text-tertiary, #a3a3aa); + opacity: 0.72; } -.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-skills-row, +.workspace-instructions-row, +.workspace-agents-row { + align-items: start; } -.workspace-instruction-summary-item.available { - border-style: dashed; - color: var(--color-text-tertiary, #a3a3aa); +.workspace-skills-list { + flex-wrap: wrap; + overflow: visible; } -.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 +510,7 @@ body { } .workspace-instruction-list { - display: none; + display: grid; min-width: 0; overflow: hidden; border: 1px solid var(--tool-card-divider); @@ -543,12 +518,38 @@ 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: block; + min-width: 0; } -.workspace-instructions-disclosure.expanded .workspace-instruction-list { - display: grid; +.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-toggle:hover { + background: var(--tool-card-hover-bg); +} + +.workspace-instructions-toggle: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 +907,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..eab9c501 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, @@ -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(); }; @@ -524,32 +522,48 @@ 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 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, + profile: true, + tone: unavailable ? "muted" as const : undefined, + title: title || undefined, }; }); + 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) { - appendWorkspaceChipRow(rows, "Agents", agentChips, toolIcons.agents); + const chipList = renderWorkspaceChips([...agentChips, ...providerChips]); + chipList.classList.add("workspace-agents-list"); + appendWorkspaceRow(rows, "Agents", chipList, toolIcons.agents, "workspace-agents-row"); + } else if (providerChips.length > 0) { + appendWorkspaceChipRow(rows, "Providers", providerChips, toolIcons.providers); } if (rows.childElementCount > 0) details.append(rows); @@ -563,6 +577,10 @@ function renderWorkspacePayload(container: HTMLElement, card: ToolResultCard): v interface WorkspaceChip { label: string; + logo?: string; + profile?: boolean; + bareLogo?: boolean; + ariaLabel?: string; title?: string; tone?: "muted"; } @@ -575,18 +593,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 +621,42 @@ 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, - }); -} - -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); - } - - 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 (loaded.length === 0 && available.length === 0) return; + + const instructions = showAvailableWorkspaceInstructions + ? [...loaded, ...available] + : loaded; + const list = renderWorkspaceInstructionList(instructions); + + if (available.length > 0) { + const showAll = showAvailableWorkspaceInstructions; + const toggle = element("button", { + className: "workspace-instructions-toggle", + type: "button", + text: showAll ? "Show less" : "View all", + ariaLabel: showAll + ? "Show only loaded instruction files" + : `View all ${available.length} available instruction files`, + ariaExpanded: String(showAll), }); - item.append( - renderWorkspaceInstructionStatus(instruction.status), - element("span", { - className: "workspace-instruction-summary-name", - text: basenameCounts.get(basename) === 1 - ? basename - : workspaceCompactPath(instruction.label), - }), - ); - summary.append(item); + toggle.addEventListener("click", () => { + showAvailableWorkspaceInstructions = !showAvailableWorkspaceInstructions; + if (!showAvailableWorkspaceInstructions) openWorkspaceInstructionKey = null; + render(); + }); + list.append(toggle); } - return summary; + + const content = element("div", { className: "workspace-instructions-content" }); + content.append(list); + + appendWorkspaceRow( + container, + "Instructions", + content, + toolIcons.instructions, + "workspace-instructions-row", + ); } function renderWorkspaceInstructionList( @@ -777,11 +763,6 @@ 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 appendWorkspaceTextRow( container: HTMLElement, label: string, @@ -797,37 +778,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 +792,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 +811,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 +831,38 @@ 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" + : chip.profile + ? "workspace-agent-profile" + : "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" + : chip.profile + ? "workspace-agent-profile-logo" + : "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; } @@ -1008,4 +909,3 @@ function element( } return node; } -