diff --git a/packages/session-ui/component-tests/session-notice-projection.spec.ts b/packages/session-ui/component-tests/session-notice-projection.spec.ts index ad7d2b003f3..0604ee252a1 100644 --- a/packages/session-ui/component-tests/session-notice-projection.spec.ts +++ b/packages/session-ui/component-tests/session-notice-projection.spec.ts @@ -5,9 +5,9 @@ story("shows a delegating row while subagent input streams", async ({ mount }) = const timeline = await mount("current-session-research-agents--agent-research", { args: { scenario: "delegation" } }) const delegating = timeline.locator('[data-component="task-tool-delegating"]') await expect(delegating).toBeVisible() - const shimmer = delegating.locator('[data-component="text-shimmer"]') - await expect(shimmer).toHaveAttribute("aria-label", "Delegating agent…") - await expect(shimmer).toHaveCSS("line-height", "16px") + await expect(delegating).toHaveText("Delegating agent…") + await expect(delegating).toHaveCSS("line-height", "16px") + await expect(delegating.locator('[data-component="session-progress-indicator-v2"]')).toBeVisible() await expect(timeline.locator('[data-component="task-tool-card"]')).toHaveCount(0) await expect(timeline.locator('[data-timeline-row="Thinking"]')).toHaveCount(0) }) diff --git a/packages/session-ui/src/components/basic-tool.css b/packages/session-ui/src/components/basic-tool.css index 428627985b6..bd409fe2137 100644 --- a/packages/session-ui/src/components/basic-tool.css +++ b/packages/session-ui/src/components/basic-tool.css @@ -283,18 +283,6 @@ gap: 8px; } - [data-component="task-tool-spinner"] { - display: inline-flex; - align-items: center; - justify-content: center; - flex-shrink: 0; - - [data-component="spinner"] { - width: 16px; - height: 16px; - } - } - [data-component="task-tool-action"] { display: inline-flex; align-items: center; @@ -338,7 +326,7 @@ line-height: var(--line-height-compact); letter-spacing: var(--letter-spacing-normal); text-transform: capitalize; - color: var(--task-agent-color-fallback, var(--text-strong)); + color: var(--text-strong); } [data-slot="basic-tool-tool-subtitle"] { @@ -382,18 +370,6 @@ background: var(--v2-background-bg-layer-02); } - [data-component="task-tool-spinner"], - [data-component="task-tool-icon"] { - display: inline-flex; - align-items: center; - justify-content: center; - flex-shrink: 0; - - [data-slot="icon-svg"] { - color: var(--task-agent-color, var(--v2-icon-icon-base)); - } - } - [data-component="task-tool-title"] { height: 20px; font-family: var(--v2-font-family-sans, "Inter", sans-serif); @@ -420,10 +396,6 @@ flex-grow: 0; } - [data-component="session-progress-indicator-v2"] { - color: var(--task-agent-color, light-dark(var(--v2-text-text-base), #ffffff)); - } - &:hover, &:focus-visible { border-color: transparent; diff --git a/packages/session-ui/src/tools/subagent-row-variants.stories.tsx b/packages/session-ui/src/tools/subagent-row-variants.stories.tsx new file mode 100644 index 00000000000..9584a40eedc --- /dev/null +++ b/packages/session-ui/src/tools/subagent-row-variants.stories.tsx @@ -0,0 +1,127 @@ +import { TextShimmer } from "@opencode/ui/text-shimmer" +import { For, type JSX } from "solid-js" +import { CurrentSessionProviders } from "../storybook/current-session-story" +import { storyDocument, storyTool } from "../storybook/current-session-scenarios" +import { timelinePresets } from "../timeline/detail" +import { SessionTimeline } from "../timeline/session-timeline" + +export default { + title: "OpenCode/Work/Subagent row variants", + id: "current-subagent-row-variants", + parameters: { + layout: "fullscreen", + docs: { + description: { + component: + "Design alternatives for child-Session rows without the old agent square. Production is the first variant; the others are static story-only sketches.", + }, + }, + }, +} + +const productionDocument = storyDocument( + [ + storyTool("variant_glob", "glob", "completed", { pattern: "src/timeline/**/*.ts" }), + storyTool("variant_grep", "grep", "completed", { pattern: "timelineDetail" }), + storyTool("variant_read", "read", "completed", { path: "src/timeline/projection.ts" }), + storyTool( + "variant_explore", + "subagent", + "completed", + { agent: "explore", description: "Map the timeline grouping rules", prompt: "Find where tool rows are grouped." }, + { output: "Grouping lives in the timeline projection.", metadata: { sessionID: "session_child_explore" } }, + ), + storyTool( + "variant_test", + "subagent", + "running", + { agent: "test", description: "Run the focused timeline tests", prompt: "Run the timeline tests." }, + { metadata: { sessionID: "session_child_test" } }, + ), + storyTool("variant_delegating", "subagent", "streaming", {}, { raw: "" }), + ], + true, +) + +const sketchRows = [ + { title: "Explore", subtitle: "Map the timeline grouping rules", running: false }, + { title: "Test", subtitle: "Run the focused timeline tests", running: true }, +] + +function Frame(props: { title: string; children: JSX.Element }) { + return ( +
+

{props.title}

+ {props.children} +
+ ) +} + +function SketchContext() { + return ( +
+ Used 3 Glob, Grep, Read +
+ ) +} + +function ShimmerWhileRunning() { + return ( +
+ + + {(row) => ( +
+
+
+ + + + {row.subtitle} +
+
+
+ )} +
+
+ ) +} + +function PlainLabel() { + return ( +
+ + + {(row) => ( +
+ + + + {row.subtitle} +
+ )} +
+
+ ) +} + +export const Variants = { + render: () => ( +
+ + + + + + + + + + + +
+ ), +} diff --git a/packages/session-ui/src/tools/tool-renderer.tsx b/packages/session-ui/src/tools/tool-renderer.tsx index b3ca0f834ee..5e8db5e4208 100644 --- a/packages/session-ui/src/tools/tool-renderer.tsx +++ b/packages/session-ui/src/tools/tool-renderer.tsx @@ -229,83 +229,14 @@ function displayDirectory(path: string | undefined) { import { resolveFileDiff } from "../components/session-diff" -const agentTones = new Map([ - ["ask", "var(--icon-agent-ask-base)"], - ["build", "var(--icon-agent-build-base)"], - ["docs", "var(--icon-agent-docs-base)"], - ["plan", "var(--icon-agent-plan-base)"], -]) - -const v2AgentTones = new Map([ - ["build", "var(--v2-agent-build-solid)"], - ["explore", "var(--v2-agent-explore-solid)"], - ["plan", "var(--v2-agent-plan-solid)"], - ["review", "var(--v2-agent-review-solid)"], - ["writer", "var(--v2-agent-writer-solid)"], -]) - -const agentThemeColors = new Map([ - ["primary", "var(--text-interactive-base)"], - ["secondary", "var(--text-base)"], - ["accent", "var(--icon-info-base)"], - ["success", "var(--icon-success-base)"], - ["warning", "var(--icon-warning-base)"], - ["error", "var(--icon-critical-base)"], - ["info", "var(--icon-info-base)"], -]) - -const v2AgentThemeColors = new Map([ - ["primary", "var(--v2-text-text-accent)"], - ["secondary", "var(--v2-text-text-muted)"], - ["accent", "var(--v2-icon-icon-accent)"], - ["success", "var(--v2-state-fg-success)"], - ["warning", "var(--v2-state-fg-warning)"], - ["error", "var(--v2-state-fg-danger)"], - ["info", "var(--v2-state-fg-info)"], -]) - -const agentPalette = [ - "var(--icon-agent-ask-base)", - "var(--icon-agent-build-base)", - "var(--icon-agent-docs-base)", - "var(--icon-agent-plan-base)", - "var(--syntax-info)", - "var(--syntax-success)", - "var(--syntax-warning)", - "var(--syntax-property)", - "var(--syntax-constant)", - "var(--text-diff-add-base)", - "var(--text-diff-delete-base)", - "var(--icon-warning-base)", -] - -function tone(name: string) { - let hash = 0 - - for (const char of name) hash = (hash * 31 + char.charCodeAt(0)) >>> 0 - - return agentPalette[hash % agentPalette.length] -} - -function taskAgent(raw: string | undefined, list?: readonly { name: string; color?: string }[]) { - if (!raw) return {} +function taskAgentName(raw: string | undefined, list?: readonly { name: string }[]) { + if (!raw) return undefined const key = raw.toLowerCase() - const item = list?.find((entry) => entry.name === raw || entry.name.toLowerCase() === key) - const v2Tone = item?.color ? undefined : v2AgentTones.get(key) - const color = agentColor(item?.color, agentThemeColors) ?? agentTones.get(key) ?? tone(key) - const v2Color = agentColor(item?.color, v2AgentThemeColors) ?? v2Tone ?? color - return { - name: item?.name ?? `${raw[0].toUpperCase()}${raw.slice(1)}`, - color, - v2Color, - } -} - -function agentColor(value: string | undefined, themeColors: ReadonlyMap) { - if (!value) return undefined - - return themeColors.get(value) ?? value + return ( + list?.find((entry) => entry.name === raw || entry.name.toLowerCase() === key)?.name ?? + `${raw[0].toUpperCase()}${raw.slice(1)}` + ) } const webSearchProviderNames = new Map([ @@ -1758,10 +1689,7 @@ ToolRegistry.register({ () => metadata().sessionID || taskSession(input().description, data.sessionID, data.store.session), ) - const agent = createMemo(() => taskAgent(input().agent, data.store.agent)) - const title = createMemo(() => agent().name ?? i18n.t("ui.tool.agent.default")) - const tone = createMemo(() => agent().color) - const v2Tone = createMemo(() => agent().v2Color) + const title = createMemo(() => taskAgentName(input().agent, data.store.agent) ?? i18n.t("ui.tool.agent.default")) const background = createMemo(() => { return metadata().background === true || (props.status === "completed" && metadata().status === "running") @@ -1813,29 +1741,12 @@ ToolRegistry.register({ } const trigger = () => ( -
+
- - - - } - > - - - + + {title()} @@ -1871,10 +1782,10 @@ ToolRegistry.register({ >
- - + + {i18n.t("ui.tool.agent.delegating")}
) diff --git a/packages/ui/src/icons/icon/additional-icons.ts b/packages/ui/src/icons/icon/additional-icons.ts index 3c1a745b4e7..e2a06bd6a1e 100644 --- a/packages/ui/src/icons/icon/additional-icons.ts +++ b/packages/ui/src/icons/icon/additional-icons.ts @@ -52,7 +52,6 @@ export const additionalIcons = { "magnifying-glass-menu": ``, "window-cursor": ``, task: ``, - subagent: ``, stop: ``, status: ``, "status-active": ` @@ -114,7 +113,6 @@ export const additionalIcons = { export function additionalIconViewBox(name: keyof typeof additionalIcons) { return name === "magnifying-glass" || name === "arrow-undo-down" || - name === "subagent" || name === "notifications" || name === "appearance" || name === "extensions" ||