diff --git a/source/components/user-input.spec.tsx b/source/components/user-input.spec.tsx
index 2e57a44f..eafd4a2b 100644
--- a/source/components/user-input.spec.tsx
+++ b/source/components/user-input.spec.tsx
@@ -3,7 +3,7 @@ import {render} from 'ink-testing-library';
import React from 'react';
import {themes} from '../config/themes';
import {ThemeContext} from '../hooks/useTheme';
-import {UIStateProvider} from '../hooks/useUIState';
+import {UIStateProvider, useUIStateContext} from '../hooks/useUIState';
import UserInput from './user-input';
console.log(`\nuser-input.spec.tsx – ${React.version}`);
@@ -393,6 +393,7 @@ test('UserInput does not show ctrl-o hint when onToggleCompactDisplay is not pro
unmount();
});
+
// ============================================================================
// Command Completion Navigation Tests
// ============================================================================
@@ -484,3 +485,91 @@ test('completion menu dismissal/reset after selection or escape', async t => {
unmount();
});
+
+test('UserInput renders completions text when typing /', async t => {
+ const {stdin, lastFrame, unmount} = render(
+
+
+
+ );
+
+ await new Promise(resolve => setTimeout(resolve, 50));
+ stdin.write('/');
+ await new Promise(resolve => setTimeout(resolve, 150));
+
+ const output = lastFrame()!;
+ t.truthy(output);
+ t.regex(output, /Available commands:/);
+ unmount();
+});
+
+test('UserInput renders completions BEFORE the mode indicator (inside the input box)', async t => {
+ const {stdin, lastFrame, unmount} = render(
+
+
+
+ );
+
+ await new Promise(resolve => setTimeout(resolve, 50));
+ stdin.write('/');
+ await new Promise(resolve => setTimeout(resolve, 150));
+
+ const output = lastFrame()!;
+ t.truthy(output);
+
+ const completionsIdx = output.indexOf('Available commands:');
+ const modeIdx = output.indexOf('normal mode');
+ t.true(completionsIdx > -1, 'Completions text should be present');
+ t.true(modeIdx > -1, 'Mode indicator should be present');
+ t.true(
+ completionsIdx < modeIdx,
+ 'Completions must render before the mode indicator (inside the bordered input box)',
+ );
+ unmount();
+});
+
+test('UserInput completions appear on a line above the mode indicator', async t => {
+ const {stdin, lastFrame, unmount} = render(
+
+
+
+ );
+
+ await new Promise(resolve => setTimeout(resolve, 50));
+ stdin.write('/');
+ await new Promise(resolve => setTimeout(resolve, 150));
+
+ const output = lastFrame()!;
+ const lines = output.split('\n');
+
+ let completionLine = -1;
+ let modeLine = -1;
+ for (let i = 0; i < lines.length; i++) {
+ if (lines[i].includes('Available commands:')) completionLine = i;
+ if (lines[i].includes('normal mode')) modeLine = i;
+ }
+
+ t.true(completionLine > -1, 'Should find completions line');
+ t.true(modeLine > -1, 'Should find mode indicator line');
+ t.true(
+ completionLine < modeLine,
+ `Completions (line ${completionLine}) must be above mode indicator (line ${modeLine})`,
+ );
+ unmount();
+});
+
+test('UserInput does not show completions when input is empty', t => {
+ const {lastFrame, unmount} = render(
+
+
+
+ );
+
+ const output = lastFrame()!;
+ t.truthy(output);
+ t.notRegex(output, /Available commands:/);
+ unmount();
+});
+
+
+
diff --git a/source/components/user-input.tsx b/source/components/user-input.tsx
index a291bbb1..b654f8b3 100644
--- a/source/components/user-input.tsx
+++ b/source/components/user-input.tsx
@@ -651,47 +651,6 @@ export default function UserInput({
)}
- {showCompletions && completions.length > 0 && (
-
- Available commands:
- {completions.map((completion, index) => {
- const isSelected = index === selectedCompletionIndex;
- return (
-
- {isSelected ? '▸ ' : ' '}/{completion.name}
-
- );
- })}
-
- )}
- {isFileAutocompleteMode && fileCompletions.length > 0 && (
-
-
- File suggestions (↑/↓ to navigate, Tab to select):
-
- {fileCompletions.slice(0, 5).map((file, index) => (
-
- {index === selectedFileIndex ? '▸ ' : ' '}
- {file.path}
-
- ))}
-
- )}
-
Press escape again to clear
)}
+
+ {showCompletions && completions.length > 0 && (
+
+ Available commands:
+ {completions.map((completion, index) => {
+ const isSelected = index === selectedCompletionIndex;
+ return (
+
+ {isSelected ? '▸ ' : ' '}/{completion.name}
+
+ );
+ })}
+
+ )}
+ {isFileAutocompleteMode && fileCompletions.length > 0 && (
+
+
+ File suggestions (↑/↓ to navigate, Tab to select):
+
+ {fileCompletions.slice(0, 5).map((file, index) => (
+
+ {index === selectedFileIndex ? '▸ ' : ' '}
+ {file.path}
+
+ ))}
+
+ )}
{attachments.length > 0 && (
@@ -738,7 +740,6 @@ export default function UserInput({
· ctrl-x remove last
)}
-
{/* Development mode indicator - always visible */}