refactor(tui): simplify inline tool spacing (#33097)

This commit is contained in:
Dax 2026-06-20 15:35:20 +02:00 committed by GitHub
commit 009f3799cd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 62 additions and 72 deletions

View file

@ -91,6 +91,8 @@ const GO_UPSELL_ACCOUNT_RATE_LIMIT_DONT_SHOW = "go_upsell_account_rate_limit_don
const GO_UPSELL_WINDOW = 86_400_000 // 24 hrs
const GO_UPSELL_PROVIDERS = new Set(["opencode", "opencode-go"])
export const alwaysSeparate = new WeakSet<BoxRenderable>()
type RetryAction = Extract<SessionStatus, { type: "retry" }>["action"]
function goUpsellKeys(action: RetryAction) {
@ -160,7 +162,6 @@ const context = createContext<{
showTimestamps: () => boolean
showDetails: () => boolean
showGenericToolOutput: () => boolean
userMessageIDs: () => ReadonlySet<string>
diffWrapMode: () => "word" | "none"
providers: () => ReadonlyMap<string, Provider>
sync: ReturnType<typeof useSync>
@ -218,14 +219,6 @@ export function Session() {
)
: [],
)
const userMessageIDs = createMemo(
() =>
new Set(
messages()
.filter((message) => message.role === "user")
.map((message) => message.id),
),
)
const permissions = createMemo(() => {
if (session()?.parentID) return []
return children().flatMap((x) => sync.data.permission[x.id] ?? [])
@ -1158,7 +1151,6 @@ export function Session() {
showTimestamps,
showDetails,
showGenericToolOutput,
userMessageIDs,
diffWrapMode,
providers,
sync,
@ -1395,6 +1387,7 @@ function UserMessage(props: {
<Show when={text()}>
<box
id={props.message.id}
ref={(el: BoxRenderable) => alwaysSeparate.add(el)}
border={["left"]}
borderColor={color()}
customBorderChars={SplitBorder.customBorderChars}
@ -1532,7 +1525,7 @@ function AssistantMessage(props: { message: AssistantMessage; parts: Part[]; las
</Show>
<Show when={props.message.error && props.message.error.name !== "MessageAbortedError"}>
<box
id={`assistant-error-${props.message.id}`}
ref={(el: BoxRenderable) => alwaysSeparate.add(el)}
border={["left"]}
paddingTop={1}
paddingBottom={1}
@ -1547,7 +1540,7 @@ function AssistantMessage(props: { message: AssistantMessage; parts: Part[]; las
</Show>
<Switch>
<Match when={props.last || final() || props.message.error?.name === "MessageAbortedError"}>
<box id={`assistant-summary-${props.message.id}`} paddingLeft={3}>
<box ref={(el: BoxRenderable) => alwaysSeparate.add(el)} paddingLeft={3}>
<text marginTop={1}>
<span
style={{
@ -1613,7 +1606,7 @@ function ReasoningPart(props: { last: boolean; part: ReasoningPart; message: Ass
return (
<Show when={content()}>
<box
id={`text-${props.part.messageID}-${props.part.id}`}
ref={(el: BoxRenderable) => alwaysSeparate.add(el)}
paddingLeft={3}
marginTop={1}
flexDirection="column"
@ -1695,7 +1688,7 @@ function TextPart(props: { last: boolean; part: TextPart; message: AssistantMess
const { theme, syntax } = useTheme()
return (
<Show when={props.part.text.trim()}>
<box id={`text-${props.part.messageID}-${props.part.id}`} paddingLeft={3} marginTop={1} flexShrink={0}>
<box ref={(el: BoxRenderable) => alwaysSeparate.add(el)} paddingLeft={3} marginTop={1} flexShrink={0}>
<markdown
syntaxStyle={syntax()}
streaming={true}
@ -1845,7 +1838,6 @@ function InlineTool(props: {
pending: string
failure?: string
spinner?: boolean
subagent?: boolean
children: JSX.Element
part: ToolPart
onClick?: () => void
@ -1886,7 +1878,6 @@ function InlineTool(props: {
return (
<InlineToolRow
id={`tool-inline-${props.subagent ? "subagent-" : ""}${props.part.messageID}-${props.part.id}`}
icon={props.icon}
iconColor={props.iconColor}
color={fg()}
@ -1899,8 +1890,6 @@ function InlineTool(props: {
pending={props.pending}
failure={props.failure}
spinner={props.spinner}
subagent={props.subagent}
separateAfter={(id) => id !== undefined && ctx.userMessageIDs().has(id)}
onMouseOver={() => clickable() && setHover(true)}
onMouseOut={() => setHover(false)}
onMouseUp={() => {
@ -1918,7 +1907,6 @@ function InlineTool(props: {
}
export function InlineToolRow(props: {
id?: string
icon: string
iconColor?: RGBA
color?: RGBA
@ -1931,32 +1919,20 @@ export function InlineToolRow(props: {
pending: string
failure?: string
spinner?: boolean
subagent?: boolean
children: JSX.Element
separateAfter?: (id: string | undefined) => boolean
onMouseOver?: () => void
onMouseOut?: () => void
onMouseUp?: () => void
}) {
return (
<box
id={props.id}
paddingLeft={3}
onMouseOver={props.onMouseOver}
onMouseOut={props.onMouseOut}
onMouseUp={props.onMouseUp}
ref={(el: BoxRenderable) => {
setPreLayoutSiblingMargin(el, (previous) => {
const previousInline = previous?.id.startsWith("tool-inline-") ?? false
const previousSubagent = previous?.id.startsWith("tool-inline-subagent-") ?? false
return previous?.id.startsWith("text-") ||
previous?.id.startsWith("tool-block-") ||
previous?.id.startsWith("assistant-error-") ||
previous?.id.startsWith("assistant-summary-") ||
(previousInline && previousSubagent !== Boolean(props.subagent)) ||
props.separateAfter?.(previous?.id)
? 1
: 0
return previous instanceof BoxRenderable && (previous.height > 1 || alwaysSeparate.has(previous)) ? 1 : 0
})
}}
>
@ -2018,7 +1994,7 @@ function BlockTool(props: {
const error = createMemo(() => (props.part?.state.status === "error" ? props.part.state.error : undefined))
return (
<box
id={props.part ? `tool-block-${props.part.messageID}-${props.part.id}` : undefined}
ref={(el: BoxRenderable) => alwaysSeparate.add(el)}
border={["left"]}
paddingTop={1}
paddingBottom={1}
@ -2184,8 +2160,8 @@ function Read(props: ToolProps) {
Read {pathFormatter.format(stringValue(props.input.filePath))} {input(props.input, ["filePath"])}
</InlineTool>
<For each={loaded()}>
{(filepath, index) => (
<box id={`tool-inline-loaded-${props.part.messageID}-${props.part.id}-${index()}`} paddingLeft={3}>
{(filepath) => (
<box paddingLeft={3}>
<text paddingLeft={3} fg={theme.textMuted}>
Loaded {pathFormatter.format(filepath)}
</text>
@ -2305,7 +2281,6 @@ function Task(props: ToolProps) {
return (
<InlineTool
icon={props.part.state.status === "completed" ? "✓" : "│"}
subagent={true}
color={retry() ? theme.error : undefined}
spinner={isRunning()}
complete={stringValue(props.input.description)}