@tiens.nguyen/gonext-local-worker 1.0.210 → 1.0.211

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.
Files changed (2) hide show
  1. package/gonext-repl.mjs +41 -18
  2. package/package.json +1 -1
package/gonext-repl.mjs CHANGED
@@ -175,7 +175,7 @@ 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
- function printCommands(prefix = "") {
178
+ function commandRows(prefix = "") {
179
179
  const p = (prefix ?? "").trim();
180
180
  const show = COMMANDS.filter(
181
181
  (c) =>
@@ -183,12 +183,18 @@ function printCommands(prefix = "") {
183
183
  p === "/" ||
184
184
  [c.name, ...(c.aliases ?? [])].some((n) => n.startsWith(p))
185
185
  );
186
- console.log(dim(" commands (Tab completes):"));
187
- for (const c of show.length ? show : COMMANDS) {
186
+ const list = show.length ? show : COMMANDS;
187
+ const rows = [dim(" commands (Tab completes):")];
188
+ for (const c of list) {
188
189
  const label = c.usage ?? c.name;
189
190
  const alias = c.aliases?.length ? dim(` (${c.aliases.join(", ")})`) : "";
190
- console.log(` ${cyan(label)}${alias}${dim(" — " + c.desc)}`);
191
+ rows.push(` ${cyan(label)}${alias}${dim(" — " + c.desc)}`);
191
192
  }
193
+ return rows;
194
+ }
195
+ // Permanent print (used by /help and a lone-"/" submit — scrolls with history).
196
+ function printCommands(prefix = "") {
197
+ for (const r of commandRows(prefix)) console.log(r);
192
198
  console.log("");
193
199
  }
194
200
 
@@ -221,6 +227,7 @@ const isNetworkError = (e) =>
221
227
  const _lineQueue = [];
222
228
  let _lineWaiter = null;
223
229
  let _stdinClosed = false;
230
+ let slashHintRows = 0; // live slash-command hint rows drawn below the prompt (0 = none)
224
231
  rl.on("line", (l) => {
225
232
  // While a turn is running, IGNORE typed input entirely — no type-ahead, no queued
226
233
  // next question. Only Ctrl+C (handled via SIGINT) does anything mid-turn. Clear the
@@ -230,6 +237,12 @@ rl.on("line", (l) => {
230
237
  rl.cursor = 0;
231
238
  return;
232
239
  }
240
+ // 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.
242
+ if (slashHintRows > 0) {
243
+ process.stdout.write("\x1b[J");
244
+ slashHintRows = 0;
245
+ }
233
246
  const clean = stripAnsiEscapes(l);
234
247
  if (_lineWaiter) {
235
248
  const w = _lineWaiter;
@@ -243,7 +256,28 @@ rl.on("line", (l) => {
243
256
  // turn: echo is already muted, but readline still recalls history into the invisible
244
257
  // buffer on ↑/↓ — reset the line state after every keypress so nothing accumulates or
245
258
  // moves. Ctrl+C is unaffected (readline emits its SIGINT before this handler runs).
246
- let slashHintShown = false; // the live command hint is currently displayed for a "/…" line
259
+ // Live command hint = an EPHEMERAL overlay drawn BELOW the prompt while the line starts
260
+ // with "/", so it redraws as you type (narrowing) and DISAPPEARS when you clear the slash
261
+ // (instead of stacking permanent copies in scrollback). Rendered with relative cursor
262
+ // moves (down to draw, back up to the input line) so it survives terminal scrolling.
263
+ const inputCol = () => 4 + rl.cursor; // ">> " is 3 cols; content starts at col 4 (1-based)
264
+ function clearSlashHint() {
265
+ if (slashHintRows === 0) return;
266
+ // Cursor is on the input line. Drop into the hint region, erase to end of screen, return.
267
+ process.stdout.write("\n\x1b[J" + `\x1b[1A\x1b[${inputCol()}G`);
268
+ slashHintRows = 0;
269
+ }
270
+ function drawSlashHint(line) {
271
+ const rows = commandRows(line);
272
+ // Repaint in place: erase the old block, draw the new one, move the cursor back up.
273
+ const body = rows.map((r) => "\x1b[K" + r).join("\n");
274
+ let out = "";
275
+ if (slashHintRows > 0) out += "\n\x1b[J\x1b[1A"; // wipe previous block, back to input row
276
+ out += "\n" + body; // draw below the input line
277
+ out += `\x1b[${rows.length}A\x1b[${inputCol()}G`; // back up to the input line + column
278
+ process.stdout.write(out);
279
+ slashHintRows = rows.length;
280
+ }
247
281
  if (process.stdin.isTTY) {
248
282
  process.stdin.on("keypress", () => {
249
283
  if (following) {
@@ -252,19 +286,8 @@ if (process.stdin.isTTY) {
252
286
  rl.historyIndex = -1;
253
287
  return;
254
288
  }
255
- // Live command hint: the moment the line starts with "/", pop the command list ABOVE
256
- // the prompt (once) so the user sees /model etc. without typing the whole thing or
257
- // pressing Tab. Shown once per "/…" run; cleared/reset when the line stops being a
258
- // slash command (so a fresh "/" shows it again).
259
- const l = rl.line;
260
- if (l.startsWith("/") && !slashHintShown) {
261
- slashHintShown = true;
262
- process.stdout.write("\r\x1b[K"); // wipe the ">> /" input row
263
- printCommands(l); // list where the prompt was + below
264
- rl._refreshLine(); // redraw ">> /" (with cursor) beneath the list
265
- } else if (!l.startsWith("/")) {
266
- slashHintShown = false;
267
- }
289
+ if (rl.line.startsWith("/")) drawSlashHint(rl.line);
290
+ else clearSlashHint();
268
291
  });
269
292
  }
270
293
  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.210",
3
+ "version": "1.0.211",
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",