feat(cli): focus background-tasks pill on ↓, cap detail view at 50% height

Pressing ↓ from an empty composer now focuses the footer pill instead
of opening the dialog — Enter opens it, ↑/Esc returns to the input.
Mirrors the Arena tab-bar focus pattern so the two surfaces feel the
same and accidental dialog opens go away.

The detail view is capped at ~50% of the available terminal height,
using MaxSizedBox for content-aware truncation so long prompts and
activity logs don't swallow the screen.
This commit is contained in:
tanzhenxin 2026-04-22 20:01:31 +08:00
parent fcf62011c3
commit e6ef8382f5
7 changed files with 192 additions and 74 deletions

View file

@ -128,9 +128,12 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
const { pasteWorkaround } = useKeypressContext();
const { agents, agentTabBarFocused } = useAgentViewState();
const { setAgentTabBarFocused } = useAgentViewActions();
const { entries: bgEntries, dialogOpen: bgDialogOpen } =
useBackgroundAgentViewState();
const { openDialog: openBgDialog } = useBackgroundAgentViewActions();
const {
entries: bgEntries,
dialogOpen: bgDialogOpen,
pillFocused: bgPillFocused,
} = useBackgroundAgentViewState();
const { setPillFocused: setBgPillFocused } = useBackgroundAgentViewActions();
const hasAgents = agents.size > 0;
const hasBgAgents = bgEntries.length > 0;
const [justNavigatedHistory, setJustNavigatedHistory] = useState(false);
@ -433,12 +436,12 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
const handleInput = useCallback(
(key: Key): boolean => {
// When the Arena tab bar has focus, block non-printable keys so
// arrow keys and shortcuts don't interfere. Printable characters
// fall through to BaseTextInput's default handler so the first
// keystroke appears in the input immediately (the tab bar handler
// releases focus on the same event).
if (agentTabBarFocused) {
// When the Arena tab bar or background pill has focus, block
// non-printable keys so arrow keys and shortcuts don't interfere.
// Printable characters fall through to BaseTextInput's default
// handler so the first keystroke appears in the input immediately
// (each surface's own handler releases focus on the same event).
if (agentTabBarFocused || bgPillFocused) {
if (
key.sequence &&
key.sequence.length === 1 &&
@ -916,16 +919,17 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
return true;
}
// Focus order on Down from an empty composer:
// team tab bar (if any Arena agents) → Background tasks dialog
// (if any bg agents) → otherwise stay put. The tab bar itself
// re-routes Down into the bg dialog once it has focus, so both
// surfaces remain reachable in sequence.
// team tab bar (if any Arena agents) → Background tasks pill
// (if any bg agents) → otherwise stay put. The pill itself
// opens the dialog on Enter; the tab bar re-routes Down into
// the pill once it has focus, so both surfaces remain reachable
// in sequence.
if (hasAgents) {
setAgentTabBarFocused(true);
return true;
}
if (hasBgAgents) {
openBgDialog();
setBgPillFocused(true);
return true;
}
return true;
@ -1092,10 +1096,11 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
freePlaceholderId,
agentTabBarFocused,
bgDialogOpen,
bgPillFocused,
hasAgents,
hasBgAgents,
setAgentTabBarFocused,
openBgDialog,
setBgPillFocused,
followup,
onPromptSuggestionDismiss,
],