mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-05-20 01:01:53 +00:00
fix(cli): widen LiveAgentPanel, drop [in turn] marker, point overflow at dialog
Three usability fixes from review: 1. Use `terminalWidth` instead of `mainAreaWidth`. The latter is capped at 100 cols (intended for markdown / code where soft-wrap matters), which on a 200-col terminal left half the screen empty to the right of an already-truncating row. Live progress lines have nothing to soft-wrap, so the panel wants the full width. 2. Drop the `[in turn]` foreground marker. The flavor distinction matters in BackgroundTasksDialog (cancel semantics differ for foreground vs background entries) but in the glance panel the marker reads as cryptic noise — users asked what it meant. Keep the dialog as the surface that surfaces it. 3. Annotate the overflow callout with `(↓ to view all)`. The panel is intentionally read-only (it has no keyboard focus so it can't steal input from the composer), so when the roster outgrows the row budget we point users at the existing dialog — same keystroke the footer pill uses, kept in sync so users only learn one gesture.
This commit is contained in:
parent
860c4ee212
commit
bfecc094c8
3 changed files with 37 additions and 7 deletions
|
|
@ -156,7 +156,10 @@ describe('<LiveAgentPanel />', () => {
|
|||
expect(frame).toContain('5s');
|
||||
});
|
||||
|
||||
it('marks foreground agents with the [in turn] prefix', () => {
|
||||
it('does NOT surface a flavor marker on foreground agents', () => {
|
||||
// Foreground vs background distinction stays with BackgroundTasksDialog
|
||||
// (where cancel semantics differ); the panel reads as a glance roster
|
||||
// and the marker added more confusion than signal.
|
||||
const { lastFrame } = renderPanel({
|
||||
entries: [
|
||||
agentEntry({
|
||||
|
|
@ -167,7 +170,10 @@ describe('<LiveAgentPanel />', () => {
|
|||
}),
|
||||
],
|
||||
});
|
||||
expect(lastFrame() ?? '').toContain('[in turn]');
|
||||
const frame = lastFrame() ?? '';
|
||||
expect(frame).not.toContain('[in turn]');
|
||||
expect(frame).toContain('editor');
|
||||
expect(frame).toContain('tighten import order');
|
||||
});
|
||||
|
||||
it('windows from the tail when entries exceed maxRows', () => {
|
||||
|
|
@ -190,8 +196,11 @@ describe('<LiveAgentPanel />', () => {
|
|||
];
|
||||
const { lastFrame } = renderPanel({ entries, maxRows: 2 });
|
||||
const frame = lastFrame() ?? '';
|
||||
// `more above` callout flagged with the dropped count.
|
||||
// `more above` callout flagged with the dropped count and points
|
||||
// at the dialog (the only surface where the user can scroll
|
||||
// through the full roster + take action).
|
||||
expect(frame).toContain('1 more above');
|
||||
expect(frame).toContain('to view all');
|
||||
// Tail window keeps the newest two rows.
|
||||
expect(frame).toContain('mid-agent');
|
||||
expect(frame).toContain('fresh-agent');
|
||||
|
|
|
|||
|
|
@ -237,9 +237,17 @@ export const LiveAgentPanel: React.FC<LiveAgentPanelProps> = ({
|
|||
</Box>
|
||||
{overflow > 0 && (
|
||||
<Box>
|
||||
{/*
|
||||
The panel is read-only (no keyboard focus — that would
|
||||
steal input from the composer), so when the roster
|
||||
overflows the row budget we point users at the dialog
|
||||
that DOES support selection / scroll / cancel / resume.
|
||||
Same keystroke the footer pill uses, kept in sync so
|
||||
users only have to learn one thing.
|
||||
*/}
|
||||
<Text
|
||||
color={theme.text.secondary}
|
||||
>{` ^ ${overflow} more above`}</Text>
|
||||
>{` ^ ${overflow} more above (↓ to view all)`}</Text>
|
||||
</Box>
|
||||
)}
|
||||
{visible.map((entry) => (
|
||||
|
|
@ -255,7 +263,13 @@ const AgentRow: React.FC<{ entry: AgentDialogEntry; now: number }> = ({
|
|||
}) => {
|
||||
const { glyph, color } = statusIcon(entry);
|
||||
const label = descriptionWithoutPrefix(entry);
|
||||
const flavorPrefix = entry.flavor === 'foreground' ? '[in turn] ' : '';
|
||||
// Note: foreground vs background is intentionally not surfaced here.
|
||||
// Earlier iterations prefixed foreground rows with `[in turn]` (the
|
||||
// BackgroundTasksDialog convention), but in the panel context the
|
||||
// marker reads as cryptic — the foreground / background distinction
|
||||
// matters in the dialog (where cancel semantics differ) but the
|
||||
// glance roster just needs identity + intent + cost. Keep the
|
||||
// dialog as the place that surfaces the flavor distinction.
|
||||
const activity = activityLabel(entry);
|
||||
const elapsed = elapsedLabel(entry, now);
|
||||
const showType =
|
||||
|
|
@ -296,7 +310,7 @@ const AgentRow: React.FC<{ entry: AgentDialogEntry; now: number }> = ({
|
|||
<Text color={theme.text.secondary}>{': '}</Text>
|
||||
</>
|
||||
)}
|
||||
<Text color={theme.text.secondary}>{`${flavorPrefix}${label}`}</Text>
|
||||
<Text color={theme.text.secondary}>{label}</Text>
|
||||
{activity && (
|
||||
<Text color={theme.text.secondary}>{` (${activity})`}</Text>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -115,9 +115,16 @@ export const DefaultAppLayout: React.FC = () => {
|
|||
open (auth / permission / background tasks / etc.) so the modal
|
||||
surface doesn't compete with the live roster, and the panel's
|
||||
own internal self-hide handles the empty-roster case.
|
||||
|
||||
Panel uses `terminalWidth`, not `mainAreaWidth` — `mainAreaWidth`
|
||||
is hard-capped at 100 cols (intended for markdown / code blocks
|
||||
where soft-wrap matters), which on wider terminals leaves a
|
||||
large empty gutter to the right of an already-truncating row.
|
||||
Live progress lines have nothing to soft-wrap, so the panel
|
||||
wants the full terminal width.
|
||||
*/}
|
||||
{!isAgentTab && !uiState.dialogsVisible && (
|
||||
<LiveAgentPanel width={uiState.mainAreaWidth} />
|
||||
<LiveAgentPanel width={uiState.terminalWidth} />
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue