@tiens.nguyen/gonext-local-worker 1.0.211 → 1.0.213
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 +61 -22
- package/package.json +1 -1
package/gonext-repl.mjs
CHANGED
|
@@ -175,26 +175,31 @@ function slashCompleter(line) {
|
|
|
175
175
|
const hits = COMMAND_NAMES.filter((n) => n.startsWith(line));
|
|
176
176
|
return [hits.length ? hits : COMMAND_NAMES, line];
|
|
177
177
|
}
|
|
178
|
-
|
|
178
|
+
// Commands whose name/alias matches the typed prefix (all when prefix is "" or "/").
|
|
179
|
+
function filteredCommands(prefix = "") {
|
|
179
180
|
const p = (prefix ?? "").trim();
|
|
180
|
-
const
|
|
181
|
+
const m = COMMANDS.filter(
|
|
181
182
|
(c) =>
|
|
182
183
|
!p ||
|
|
183
184
|
p === "/" ||
|
|
184
185
|
[c.name, ...(c.aliases ?? [])].some((n) => n.startsWith(p))
|
|
185
186
|
);
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
187
|
+
return m.length ? m : COMMANDS;
|
|
188
|
+
}
|
|
189
|
+
// Display rows for a filtered command set; row `sel` (if >=0) is highlighted (❯ + bold).
|
|
190
|
+
function hintRows(cmds, sel) {
|
|
191
|
+
const rows = [dim(" commands (↑/↓ select · Enter run · Tab complete):")];
|
|
192
|
+
cmds.forEach((c, i) => {
|
|
189
193
|
const label = c.usage ?? c.name;
|
|
190
194
|
const alias = c.aliases?.length ? dim(` (${c.aliases.join(", ")})`) : "";
|
|
191
|
-
|
|
192
|
-
}
|
|
195
|
+
const body = `${cyan(label)}${alias}${dim(" — " + c.desc)}`;
|
|
196
|
+
rows.push(i === sel ? `${green("❯")} ${bold(body)}` : ` ${body}`);
|
|
197
|
+
});
|
|
193
198
|
return rows;
|
|
194
199
|
}
|
|
195
200
|
// Permanent print (used by /help and a lone-"/" submit — scrolls with history).
|
|
196
201
|
function printCommands(prefix = "") {
|
|
197
|
-
for (const r of
|
|
202
|
+
for (const r of hintRows(filteredCommands(prefix), -1)) console.log(r);
|
|
198
203
|
console.log("");
|
|
199
204
|
}
|
|
200
205
|
|
|
@@ -228,6 +233,9 @@ const _lineQueue = [];
|
|
|
228
233
|
let _lineWaiter = null;
|
|
229
234
|
let _stdinClosed = false;
|
|
230
235
|
let slashHintRows = 0; // live slash-command hint rows drawn below the prompt (0 = none)
|
|
236
|
+
let slashSel = -1; // highlighted row in the current filtered list (-1 = none)
|
|
237
|
+
let slashNames = []; // command names in the current filtered list (for Enter → run)
|
|
238
|
+
let slashPrefix = ""; // the "/…" text the user typed (restored when ↑/↓ recall history)
|
|
231
239
|
rl.on("line", (l) => {
|
|
232
240
|
// While a turn is running, IGNORE typed input entirely — no type-ahead, no queued
|
|
233
241
|
// next question. Only Ctrl+C (handled via SIGINT) does anything mid-turn. Clear the
|
|
@@ -238,12 +246,17 @@ rl.on("line", (l) => {
|
|
|
238
246
|
return;
|
|
239
247
|
}
|
|
240
248
|
// Enter left the cursor on the row where the live slash-hint started — erase it so the
|
|
241
|
-
// command's own output doesn't print over/under a stale hint block.
|
|
249
|
+
// command's own output doesn't print over/under a stale hint block. If a menu item was
|
|
250
|
+
// highlighted (↑/↓), RUN that command instead of the raw typed line.
|
|
251
|
+
let submitted = l;
|
|
242
252
|
if (slashHintRows > 0) {
|
|
253
|
+
if (slashSel >= 0 && slashNames[slashSel]) submitted = slashNames[slashSel];
|
|
243
254
|
process.stdout.write("\x1b[J");
|
|
244
255
|
slashHintRows = 0;
|
|
256
|
+
slashSel = -1;
|
|
257
|
+
slashNames = [];
|
|
245
258
|
}
|
|
246
|
-
const clean = stripAnsiEscapes(
|
|
259
|
+
const clean = stripAnsiEscapes(submitted);
|
|
247
260
|
if (_lineWaiter) {
|
|
248
261
|
const w = _lineWaiter;
|
|
249
262
|
_lineWaiter = null;
|
|
@@ -256,38 +269,64 @@ rl.on("line", (l) => {
|
|
|
256
269
|
// turn: echo is already muted, but readline still recalls history into the invisible
|
|
257
270
|
// buffer on ↑/↓ — reset the line state after every keypress so nothing accumulates or
|
|
258
271
|
// moves. Ctrl+C is unaffected (readline emits its SIGINT before this handler runs).
|
|
259
|
-
// Live command hint = an EPHEMERAL overlay drawn BELOW the prompt while the
|
|
260
|
-
// with "/"
|
|
261
|
-
//
|
|
262
|
-
//
|
|
272
|
+
// Live command hint = an EPHEMERAL, SELECTABLE overlay drawn BELOW the prompt while the
|
|
273
|
+
// line starts with "/". It redraws as you type (narrowing), ↑/↓ move a highlight, Enter
|
|
274
|
+
// runs the highlighted command, and it DISAPPEARS when you clear the slash — no permanent
|
|
275
|
+
// scrollback copies. Relative cursor moves so it survives terminal scrolling.
|
|
276
|
+
// (slashSel / slashNames / slashPrefix are declared above, near rl.on("line").)
|
|
263
277
|
const inputCol = () => 4 + rl.cursor; // ">> " is 3 cols; content starts at col 4 (1-based)
|
|
264
278
|
function clearSlashHint() {
|
|
265
279
|
if (slashHintRows === 0) return;
|
|
266
|
-
// Cursor is on the input line. Drop into the hint region, erase to end of screen, return.
|
|
267
280
|
process.stdout.write("\n\x1b[J" + `\x1b[1A\x1b[${inputCol()}G`);
|
|
268
281
|
slashHintRows = 0;
|
|
282
|
+
slashSel = -1;
|
|
283
|
+
slashNames = [];
|
|
269
284
|
}
|
|
270
|
-
function drawSlashHint(
|
|
271
|
-
const
|
|
272
|
-
|
|
285
|
+
function drawSlashHint(prefix, sel) {
|
|
286
|
+
const cmds = filteredCommands(prefix);
|
|
287
|
+
slashNames = cmds.map((c) => c.name);
|
|
288
|
+
if (sel >= cmds.length) sel = cmds.length - 1;
|
|
289
|
+
slashSel = sel;
|
|
290
|
+
const rows = hintRows(cmds, sel);
|
|
273
291
|
const body = rows.map((r) => "\x1b[K" + r).join("\n");
|
|
274
292
|
let out = "";
|
|
275
293
|
if (slashHintRows > 0) out += "\n\x1b[J\x1b[1A"; // wipe previous block, back to input row
|
|
276
294
|
out += "\n" + body; // draw below the input line
|
|
277
|
-
out += `\x1b[${rows.length}A\x1b[${inputCol()}G`; // back up to
|
|
295
|
+
out += `\x1b[${rows.length}A\x1b[${inputCol()}G`; // back up to input line + column
|
|
278
296
|
process.stdout.write(out);
|
|
279
297
|
slashHintRows = rows.length;
|
|
280
298
|
}
|
|
281
299
|
if (process.stdin.isTTY) {
|
|
282
|
-
process.stdin.on("keypress", () => {
|
|
300
|
+
process.stdin.on("keypress", (_ch, key) => {
|
|
283
301
|
if (following) {
|
|
284
302
|
rl.line = "";
|
|
285
303
|
rl.cursor = 0;
|
|
286
304
|
rl.historyIndex = -1;
|
|
287
305
|
return;
|
|
288
306
|
}
|
|
289
|
-
|
|
290
|
-
|
|
307
|
+
// Enter is handled by rl.on("line") — don't redraw the hint on the way out.
|
|
308
|
+
if (key && (key.name === "return" || key.name === "enter")) return;
|
|
309
|
+
// ↑/↓ while the menu is up = move the highlight, NOT recall shell history. readline
|
|
310
|
+
// already ran history-nav (may have changed the line); restore the typed "/…" prefix
|
|
311
|
+
// and redraw with the new selection.
|
|
312
|
+
if (slashHintRows > 0 && key && (key.name === "up" || key.name === "down")) {
|
|
313
|
+
rl.line = slashPrefix;
|
|
314
|
+
rl.cursor = slashPrefix.length;
|
|
315
|
+
const n = slashNames.length;
|
|
316
|
+
if (n > 0) {
|
|
317
|
+
slashSel =
|
|
318
|
+
key.name === "down" ? (slashSel + 1 + n) % n : (slashSel - 1 + n) % n;
|
|
319
|
+
}
|
|
320
|
+
rl._refreshLine();
|
|
321
|
+
drawSlashHint(slashPrefix, slashSel);
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
if (rl.line.startsWith("/")) {
|
|
325
|
+
slashPrefix = rl.line;
|
|
326
|
+
drawSlashHint(rl.line, -1); // typing resets the selection to none
|
|
327
|
+
} else {
|
|
328
|
+
clearSlashHint();
|
|
329
|
+
}
|
|
291
330
|
});
|
|
292
331
|
}
|
|
293
332
|
rl.on("close", () => {
|
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.213",
|
|
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",
|