klyro 1.0.13 → 1.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/repl.js +1 -1
- package/dist/tui/app.js +14 -10
- package/dist/tui/app.test.js +29 -0
- package/package.json +1 -1
package/dist/cli/repl.js
CHANGED
|
@@ -2689,7 +2689,7 @@ export async function startRepl(opts = {}) {
|
|
|
2689
2689
|
' Enter send · Shift+Enter newline · Tab complete slash · Esc drop queued / Esc×2 cancel run',
|
|
2690
2690
|
' Ctrl+C cancel (1st) / quit (2nd) · Ctrl+O expand last tool group · Ctrl+G jump bottom',
|
|
2691
2691
|
' PgUp/PgDn or Ctrl+U/Ctrl+D half-page · Ctrl+Home/End top/bottom · Home/End jump · Space jump to unread',
|
|
2692
|
-
' Ctrl+B/F page · Shift/Ctrl+↑/↓ line · ↑ history at live tail, scrolls while reading above · PgUp/Dn scroll (KLYRO_MOUSE=1 adds wheel ±3 lines)',
|
|
2692
|
+
' Ctrl+B/F page · Shift/Ctrl+↑/↓ line · ↑ history at live tail, scrolls while reading above · Ctrl+P/N history always · PgUp/Dn or Ctrl+U/D scroll (KLYRO_MOUSE=1 adds wheel ±3 lines)',
|
|
2693
2693
|
' Text selection/copy and right-click paste work natively · Shift+Enter newline · /vim toggles vim input mode · /keymap <note> saves a display note',
|
|
2694
2694
|
].join('\n'),
|
|
2695
2695
|
});
|
package/dist/tui/app.js
CHANGED
|
@@ -1056,10 +1056,15 @@ export function App(props) {
|
|
|
1056
1056
|
// Scrolled up reading the transcript (not at the live tail, not already
|
|
1057
1057
|
// browsing) → plain ↑ keeps scrolling so chat scroll is never lost.
|
|
1058
1058
|
// At the live tail (or no scroll surface) → ↑ browses input history
|
|
1059
|
-
// newest-first, so send + ↑ restores the last prompt.
|
|
1060
|
-
//
|
|
1061
|
-
|
|
1062
|
-
|
|
1059
|
+
// newest-first, so send + ↑ restores the last prompt. Ctrl+P / Ctrl+N
|
|
1060
|
+
// are escape-free aliases for history prev/next: single-byte codes that
|
|
1061
|
+
// always browse, even on terminals that mangle arrow-key sequences.
|
|
1062
|
+
// ↓ is otherwise unchanged: browse newer while browsing, else scroll
|
|
1063
|
+
// toward the tail.
|
|
1064
|
+
const wantHistPrev = (key.upArrow && !key.shift && !key.ctrl) || (key.ctrl && inputStr === 'p');
|
|
1065
|
+
const wantHistNext = (key.downArrow && !key.shift && !key.ctrl) || (key.ctrl && inputStr === 'n');
|
|
1066
|
+
if (wantHistPrev) {
|
|
1067
|
+
if (!key.ctrl && isFullscreen && maxTop > 0 && !atBottom && histIdx === null && input.trim() === '') {
|
|
1063
1068
|
commands.lineUp();
|
|
1064
1069
|
return;
|
|
1065
1070
|
}
|
|
@@ -1068,14 +1073,13 @@ export function App(props) {
|
|
|
1068
1073
|
setHistIdx(next);
|
|
1069
1074
|
setInput(history[next] ?? '');
|
|
1070
1075
|
setVimCursor(null);
|
|
1071
|
-
return;
|
|
1072
1076
|
}
|
|
1073
|
-
if (isFullscreen && maxTop > 0) {
|
|
1077
|
+
else if (!key.ctrl && isFullscreen && maxTop > 0) {
|
|
1074
1078
|
commands.lineUp();
|
|
1075
|
-
return;
|
|
1076
1079
|
}
|
|
1080
|
+
return;
|
|
1077
1081
|
}
|
|
1078
|
-
if (
|
|
1082
|
+
if (wantHistNext) {
|
|
1079
1083
|
if (histIdx !== null) {
|
|
1080
1084
|
const next = histIdx + 1;
|
|
1081
1085
|
if (next >= history.length) {
|
|
@@ -1089,8 +1093,8 @@ export function App(props) {
|
|
|
1089
1093
|
setVimCursor(null);
|
|
1090
1094
|
return;
|
|
1091
1095
|
}
|
|
1092
|
-
if (input.trim() !== '')
|
|
1093
|
-
return; //
|
|
1096
|
+
if (input.trim() !== '' || key.ctrl)
|
|
1097
|
+
return; // text present, or Ctrl+N: nothing newer
|
|
1094
1098
|
if (isFullscreen && maxTop > 0) {
|
|
1095
1099
|
commands.lineDown();
|
|
1096
1100
|
return;
|
package/dist/tui/app.test.js
CHANGED
|
@@ -292,6 +292,35 @@ describe('App', () => {
|
|
|
292
292
|
await new Promise((r) => setTimeout(r, 30));
|
|
293
293
|
expect(lastFrame() ?? '').not.toContain('zznoscroll!');
|
|
294
294
|
});
|
|
295
|
+
it('Ctrl+P / Ctrl+N browse history without arrow keys', async () => {
|
|
296
|
+
const onPrompt = vi.fn(async () => { });
|
|
297
|
+
const { stdin, lastFrame } = render(_jsx(App, { initialModel: "m", maxSteps: 10, cwd: "/test", onPrompt: onPrompt, onSlash: async () => { } }));
|
|
298
|
+
stdin.write('zzfirst');
|
|
299
|
+
await new Promise((r) => setTimeout(r, 20));
|
|
300
|
+
stdin.write('\x0d');
|
|
301
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
302
|
+
stdin.write('zzsecond');
|
|
303
|
+
await new Promise((r) => setTimeout(r, 20));
|
|
304
|
+
stdin.write('\x0d');
|
|
305
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
306
|
+
expect(onPrompt).toHaveBeenCalledTimes(2);
|
|
307
|
+
// Ctrl+P recalls newest-first with zero escape sequences involved.
|
|
308
|
+
stdin.write('\x10');
|
|
309
|
+
await new Promise((r) => setTimeout(r, 30));
|
|
310
|
+
stdin.write('!');
|
|
311
|
+
await new Promise((r) => setTimeout(r, 30));
|
|
312
|
+
expect(lastFrame() ?? '').toContain('zzsecond!');
|
|
313
|
+
// Ctrl+P twice walks older with no typing between, Ctrl+N walks newer.
|
|
314
|
+
stdin.write('\x10');
|
|
315
|
+
await new Promise((r) => setTimeout(r, 30));
|
|
316
|
+
stdin.write('\x10');
|
|
317
|
+
await new Promise((r) => setTimeout(r, 30));
|
|
318
|
+
stdin.write('\x0e');
|
|
319
|
+
await new Promise((r) => setTimeout(r, 30));
|
|
320
|
+
stdin.write('#');
|
|
321
|
+
await new Promise((r) => setTimeout(r, 30));
|
|
322
|
+
expect(lastFrame() ?? '').toContain('zzsecond#');
|
|
323
|
+
});
|
|
295
324
|
it('/c shows top-6 suggestions and Tab completes', async () => {
|
|
296
325
|
const { stdin, lastFrame } = render(_jsx(App, { initialModel: "m", maxSteps: 10, cwd: "/test", onPrompt: async () => { }, onSlash: async () => { } }));
|
|
297
326
|
stdin.write('/c');
|