Browse Source

fix(tui): separate subagent tool rows (#33158)

Dax 1 month ago
parent
commit
d99f86b28d

+ 9 - 1
packages/tui/src/routes/session/index.tsx

@@ -1838,6 +1838,7 @@ function InlineTool(props: {
   pending: string
   failure?: string
   spinner?: boolean
+  separate?: boolean
   children: JSX.Element
   part: ToolPart
   onClick?: () => void
@@ -1890,6 +1891,7 @@ function InlineTool(props: {
       pending={props.pending}
       failure={props.failure}
       spinner={props.spinner}
+      separate={props.separate}
       onMouseOver={() => clickable() && setHover(true)}
       onMouseOut={() => setHover(false)}
       onMouseUp={() => {
@@ -1919,6 +1921,7 @@ export function InlineToolRow(props: {
   pending: string
   failure?: string
   spinner?: boolean
+  separate?: boolean
   children: JSX.Element
   onMouseOver?: () => void
   onMouseOut?: () => void
@@ -1931,8 +1934,12 @@ export function InlineToolRow(props: {
       onMouseOut={props.onMouseOut}
       onMouseUp={props.onMouseUp}
       ref={(el: BoxRenderable) => {
+        if (props.separate) alwaysSeparate.add(el)
         setPreLayoutSiblingMargin(el, (previous) => {
-          return previous instanceof BoxRenderable && (previous.height > 1 || alwaysSeparate.has(previous)) ? 1 : 0
+          return props.separate ||
+            (previous instanceof BoxRenderable && (previous.height > 1 || alwaysSeparate.has(previous)))
+            ? 1
+            : 0
         })
       }}
     >
@@ -2281,6 +2288,7 @@ function Task(props: ToolProps) {
   return (
     <InlineTool
       icon={props.part.state.status === "completed" ? "✓" : "│"}
+      separate={true}
       color={retry() ? theme.error : undefined}
       spinner={isRunning()}
       complete={stringValue(props.input.description)}

+ 4 - 1
packages/tui/test/cli/tui/__snapshots__/inline-tool-wrap-snapshot.test.tsx.snap

@@ -60,16 +60,19 @@ exports[`TUI inline tool wrapping keeps separation after a padded user message 1
 
 exports[`TUI inline tool wrapping separates after a multi-line task row 1`] = `
 "   ✱ Grep "Task" (2 matches)
+
    ⠙ Explore Task — Inspect active task spacing
+
    ✓ General Task — Confirm completed task spacing
      ↳ 1 toolcall · 501ms
 
    → Read src/cli/cmd/tui/routes/session/index.tsx"
 `;
 
-exports[`TUI inline tool wrapping does not treat task rows differently from other inline rows 1`] = `
+exports[`TUI inline tool wrapping separates a task row from a preceding inline detail 1`] = `
 "   → Read src/cli/cmd/tui/routes/session/index.tsx
    ↳ Loaded src/cli/cmd/tui/routes/session/tools.tsx
+
    ✓ Explore Task — Inspect active task spacing
      ↳ 1 toolcall · 501ms"
 `;

+ 4 - 4
packages/tui/test/cli/tui/inline-tool-wrap-snapshot.test.tsx

@@ -112,10 +112,10 @@ function TaskRowsFixture() {
       <InlineToolRow icon="✱" complete={true} pending="">
         Grep "Task" (2 matches)
       </InlineToolRow>
-      <InlineToolRow icon="⠙" complete={true} pending="">
+      <InlineToolRow icon="⠙" complete={true} pending="" separate={true}>
         Explore Task — Inspect active task spacing
       </InlineToolRow>
-      <InlineToolRow icon="✓" complete={true} pending="">
+      <InlineToolRow icon="✓" complete={true} pending="" separate={true}>
         {"General Task — Confirm completed task spacing\n↳ 1 toolcall · 501ms"}
       </InlineToolRow>
       <InlineToolRow icon="→" complete={true} pending="">
@@ -134,7 +134,7 @@ function LoadedReadBeforeTaskFixture() {
       <box paddingLeft={3}>
         <text paddingLeft={3}>↳ Loaded src/cli/cmd/tui/routes/session/tools.tsx</text>
       </box>
-      <InlineToolRow icon="✓" complete={true} pending="">
+      <InlineToolRow icon="✓" complete={true} pending="" separate={true}>
         {"Explore Task — Inspect active task spacing\n↳ 1 toolcall · 501ms"}
       </InlineToolRow>
     </box>
@@ -312,7 +312,7 @@ describe("TUI inline tool wrapping", () => {
     expect(await renderFrame(() => <TaskRowsFixture />, { width: 72, height: 10 })).toMatchSnapshot()
   })
 
-  test("does not treat task rows differently from other inline rows", async () => {
+  test("separates a task row from a preceding inline detail", async () => {
     expect(await renderFrame(() => <LoadedReadBeforeTaskFixture />, { width: 72, height: 8 })).toMatchSnapshot()
   })