@tiens.nguyen/gonext-local-worker 1.0.262 → 1.0.263

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 +44 -33
  2. package/package.json +1 -1
package/gonext-repl.mjs CHANGED
@@ -48,6 +48,9 @@ const white = (s) => `\x1b[37m${s}\x1b[0m`;
48
48
  // code 34 (blue) which many dark-theme palettes render as purple/indigo instead.
49
49
  const codeColor = (s) => `\x1b[90m${s}\x1b[0m`;
50
50
  const bold = (s) => `\x1b[1m${s}\x1b[0m`;
51
+ // Hover highlight for the clickable thinking line (task #96): bold + underline + bright
52
+ // green so it reads as an interactive "link" the moment the mouse is over it.
53
+ const hoverThought = (s) => `\x1b[1;4;92m${s}\x1b[0m`;
51
54
 
52
55
  // The worker's "still thinking" heartbeat lines look like "Caffeinating… (90s)" — we
53
56
  // SWALLOW them from the terminal (the local ticker below drives progress instead), and
@@ -383,13 +386,15 @@ rl.on("close", () => {
383
386
  w(null);
384
387
  }
385
388
  });
386
- // Mouse click-to-expand (task #96): while a turn runs we turn ON xterm button tracking so a
387
- // click on the live "thinking" status can expand it (see runAgentTurn). Trade-off the user
388
- // accepted: with tracking on, the terminal's own drag-to-select/copy is captured by us — the
389
- // user holds Option/Shift to select text meanwhile. ?1000 = report button press+release only
390
- // (no motion, so we don't flood on mouse-move); ?1006 = SGR extended coords (no 223-col cap).
391
- const MOUSE_ON = "\x1b[?1000h\x1b[?1006h";
392
- const MOUSE_OFF = "\x1b[?1000l\x1b[?1006l";
389
+ // Mouse hover+click on the thinking line (task #96): while a turn runs we turn ON xterm
390
+ // ANY-MOTION tracking so we can both HIGHLIGHT the line as the mouse hovers it AND expand it
391
+ // on click (see runAgentTurn). Trade-off the user accepted: with tracking on, the terminal's
392
+ // own drag-to-select/copy is captured by us — the user holds Option/Shift to select text
393
+ // meanwhile. ?1003 = report every button press/release AND all mouse MOTION (needed for hover
394
+ // with no button held); ?1006 = SGR extended coords (no 223-col cap). We disable ?1000/?1002
395
+ // too on teardown in case a terminal had them latched.
396
+ const MOUSE_ON = "\x1b[?1003h\x1b[?1006h";
397
+ const MOUSE_OFF = "\x1b[?1003l\x1b[?1002l\x1b[?1000l\x1b[?1006l";
393
398
  let _mouseEnabled = false;
394
399
  const enableMouse = () => {
395
400
  if (_mouseEnabled || !process.stdin.isTTY) return;
@@ -404,8 +409,9 @@ const disableMouse = () => {
404
409
  // Safety net: never leave the user's terminal in mouse-reporting mode (would break their
405
410
  // shell's selection) if we exit ungracefully.
406
411
  process.on("exit", disableMouse);
407
- // SGR mouse report: ESC [ < btn ; col ; row (M=press, m=release). A left-button PRESS is
408
- // btn low-2-bits 0 with the wheel bit (64) clear.
412
+ // SGR mouse report: ESC [ < btn ; col ; row (M=press, m=release). btn bit 32 = MOTION
413
+ // (hover/drag), bit 64 = wheel; low 2 bits = which button (0=left). A left CLICK is a press
414
+ // (M) with motion+wheel bits clear and low bits 0; a HOVER is any event with the motion bit.
409
415
  const MOUSE_RE = /\x1b\[<(\d+);(\d+);(\d+)([Mm])/g;
410
416
  const nextLine = () =>
411
417
  _lineQueue.length > 0
@@ -1213,6 +1219,7 @@ async function runAgentTurn(history) {
1213
1219
  // whether the user has clicked to expand it. Both reset when the step's Thought changes.
1214
1220
  let fullLiveThought = "";
1215
1221
  let thoughtExpanded = false;
1222
+ let hoverRow = -1; // last mouse viewport row (from motion events) → highlight when it's on the thought line
1216
1223
  const thinkSecs = () => Math.max(1, Math.round((Date.now() - thinkingSince) / 1000));
1217
1224
 
1218
1225
  // The status is normally TWO in-place lines — the in-progress action, then the playful
@@ -1267,28 +1274,30 @@ async function runAgentTurn(history) {
1267
1274
  : liveThought || (almostDone ? "Almost done" : "Thinking");
1268
1275
  const max = Math.max(24, (process.stdout.columns || 80) - 14);
1269
1276
  const fit = (s) => (s.length > max ? s.slice(0, max - 1) + "…" : s);
1270
- clearStatus();
1271
- // Line 1: green ● + green phase/seconds (matches the web's green streaming label).
1272
- // Line 2 (indented): the SPINNER + the playful word together, both in the cycling
1273
- // color — the spinner belongs with the word (dot on the label line, spinner leading
1274
- // the flavor line).
1275
- // Task #83/#84: after the playful word, show the running agent-code-model token
1276
- // count (dim) plus the per-workspace PEAK single-request count (orange), e.g.
1277
- // " ⠙ Caffeinating… · 12.3k tok · 8.1k max". Both start at 0 and climb.
1278
1277
  const tokLabel =
1279
1278
  dim(` · ${fmtTokens(latestCodeTokens)} tok`) +
1280
1279
  orange(` · ${fmtTokens(sessionMaxCodeTokens)} max`);
1280
+ // Task #96: the thought line is "clickable" only when there's a fuller thought to reveal
1281
+ // (a live command / bare "Thinking" has nothing to expand). When clicked, unfold it below
1282
+ // as up to 4 wrapped GREY lines (dim). The whole status block rides the viewport bottom as
1283
+ // output scrolls, so line 1 (the thought) sits at row `rows - drawn + 1` — if the mouse is
1284
+ // hovering THAT row, render the label as an underlined bright-green "link".
1285
+ const clickable = !!(fullLiveThought && !runningCmd);
1286
+ const expLines = thoughtExpanded && clickable ? wrapThought(fullLiveThought, max, 4) : [];
1287
+ const drawn = 2 + expLines.length;
1288
+ const rows = process.stdout.rows || 24;
1289
+ const hovering = clickable && hoverRow === rows - drawn + 1;
1290
+ const label = `${fit(primary)}… (${secs}s)`;
1291
+ clearStatus();
1292
+ // Line 1: green ● + green phase/seconds (matches the web's green streaming label), or the
1293
+ // underlined bright-green hover style when the mouse is over a clickable thought.
1294
+ // Line 2 (indented): the SPINNER + the playful word together, both in the cycling color.
1295
+ // Task #83/#84: after the word, the running agent-code token count (dim) + per-workspace
1296
+ // PEAK single-request count (orange), e.g. " ⠙ Caffeinating… · 12.3k tok · 8.1k max".
1281
1297
  let out =
1282
- `${green(BULLET)} ${green(`${fit(primary)}… (${secs}s)`)}` +
1298
+ `${green(BULLET)} ${hovering ? hoverThought(label) : green(label)}` +
1283
1299
  "\n" + ` ${color256(wc, `${glyph} ${thinkWord}…`)}${tokLabel}`;
1284
- let drawn = 2;
1285
- // Task #96: when the user has clicked the thought line, unfold the fuller thought below
1286
- // it as up to 4 wrapped GREY lines (dim). Only when there's genuinely more to show than
1287
- // the truncated primary (a live command / bare "Thinking" has nothing to expand).
1288
- if (thoughtExpanded && fullLiveThought && !runningCmd) {
1289
- const wrapped = wrapThought(fullLiveThought, max, 4);
1290
- for (const wl of wrapped) { out += "\n " + dim(wl); drawn++; }
1291
- }
1300
+ for (const wl of expLines) out += "\n " + dim(wl);
1292
1301
  process.stdout.write(out);
1293
1302
  statusLines = drawn;
1294
1303
  statusShown = true;
@@ -1297,12 +1306,13 @@ async function runAgentTurn(history) {
1297
1306
  // seconds still tick once/sec). Only redraws when the model has gone quiet >QUIET_MS.
1298
1307
  const ticker = setInterval(tick, 120);
1299
1308
 
1300
- // --- Click-to-expand the thinking line (task #96) --------------------------------------
1301
- // A left-click on the live status block toggles the fuller thought (3-4 grey lines). The
1302
- // status block sits at the bottom of the viewport (new output keeps scrolling it there),
1303
- // so we hit-test the click row against the bottom `statusLines` rows. We enable xterm
1304
- // mouse reporting only for the duration of the turn and always turn it back off in the
1305
- // finally + on process exit, so the user's shell is never left in mouse-capture mode.
1309
+ // --- Hover + click on the thinking line (task #96) -------------------------------------
1310
+ // HOVER: motion events keep `hoverRow`; the tick highlights the thought line (underlined
1311
+ // bright-green "link") whenever the mouse is on its row. CLICK: a left-click on the status
1312
+ // block toggles the fuller thought (3-4 grey lines). The block sits at the bottom of the
1313
+ // viewport (new output keeps scrolling it there), so we hit-test rows against the bottom
1314
+ // `statusLines` rows. Mouse reporting is on only for the turn and always turned back off in
1315
+ // the finally + on process exit, so the user's shell is never left in mouse-capture mode.
1306
1316
  const onStatusClick = (row) => {
1307
1317
  if (!statusShown) return;
1308
1318
  const rows = process.stdout.rows || 24;
@@ -1324,7 +1334,8 @@ async function runAgentTurn(history) {
1324
1334
  const btn = parseInt(m[1], 10);
1325
1335
  const row = parseInt(m[3], 10);
1326
1336
  const isPress = m[4] === "M";
1327
- if (isPress && (btn & 0b11) === 0 && (btn & 64) === 0) onStatusClick(row); // left press
1337
+ if (btn & 32) { hoverRow = row; continue; } // MOTION (hover/drag) track for the highlight
1338
+ if (isPress && (btn & 0b11) === 0 && (btn & 64) === 0) onStatusClick(row); // left click → expand
1328
1339
  }
1329
1340
  };
1330
1341
  if (process.stdin.isTTY) process.stdin.on("data", onMouseData);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tiens.nguyen/gonext-local-worker",
3
- "version": "1.0.262",
3
+ "version": "1.0.263",
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",