mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-04-28 11:41:04 +00:00
feat(cli): background-agent UI — pill, combined dialog, detail view
Surfaces background agents in the TUI with a combined pill/dialog pattern and a responsive detail view that splits or full-swaps based on terminal height. Transcript state (messages, pending approvals, live outputs, shell PIDs) moves from AgentInteractive into AgentCore so both Arena and background agents render through a shared AgentChatContent component without duplicating renderer logic.
This commit is contained in:
parent
5ac51f353d
commit
fcf62011c3
25 changed files with 1748 additions and 366 deletions
|
|
@ -47,6 +47,10 @@ import {
|
|||
useAgentViewState,
|
||||
useAgentViewActions,
|
||||
} from '../contexts/AgentViewContext.js';
|
||||
import {
|
||||
useBackgroundAgentViewState,
|
||||
useBackgroundAgentViewActions,
|
||||
} from '../contexts/BackgroundAgentViewContext.js';
|
||||
import { FEEDBACK_DIALOG_KEYS } from '../FeedbackDialog.js';
|
||||
import { BaseTextInput } from './BaseTextInput.js';
|
||||
import type { RenderLineOptions } from './BaseTextInput.js';
|
||||
|
|
@ -124,7 +128,11 @@ 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 hasAgents = agents.size > 0;
|
||||
const hasBgAgents = bgEntries.length > 0;
|
||||
const [justNavigatedHistory, setJustNavigatedHistory] = useState(false);
|
||||
const [escPressCount, setEscPressCount] = useState(0);
|
||||
const [showEscapePrompt, setShowEscapePrompt] = useState(false);
|
||||
|
|
@ -425,11 +433,11 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
|
|||
|
||||
const handleInput = useCallback(
|
||||
(key: Key): boolean => {
|
||||
// When the tab bar has focus, block all 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).
|
||||
// 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) {
|
||||
if (
|
||||
key.sequence &&
|
||||
|
|
@ -442,6 +450,16 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
|
|||
return true; // consume non-printable keys
|
||||
}
|
||||
|
||||
// When the Background tasks dialog is open, swallow every key so
|
||||
// nothing reaches the composer buffer — the dialog's own keypress
|
||||
// handler owns selection, open/close, and stop actions. Unlike
|
||||
// the tab bar we do NOT let printable chars type through, because
|
||||
// the dialog doesn't auto-close on printable input and users
|
||||
// would leak text into the hidden composer.
|
||||
if (bgDialogOpen) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO(jacobr): this special case is likely not needed anymore.
|
||||
// We should probably stop supporting paste if the InputPrompt is not
|
||||
// focused.
|
||||
|
|
@ -897,10 +915,19 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
|
|||
if (inputHistory.navigateDown()) {
|
||||
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.
|
||||
if (hasAgents) {
|
||||
setAgentTabBarFocused(true);
|
||||
return true;
|
||||
}
|
||||
if (hasBgAgents) {
|
||||
openBgDialog();
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
|
|
@ -1064,8 +1091,11 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
|
|||
parsePlaceholder,
|
||||
freePlaceholderId,
|
||||
agentTabBarFocused,
|
||||
bgDialogOpen,
|
||||
hasAgents,
|
||||
hasBgAgents,
|
||||
setAgentTabBarFocused,
|
||||
openBgDialog,
|
||||
followup,
|
||||
onPromptSuggestionDismiss,
|
||||
],
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue