diff --git a/packages/tui/src/routes/session/index.tsx b/packages/tui/src/routes/session/index.tsx index c9eb7ed5fa13..67f727a9c87c 100644 --- a/packages/tui/src/routes/session/index.tsx +++ b/packages/tui/src/routes/session/index.tsx @@ -2819,10 +2819,15 @@ function Shell(props: ToolProps) { }) const maxLines = 10 const maxChars = createMemo(() => maxLines * Math.max(20, ctx.width - 6)) + const prompt = createMemo(() => (workdir() && workdir() !== "." ? `${workdir()}$` : "$")) const input = createMemo(() => { - if (!command()) return "" - const prompt = workdir() && workdir() !== "." ? `${workdir()}$ ` : isRunning() ? "" : "$ " - return `${prompt}${command()}` + const cmd = command() + if (!cmd) return "" + // While running, the workdir prompt shares the spinner's text column; when + // settled, the prompt renders as its own column so wrapped command lines + // keep a stable hanging indent instead of jumping to the card inset. + if (isRunning() && prompt() !== "$") return `${prompt()} ${cmd}` + return cmd }) const content = createMemo(() => [input(), output()].filter(Boolean).join("\n\n")) const collapsed = createMemo(() => collapseToolOutput(content(), maxLines, maxChars())) @@ -2830,6 +2835,8 @@ function Shell(props: ToolProps) { if (expanded() || !collapsed().overflow) return content() return collapsed().output }) + const limitedInput = createMemo(() => limited().slice(0, input().length)) + const limitedOutput = createMemo(() => limited().slice(Math.min(limited().length, input().length + 2))) const expandable = createMemo(() => Boolean(shellID()) || collapsed().overflow) const toggle = () => { const next = !expanded() @@ -2853,16 +2860,16 @@ function Shell(props: ToolProps) { - {limited().slice(0, input().length)} - {limited().slice(input().length)} - + + {prompt()} + {limitedInput()} + } > - - {limited().slice(0, input().length)} - {limited().slice(input().length)} - + {limitedInput()} + + + {limitedOutput()}