diff --git a/ui/src/pages/chat/components/chat-tool-cards.test.ts b/ui/src/pages/chat/components/chat-tool-cards.test.ts index 5a3b2400959d..103459550748 100644 --- a/ui/src/pages/chat/components/chat-tool-cards.test.ts +++ b/ui/src/pages/chat/components/chat-tool-cards.test.ts @@ -6,13 +6,7 @@ import { describe, expect, it, vi } from "vitest"; vi.mock("../markdown.ts", async (importOriginal) => await importOriginal()); vi.mock("../tool-display.ts", () => ({ formatToolDetail: (display: { detail?: string }) => display.detail, - resolveToolDisplay: ({ - name, - args, - }: { - name: string; - args?: unknown; - }) => { + resolveToolDisplay: ({ name, args }: { name: string; args?: unknown }) => { const labels: Record = { sessions_spawn: "Sub-agent", skill_workshop: "Skill Workshop", @@ -39,7 +33,7 @@ import { formatCollapsedToolSummaryText, isToolErrorOutput, } from "../../../lib/chat/tool-cards.ts"; -import { renderToolCard, renderToolCardSidebar } from "./chat-tool-cards.ts"; +import { renderToolCard } from "./chat-tool-cards.ts"; function requireFirstMockArg( mock: ReturnType, @@ -106,7 +100,6 @@ describe("tool-cards", () => { expect(blocks[0]?.querySelector("code")?.textContent).toBe( '{\n "mode": "session",\n "thread": true\n}', ); - expect(container.querySelector(".chat-tool-card__block-empty")).toBeNull(); }); it("labels collapsed tool calls with the display summary", () => { @@ -461,7 +454,7 @@ describe("tool-cards", () => { const summaryButton = container.querySelector("button.chat-tool-msg-summary"); expect(summaryButton?.classList.contains("chat-tool-msg-summary--error")).toBe(true); expect(container.querySelector(".chat-tool-msg-summary__error-badge")).not.toBeNull(); - const expandedCard = container.querySelector(".chat-tool-card--expanded"); + const expandedCard = container.querySelector(".chat-tool-card"); expect(expandedCard?.classList.contains("chat-tool-card--error")).toBe(true); expect(container.querySelector(".chat-tool-card__status-badge")).not.toBeNull(); }); @@ -551,71 +544,6 @@ describe("tool-cards", () => { expect(container.querySelector(".chat-tool-msg-summary__error-badge")).toBeNull(); }); - it("does not render View with a checkmark for sidebar cards whose output is an error JSON", () => { - const container = document.createElement("div"); - render( - renderToolCardSidebar( - { - id: "msg:err:sidebar", - name: "web_search", - outputText: JSON.stringify({ - error: "missing_brave_api_key", - message: "BRAVE_API_KEY is not configured", - }), - }, - vi.fn(), - ), - container, - ); - - const card = container.querySelector(".chat-tool-card"); - const action = container.querySelector(".chat-tool-card__action"); - expect(card?.classList.contains("chat-tool-card--error")).toBe(true); - expect(action?.classList.contains("chat-tool-card__action--error")).toBe(true); - expect(action?.textContent).toContain("View error"); - expect(action?.textContent).not.toContain("✓"); - }); - - it("marks Tool not found sidebar output as an error instead of View with a checkmark", () => { - const container = document.createElement("div"); - render( - renderToolCardSidebar( - { - id: "msg:err:sidebar-tool-not-found", - name: "Unknown", - outputText: "Tool not found", - }, - vi.fn(), - ), - container, - ); - - const action = container.querySelector(".chat-tool-card__action"); - expect(container.querySelector(".chat-tool-card--error")).not.toBeNull(); - expect(action?.textContent).toContain("View error"); - expect(action?.textContent).not.toContain("✓"); - }); - - it("marks status-only sidebar output as an error instead of View with a checkmark", () => { - const container = document.createElement("div"); - render( - renderToolCardSidebar( - { - id: "msg:err:sidebar-status", - name: "sessions_wait", - outputText: JSON.stringify({ status: "timeout" }), - }, - vi.fn(), - ), - container, - ); - - const action = container.querySelector(".chat-tool-card__action"); - expect(container.querySelector(".chat-tool-card--error")).not.toBeNull(); - expect(action?.textContent).toContain("View error"); - expect(action?.textContent).not.toContain("✓"); - }); - it("keeps Tool output labelling for successful results", () => { const container = document.createElement("div"); render( diff --git a/ui/src/pages/chat/components/chat-tool-cards.ts b/ui/src/pages/chat/components/chat-tool-cards.ts index f4a6628821e9..6eb0ead53a91 100644 --- a/ui/src/pages/chat/components/chat-tool-cards.ts +++ b/ui/src/pages/chat/components/chat-tool-cards.ts @@ -21,8 +21,7 @@ import { } from "../../../lib/chat/tool-display.ts"; import type { SidebarContent } from "./chat-sidebar.ts"; -const TOOL_PREVIEW_MAX_LINES = 2; -const TOOL_PREVIEW_MAX_CHARS = 100; +type FullMessageRequest = NonNullable; function formatToolOutputForSidebar(text: string): string { if (isMarkdownBlockArtText(text)) { @@ -40,18 +39,6 @@ function formatToolOutputForSidebar(text: string): string { return text; } -function getTruncatedPreview(text: string): string { - const allLines = text.split("\n"); - const lines = allLines.slice(0, TOOL_PREVIEW_MAX_LINES); - const preview = lines.join("\n"); - if (preview.length > TOOL_PREVIEW_MAX_CHARS) { - return `${preview.slice(0, TOOL_PREVIEW_MAX_CHARS)}…`; - } - return lines.length < allLines.length ? `${preview}…` : preview; -} - -type FullMessageRequest = NonNullable; - function renderToolIcon(name: string) { return icons[name as IconName] ?? icons.puzzle; } @@ -243,39 +230,22 @@ export function renderRawOutputToggle(text: string) { ${icons.chevronDown} `; } -function renderToolDataBlock(params: { - label: string; - text: string; - expanded: boolean; - empty?: boolean; -}) { - const { label, text, expanded, empty } = params; +function renderToolDataBlock(params: { label: string; text: string }) { + const { label, text } = params; const codeClass = isMarkdownBlockArtText(text) ? "markdown-block-art" : ""; return html` -
+
${icons.zap} ${label}
- ${empty - ? html`
${text}
` - : expanded - ? html`
${text}
` - : html`
- ${getTruncatedPreview(text)} -
`} +
${text}
`; } @@ -437,7 +407,7 @@ export function renderExpandedToolCardContent( : nothing; return html` -
+
${renderToolIcon(display.icon)} @@ -470,7 +440,6 @@ export function renderExpandedToolCardContent( ? renderToolDataBlock({ label: "Tool input", text: card.inputText!, - expanded: true, }) : nothing} ${hasOutput @@ -479,110 +448,8 @@ export function renderExpandedToolCardContent( : renderToolDataBlock({ label: isError ? "Tool error" : "Tool output", text: card.outputText!, - expanded: true, }) : nothing}
`; } - -export function renderToolCardSidebar( - card: ToolCard, - onOpenSidebar?: (content: SidebarContent) => void, - canvasPluginSurfaceUrl?: string | null, - embedSandboxMode: EmbedSandboxMode = "scripts", - options?: { sessionKey?: string; agentId?: string }, -) { - const display = resolveToolDisplay({ name: card.name, args: card.args }); - const detail = formatToolDetail(display); - const preview = card.preview; - const hasText = Boolean(card.outputText?.trim()); - const hasPreview = Boolean(preview); - const isError = isToolCardError(card); - const fullMessageRequest = buildToolSidebarFullMessageRequest(card, options?.sessionKey); - const sidebarContent = - preview?.kind === "canvas" - ? buildPreviewSidebarContent(preview, card.outputText, { fullMessageRequest }) - : buildSidebarContent(buildToolCardSidebarContent(card), { - fullMessageRequest, - rawText: card.outputText ?? null, - }); - const actionContent = - sidebarContent ?? - buildSidebarContent(buildToolCardSidebarContent(card), { - fullMessageRequest, - rawText: card.outputText ?? null, - }); - const canClick = Boolean(onOpenSidebar); - const handleClick = canClick ? () => onOpenSidebar?.(actionContent) : undefined; - const isShort = hasText && !hasPreview && (card.outputText?.length ?? 0) <= 240; - const showCollapsed = hasText && !hasPreview && !isShort; - const showInline = hasText && !hasPreview && isShort; - const isEmpty = !hasText && !hasPreview; - const statusIcon = isError ? icons.x : icons.check; - - return html` -
{ - if (e.key !== "Enter" && e.key !== " ") { - return; - } - e.preventDefault(); - handleClick?.(); - } - : nothing} - > -
-
- ${renderToolIcon(display.icon)} - ${display.label} -
- ${canClick - ? html`${isError ? "View error" : hasText || hasPreview ? "View" : ""} ${statusIcon}` - : nothing} - ${isEmpty && !canClick - ? html`${statusIcon}` - : nothing} -
- ${detail ? html`
${detail}
` : nothing} - ${isEmpty - ? html`
- ${isError ? "Failed" : "Completed"} -
` - : nothing} - ${preview - ? html`${renderToolPreview(preview, "chat_tool", { - onOpenSidebar, - rawText: card.outputText, - canvasPluginSurfaceUrl, - embedSandboxMode, - })}` - : nothing} - ${showCollapsed - ? html`
- ${getTruncatedPreview(card.outputText!)} -
` - : nothing} - ${showInline - ? html`
${card.outputText}
` - : nothing} -
- `; -} diff --git a/ui/src/styles/chat/grouped.css b/ui/src/styles/chat/grouped.css index 832e7fd0ad23..f566f8a0d7b4 100644 --- a/ui/src/styles/chat/grouped.css +++ b/ui/src/styles/chat/grouped.css @@ -225,15 +225,15 @@ img.chat-avatar { word-wrap: break-word; } -/* Width-only shell: bubble chrome (border/background/padding) comes from - .chat-bubble above. */ +/* Tool messages render flat rows, not bubbles: the shell only constrains + width; all bubble chrome (border/background/padding) is reset. */ .chat-bubble--tool-shell { align-self: stretch; width: min(100%, 760px); -} - -.chat-bubble--tool-shell:hover { + padding: 0; + border: 0; background: transparent; + box-shadow: none; } .chat-bubble.has-copy { @@ -381,10 +381,6 @@ img.chat-avatar { box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent) 20%, transparent); } -.chat-bubble--tool-shell:hover { - background: transparent; -} - /* User bubbles have different styling */ .chat-group.user .chat-bubble { background: var(--accent-subtle); @@ -405,6 +401,8 @@ img.chat-avatar { } :root[data-theme-mode="light"] .chat-bubble--tool-shell { + border-color: transparent; + background: transparent; box-shadow: none; } diff --git a/ui/src/styles/chat/tool-cards.css b/ui/src/styles/chat/tool-cards.css index 34bcf18048c9..c66eeccf4e02 100644 --- a/ui/src/styles/chat/tool-cards.css +++ b/ui/src/styles/chat/tool-cards.css @@ -1,79 +1,195 @@ -/* Tool Card Styles */ +/* Tool call rendering: flat rows (summary) + inline detail blocks (expanded). + Tool rows deliberately carry no card chrome (border/background/shadow) so + dense agent activity stays scannable; detail surfaces appear only on expand. */ + +/* ── Collapsed tool row ── */ +.chat-tool-msg-collapse { + width: 100%; + min-width: 0; + max-width: 100%; + margin-top: 2px; +} + +.chat-bubble--tool-shell > .chat-tool-msg-collapse { + margin-top: 0; +} + +.chat-tool-msg-summary { + display: flex; + align-items: center; + gap: 7px; + min-width: 0; + width: 100%; + box-sizing: border-box; + padding: 4px 8px; + cursor: pointer; + font-size: var(--control-ui-text-sm); + line-height: 1.5; + color: var(--muted); + user-select: none; + list-style: none; + border: 0; + border-radius: var(--radius-sm); + background: transparent; + text-align: left; + appearance: none; + -webkit-appearance: none; + font-family: inherit; + transition: + color 150ms ease, + background 150ms ease; +} + +.chat-tool-msg-summary:hover, +.chat-tool-msg-summary:focus-visible { + background: color-mix(in srgb, var(--bg-hover) 60%, transparent); + color: var(--text); +} + +.chat-tool-msg-summary::-webkit-details-marker { + display: none; +} + +.chat-tool-msg-summary::before { + content: "▸"; + display: inline-flex; + align-items: center; + justify-content: center; + width: 9px; + font-size: 12px; + line-height: 1; + color: var(--muted); + opacity: 0.7; + flex-shrink: 0; + transition: transform 150ms ease; +} + +.chat-tool-msg-summary[aria-expanded="true"]::before { + transform: rotate(90deg); +} + +.chat-tool-msg-summary__icon { + display: inline-flex; + align-items: center; + justify-content: center; + width: 15px; + height: 15px; + color: var(--muted); + flex-shrink: 0; +} + +.chat-tool-msg-summary__icon svg { + width: 15px; + height: 15px; + stroke: currentColor; + fill: none; + stroke-width: 1.5px; + stroke-linecap: round; + stroke-linejoin: round; +} + +/* Collapsed rows clamp to one line; the full text is available on expand. */ +.chat-tool-msg-summary__label { + flex: 0 1 auto; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-weight: 500; + color: var(--text); +} + +.chat-tool-msg-summary__names, +.chat-tool-msg-summary__preview { + flex: 1 1 0; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-family: var(--mono); + font-size: calc(var(--control-ui-text-sm) - 1px); + color: color-mix(in srgb, var(--muted) 82%, var(--text) 18%); +} + +.chat-tool-msg-summary--error .chat-tool-msg-summary__icon, +.chat-tool-msg-summary--error .chat-tool-msg-summary__label { + color: var(--destructive, var(--danger, #c0392b)); +} + +.chat-tool-msg-summary__error-badge { + display: inline-flex; + align-items: center; + gap: 3px; + box-sizing: border-box; + padding: 1px 6px; + margin-left: auto; + border-radius: 999px; + background: color-mix(in srgb, var(--destructive, var(--danger, #c0392b)) 14%, transparent); + color: var(--destructive, var(--danger, #c0392b)); + font-size: 11px; + font-weight: 600; + letter-spacing: 0.04em; + text-transform: uppercase; + line-height: 1.4; + flex-shrink: 0; +} + +.chat-tool-msg-summary__error-badge svg { + width: 10px; + height: 10px; + stroke: currentColor; + fill: none; + stroke-width: 2px; + stroke-linecap: round; + stroke-linejoin: round; +} + +/* ── Expanded tool row body ── */ +.chat-tool-msg-body { + box-sizing: border-box; + min-width: 0; + max-width: 100%; + padding: 2px 8px 8px 24px; +} + +.chat-bubble--tool-shell .chat-tool-msg-body > .chat-text { + max-height: min(46vh, 540px); + margin: 0; + overflow: auto; + font-family: var(--mono); + font-size: 12px; + line-height: 1.55; + white-space: pre-wrap; + overflow-wrap: anywhere; +} + +/* ── Expanded tool detail ── */ +.chat-tools-inline { + display: grid; + gap: 2px; +} + .chat-tool-card { box-sizing: border-box; min-width: 0; max-width: 100%; - border: 1px solid var(--border); - border-radius: var(--radius-md); - padding: 10px 12px; - margin-top: 8px; - background: var(--secondary); - display: grid; - gap: 4px; - box-shadow: inset 0 1px 0 color-mix(in srgb, var(--bg) 75%, transparent); - transition: - border-color var(--duration-fast) ease-out, - background var(--duration-fast) ease-out, - box-shadow var(--duration-fast) ease-out; - max-height: 120px; - overflow: hidden; } -:root[data-theme-mode="light"] .chat-tool-card { - background: var(--bg-muted); -} - -.chat-tool-card--expanded { - max-height: none; - overflow: hidden; -} - -.chat-tool-card:hover { - border-color: color-mix(in srgb, var(--border-strong) 80%, transparent); - background: color-mix(in srgb, var(--card) 70%, var(--bg-hover) 30%); - box-shadow: inset 0 1px 0 color-mix(in srgb, var(--bg) 85%, transparent); -} - -/* First tool card in a group - no top margin */ -.chat-tool-card:first-child { - margin-top: 0; -} - -.chat-tool-card--clickable { - cursor: pointer; -} - -.chat-tool-card--clickable:focus-visible { - outline: 2px solid var(--accent); - outline-offset: 2px; -} - -/* Header with title and chevron */ .chat-tool-card__header { display: flex; justify-content: space-between; - align-items: flex-start; + align-items: center; gap: 10px; min-width: 0; } -.chat-tool-card__actions { - display: inline-flex; - align-items: center; - gap: 6px; - flex-wrap: wrap; - justify-content: flex-end; -} - .chat-tool-card__title { display: inline-flex; align-items: center; gap: 6px; flex: 1 1 auto; - font-family: var(--mono); - font-weight: 500; - font-size: 12px; - color: var(--text); + font-weight: 600; + font-size: var(--control-ui-text-sm); line-height: 1.2; min-width: 0; max-width: 100%; @@ -86,6 +202,7 @@ justify-content: center; width: 16px; height: 16px; + color: var(--muted); flex-shrink: 0; } @@ -99,33 +216,35 @@ stroke-linejoin: round; } -/* "View >" action link */ -.chat-tool-card__action, +.chat-tool-card__actions { + display: inline-flex; + align-items: center; + gap: 6px; + justify-content: flex-end; +} + .chat-tool-card__action-btn { display: inline-flex; align-items: center; justify-content: center; - gap: 4px; - flex-shrink: 0; - font-size: 12px; /* was 11px */ - white-space: nowrap; - color: var(--muted); - opacity: 1; - transition: - color 150ms ease-out, - background 150ms ease-out, - border-color 150ms ease-out; -} - -.chat-tool-card__action-btn { - width: 28px; - height: 28px; + width: 24px; + height: 24px; padding: 0; - border: 1px solid color-mix(in srgb, var(--border) 80%, transparent); - border-radius: 999px; - background: color-mix(in srgb, var(--secondary) 55%, transparent); + border: 0; + border-radius: var(--radius-sm); + background: transparent; + color: var(--muted); cursor: pointer; font: inherit; + transition: + color 150ms ease-out, + background 150ms ease-out; +} + +.chat-tool-card__action-btn:hover, +.chat-tool-card__action-btn:focus-visible { + color: var(--text); + background: color-mix(in srgb, var(--bg-hover) 70%, transparent); } .chat-tool-card__action-icon { @@ -135,8 +254,8 @@ } .chat-tool-card__action-icon svg { - width: 11px; - height: 11px; + width: 12px; + height: 12px; stroke: currentColor; fill: none; stroke-width: 1.5px; @@ -144,43 +263,6 @@ stroke-linejoin: round; } -.chat-tool-card--clickable:hover .chat-tool-card__action, -.chat-tool-card__action-btn:hover, -.chat-tool-card__action-btn:focus-visible { - color: var(--text); - background: color-mix(in srgb, var(--bg-hover) 70%, transparent); - border-color: color-mix(in srgb, var(--border-strong) 70%, transparent); -} - -/* Status indicator for completed/empty results */ -.chat-tool-card__status { - display: inline-flex; - align-items: center; - color: var(--ok); -} - -.chat-tool-card__status svg { - width: 14px; - height: 14px; - stroke: currentColor; - fill: none; - stroke-width: 2px; - stroke-linecap: round; - stroke-linejoin: round; -} - -.chat-tool-card__status-text { - font-size: var(--control-ui-text-xs); - margin-top: 10px; -} - -/* Error state: tool returned an error payload or "Tool not found" */ -.chat-tool-card__status--error, -.chat-tool-card__action--error, -.chat-tool-card__status-text--error { - color: var(--destructive, var(--danger, #c0392b)); -} - .chat-tool-card__status-badge { display: inline-flex; align-items: center; @@ -188,9 +270,9 @@ padding: 1px 6px; margin-left: 6px; border-radius: 999px; - background: color-mix(in srgb, var(--destructive, var(--danger, #c0392b)) 15%, transparent); + background: color-mix(in srgb, var(--destructive, var(--danger, #c0392b)) 14%, transparent); color: var(--destructive, var(--danger, #c0392b)); - font-size: 12px; /* was 10px */ + font-size: 11px; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; @@ -207,215 +289,26 @@ stroke-linejoin: round; } -.chat-tool-card--error { - border-color: color-mix(in srgb, var(--destructive, var(--danger, #c0392b)) 35%, var(--border)); - background: - linear-gradient( - 90deg, - color-mix(in srgb, var(--destructive, var(--danger, #c0392b)) 8%, transparent), - transparent 36% - ), - color-mix(in srgb, var(--card) 86%, var(--secondary) 14%); -} - -.chat-tool-card--error:hover { - border-color: color-mix( - in srgb, - var(--destructive, var(--danger, #c0392b)) 44%, - var(--border-strong) - ); - background: - linear-gradient( - 90deg, - color-mix(in srgb, var(--destructive, var(--danger, #c0392b)) 11%, transparent), - transparent 38% - ), - color-mix(in srgb, var(--card) 78%, var(--bg-hover) 22%); -} - -.chat-tool-card--error .chat-tool-card__icon, -.chat-tool-msg-summary.chat-tool-msg-summary--error .chat-tool-msg-summary__icon { +.chat-tool-card--error .chat-tool-card__icon { color: var(--destructive, var(--danger, #c0392b)); - opacity: 0.9; -} - -.chat-tool-msg-summary__error-badge { - display: inline-flex; - align-items: center; - align-self: center; - gap: 3px; - min-height: 20px; - box-sizing: border-box; - padding: 1px 6px; - margin-left: auto; - border-radius: 999px; - background: color-mix(in srgb, var(--destructive, var(--danger, #c0392b)) 15%, transparent); - color: var(--destructive, var(--danger, #c0392b)); - font-size: 12px; /* was 10px */ - font-weight: 600; - letter-spacing: 0.04em; - text-transform: uppercase; - line-height: 1; - flex-shrink: 0; -} - -.chat-tool-msg-summary__error-badge svg { - width: 10px; - height: 10px; - stroke: currentColor; - fill: none; - stroke-width: 2px; - stroke-linecap: round; - stroke-linejoin: round; } .chat-tool-card__detail { - font-family: var(--mono); - font-size: 12px; + font-size: var(--control-ui-text-sm); color: var(--muted); - margin-top: 6px; -} - -/* Collapsed preview - fixed height with truncation */ -.chat-tools-inline { - display: grid; - gap: 6px; + margin-top: 4px; } .chat-tool-card__block { - margin-top: 12px; - min-width: 0; -} - -.chat-tool-card__preview, -.chat-tool-card__raw { - margin-top: 12px; -} - -.chat-tool-card__preview { - border: 1px solid color-mix(in srgb, var(--border) 78%, transparent); - border-radius: var(--radius-md); - background: color-mix(in srgb, var(--secondary) 78%, transparent); - overflow: hidden; -} - -.chat-tool-card__preview-header { - display: flex; - align-items: center; - justify-content: space-between; - gap: 10px; - padding: 10px 12px; - border-bottom: 1px solid color-mix(in srgb, var(--border) 72%, transparent); - background: color-mix(in srgb, var(--card) 82%, transparent); -} - -.chat-tool-card__preview-label { - font-size: 12px; /* was 11px */ - font-weight: 600; - letter-spacing: 0.04em; - text-transform: uppercase; - color: var(--muted); -} - -.chat-tool-card__preview-tabs { - display: inline-flex; - align-items: center; - gap: 4px; -} - -.chat-tool-card__preview-tab { - border: 1px solid color-mix(in srgb, var(--border) 76%, transparent); - background: color-mix(in srgb, var(--bg) 72%, transparent); - color: var(--muted); - border-radius: 999px; - padding: 4px 10px; - font: inherit; - font-size: 12px; - cursor: pointer; - transition: - color 150ms ease-out, - border-color 150ms ease-out, - background 150ms ease-out; -} - -.chat-tool-card__preview-tab.is-active, -.chat-tool-card__preview-tab:hover, -.chat-tool-card__preview-tab:focus-visible { - color: var(--text); - border-color: color-mix(in srgb, var(--border-strong) 80%, transparent); - background: color-mix(in srgb, var(--card) 92%, transparent); -} - -.chat-tool-card__preview-panel { - padding: 12px; -} - -.chat-tool-card__preview-frame { - display: block; - width: 100%; - min-height: 420px; - border: 1px solid color-mix(in srgb, var(--border) 68%, transparent); - border-radius: var(--radius-md); - background: #fff; -} - -.chat-tool-card__raw-toggle { - width: 100%; - display: inline-flex; - align-items: center; - justify-content: space-between; - gap: 8px; - padding: 10px 12px; - border: 1px solid color-mix(in srgb, var(--border) 75%, transparent); - border-radius: var(--radius-md); - background: color-mix(in srgb, var(--secondary) 68%, transparent); - color: var(--muted); - font: inherit; - font-size: 12px; - cursor: pointer; - transition: - color 150ms ease-out, - border-color 150ms ease-out, - background 150ms ease-out; -} - -.chat-tool-card__raw-toggle:hover, -.chat-tool-card__raw-toggle:focus-visible { - color: var(--text); - border-color: color-mix(in srgb, var(--border-strong) 80%, transparent); - background: color-mix(in srgb, var(--card) 92%, transparent); -} - -.chat-tool-card__raw-toggle[aria-expanded="true"] .chat-tool-card__raw-toggle-icon { - transform: rotate(180deg); -} - -.chat-tool-card__raw-toggle-icon { - display: inline-flex; - align-items: center; - justify-content: center; - transition: transform 150ms ease-out; -} - -.chat-tool-card__raw-toggle-icon svg { - width: 14px; - height: 14px; - stroke: currentColor; - fill: none; - stroke-width: 1.6px; - stroke-linecap: round; - stroke-linejoin: round; -} - -.chat-tool-card__raw-body { margin-top: 8px; + min-width: 0; } .chat-tool-card__block-header { display: inline-flex; align-items: center; gap: 6px; - margin-bottom: 6px; + margin-bottom: 4px; color: var(--muted); } @@ -438,38 +331,27 @@ } .chat-tool-card__block-label { - font-size: 12px; /* was 11px */ + font-size: 11px; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; color: inherit; } -.chat-tool-card__block-preview, -.chat-tool-card__block-content, -.chat-tool-card__block-empty { +.chat-tool-card__block-content { box-sizing: border-box; max-width: 100%; min-width: 0; margin: 0; - padding: 11px 12px; - border-radius: var(--radius-md); - border: 1px solid color-mix(in srgb, var(--border) 75%, transparent); + padding: 10px 12px; + border-radius: var(--radius-sm); background: color-mix(in srgb, var(--secondary) 82%, transparent); - font-size: 12px; /* was 11px */ + color: var(--text); + font-size: 12px; line-height: 1.45; white-space: pre-wrap; overflow-wrap: anywhere; word-break: break-word; -} - -.chat-tool-card__block-preview, -.chat-tool-card__block-empty { - color: var(--muted); -} - -.chat-tool-card__block-content { - color: var(--text); overflow: auto; max-height: min(520px, 60vh); } @@ -499,100 +381,98 @@ word-break: normal; } -.chat-tool-card__inline { - margin-top: 10px; - white-space: pre-wrap; - overflow-wrap: anywhere; - word-break: break-word; +/* ── Canvas/document preview (frames embedded content, so it keeps a border) ── */ +.chat-tool-card__preview, +.chat-tool-card__raw { + margin-top: 8px; } -.chat-tool-card__block-preview { +.chat-tool-card__preview { + border: 1px solid color-mix(in srgb, var(--border) 78%, transparent); + border-radius: var(--radius-md); + background: color-mix(in srgb, var(--secondary) 78%, transparent); overflow: hidden; - max-height: 52px; } -.chat-tools-summary { +.chat-tool-card__preview-header { display: flex; align-items: center; - gap: 6px; - padding: 8px 12px; - cursor: pointer; - font-size: 12px; - font-weight: 500; + justify-content: space-between; + gap: 10px; + padding: 10px 12px; + border-bottom: 1px solid color-mix(in srgb, var(--border) 72%, transparent); + background: color-mix(in srgb, var(--card) 82%, transparent); +} + +.chat-tool-card__preview-label { + font-size: 11px; + font-weight: 600; + letter-spacing: 0.04em; + text-transform: uppercase; color: var(--muted); - user-select: none; - list-style: none; - transition: - color 150ms ease, - background 150ms ease; } -.chat-tools-summary::-webkit-details-marker { - display: none; +.chat-tool-card__preview-panel { + padding: 12px; } -.chat-tools-summary::before { - content: "▸"; - font-size: 12px; /* was 10px */ - flex-shrink: 0; - transition: transform 150ms ease; +.chat-tool-card__preview-frame { + display: block; + width: 100%; + min-height: 420px; + border: 1px solid color-mix(in srgb, var(--border) 68%, transparent); + border-radius: var(--radius-md); + background: #fff; } -.chat-tools-collapse[open] > .chat-tools-summary::before { - transform: rotate(90deg); -} - -.chat-tools-summary:hover { - color: var(--text); - background: color-mix(in srgb, var(--bg-hover) 50%, transparent); -} - -.chat-tools-summary__icon { +.chat-tool-card__raw-toggle { display: inline-flex; align-items: center; - width: 14px; - height: 14px; - color: var(--accent); - opacity: 0.7; - flex-shrink: 0; + gap: 6px; + padding: 4px 0; + border: 0; + background: transparent; + color: var(--muted); + font: inherit; + font-size: 12px; + cursor: pointer; + transition: color 150ms ease-out; } -.chat-tools-summary__icon svg { +.chat-tool-card__raw-toggle:hover, +.chat-tool-card__raw-toggle:focus-visible { + color: var(--text); +} + +.chat-tool-card__raw-toggle[aria-expanded="true"] .chat-tool-card__raw-toggle-icon { + transform: rotate(180deg); +} + +.chat-tool-card__raw-toggle-icon { + display: inline-flex; + align-items: center; + justify-content: center; + transition: transform 150ms ease-out; +} + +.chat-tool-card__raw-toggle-icon svg { width: 14px; height: 14px; stroke: currentColor; fill: none; - stroke-width: 1.5px; + stroke-width: 1.6px; stroke-linecap: round; stroke-linejoin: round; } -.chat-tools-summary__count { - font-weight: 600; - color: var(--text); -} - -.chat-tools-summary__names { - color: var(--muted); - font-weight: 400; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.chat-tools-collapse__body { - padding: 4px 12px 12px; - border-top: 1px solid color-mix(in srgb, var(--border) 60%, transparent); -} - -.chat-tools-collapse__body .chat-tool-card:first-child { - margin-top: 8px; +.chat-tool-card__raw-body { + margin-top: 4px; } +/* ── Inline JSON disclosure ── */ .chat-json-collapse { margin-top: 4px; - border: 1px solid color-mix(in srgb, var(--border) 80%, transparent); - border-radius: var(--radius-md); + border-radius: var(--radius-sm); background: color-mix(in srgb, var(--secondary) 60%, transparent); overflow: hidden; } @@ -618,7 +498,7 @@ .chat-json-summary::before { content: "▸"; - font-size: 12px; /* was 10px */ + font-size: 12px; flex-shrink: 0; transition: transform 150ms ease; } @@ -639,7 +519,7 @@ border-radius: var(--radius-sm); background: color-mix(in srgb, var(--accent) 15%, transparent); color: var(--accent); - font-size: 12px; /* was 10px */ + font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; @@ -649,7 +529,7 @@ .chat-json-label { font-family: var(--mono); - font-size: 12px; /* was 11px */ + font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; @@ -673,253 +553,109 @@ font-size: inherit; } -.chat-tool-msg-collapse { - width: 100%; +/* ── Activity group (a turn's run of tool rows) ── */ +.chat-group--activity .chat-group-messages { + max-width: min(760px, 100%); +} + +.chat-activity-group { min-width: 0; max-width: 100%; - margin-top: 6px; } -.chat-bubble--tool-shell > .chat-tool-msg-collapse { - margin-top: 0; -} - -.chat-tool-msg-summary { +.chat-activity-group__summary { display: flex; align-items: center; - align-content: center; - flex-wrap: wrap; - gap: 12px; - min-width: 0; - box-sizing: border-box; - padding: 8px 11px; - cursor: pointer; - font-size: var(--control-ui-text-sm); - line-height: 1; - color: var(--text); - user-select: none; - list-style: none; - border: 1px solid color-mix(in srgb, var(--border) 75%, transparent); - border-radius: var(--radius-md); - background: - linear-gradient(90deg, color-mix(in srgb, var(--accent) 9%, transparent), transparent 34%), - color-mix(in srgb, var(--card) 86%, var(--secondary) 14%); - box-shadow: - inset 0 1px 0 color-mix(in srgb, var(--bg) 76%, transparent), - 0 8px 22px color-mix(in srgb, black 12%, transparent); + gap: 8px; width: 100%; - text-align: left; - appearance: none; - -webkit-appearance: none; - font-family: inherit; - transition: - color 150ms ease, - background 150ms ease, - border-color 150ms ease; -} - -.chat-tool-msg-summary svg, -.chat-tool-msg-summary__icon { - flex-shrink: 0; -} - -.chat-tool-msg-summary span { - display: inline-flex; - align-items: center; - line-height: var(--control-ui-text-sm); -} - -.chat-tool-msg-summary[type="button"] { - background: - linear-gradient(90deg, color-mix(in srgb, var(--accent) 9%, transparent), transparent 34%), - color-mix(in srgb, var(--card) 86%, var(--secondary) 14%); -} - -.chat-tool-msg-summary.chat-tool-msg-summary--error { - border-color: color-mix(in srgb, var(--destructive, var(--danger, #c0392b)) 30%, var(--border)); - background: - linear-gradient( - 90deg, - color-mix(in srgb, var(--destructive, var(--danger, #c0392b)) 11%, transparent), - transparent 36% - ), - color-mix(in srgb, var(--card) 86%, var(--secondary) 14%); -} - -.chat-tool-msg-summary::-webkit-details-marker { - display: none; -} - -.chat-tool-msg-summary::before { - content: "▸"; - display: inline-flex; - align-items: center; - justify-content: center; - width: 8px; - height: 20px; - font-size: 12px; /* was 10px */ - line-height: 1; - flex-shrink: 0; - transition: transform 150ms ease; -} - -.chat-tool-msg-collapse--static > .chat-tool-msg-summary::before { - display: none; -} - -.chat-tool-msg-collapse--manual.is-open > .chat-tool-msg-summary::before, -.chat-tool-msg-summary[aria-expanded="true"]::before { - transform: rotate(90deg); -} - -.chat-tool-msg-collapse[open] > .chat-tool-msg-summary::before { - transform: rotate(90deg); -} - -.chat-tool-msg-summary:hover { - color: var(--text); - background: - linear-gradient(90deg, color-mix(in srgb, var(--accent) 13%, transparent), transparent 38%), - color-mix(in srgb, var(--card) 76%, var(--bg-hover) 24%); - border-color: color-mix(in srgb, var(--border-strong) 70%, transparent); -} - -.chat-tool-msg-summary.chat-tool-msg-summary--error:hover { - background: - linear-gradient( - 90deg, - color-mix(in srgb, var(--destructive, var(--danger, #c0392b)) 14%, transparent), - transparent 38% - ), - color-mix(in srgb, var(--card) 78%, var(--bg-hover) 22%); - border-color: color-mix( - in srgb, - var(--destructive, var(--danger, #c0392b)) 38%, - var(--border-strong) - ); -} - -.chat-tool-msg-collapse--manual.is-open > .chat-tool-msg-summary, -.chat-tool-msg-collapse[open] > .chat-tool-msg-summary { - border-bottom-right-radius: 0; - border-bottom-left-radius: 0; -} - -.chat-tool-msg-summary__icon { - display: inline-flex; - align-items: center; - justify-content: center; - align-self: center; - width: 18px; - height: 18px; - color: var(--accent); - opacity: 0.75; -} - -.chat-tool-msg-summary__icon svg { - width: 18px; - height: 18px; - stroke: currentColor; - fill: none; - stroke-width: 1.5px; - stroke-linecap: round; - stroke-linejoin: round; -} - -.chat-tool-msg-summary__label { - align-self: center; - min-height: 20px; - font-weight: 600; - color: var(--text); - flex: 0 1 auto; min-width: 0; - max-width: 100%; - overflow: visible; - overflow-wrap: anywhere; - text-overflow: clip; - white-space: normal; -} - -.chat-tool-msg-summary__names, -.chat-tool-msg-summary__preview { - align-self: center; - min-height: 20px; - font-family: var(--mono); - font-size: var(--control-ui-text-sm); - transform: translateY(0.8px); - color: color-mix(in srgb, var(--text) 76%, var(--muted) 24%); - flex: 0 1 auto; - max-width: 100%; - overflow: visible; - overflow-wrap: anywhere; - text-overflow: clip; - white-space: normal; - min-width: 0; -} - -.chat-tool-msg-summary__spacer { - flex: 1 1 auto; -} - -.chat-tool-msg-summary__btn { - width: 24px; - height: 24px; - padding: 0; - border: 1px solid color-mix(in srgb, var(--border) 70%, transparent); - border-radius: 999px; - background: color-mix(in srgb, var(--secondary) 55%, transparent); - color: var(--muted); - display: inline-flex; - align-items: center; - justify-content: center; - cursor: pointer; - flex-shrink: 0; -} - -.chat-tool-msg-summary__btn:hover, -.chat-tool-msg-summary__btn:focus-visible { - color: var(--text); - background: color-mix(in srgb, var(--bg-hover) 70%, transparent); - border-color: color-mix(in srgb, var(--border-strong) 70%, transparent); -} - -.chat-tool-msg-summary__btn svg { - width: 11px; - height: 11px; - stroke: currentColor; - fill: none; - stroke-width: 1.5px; - stroke-linecap: round; - stroke-linejoin: round; -} - -.chat-tool-msg-body { box-sizing: border-box; + padding: 5px 8px; + border: 0; + border-radius: var(--radius-sm); + background: transparent; + color: var(--text); + font: inherit; + text-align: left; + cursor: pointer; + transition: background 150ms ease; +} + +.chat-activity-group__summary:hover, +.chat-activity-group__summary:focus-visible { + background: color-mix(in srgb, var(--bg-hover) 60%, transparent); +} + +.chat-activity-group__summary--error { + color: var(--destructive, var(--danger, #c0392b)); +} + +.chat-activity-group__icon, +.chat-activity-group__badge { + display: inline-flex; + align-items: center; + justify-content: center; + flex-shrink: 0; +} + +.chat-activity-group__icon { + color: var(--muted); +} + +.chat-activity-group__summary--error .chat-activity-group__icon { + color: inherit; +} + +.chat-activity-group__icon svg, +.chat-activity-group__badge svg { + width: 14px; + height: 14px; + fill: none; + stroke: currentColor; + stroke-width: 1.8px; + stroke-linecap: round; + stroke-linejoin: round; +} + +.chat-activity-group__label { + flex-shrink: 0; + font-size: var(--control-ui-text-sm); + font-weight: 600; + line-height: 1.4; +} + +.chat-activity-group__preview { + flex: 1 1 0; min-width: 0; - max-width: 100%; - padding-top: 8px; -} - -.chat-bubble--tool-shell .chat-tool-msg-body { - margin-top: 0; - padding: 12px 14px 14px; - border: 1px solid color-mix(in srgb, var(--border) 75%, transparent); - border-top: 0; - border-radius: 0 0 var(--radius-md) var(--radius-md); - background: color-mix(in srgb, var(--card) 82%, var(--secondary) 18%); - box-shadow: inset 0 1px 0 color-mix(in srgb, var(--bg) 68%, transparent); overflow: hidden; + color: var(--muted); + font-size: var(--control-ui-text-sm); + line-height: 1.4; + text-overflow: ellipsis; + white-space: nowrap; } -.chat-bubble--tool-shell .chat-tool-msg-body > .chat-text { - max-height: min(46vh, 540px); - margin: 0; - overflow: auto; - font-family: var(--mono); - font-size: 12px; - line-height: 1.55; - white-space: pre-wrap; - overflow-wrap: anywhere; +.chat-activity-group__badge { + gap: 3px; + padding: 2px 6px; + border-radius: var(--radius-full); + background: color-mix(in srgb, var(--destructive, var(--danger, #c0392b)) 14%, transparent); + color: var(--destructive, var(--danger, #c0392b)); + font-size: 11px; + font-weight: 650; + letter-spacing: 0.04em; + line-height: 1; + text-transform: uppercase; +} + +/* Left rule groups the rows without wrapping them in another card. */ +.chat-activity-group__body { + display: flex; + flex-direction: column; + gap: 0; + margin: 2px 0 0 14px; + padding-left: 8px; + border-left: 1px solid color-mix(in srgb, var(--border) 85%, transparent); } /* Reading Indicator: bubble chrome and sizing come from .chat-bubble and @@ -980,149 +716,28 @@ } } -.chat-group--activity .chat-group-messages { - max-width: min(760px, 100%); -} - -.chat-activity-group { - overflow: hidden; - border: 1px solid color-mix(in srgb, var(--border) 75%, transparent); - border-radius: var(--radius-md); - background: color-mix(in srgb, var(--card) 88%, var(--secondary) 12%); -} - -.chat-activity-group__summary { - display: flex; - align-items: center; - gap: 8px; - width: 100%; - min-height: 40px; - padding: 9px 12px; - border: 0; - background: transparent; - color: var(--text); - font: inherit; - text-align: left; - cursor: pointer; -} - -.chat-activity-group__summary:hover, -.chat-activity-group__summary:focus-visible { - background: color-mix(in srgb, var(--bg-hover) 70%, transparent); -} - -.chat-activity-group__summary--error { - color: var(--destructive, var(--danger, #c0392b)); -} - -.chat-activity-group__icon, -.chat-activity-group__badge { - display: inline-flex; - align-items: center; - justify-content: center; - flex-shrink: 0; -} - -.chat-activity-group__icon svg, -.chat-activity-group__badge svg { - width: 14px; - height: 14px; - fill: none; - stroke: currentColor; - stroke-width: 1.8px; - stroke-linecap: round; - stroke-linejoin: round; -} - -.chat-activity-group__label { - flex-shrink: 0; - font-size: var(--control-ui-text-sm); - font-weight: 650; - line-height: 1.2; -} - -.chat-activity-group__preview { - min-width: 0; - overflow: hidden; - color: var(--muted); - font-size: var(--control-ui-text-sm); - line-height: 1.2; - text-overflow: ellipsis; - white-space: nowrap; -} - -.chat-activity-group__badge { - gap: 3px; - margin-left: auto; - padding: 2px 6px; - border-radius: var(--radius-full); - background: color-mix(in srgb, var(--destructive, var(--danger, #c0392b)) 15%, transparent); - color: var(--destructive, var(--danger, #c0392b)); - font-size: 12px; /* was 10px */ - font-weight: 650; - letter-spacing: 0.04em; - line-height: 1; - text-transform: uppercase; -} - -.chat-activity-group__body { - display: flex; - flex-direction: column; - gap: 8px; - padding: 0 10px 10px; +@media (pointer: coarse) { + .chat-tool-msg-summary, + .chat-activity-group__summary { + padding-top: 8px; + padding-bottom: 8px; + } } @media (max-width: 768px) { - .chat-tool-card { - max-height: 100px; - } - - .chat-tool-card--expanded { - max-height: none; - } - - .chat-tool-card__title { - font-size: 12px; - } - - .chat-tool-card__preview { - padding: 6px 8px; - margin-top: 6px; - font-size: 12px; /* was 10px */ - max-height: 36px; - } - - .chat-tool-card__detail { - font-size: 12px; /* was 11px */ - } - - .chat-tools-summary { - padding: 6px 10px; - } - - .chat-tools-collapse__body { - padding: 4px 10px 10px; + .chat-tool-msg-body { + padding-left: 16px; } .chat-json-content { padding: 8px 10px; - font-size: 12px; /* was 11px */ max-height: 300px; } } @media (max-width: 480px) { - .chat-tool-card { - max-height: 80px; - } - - .chat-tool-card--expanded { - max-height: none; - } - - .chat-tool-card__preview { - padding: 4px 6px; - max-height: 28px; + .chat-tool-card__block-content { + max-height: min(360px, 55vh); } .chat-json-content { diff --git a/ui/src/styles/components.css b/ui/src/styles/components.css index 9fc1051cc45b..1bade9093933 100644 --- a/ui/src/styles/components.css +++ b/ui/src/styles/components.css @@ -3956,62 +3956,6 @@ td.data-table-key-col { } } -/* Tool cards: canonical card/title/detail styles live in chat/tool-cards.css; - the details/summary/output rules below have no counterpart there. */ -.chat-tool-card__details { - margin-top: 6px; -} - -.chat-tool-card__summary { - font-family: var(--mono); - font-size: 12px; /* was 11px */ - color: var(--muted); - cursor: pointer; - list-style: none; - display: inline-flex; - align-items: center; - gap: 6px; -} - -.chat-tool-card__summary::-webkit-details-marker { - display: none; -} - -.chat-tool-card__summary-meta { - color: var(--muted); - opacity: 0.7; -} - -.chat-tool-card__details[open] .chat-tool-card__summary { - color: var(--text); -} - -.chat-tool-card__output { - margin-top: 8px; - font-family: var(--mono); - font-size: 12px; /* was 11px */ - line-height: 1.5; - white-space: pre-wrap; - color: var(--chat-text); - padding: 8px 10px; - border-radius: var(--radius-md); - border: 1px solid var(--border); - background: var(--card); -} - -:root[data-theme-mode="light"] .chat-tool-card__output { - background: var(--bg); -} - -.chat-stamp { - font-size: 12px; /* was 11px */ - color: var(--muted); -} - -.chat-line.user .chat-stamp { - text-align: right; -} - /* =========================================== QR Code =========================================== */