@tiens.nguyen/gonext-local-worker 1.0.305 → 1.0.307
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/gonext-repl.mjs +97 -35
- package/package.json +1 -1
package/gonext-repl.mjs
CHANGED
|
@@ -297,12 +297,13 @@ rl.on("line", (l) => {
|
|
|
297
297
|
// command's own output doesn't print over/under a stale hint block. If a menu item was
|
|
298
298
|
// highlighted (↑/↓), RUN that command instead of the raw typed line.
|
|
299
299
|
let submitted = l;
|
|
300
|
-
if (
|
|
300
|
+
if (slashOverlayActive) {
|
|
301
|
+
// A highlighted menu item (↑/↓) RUNS that command instead of the raw typed line.
|
|
301
302
|
if (slashSel >= 0 && slashNames[slashSel]) submitted = slashNames[slashSel];
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
303
|
+
closeSlashOverlay(); // wipe the menu, re-enable readline, restore history
|
|
304
|
+
// readline's own newline echo was suppressed during the overlay — emit it so the
|
|
305
|
+
// command's output starts on a fresh line, not over the ">> …" input line (#109).
|
|
306
|
+
process.stdout.write("\r\n");
|
|
306
307
|
}
|
|
307
308
|
const clean = stripAnsiEscapes(submitted);
|
|
308
309
|
if (_lineWaiter) {
|
|
@@ -322,27 +323,81 @@ rl.on("line", (l) => {
|
|
|
322
323
|
// runs the highlighted command, and it DISAPPEARS when you clear the slash — no permanent
|
|
323
324
|
// scrollback copies. Relative cursor moves so it survives terminal scrolling.
|
|
324
325
|
// (slashSel / slashNames / slashPrefix are declared above, near rl.on("line").)
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
326
|
+
// While the slash-hint overlay is open, EMPTY readline's history so ↑/↓ don't recall a
|
|
327
|
+
// history entry and trigger readline's OWN line-refresh — that refresh fought our overlay
|
|
328
|
+
// and reprinted the whole ">> /" + hint block on every arrow press (bug #109). With no
|
|
329
|
+
// history to recall, readline's ↑/↓ are inert and our keypress handler owns the highlight.
|
|
330
|
+
// Restored the instant the overlay closes, so normal task-history recall still works.
|
|
331
|
+
let _slashSavedHistory = null;
|
|
332
|
+
function slashNeutralizeHistory() {
|
|
333
|
+
if (_slashSavedHistory === null && Array.isArray(rl.history)) {
|
|
334
|
+
_slashSavedHistory = rl.history;
|
|
335
|
+
rl.history = [];
|
|
336
|
+
rl.historyIndex = -1;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
function slashRestoreHistory() {
|
|
340
|
+
if (_slashSavedHistory !== null) {
|
|
341
|
+
rl.history = _slashSavedHistory;
|
|
342
|
+
_slashSavedHistory = null;
|
|
343
|
+
rl.historyIndex = -1;
|
|
344
|
+
}
|
|
332
345
|
}
|
|
333
|
-
|
|
334
|
-
|
|
346
|
+
// While the overlay is active WE own all terminal drawing: readline's own echo +
|
|
347
|
+
// line-refresh are no-op'd (see slashOverlayActive in _writeToOutput/_refreshLine
|
|
348
|
+
// below), exactly like the /server + approval pickers. That's what makes the redraw
|
|
349
|
+
// stable — readline can't clearScreenDown over our menu or fight our cursor (bug #109,
|
|
350
|
+
// which the earlier relative-cursor overlay hit whenever readline refreshed or the
|
|
351
|
+
// menu was near the bottom of the screen and scrolled).
|
|
352
|
+
let slashOverlayActive = false;
|
|
353
|
+
|
|
354
|
+
// Redraw the input line (">> " + typed text) AND the command menu below it, fully
|
|
355
|
+
// in place. On first activation we scroll N blank rows into existence below the input
|
|
356
|
+
// line then return to it, so every later redraw writes into that reserved space
|
|
357
|
+
// WITHOUT scrolling — which keeps the save/restore-cursor (ESC 7 / ESC 8) anchor valid.
|
|
358
|
+
function renderSlashOverlay(sel) {
|
|
359
|
+
const line = rl.line;
|
|
360
|
+
const cmds = filteredCommands(line);
|
|
335
361
|
slashNames = cmds.map((c) => c.name);
|
|
336
362
|
if (sel >= cmds.length) sel = cmds.length - 1;
|
|
337
363
|
slashSel = sel;
|
|
338
|
-
|
|
339
|
-
const
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
364
|
+
slashPrefix = line;
|
|
365
|
+
const menu = hintRows(cmds, sel);
|
|
366
|
+
const n = menu.length;
|
|
367
|
+
|
|
368
|
+
if (!slashOverlayActive) {
|
|
369
|
+
slashOverlayActive = true;
|
|
370
|
+
slashNeutralizeHistory();
|
|
371
|
+
process.stdout.write("\n".repeat(n) + `\x1b[${n}A`); // reserve n rows, back to input
|
|
372
|
+
slashHintRows = n;
|
|
373
|
+
} else if (n !== slashHintRows) {
|
|
374
|
+
// The filtered list changed size: clear the old menu, re-reserve.
|
|
375
|
+
process.stdout.write("\x1b7\n\x1b[J\x1b8");
|
|
376
|
+
process.stdout.write("\n".repeat(n) + `\x1b[${n}A`);
|
|
377
|
+
slashHintRows = n;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Input line (carriage-return, clear, prompt + typed), then the menu below, anchored
|
|
381
|
+
// by ESC 7 (save at end of input) / ESC 8 (restore) so the caret ends on the input.
|
|
382
|
+
let out = "\r\x1b[2K" + PROMPT + line + "\x1b7";
|
|
383
|
+
for (const r of menu) out += "\n\x1b[2K" + r;
|
|
384
|
+
out += "\x1b8";
|
|
344
385
|
process.stdout.write(out);
|
|
345
|
-
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// Close the overlay: erase the menu below, keep the typed input line, hand drawing back
|
|
389
|
+
// to readline. Callers that changed the line to a non-slash value should refresh after.
|
|
390
|
+
function closeSlashOverlay() {
|
|
391
|
+
if (!slashOverlayActive) {
|
|
392
|
+
slashRestoreHistory();
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
process.stdout.write("\x1b7\n\x1b[J\x1b8"); // save, wipe below, restore to input line
|
|
396
|
+
slashOverlayActive = false;
|
|
397
|
+
slashHintRows = 0;
|
|
398
|
+
slashSel = -1;
|
|
399
|
+
slashNames = [];
|
|
400
|
+
slashRestoreHistory();
|
|
346
401
|
}
|
|
347
402
|
if (process.stdin.isTTY) {
|
|
348
403
|
process.stdin.on("keypress", (_ch, key) => {
|
|
@@ -370,26 +425,27 @@ if (process.stdin.isTTY) {
|
|
|
370
425
|
}
|
|
371
426
|
// Enter is handled by rl.on("line") — don't redraw the hint on the way out.
|
|
372
427
|
if (key && (key.name === "return" || key.name === "enter")) return;
|
|
373
|
-
// ↑/↓ while the menu is up = move the highlight
|
|
374
|
-
//
|
|
375
|
-
//
|
|
376
|
-
if (
|
|
377
|
-
rl.
|
|
378
|
-
rl.cursor = slashPrefix.length;
|
|
428
|
+
// ↑/↓ while the menu is up = move the highlight IN PLACE. readline drawing is
|
|
429
|
+
// suppressed (slashOverlayActive) and history neutralized, so its own ↑/↓ do
|
|
430
|
+
// nothing — we just move the selection and re-render the overlay (bug #109).
|
|
431
|
+
if (slashOverlayActive && key && (key.name === "up" || key.name === "down")) {
|
|
432
|
+
rl.historyIndex = -1;
|
|
379
433
|
const n = slashNames.length;
|
|
380
434
|
if (n > 0) {
|
|
381
435
|
slashSel =
|
|
382
436
|
key.name === "down" ? (slashSel + 1 + n) % n : (slashSel - 1 + n) % n;
|
|
383
437
|
}
|
|
384
|
-
|
|
385
|
-
drawSlashHint(slashPrefix, slashSel);
|
|
438
|
+
renderSlashOverlay(slashSel);
|
|
386
439
|
return;
|
|
387
440
|
}
|
|
388
441
|
if (rl.line.startsWith("/")) {
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
} else {
|
|
392
|
-
|
|
442
|
+
// Typing narrows the list and resets the selection to none.
|
|
443
|
+
renderSlashOverlay(-1);
|
|
444
|
+
} else if (slashOverlayActive) {
|
|
445
|
+
// The line no longer starts with "/" (e.g. backspaced the slash away): tear down
|
|
446
|
+
// the overlay and let readline redraw the now-normal line.
|
|
447
|
+
closeSlashOverlay();
|
|
448
|
+
rl._refreshLine();
|
|
393
449
|
}
|
|
394
450
|
});
|
|
395
451
|
}
|
|
@@ -1090,7 +1146,9 @@ let globalOutputTokens = 0;
|
|
|
1090
1146
|
// writes to stdout directly, so it's unaffected — only readline's echo is swallowed.
|
|
1091
1147
|
const _origWriteToOutput = rl._writeToOutput ? rl._writeToOutput.bind(rl) : null;
|
|
1092
1148
|
rl._writeToOutput = (s) => {
|
|
1093
|
-
|
|
1149
|
+
// slashOverlayActive: the slash-command overlay owns drawing (input line + menu), so
|
|
1150
|
+
// readline's own echo must be swallowed or it fights the overlay (bug #109).
|
|
1151
|
+
if (following || listPickerActive || slashOverlayActive) return;
|
|
1094
1152
|
if (_origWriteToOutput) _origWriteToOutput(s);
|
|
1095
1153
|
else process.stdout.write(s);
|
|
1096
1154
|
};
|
|
@@ -1100,7 +1158,9 @@ rl._writeToOutput = (s) => {
|
|
|
1100
1158
|
// ZERO terminal output.
|
|
1101
1159
|
const _origRefreshLine = rl._refreshLine ? rl._refreshLine.bind(rl) : null;
|
|
1102
1160
|
rl._refreshLine = () => {
|
|
1103
|
-
|
|
1161
|
+
// Suppress readline's line refresh (which does clearScreenDown → would wipe our menu)
|
|
1162
|
+
// while the slash overlay owns the screen. We redraw the input line ourselves (#109).
|
|
1163
|
+
if (following || listPickerActive || slashOverlayActive) return;
|
|
1104
1164
|
if (_origRefreshLine) _origRefreshLine();
|
|
1105
1165
|
};
|
|
1106
1166
|
|
|
@@ -1979,6 +2039,8 @@ async function main() {
|
|
|
1979
2039
|
process.stdout.write(red("\n^C "));
|
|
1980
2040
|
}
|
|
1981
2041
|
} else {
|
|
2042
|
+
// Ctrl+C at a prompt with the slash overlay open = dismiss the overlay first (#109).
|
|
2043
|
+
if (slashOverlayActive) closeSlashOverlay();
|
|
1982
2044
|
process.stdout.write(dim("\n(/exit to quit)\n"));
|
|
1983
2045
|
// Discard anything half-typed (Ctrl-C at a prompt means "never mind") and
|
|
1984
2046
|
// redraw through readline so its cursor tracking stays right.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tiens.nguyen/gonext-local-worker",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.307",
|
|
4
4
|
"description": "Polls GoNext cloud API for async local LLM jobs and runs them against Ollama/OpenAI-compatible servers on this Mac",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|