From 5c860d4142332caa65f9f3265d88f7cb09fa91f6 Mon Sep 17 00:00:00 2001 From: usrnk1 <7547651+usrnk1@users.noreply.github.com> Date: Thu, 9 Jul 2026 10:07:02 +0200 Subject: [PATCH] feat(feat): align sub-agent task rows with v2 design (#35167) --- packages/app/src/components/prompt-input.tsx | 2 +- .../session-ui/src/components/basic-tool.css | 43 +++++++++-- .../src/components/message-part.tsx | 73 +++++++++---------- .../timeline-playground.stories.tsx | 47 +++++++++++- packages/ui/src/components/icon.stories.tsx | 1 + packages/ui/src/components/icon.tsx | 5 +- packages/ui/src/styles/theme.css | 36 +++++---- packages/ui/src/theme/v2/mapping.ts | 36 +++++---- packages/ui/src/v2/styles/theme.css | 54 ++++++++------ 9 files changed, 191 insertions(+), 106 deletions(-) diff --git a/packages/app/src/components/prompt-input.tsx b/packages/app/src/components/prompt-input.tsx index 295899197d..324a789825 100644 --- a/packages/app/src/components/prompt-input.tsx +++ b/packages/app/src/components/prompt-input.tsx @@ -1776,7 +1776,7 @@ export const PromptInput: Component = (props) => { class="max-w-[160px] justify-start capitalize" style={control()} > - + {props.controls.model.selection.variant.current() ?? language.t("common.default")} diff --git a/packages/session-ui/src/components/basic-tool.css b/packages/session-ui/src/components/basic-tool.css index bda3069351..f8f20c9ffa 100644 --- a/packages/session-ui/src/components/basic-tool.css +++ b/packages/session-ui/src/components/basic-tool.css @@ -185,6 +185,14 @@ background-color 0.15s ease, color 0.15s ease; + [data-component="task-tool-surface"] { + display: flex; + align-items: center; + gap: 8px; + min-width: 0; + flex: 1 1 auto; + } + [data-slot="basic-tool-tool-info-structured"] { flex: 1 1 auto; min-width: 0; @@ -250,16 +258,30 @@ body[data-new-layout] { [data-component="task-tool-card"] { - padding: 8px 12px 8px 10px; - border-radius: 8px; + gap: 8px; + padding: 0; + border-radius: 0; border: 0; border-color: transparent; - background: var(--task-agent-background, light-dark(#fafafa, rgba(250, 250, 250, 0.15))); - box-shadow: inset 0 0 0 0.5px var(--task-agent-border, var(--v2-border-border-base)); + background: transparent; + box-shadow: none; + + [data-component="task-tool-surface"] { + padding: 8px 12px; + border-radius: 8px; + background: var(--v2-background-bg-layer-01); + } [data-component="task-tool-spinner"], - [data-component="task-tool-title"] { - color: var(--task-agent-color, var(--text-strong)); + [data-component="task-tool-icon"] { + display: inline-flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + + [data-component="icon"] { + color: var(--task-agent-color, var(--v2-icon-icon-base)); + } } [data-component="task-tool-title"] { @@ -270,6 +292,7 @@ body[data-new-layout] { font-size: 13px; line-height: 20px; letter-spacing: -0.04px; + color: var(--v2-text-text-base); flex: none; flex-grow: 0; } @@ -281,7 +304,7 @@ body[data-new-layout] { font-size: 13px; line-height: 20px; letter-spacing: -0.04px; - color: light-dark(var(--v2-text-text-base), #fafafa); + color: var(--v2-text-text-base); font-variation-settings: "slnt" 0; flex: none; flex-grow: 0; @@ -294,7 +317,11 @@ body[data-new-layout] { &:hover, &:focus-visible { border-color: transparent; - background: var(--task-agent-background, light-dark(#fafafa, rgba(250, 250, 250, 0.15))); + background: transparent; + + [data-component="task-tool-surface"] { + background: var(--v2-background-bg-layer-01); + } } } } diff --git a/packages/session-ui/src/components/message-part.tsx b/packages/session-ui/src/components/message-part.tsx index 098ecdbd62..1ba6be82c8 100644 --- a/packages/session-ui/src/components/message-part.tsx +++ b/packages/session-ui/src/components/message-part.tsx @@ -371,22 +371,12 @@ const agentTones: Record = { plan: "var(--icon-agent-plan-base)", } -const v2AgentTones: Record = { - build: { - color: "var(--v2-agent-build-solid)", - border: "var(--v2-agent-build-border)", - background: "var(--v2-agent-build-background)", - }, - explore: { - color: "var(--v2-agent-explore-solid)", - border: "var(--v2-agent-explore-border)", - background: "var(--v2-agent-explore-background)", - }, - plan: { - color: "var(--v2-agent-plan-solid)", - border: "var(--v2-agent-plan-border)", - background: "var(--v2-agent-plan-background)", - }, +const v2AgentTones: Record = { + 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: Record = { @@ -433,19 +423,17 @@ function tone(name: string) { function taskAgent( raw: unknown, list?: readonly { name: string; color?: string }[], -): { name?: string; color?: string; v2Color?: string; border?: string; background?: string } { +): { name?: string; color?: string; v2Color?: string } { if (typeof raw !== "string" || !raw) return {} const key = raw.toLowerCase() const item = list?.find((entry) => entry.name === raw || entry.name.toLowerCase() === key) const v2Tone = item?.color ? undefined : v2AgentTones[key] const color = agentColor(item?.color, agentThemeColors) ?? agentTones[key] ?? tone(key) - const v2Color = agentColor(item?.color, v2AgentThemeColors) ?? v2Tone?.color ?? color + const v2Color = agentColor(item?.color, v2AgentThemeColors) ?? v2Tone ?? color return { name: item?.name ?? `${raw[0]!.toUpperCase()}${raw.slice(1)}`, color, v2Color, - border: v2Tone?.border ?? `color-mix(in srgb, ${v2Color} 48%, transparent)`, - background: v2Tone?.background ?? `color-mix(in srgb, ${v2Color} 12%, transparent)`, } } @@ -1937,8 +1925,6 @@ ToolRegistry.register({ const title = createMemo(() => agent().name ?? i18n.t("ui.tool.agent.default")) const tone = createMemo(() => agent().color) const v2Tone = createMemo(() => agent().v2Color) - const border = createMemo(() => agent().border) - const background = createMemo(() => agent().background) const subtitle = createMemo(() => { const value = typeof props.input.description === "string" && props.input.description @@ -1983,25 +1969,34 @@ ToolRegistry.register({ style={{ "--task-agent-color": v2Tone(), "--task-agent-legacy-color": tone(), - "--task-agent-border": border(), - "--task-agent-background": background(), }} > -
-
- - - }> - - - - - {title()} - - {subtitle()} - +
+
+
+ + + + + + } + > + + }> + + + + + {title()} + + {subtitle()} + +
diff --git a/packages/session-ui/src/components/timeline-playground.stories.tsx b/packages/session-ui/src/components/timeline-playground.stories.tsx index 53699094ac..d56fc55c4d 100644 --- a/packages/session-ui/src/components/timeline-playground.stories.tsx +++ b/packages/session-ui/src/components/timeline-playground.stories.tsx @@ -359,6 +359,34 @@ const TOOL_SAMPLES = { title: "Agent (Explore)", metadata: { sessionId: "sub-session-1" }, }, + "task build": { + tool: "task", + input: { description: "Implement the settings page", subagent_type: "build", prompt: "Build it" }, + output: "Implemented the settings page.", + title: "Agent (Build)", + metadata: { sessionId: "sub-session-2" }, + }, + "task plan": { + tool: "task", + input: { description: "Plan the migration strategy", subagent_type: "plan", prompt: "Plan it" }, + output: "Drafted a 4-step migration plan.", + title: "Agent (Plan)", + metadata: { sessionId: "sub-session-3" }, + }, + "task themed": { + tool: "task", + input: { description: "Review the diff for regressions", subagent_type: "review", prompt: "Review it" }, + output: "No regressions found.", + title: "Agent (Review)", + metadata: { sessionId: "sub-session-4" }, + }, + "task fallback": { + tool: "task", + input: { description: "Write release notes", subagent_type: "writer", prompt: "Write them" }, + output: "Release notes drafted.", + title: "Agent (Writer)", + metadata: { sessionId: "sub-session-5" }, + }, webfetch: { tool: "webfetch", input: { url: "https://solidjs.com/docs/latest/api" }, @@ -1216,6 +1244,7 @@ function Playground() { const data = createMemo(() => ({ session: [session()], + agent: [{ name: "build" }, { name: "plan" }, { name: "explore" }, { name: "review" }], session_status: {}, session_diff: {}, message: { [session().id]: state.messages }, @@ -1342,6 +1371,19 @@ function Playground() { ]) } + const addAgentTasksTurn = () => { + addFullTurn("Run subagents with every agent type", [ + toolPart(TOOL_SAMPLES.task), + toolPart(TOOL_SAMPLES["task build"]), + toolPart(TOOL_SAMPLES["task plan"]), + toolPart(TOOL_SAMPLES["task themed"]), + toolPart(TOOL_SAMPLES["task fallback"]), + toolPart(TOOL_SAMPLES.task, "running"), + toolPart(TOOL_SAMPLES["task build"], "running"), + toolPart(TOOL_SAMPLES["task plan"], "running"), + ]) + } + const addKitchenSink = () => { // User message variants addUser("short") @@ -1727,6 +1769,9 @@ function Playground() { + @@ -1966,7 +2011,7 @@ function Playground() { {/* Main area: timeline preview */}
diff --git a/packages/ui/src/components/icon.stories.tsx b/packages/ui/src/components/icon.stories.tsx index 1986d74772..60c5312988 100644 --- a/packages/ui/src/components/icon.stories.tsx +++ b/packages/ui/src/components/icon.stories.tsx @@ -68,6 +68,7 @@ const names = [ "magnifying-glass-menu", "window-cursor", "task", + "subagent", "stop", "layout-left", "layout-left-partial", diff --git a/packages/ui/src/components/icon.tsx b/packages/ui/src/components/icon.tsx index 7bd461f110..c87a73ce3c 100644 --- a/packages/ui/src/components/icon.tsx +++ b/packages/ui/src/components/icon.tsx @@ -52,6 +52,7 @@ const icons = { "magnifying-glass-menu": ``, "window-cursor": ``, task: ``, + subagent: ``, stop: ``, status: ``, "status-active": ` @@ -110,7 +111,9 @@ const symbol = (name: keyof typeof icons) => `opencode-icon-${name}` let spriteInserted = false function viewBox(name: keyof typeof icons) { - return name === "magnifying-glass" || name === "arrow-undo-down" ? "0 0 16 16" : "0 0 20 20" + return name === "magnifying-glass" || name === "arrow-undo-down" || name === "subagent" + ? "0 0 16 16" + : "0 0 20 20" } function ensureSprite() { diff --git a/packages/ui/src/styles/theme.css b/packages/ui/src/styles/theme.css index 894f65e45d..e1672efe3f 100644 --- a/packages/ui/src/styles/theme.css +++ b/packages/ui/src/styles/theme.css @@ -281,15 +281,17 @@ --icon-agent-docs-base: #fcb239; --icon-agent-ask-base: #2090f5; --icon-agent-build-base: #034cff; - --v2-agent-plan-solid: var(--v2-pink-900); - --v2-agent-plan-border: rgba(200, 61, 139, 0.5); - --v2-agent-plan-background: rgba(253, 236, 243, 0.33); - --v2-agent-build-solid: var(--v2-blue-900); - --v2-agent-build-border: rgba(59, 92, 246, 0.5); - --v2-agent-build-background: rgba(236, 241, 254, 0.33); + --v2-agent-plan-solid: var(--v2-pink-800); + --v2-agent-plan-border: rgba(200, 61, 139, 0.20); + --v2-agent-plan-background: rgba(253, 236, 243, 0.10); + --v2-agent-build-solid: var(--v2-blue-800); + --v2-agent-build-border: rgba(44, 71, 200, 0.10); + --v2-agent-build-background: rgba(236, 241, 254, 0.10); --v2-agent-explore-solid: var(--v2-yellow-900); - --v2-agent-explore-border: rgba(142, 114, 49, 0.5); - --v2-agent-explore-background: rgba(254, 250, 236, 0.33); + --v2-agent-explore-border: rgba(203, 159, 52, 0.20); + --v2-agent-explore-background: rgba(254, 250, 236, 0.1); + --v2-agent-review-solid: var(--v2-green-800); + --v2-agent-writer-solid: var(--v2-purple-700); --icon-on-success-base: rgba(18, 201, 5, 0.9); --icon-on-success-hover: rgba(45, 186, 38, 0.9); --icon-on-success-selected: rgba(7, 137, 1, 0.9); @@ -551,14 +553,16 @@ --icon-agent-ask-base: #2090f5; --icon-agent-build-base: #9dbefe; --v2-agent-plan-solid: var(--v2-pink-400); - --v2-agent-plan-border: rgba(250, 188, 216, 0.5); - --v2-agent-plan-background: rgba(247, 213, 228, 0.1); - --v2-agent-build-solid: var(--v2-blue-400); - --v2-agent-build-border: rgba(215, 226, 252, 0.5); - --v2-agent-build-background: rgba(215, 226, 252, 0.1); - --v2-agent-explore-solid: var(--v2-yellow-400); - --v2-agent-explore-border: rgba(247, 229, 181, 0.5); - --v2-agent-explore-background: rgba(252, 239, 208, 0.1); + --v2-agent-plan-border: rgba(247, 153, 198, 0.20); + --v2-agent-plan-background: rgba(170, 53, 118, 0.05); + --v2-agent-build-solid: var(--v2-blue-300); + --v2-agent-build-border: rgba(162, 188, 255, 0.20); + --v2-agent-build-background: rgba(38, 63, 169, 0.05); + --v2-agent-explore-solid: var(--v2-yellow-300); + --v2-agent-explore-border: rgba(243, 218, 155, 0.20); + --v2-agent-explore-background: rgba(172, 136, 51, 0.05); + --v2-agent-review-solid: var(--v2-green-300); + --v2-agent-writer-solid: var(--v2-purple-400); --icon-on-success-base: rgba(18, 201, 5, 0.9); --icon-on-success-hover: rgba(53, 192, 45, 0.9); --icon-on-success-selected: rgba(77, 225, 68, 0.9); diff --git a/packages/ui/src/theme/v2/mapping.ts b/packages/ui/src/theme/v2/mapping.ts index 9bb29fbfa9..e019fa466e 100644 --- a/packages/ui/src/theme/v2/mapping.ts +++ b/packages/ui/src/theme/v2/mapping.ts @@ -4,27 +4,31 @@ import { V2_AVATAR_DARK, V2_AVATAR_LIGHT } from "./avatar" const ref = (name: string): V2ColorValue => `var(--${name})` const lightAgentTokens: Record = { - "v2-agent-plan-solid": ref("v2-pink-900"), - "v2-agent-plan-border": "rgba(200, 61, 139, 0.5)", - "v2-agent-plan-background": "rgba(253, 236, 243, 0.33)", - "v2-agent-build-solid": ref("v2-blue-900"), - "v2-agent-build-border": "rgba(59, 92, 246, 0.5)", - "v2-agent-build-background": "rgba(236, 241, 254, 0.33)", + "v2-agent-plan-solid": ref("v2-pink-800"), + "v2-agent-plan-border": "rgba(200, 61, 139, 0.20)", + "v2-agent-plan-background": "rgba(253, 236, 243, 0.10)", + "v2-agent-build-solid": ref("v2-blue-800"), + "v2-agent-build-border": "rgba(44, 71, 200, 0.20)", + "v2-agent-build-background": "rgba(236, 241, 254, 0.10)", "v2-agent-explore-solid": ref("v2-yellow-900"), - "v2-agent-explore-border": "rgba(142, 114, 49, 0.5)", - "v2-agent-explore-background": "rgba(254, 250, 236, 0.33)", + "v2-agent-explore-border": "rgba(203, 159, 52, 0.20)", + "v2-agent-explore-background": "rgba(254, 250, 236, 0.1)", + "v2-agent-review-solid": ref("v2-green-800"), + "v2-agent-writer-solid": ref("v2-purple-700"), } const darkAgentTokens: Record = { "v2-agent-plan-solid": ref("v2-pink-400"), - "v2-agent-plan-border": "rgba(250, 188, 216, 0.5)", - "v2-agent-plan-background": "rgba(247, 213, 228, 0.1)", - "v2-agent-build-solid": ref("v2-blue-400"), - "v2-agent-build-border": "rgba(215, 226, 252, 0.5)", - "v2-agent-build-background": "rgba(215, 226, 252, 0.1)", - "v2-agent-explore-solid": ref("v2-yellow-400"), - "v2-agent-explore-border": "rgba(247, 229, 181, 0.5)", - "v2-agent-explore-background": "rgba(252, 239, 208, 0.1)", + "v2-agent-plan-border": "rgba(247, 153, 198, 0.20)", + "v2-agent-plan-background": "rgba(170, 53, 118, 0.05)", + "v2-agent-build-solid": ref("v2-blue-300"), + "v2-agent-build-border": "rgba(162, 188, 255, 0.20)", + "v2-agent-build-background": "rgba(38, 63, 169, 0.05)", + "v2-agent-explore-solid": ref("v2-yellow-300"), + "v2-agent-explore-border": "rgba(243, 218, 155, 0.20)", + "v2-agent-explore-background": "rgba(172, 136, 51, 0.05)", + "v2-agent-review-solid": ref("v2-green-300"), + "v2-agent-writer-solid": ref("v2-purple-400"), } const light: Record = { diff --git a/packages/ui/src/v2/styles/theme.css b/packages/ui/src/v2/styles/theme.css index 5ed135b7e9..66d4294a51 100644 --- a/packages/ui/src/v2/styles/theme.css +++ b/packages/ui/src/v2/styles/theme.css @@ -77,15 +77,17 @@ --v2-state-fg-info: var(--v2-blue-800); --v2-state-border-info: var(--v2-blue-300); - --v2-agent-plan-solid: var(--v2-pink-900); - --v2-agent-plan-border: rgba(200, 61, 139, 0.5); - --v2-agent-plan-background: rgba(253, 236, 243, 0.33); - --v2-agent-build-solid: var(--v2-blue-900); - --v2-agent-build-border: rgba(59, 92, 246, 0.5); - --v2-agent-build-background: rgba(236, 241, 254, 0.33); + --v2-agent-plan-solid: var(--v2-pink-800); + --v2-agent-plan-border: rgba(200, 61, 139, 0.20); + --v2-agent-plan-background: rgba(253, 236, 243, 0.10); + --v2-agent-build-solid: var(--v2-blue-800); + --v2-agent-build-border: rgba(44, 71, 200, 0.10); + --v2-agent-build-background: rgba(236, 241, 254, 0.10); --v2-agent-explore-solid: var(--v2-yellow-900); - --v2-agent-explore-border: rgba(142, 114, 49, 0.5); - --v2-agent-explore-background: rgba(254, 250, 236, 0.33); + --v2-agent-explore-border: rgba(203, 159, 52, 0.20); + --v2-agent-explore-background: rgba(254, 250, 236, 0.1); + --v2-agent-review-solid: var(--v2-green-800); + --v2-agent-writer-solid: var(--v2-purple-700); /* ── Project avatar (fixed; theme-independent) ── */ --v2-avatar-fg: #ffffffff; @@ -316,15 +318,17 @@ --v2-state-fg-info: var(--v2-blue-800); --v2-state-border-info: var(--v2-blue-300); - --v2-agent-plan-solid: var(--v2-pink-900); - --v2-agent-plan-border: rgba(200, 61, 139, 0.5); - --v2-agent-plan-background: rgba(253, 236, 243, 0.33); - --v2-agent-build-solid: var(--v2-blue-900); - --v2-agent-build-border: rgba(59, 92, 246, 0.5); - --v2-agent-build-background: rgba(236, 241, 254, 0.33); + --v2-agent-plan-solid: var(--v2-pink-800); + --v2-agent-plan-border: rgba(200, 61, 139, 0.20); + --v2-agent-plan-background: rgba(253, 236, 243, 0.10); + --v2-agent-build-solid: var(--v2-blue-800); + --v2-agent-build-border: rgba(44, 71, 200, 0.10); + --v2-agent-build-background: rgba(236, 241, 254, 0.10); --v2-agent-explore-solid: var(--v2-yellow-900); - --v2-agent-explore-border: rgba(142, 114, 49, 0.5); - --v2-agent-explore-background: rgba(254, 250, 236, 0.33); + --v2-agent-explore-border: rgba(203, 159, 52, 0.20); + --v2-agent-explore-background: rgba(254, 250, 236, 0.1); + --v2-agent-review-solid: var(--v2-green-800); + --v2-agent-writer-solid: var(--v2-purple-700); --v2-elevation-raised: 0px 2px 4px 0px var(--v2-alpha-dark-4), 0px 1px 2px -1px var(--v2-alpha-dark-8), @@ -437,14 +441,16 @@ --v2-state-border-info: var(--v2-blue-900); --v2-agent-plan-solid: var(--v2-pink-400); - --v2-agent-plan-border: rgba(250, 188, 216, 0.5); - --v2-agent-plan-background: rgba(247, 213, 228, 0.1); - --v2-agent-build-solid: var(--v2-blue-400); - --v2-agent-build-border: rgba(215, 226, 252, 0.5); - --v2-agent-build-background: rgba(215, 226, 252, 0.1); - --v2-agent-explore-solid: var(--v2-yellow-400); - --v2-agent-explore-border: rgba(247, 229, 181, 0.5); - --v2-agent-explore-background: rgba(252, 239, 208, 0.1); + --v2-agent-plan-border: rgba(247, 153, 198, 0.20); + --v2-agent-plan-background: rgba(170, 53, 118, 0.05); + --v2-agent-build-solid: var(--v2-blue-300); + --v2-agent-build-border: rgba(162, 188, 255, 0.20); + --v2-agent-build-background: rgba(38, 63, 169, 0.05); + --v2-agent-explore-solid: var(--v2-yellow-300); + --v2-agent-explore-border: rgba(243, 218, 155, 0.20); + --v2-agent-explore-background: rgba(172, 136, 51, 0.05); + --v2-agent-review-solid: var(--v2-green-300); + --v2-agent-writer-solid: var(--v2-purple-400); --v2-avatar-fg: #ffffffff; --v2-avatar-bg-orange: #723d22ff;