mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-04-30 12:40:44 +00:00
Merge pull request #2776 from QwenLM/feat/enhance-btw-command
feat(cli): enhance /btw side question with improved prompt and Ctrl+C/D cancel
This commit is contained in:
parent
61bc80fe19
commit
e855229453
16 changed files with 351 additions and 57 deletions
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { render } from 'ink-testing-library';
|
||||
import { renderWithProviders } from '../../../test-utils/render.js';
|
||||
import { BtwMessage } from './BtwMessage.js';
|
||||
|
||||
describe('BtwMessage', () => {
|
||||
|
|
@ -16,7 +16,7 @@ describe('BtwMessage', () => {
|
|||
});
|
||||
|
||||
it('renders the side question and answer', () => {
|
||||
const { lastFrame } = render(
|
||||
const { lastFrame } = renderWithProviders(
|
||||
<BtwMessage
|
||||
btw={{
|
||||
question: 'side question',
|
||||
|
|
@ -31,4 +31,57 @@ describe('BtwMessage', () => {
|
|||
expect(output).toContain('side question');
|
||||
expect(output).toContain('side answer');
|
||||
});
|
||||
|
||||
it('renders pending state with cancel hint', () => {
|
||||
const { lastFrame } = renderWithProviders(
|
||||
<BtwMessage
|
||||
btw={{
|
||||
question: 'pending question',
|
||||
answer: '',
|
||||
isPending: true,
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
|
||||
const output = lastFrame() ?? '';
|
||||
expect(output).toContain('/btw');
|
||||
expect(output).toContain('pending question');
|
||||
expect(output).toContain('Answering...');
|
||||
expect(output).toContain('Ctrl+C');
|
||||
expect(output).toContain('Ctrl+D');
|
||||
});
|
||||
|
||||
it('accepts containerWidth prop for content width calculation', () => {
|
||||
const { lastFrame } = renderWithProviders(
|
||||
<BtwMessage
|
||||
btw={{
|
||||
question: 'q',
|
||||
answer: 'some answer text',
|
||||
isPending: false,
|
||||
}}
|
||||
containerWidth={60}
|
||||
/>,
|
||||
);
|
||||
|
||||
const output = lastFrame() ?? '';
|
||||
expect(output).toContain('some answer text');
|
||||
});
|
||||
|
||||
it('renders dismiss hint when answer is complete', () => {
|
||||
const { lastFrame } = renderWithProviders(
|
||||
<BtwMessage
|
||||
btw={{
|
||||
question: 'q',
|
||||
answer: 'a',
|
||||
isPending: false,
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
|
||||
const output = lastFrame() ?? '';
|
||||
expect(output).toContain('Space');
|
||||
expect(output).toContain('Enter');
|
||||
expect(output).toContain('Escape');
|
||||
expect(output).toContain('dismiss');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -9,46 +9,80 @@ import { Box, Text } from 'ink';
|
|||
import type { BtwProps } from '../../types.js';
|
||||
import { Colors } from '../../colors.js';
|
||||
import { t } from '../../../i18n/index.js';
|
||||
import { MarkdownDisplay } from '../../utils/MarkdownDisplay.js';
|
||||
import { useTerminalSize } from '../../hooks/useTerminalSize.js';
|
||||
|
||||
export interface BtwDisplayProps {
|
||||
btw: BtwProps;
|
||||
/** Width of the parent container. Used to compute Markdown content width.
|
||||
* Falls back to terminal width when not provided. */
|
||||
containerWidth?: number;
|
||||
}
|
||||
|
||||
const BtwMessageInternal: React.FC<BtwDisplayProps> = ({ btw }) => (
|
||||
<Box
|
||||
flexDirection="column"
|
||||
borderStyle="round"
|
||||
borderColor={Colors.AccentYellow}
|
||||
paddingX={1}
|
||||
width="100%"
|
||||
>
|
||||
<Box flexDirection="row">
|
||||
<Text color={Colors.AccentYellow} bold>
|
||||
{'/btw '}
|
||||
</Text>
|
||||
<Text wrap="wrap" color={Colors.AccentYellow}>
|
||||
{btw.question}
|
||||
</Text>
|
||||
// border(1)*2 + paddingX(1)*2 = 4
|
||||
const BTW_SELF_CHROME = 4;
|
||||
|
||||
/**
|
||||
* Ensure code fences (``` or ~~~) start on their own line so that
|
||||
* MarkdownDisplay's line-based parser can detect them. Models sometimes
|
||||
* emit the opening fence right after prose text without a preceding newline.
|
||||
*/
|
||||
function normalizeCodeFences(text: string): string {
|
||||
return text.replace(/([^\n])(```|~~~)/g, '$1\n$2');
|
||||
}
|
||||
|
||||
const BtwMessageInternal: React.FC<BtwDisplayProps> = ({
|
||||
btw,
|
||||
containerWidth,
|
||||
}) => {
|
||||
const { columns: terminalWidth } = useTerminalSize();
|
||||
const baseWidth = containerWidth ?? terminalWidth;
|
||||
const contentWidth = Math.max(2, baseWidth - BTW_SELF_CHROME);
|
||||
|
||||
return (
|
||||
<Box
|
||||
flexDirection="column"
|
||||
borderStyle="round"
|
||||
borderColor={Colors.AccentYellow}
|
||||
paddingX={1}
|
||||
width="100%"
|
||||
>
|
||||
<Box flexDirection="row">
|
||||
<Text color={Colors.AccentYellow} bold>
|
||||
{'/btw '}
|
||||
</Text>
|
||||
<Text wrap="wrap" color={Colors.AccentYellow}>
|
||||
{btw.question}
|
||||
</Text>
|
||||
</Box>
|
||||
{btw.isPending ? (
|
||||
<Box flexDirection="column" marginTop={1}>
|
||||
<Box>
|
||||
<Text color={Colors.AccentYellow}>{'+ '}</Text>
|
||||
<Text color={Colors.AccentYellow}>{t('Answering...')}</Text>
|
||||
</Box>
|
||||
<Box marginTop={1}>
|
||||
<Text dimColor>
|
||||
{t('Press Escape, Ctrl+C, or Ctrl+D to cancel')}
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
) : (
|
||||
<Box flexDirection="column" marginTop={1}>
|
||||
<MarkdownDisplay
|
||||
text={normalizeCodeFences(btw.answer)}
|
||||
isPending={false}
|
||||
contentWidth={contentWidth}
|
||||
/>
|
||||
<Box marginTop={1}>
|
||||
<Text dimColor>
|
||||
{t('Press Space, Enter, or Escape to dismiss')}
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
{btw.isPending ? (
|
||||
<Box flexDirection="column" marginTop={1}>
|
||||
<Box>
|
||||
<Text color={Colors.AccentYellow}>{'+ '}</Text>
|
||||
<Text color={Colors.AccentYellow}>{t('Answering...')}</Text>
|
||||
</Box>
|
||||
<Box marginTop={1}>
|
||||
<Text dimColor>{t('Press Escape to cancel')}</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
) : (
|
||||
<Box flexDirection="column" marginTop={1}>
|
||||
<Text wrap="wrap">{btw.answer}</Text>
|
||||
<Box marginTop={1}>
|
||||
<Text dimColor>{t('Press Space, Enter, or Escape to dismiss')}</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
);
|
||||
};
|
||||
|
||||
export const BtwMessage = React.memo(BtwMessageInternal);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue