@tiens.nguyen/gonext-local-worker 1.0.210 → 1.0.212
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 +83 -21
- package/package.json +1 -1
package/gonext-repl.mjs
CHANGED
|
@@ -175,20 +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
|
-
|
|
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) => {
|
|
188
193
|
const label = c.usage ?? c.name;
|
|
189
194
|
const alias = c.aliases?.length ? dim(` (${c.aliases.join(", ")})`) : "";
|
|
190
|
-
|
|
191
|
-
}
|
|
195
|
+
const body = `${cyan(label)}${alias}${dim(" — " + c.desc)}`;
|
|
196
|
+
rows.push(i === sel ? `${green("❯")} ${bold(body)}` : ` ${body}`);
|
|
197
|
+
});
|
|
198
|
+
return rows;
|
|
199
|
+
}
|
|
200
|
+
// Permanent print (used by /help and a lone-"/" submit — scrolls with history).
|
|
201
|
+
function printCommands(prefix = "") {
|
|
202
|
+
for (const r of hintRows(filteredCommands(prefix), -1)) console.log(r);
|
|
192
203
|
console.log("");
|
|
193
204
|
}
|
|
194
205
|
|
|
@@ -221,6 +232,10 @@ const isNetworkError = (e) =>
|
|
|
221
232
|
const _lineQueue = [];
|
|
222
233
|
let _lineWaiter = null;
|
|
223
234
|
let _stdinClosed = false;
|
|
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)
|
|
224
239
|
rl.on("line", (l) => {
|
|
225
240
|
// While a turn is running, IGNORE typed input entirely — no type-ahead, no queued
|
|
226
241
|
// next question. Only Ctrl+C (handled via SIGINT) does anything mid-turn. Clear the
|
|
@@ -230,7 +245,18 @@ rl.on("line", (l) => {
|
|
|
230
245
|
rl.cursor = 0;
|
|
231
246
|
return;
|
|
232
247
|
}
|
|
233
|
-
|
|
248
|
+
// Enter left the cursor on the row where the live slash-hint started — erase it so the
|
|
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;
|
|
252
|
+
if (slashHintRows > 0) {
|
|
253
|
+
if (slashSel >= 0 && slashNames[slashSel]) submitted = slashNames[slashSel];
|
|
254
|
+
process.stdout.write("\x1b[J");
|
|
255
|
+
slashHintRows = 0;
|
|
256
|
+
slashSel = -1;
|
|
257
|
+
slashNames = [];
|
|
258
|
+
}
|
|
259
|
+
const clean = stripAnsiEscapes(submitted);
|
|
234
260
|
if (_lineWaiter) {
|
|
235
261
|
const w = _lineWaiter;
|
|
236
262
|
_lineWaiter = null;
|
|
@@ -243,27 +269,63 @@ rl.on("line", (l) => {
|
|
|
243
269
|
// turn: echo is already muted, but readline still recalls history into the invisible
|
|
244
270
|
// buffer on ↑/↓ — reset the line state after every keypress so nothing accumulates or
|
|
245
271
|
// moves. Ctrl+C is unaffected (readline emits its SIGINT before this handler runs).
|
|
246
|
-
|
|
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").)
|
|
277
|
+
const inputCol = () => 4 + rl.cursor; // ">> " is 3 cols; content starts at col 4 (1-based)
|
|
278
|
+
function clearSlashHint() {
|
|
279
|
+
if (slashHintRows === 0) return;
|
|
280
|
+
process.stdout.write("\n\x1b[J" + `\x1b[1A\x1b[${inputCol()}G`);
|
|
281
|
+
slashHintRows = 0;
|
|
282
|
+
slashSel = -1;
|
|
283
|
+
slashNames = [];
|
|
284
|
+
}
|
|
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);
|
|
291
|
+
const body = rows.map((r) => "\x1b[K" + r).join("\n");
|
|
292
|
+
let out = "";
|
|
293
|
+
if (slashHintRows > 0) out += "\n\x1b[J\x1b[1A"; // wipe previous block, back to input row
|
|
294
|
+
out += "\n" + body; // draw below the input line
|
|
295
|
+
out += `\x1b[${rows.length}A\x1b[${inputCol()}G`; // back up to input line + column
|
|
296
|
+
process.stdout.write(out);
|
|
297
|
+
slashHintRows = rows.length;
|
|
298
|
+
}
|
|
247
299
|
if (process.stdin.isTTY) {
|
|
248
|
-
process.stdin.on("keypress", () => {
|
|
300
|
+
process.stdin.on("keypress", (_ch, key) => {
|
|
249
301
|
if (following) {
|
|
250
302
|
rl.line = "";
|
|
251
303
|
rl.cursor = 0;
|
|
252
304
|
rl.historyIndex = -1;
|
|
253
305
|
return;
|
|
254
306
|
}
|
|
255
|
-
//
|
|
256
|
-
|
|
257
|
-
//
|
|
258
|
-
//
|
|
259
|
-
|
|
260
|
-
if (
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
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();
|
|
267
329
|
}
|
|
268
330
|
});
|
|
269
331
|
}
|
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.212",
|
|
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",
|