Skip to content

Commit 26c28ec

Browse files
perf(tui): don't regenerate command suggestions in chat input
1 parent f7bdabd commit 26c28ec

6 files changed

Lines changed: 23 additions & 61 deletions

File tree

src/components/Chat.tsx

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export function Chat({ model, onCommand, autoExecute }: Props) {
2727
role: ROLE.ASSISTANT,
2828
content: '',
2929
};
30+
3031
setMessages((previousMessages) => [
3132
...previousMessages,
3233
assistantMessage,
@@ -156,7 +157,9 @@ export function Chat({ model, onCommand, autoExecute }: Props) {
156157
const handleSubmit = useCallback(
157158
async (value: string) => {
158159
const userContent = value.trim();
159-
if (!userContent) return;
160+
if (!userContent) {
161+
return;
162+
}
160163

161164
if (userContent.startsWith('/')) {
162165
onCommand(userContent);
@@ -191,12 +194,8 @@ export function Chat({ model, onCommand, autoExecute }: Props) {
191194
)}
192195

193196
{!pendingToolCall && (
194-
<ChatInput
195-
isDisabled={isLoading}
196-
onSubmit={(val) => {
197-
void handleSubmit(val);
198-
}}
199-
/>
197+
// eslint-disable-next-line @typescript-eslint/no-misused-promises
198+
<ChatInput isDisabled={isLoading} onSubmit={handleSubmit} />
200199
)}
201200
</Box>
202201
);

src/components/ChatInput.test.tsx

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,7 @@
11
import { render } from 'ink-testing-library';
22

3-
import { tick } from '../utils/test';
4-
5-
vi.mock('../constants', async (importOriginal) => {
6-
const actual = await importOriginal<typeof import('../constants')>();
7-
return {
8-
...actual,
9-
COMMANDS: [
10-
{ name: '/model', description: 'switch the model' },
11-
{ name: '/mock', description: 'mock command' },
12-
],
13-
};
14-
});
15-
163
import { KEY } from '../constants';
4+
import { tick } from '../utils/test';
175
import { ChatInput } from './ChatInput';
186

197
describe('ChatInput', () => {
@@ -36,18 +24,6 @@ describe('ChatInput', () => {
3624
expect(lastFrame()).toContain('/model');
3725
});
3826

39-
it('filters inline suggestions as input narrows', async () => {
40-
const { lastFrame, stdin } = render(<ChatInput onSubmit={vi.fn()} />);
41-
stdin.write('/');
42-
await tick();
43-
stdin.write('m');
44-
await tick();
45-
expect(lastFrame()).toContain('/model');
46-
stdin.write('x');
47-
await tick();
48-
expect(lastFrame()).not.toContain('/model');
49-
});
50-
5127
it('submits typed text on Enter', async () => {
5228
const onSubmit = vi.fn();
5329
const { stdin } = render(<ChatInput onSubmit={onSubmit} />);

src/components/ChatInput.tsx

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,17 @@
11
import { TextInput } from '@inkjs/ui';
22
import { Box, Text } from 'ink';
3-
import { useCallback, useMemo, useState } from 'react';
3+
import { useCallback, useState } from 'react';
44

5-
import { COMMANDS, UI } from '../constants';
5+
import { COMMAND, UI } from '../constants';
66

77
interface Props {
88
isDisabled?: boolean;
99
onSubmit: (value: string) => void;
1010
}
1111

12-
function getSuggestions(input: string): string[] {
13-
if (!input.startsWith('/')) {
14-
return [];
15-
}
16-
17-
return COMMANDS.filter((command) => command.name.startsWith(input)).map(
18-
(command) => command.name,
19-
);
20-
}
21-
2212
export function ChatInput({ isDisabled = false, onSubmit }: Props) {
23-
const [value, setValue] = useState('');
2413
const [resetKey, setResetKey] = useState(0);
2514

26-
const suggestions = useMemo(() => getSuggestions(value), [value]);
27-
2815
const handleSubmit = useCallback(
2916
(input: string) => {
3017
const trimmed = input.trim();
@@ -33,7 +20,6 @@ export function ChatInput({ isDisabled = false, onSubmit }: Props) {
3320
}
3421

3522
onSubmit(trimmed);
36-
setValue('');
3723
setResetKey((key) => key + 1);
3824
},
3925
[onSubmit],
@@ -42,12 +28,11 @@ export function ChatInput({ isDisabled = false, onSubmit }: Props) {
4228
return (
4329
<Box>
4430
<Text>{UI.PROMPT_PREFIX}</Text>
31+
4532
<TextInput
46-
key={resetKey}
4733
isDisabled={isDisabled}
48-
defaultValue=""
49-
suggestions={suggestions}
50-
onChange={setValue}
34+
key={resetKey}
35+
suggestions={COMMAND.NAMES}
5136
onSubmit={handleSubmit}
5237
/>
5338
</Box>

src/constants/command.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
export interface CommandList {
2+
name: string;
3+
description: string;
4+
}
5+
6+
export const LIST: CommandList[] = [
7+
{ name: '/model', description: 'switch the model' },
8+
] as const;
9+
10+
export const NAMES = LIST.map(({ name }) => name);

src/constants/commands.ts

Lines changed: 0 additions & 8 deletions
This file was deleted.

src/constants/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export * from './commands';
1+
export * as COMMAND from './command';
22
export * as KEY from './key';
33
export * as PACKAGE from './package';
44
export * as PROMPT from './prompt';

0 commit comments

Comments
 (0)