mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-07-10 01:29:17 +00:00
fix(cli): render full resume preview history (#5565)
Some checks are pending
Qwen Code CI / Classify PR (push) Waiting to run
Qwen Code CI / Lint (push) Blocked by required conditions
Qwen Code CI / Test (macos-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Test (ubuntu-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Test (windows-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Post Coverage Comment (push) Blocked by required conditions
Qwen Code CI / CodeQL (push) Blocked by required conditions
Qwen Code CI / Integration Tests (CLI, No Sandbox) (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none (push) Waiting to run
E2E Tests / E2E Test - macOS (push) Waiting to run
Some checks are pending
Qwen Code CI / Classify PR (push) Waiting to run
Qwen Code CI / Lint (push) Blocked by required conditions
Qwen Code CI / Test (macos-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Test (ubuntu-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Test (windows-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Post Coverage Comment (push) Blocked by required conditions
Qwen Code CI / CodeQL (push) Blocked by required conditions
Qwen Code CI / Integration Tests (CLI, No Sandbox) (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none (push) Waiting to run
E2E Tests / E2E Test - macOS (push) Waiting to run
Co-authored-by: cyphercodes <cyphercodes@users.noreply.github.com>
This commit is contained in:
parent
e88356c6b7
commit
b6988325e9
2 changed files with 104 additions and 37 deletions
|
|
@ -35,7 +35,11 @@ function mockService(resolved: unknown) {
|
|||
} as never;
|
||||
}
|
||||
|
||||
function fakeResumedData() {
|
||||
function fakeResumedData(
|
||||
assistantParts: Array<{ text: string; thought?: boolean }> = [
|
||||
{ text: 'Hi from assistant REPLY-MARKER' },
|
||||
],
|
||||
) {
|
||||
return {
|
||||
conversation: {
|
||||
sessionId: 's1',
|
||||
|
|
@ -66,7 +70,7 @@ function fakeResumedData() {
|
|||
version: 'test',
|
||||
message: {
|
||||
role: 'model',
|
||||
parts: [{ text: 'Hi from assistant REPLY-MARKER' }],
|
||||
parts: assistantParts,
|
||||
},
|
||||
},
|
||||
],
|
||||
|
|
@ -112,6 +116,39 @@ describe('SessionPreview', () => {
|
|||
expect(frame).toContain('REPLY-MARKER');
|
||||
});
|
||||
|
||||
it('renders full resumed thinking content after load', async () => {
|
||||
const thinkingText = Array.from(
|
||||
{ length: 40 },
|
||||
(_, index) => `RESUMED-THINKING-LINE-${index + 1}`,
|
||||
).join('\n');
|
||||
const svc = mockService(
|
||||
fakeResumedData([
|
||||
{ text: thinkingText, thought: true },
|
||||
{ text: 'FINAL-ANSWER-MARKER' },
|
||||
]),
|
||||
);
|
||||
const { lastFrame } = render(
|
||||
<KeypressProvider kittyProtocolEnabled={false}>
|
||||
<SessionPreview
|
||||
sessionService={svc}
|
||||
sessionId="s1"
|
||||
sessionTitle="My session"
|
||||
onExit={vi.fn()}
|
||||
onResume={vi.fn()}
|
||||
/>
|
||||
</KeypressProvider>,
|
||||
);
|
||||
await wait(100);
|
||||
const frame = lastFrame() ?? '';
|
||||
expect(frame).toContain('Thinking…');
|
||||
expect(frame).toContain('RESUMED-THINKING-LINE-1');
|
||||
expect(frame).toContain('RESUMED-THINKING-LINE-40');
|
||||
expect(frame).toContain('FINAL-ANSWER-MARKER');
|
||||
expect(frame.indexOf('My session')).toBeLessThan(
|
||||
frame.indexOf('RESUMED-THINKING-LINE-1'),
|
||||
);
|
||||
});
|
||||
|
||||
it('renders footer metadata (messageCount · time · branch)', async () => {
|
||||
const svc = mockService(fakeResumedData());
|
||||
const { lastFrame } = render(
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { Box, Text } from 'ink';
|
||||
import { Box, Static, Text } from 'ink';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import type {
|
||||
ResumedSessionData,
|
||||
|
|
@ -126,56 +126,86 @@ export function SessionPreview(props: SessionPreviewProps) {
|
|||
}
|
||||
const metaLine = metaParts.join(' · ');
|
||||
|
||||
const header = (
|
||||
<Box key="header" paddingX={1}>
|
||||
<Text bold color={theme.text.primary}>
|
||||
{sessionTitle ?? t('Session Preview')}
|
||||
</Text>
|
||||
</Box>
|
||||
);
|
||||
const topSeparator = (
|
||||
<Box key="top-separator">
|
||||
<Text color={theme.border.default}>{'─'.repeat(separatorWidth)}</Text>
|
||||
</Box>
|
||||
);
|
||||
const footerSeparator = (
|
||||
<Box key="footer-separator">
|
||||
<Text color={theme.border.default}>{'─'.repeat(separatorWidth)}</Text>
|
||||
</Box>
|
||||
);
|
||||
const meta = metaLine ? (
|
||||
<Box key="meta" paddingX={1}>
|
||||
<Text color={theme.text.secondary}>{metaLine}</Text>
|
||||
</Box>
|
||||
) : null;
|
||||
const footer = (
|
||||
<Box key="footer" paddingX={1}>
|
||||
<Text color={theme.text.secondary}>
|
||||
{t('Enter to resume · Esc to back')}
|
||||
</Text>
|
||||
</Box>
|
||||
);
|
||||
|
||||
if (data && !error) {
|
||||
return (
|
||||
<Box flexDirection="column" width={boxWidth}>
|
||||
<Static
|
||||
key={sessionId}
|
||||
items={[
|
||||
header,
|
||||
topSeparator,
|
||||
...items.map((item) => (
|
||||
<HistoryItemDisplay
|
||||
key={item.id}
|
||||
item={item}
|
||||
terminalWidth={boxWidth}
|
||||
isPending={false}
|
||||
/>
|
||||
)),
|
||||
footerSeparator,
|
||||
...(meta ? [meta] : []),
|
||||
footer,
|
||||
]}
|
||||
>
|
||||
{(item) => item}
|
||||
</Static>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box flexDirection="column" width={boxWidth}>
|
||||
{/* Header */}
|
||||
<Box paddingX={1}>
|
||||
<Text bold color={theme.text.primary}>
|
||||
{sessionTitle ?? t('Session Preview')}
|
||||
</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text color={theme.border.default}>{'─'.repeat(separatorWidth)}</Text>
|
||||
</Box>
|
||||
{header}
|
||||
{topSeparator}
|
||||
|
||||
{/* Body: render all items, let the terminal's scrollback own overflow. */}
|
||||
{/* Body */}
|
||||
{error ? (
|
||||
<Box paddingY={1} justifyContent="center">
|
||||
<Text color={theme.status.error}>{error}</Text>
|
||||
</Box>
|
||||
) : !data ? (
|
||||
) : (
|
||||
<Box paddingY={1} justifyContent="center">
|
||||
<Text color={theme.text.secondary}>
|
||||
{t('Loading session preview...')}
|
||||
</Text>
|
||||
</Box>
|
||||
) : (
|
||||
<Box flexDirection="column">
|
||||
{items.map((item) => (
|
||||
<HistoryItemDisplay
|
||||
key={item.id}
|
||||
item={item}
|
||||
terminalWidth={boxWidth}
|
||||
isPending={false}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* Footer */}
|
||||
<Box>
|
||||
<Text color={theme.border.default}>{'─'.repeat(separatorWidth)}</Text>
|
||||
</Box>
|
||||
{metaLine && (
|
||||
<Box paddingX={1}>
|
||||
<Text color={theme.text.secondary}>{metaLine}</Text>
|
||||
</Box>
|
||||
)}
|
||||
<Box paddingX={1}>
|
||||
<Text color={theme.text.secondary}>
|
||||
{t('Enter to resume · Esc to back')}
|
||||
</Text>
|
||||
</Box>
|
||||
{footerSeparator}
|
||||
{meta}
|
||||
{footer}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue