fix(tui): better footer subagent link and alignment (#1283)

This commit is contained in:
Christina Tong
2026-03-05 17:35:43 -07:00
committed by GitHub
parent 6a65ff5008
commit a185d567ba

View File

@@ -267,7 +267,7 @@ const InputFooter = memo(function InputFooter({
// Subscribe to subagent state for background agent indicators // Subscribe to subagent state for background agent indicators
useSyncExternalStore(subscribeToSubagents, getSubagentSnapshot); useSyncExternalStore(subscribeToSubagents, getSubagentSnapshot);
const backgroundAgents = getActiveBackgroundAgents(); const backgroundAgents = [...getActiveBackgroundAgents()];
// Tick counter for elapsed time display (only active when background agents exist) // Tick counter for elapsed time display (only active when background agents exist)
const [, setTick] = useState(0); const [, setTick] = useState(0);
@@ -277,26 +277,18 @@ const InputFooter = memo(function InputFooter({
return () => clearInterval(t); return () => clearInterval(t);
}, [backgroundAgents.length]); }, [backgroundAgents.length]);
// Build background agent display text (no useMemo — must recalculate each tick for elapsed time) // Background agent display parts for the footer indicator
const bgAgentText = const bgAgentParts = backgroundAgents.map((a) => {
backgroundAgents.length === 0 const elapsedS = Math.round((Date.now() - a.startTime) / 1000);
? "" const agentId =
: backgroundAgents a.agentURL?.match(/\/(?:agents|chat)\/([^/?#]+)/)?.[1] ?? null;
.map((a) => { return {
const elapsedS = Math.round((Date.now() - a.startTime) / 1000); id: a.id,
const agentId = a.agentURL?.match(/\/agents\/([^/]+)/)?.[1]; typeLabel: a.type.toLowerCase(),
const chatUrl = agentId ? buildChatUrl(agentId) : null; chatUrl: agentId ? buildChatUrl(agentId) : null,
const typeLabel = a.type.toLowerCase(); elapsed: `${elapsedS}s`,
const linkedType = chatUrl };
? `\x1b]8;;${chatUrl}\x1b\\${typeLabel}\x1b]8;;\x1b\\` });
: typeLabel;
return `${linkedType} (${elapsedS}s)`;
})
.join(" · ");
// Width of the background agent indicator: "· " + text + " │ "
const bgIndicatorWidth =
backgroundAgents.length > 0 ? 2 + stringWidth(bgAgentText) + 3 : 0;
const maxAgentChars = Math.max(10, Math.floor(rightColumnWidth * 0.45)); const maxAgentChars = Math.max(10, Math.floor(rightColumnWidth * 0.45));
const displayAgentName = truncateEnd(agentName || "Unnamed", maxAgentChars); const displayAgentName = truncateEnd(agentName || "Unnamed", maxAgentChars);
@@ -309,11 +301,32 @@ const InputFooter = memo(function InputFooter({
const maxModelChars = Math.max(8, rightColumnWidth - baseReservedChars); const maxModelChars = Math.max(8, rightColumnWidth - baseReservedChars);
const displayModel = truncateEnd(modelWithReasoning, maxModelChars); const displayModel = truncateEnd(modelWithReasoning, maxModelChars);
const rightTextLength = const rightTextLength =
displayAgentName.length + displayModel.length + byokExtraChars + 3; displayAgentName.length + displayModel.length + byokExtraChars + 3;
const rightPrefixSpaces = Math.max(0, rightColumnWidth - rightTextLength); const rightPrefixSpaces = Math.max(0, rightColumnWidth - rightTextLength);
// When bg agents are active, widen the right column to fit the indicator + label
// "· " (2) + parts text + " │ " (3)
const bgIndicatorWidth =
backgroundAgents.length > 0
? 2 +
bgAgentParts.reduce(
(acc, p, i) =>
acc +
(i > 0 ? 3 : 0) +
p.typeLabel.length +
1 +
p.elapsed.length +
2,
0,
) +
3
: 0;
const effectiveRightWidth =
backgroundAgents.length > 0
? Math.max(rightColumnWidth, bgIndicatorWidth + rightTextLength)
: rightColumnWidth;
// Agent label without leading spaces (used by both default and bg-agent cases) // Agent label without leading spaces (used by both default and bg-agent cases)
const rightLabelCore = useMemo(() => { const rightLabelCore = useMemo(() => {
const parts: string[] = []; const parts: string[] = [];
@@ -387,7 +400,9 @@ const InputFooter = memo(function InputFooter({
statusLineRight && !hideFooterContent ? "flex-end" : undefined statusLineRight && !hideFooterContent ? "flex-end" : undefined
} }
width={ width={
statusLineRight && !hideFooterContent ? undefined : rightColumnWidth statusLineRight && !hideFooterContent
? undefined
: effectiveRightWidth
} }
flexShrink={0} flexShrink={0}
> >
@@ -401,9 +416,26 @@ const InputFooter = memo(function InputFooter({
)) ))
) : backgroundAgents.length > 0 ? ( ) : backgroundAgents.length > 0 ? (
<Text> <Text>
{" ".repeat(Math.max(0, rightPrefixSpaces - bgIndicatorWidth))}
<BlinkDot color={colors.tool.pending} symbol="·" /> <BlinkDot color={colors.tool.pending} symbol="·" />
<Text dimColor>{` ${bgAgentText}`}</Text> <Text dimColor> </Text>
{bgAgentParts.map((part, i) => (
<Text key={`bg-agent-${part}`}>
{i > 0 && (
<Text key={`bg-agent-indicator-${part}`} dimColor>
{" · "}
</Text>
)}
{part.chatUrl ? (
<Link url={part.chatUrl}>
<Text dimColor>{part.typeLabel}</Text>
</Link>
) : (
<Text dimColor>{part.typeLabel}</Text>
)}
<Text dimColor> ({part.elapsed})</Text>
</Text>
))}
<Text dimColor>{" │ "}</Text>
{rightLabelCore} {rightLabelCore}
</Text> </Text>
) : ( ) : (