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

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 +32 -15
  2. package/package.json +1 -1
package/gonext-repl.mjs CHANGED
@@ -413,6 +413,11 @@ process.on("exit", disableMouse);
413
413
  // (hover/drag), bit 64 = wheel; low 2 bits = which button (0=left). A left CLICK is a press
414
414
  // (M) with motion+wheel bits clear and low bits 0; a HOVER is any event with the motion bit.
415
415
  const MOUSE_RE = /\x1b\[<(\d+);(\d+);(\d+)([Mm])/g;
416
+ // Cursor Position Report reply to our `\x1b[6n` probe: ESC [ row ; col R. We probe right after
417
+ // clearing the status (cursor is then at the status block's TOP row) to learn the thought
418
+ // line's ABSOLUTE viewport row — so hover/click hit-testing is exact even when the block isn't
419
+ // at the bottom of the screen (task #96 fix). No `<`, so it never collides with MOUSE_RE.
420
+ const DSR_RE = /\x1b\[(\d+);(\d+)R/g;
416
421
  const nextLine = () =>
417
422
  _lineQueue.length > 0
418
423
  ? Promise.resolve(_lineQueue.shift())
@@ -1220,6 +1225,8 @@ async function runAgentTurn(history) {
1220
1225
  let fullLiveThought = "";
1221
1226
  let thoughtExpanded = false;
1222
1227
  let hoverRow = -1; // last mouse viewport row (from motion events) → highlight when it's on the thought line
1228
+ let thoughtRow = -1; // absolute viewport row of the thought line (line 1), learned via \x1b[6n
1229
+ let lastDsrAt = 0; // throttle the cursor-position probe
1223
1230
  const thinkSecs = () => Math.max(1, Math.round((Date.now() - thinkingSince) / 1000));
1224
1231
 
1225
1232
  // The status is normally TWO in-place lines — the in-progress action, then the playful
@@ -1278,26 +1285,29 @@ async function runAgentTurn(history) {
1278
1285
  dim(` · ${fmtTokens(latestCodeTokens)} tok`) +
1279
1286
  orange(` · ${fmtTokens(sessionMaxCodeTokens)} max`);
1280
1287
  // 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".
1288
+ // (a live command / bare "Thinking" has nothing to expand). When clicked, the fuller
1289
+ // thought unfolds as up to 4 wrapped GREY lines BETWEEN the thought and the playful-word
1290
+ // line. Hover-highlight only the THOUGHT line (line 1) its exact viewport row comes from
1291
+ // the \x1b[6n probe below (`thoughtRow`); we fall back to the viewport-bottom assumption
1292
+ // only when the terminal never answered the probe.
1285
1293
  const clickable = !!(fullLiveThought && !runningCmd);
1286
1294
  const expLines = thoughtExpanded && clickable ? wrapThought(fullLiveThought, max, 4) : [];
1287
1295
  const drawn = 2 + expLines.length;
1288
1296
  const rows = process.stdout.rows || 24;
1289
- const hovering = clickable && hoverRow === rows - drawn + 1;
1297
+ const tRow = thoughtRow >= 1 ? thoughtRow : rows - drawn + 1;
1298
+ const hovering = clickable && hoverRow === tRow;
1290
1299
  const label = `${fit(primary)}… (${secs}s)`;
1291
1300
  clearStatus();
1301
+ // Right after clearing, the cursor sits at the status block's TOP-LEFT = the thought line's
1302
+ // row. Probe for it (throttled) so hover/click hit-testing is exact wherever the block is.
1303
+ if (process.stdin.isTTY && now - lastDsrAt > 400) { lastDsrAt = now; process.stdout.write("\x1b[6n"); }
1292
1304
  // 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".
1297
- let out =
1298
- `${green(BULLET)} ${hovering ? hoverThought(label) : green(label)}` +
1299
- "\n" + ` ${color256(wc, `${glyph} ${thinkWord}…`)}${tokLabel}`;
1305
+ // underlined bright-green hover style when the mouse is over a clickable thought. Then the
1306
+ // GREY expanded lines (if any), then LAST the SPINNER + playful word + token labels
1307
+ // (task #83/#84), e.g. " ⠙ Caffeinating… · 12.3k tok · 8.1k max".
1308
+ let out = `${green(BULLET)} ${hovering ? hoverThought(label) : green(label)}`;
1300
1309
  for (const wl of expLines) out += "\n " + dim(wl);
1310
+ out += "\n" + ` ${color256(wc, `${glyph} ${thinkWord}…`)}${tokLabel}`;
1301
1311
  process.stdout.write(out);
1302
1312
  statusLines = drawn;
1303
1313
  statusShown = true;
@@ -1316,8 +1326,10 @@ async function runAgentTurn(history) {
1316
1326
  const onStatusClick = (row) => {
1317
1327
  if (!statusShown) return;
1318
1328
  const rows = process.stdout.rows || 24;
1319
- const top = rows - statusLines + 1; // 1-based viewport row of the first status line
1320
- if (row < top || row > rows) return; // click landed outside the status block
1329
+ const top = thoughtRow >= 1 ? thoughtRow : rows - statusLines + 1; // thought line row
1330
+ // Toggle only on the thought line + its grey expansion NOT the last row (the playful
1331
+ // word / token line), so a click there doesn't expand what it isn't part of.
1332
+ if (row < top || row > top + statusLines - 2) return;
1321
1333
  // Only meaningful when there IS a fuller thought to reveal; otherwise ignore the click
1322
1334
  // so we don't flicker an empty expand.
1323
1335
  if (!thoughtExpanded && !(fullLiveThought && !runningCmd)) return;
@@ -1327,7 +1339,11 @@ async function runAgentTurn(history) {
1327
1339
  const onMouseData = (chunk) => {
1328
1340
  if (!following) return;
1329
1341
  const s = chunk.toString("latin1");
1330
- if (s.indexOf("\x1b[<") === -1) return; // fast reject: no SGR mouse report in this chunk
1342
+ if (s.indexOf("\x1b[") === -1) return; // fast reject: no CSI (mouse report OR cursor reply) here
1343
+ // Cursor-position reply → the thought line's absolute row (see the \x1b[6n probe in tick).
1344
+ DSR_RE.lastIndex = 0;
1345
+ let d;
1346
+ while ((d = DSR_RE.exec(s))) thoughtRow = parseInt(d[1], 10);
1331
1347
  MOUSE_RE.lastIndex = 0;
1332
1348
  let m;
1333
1349
  while ((m = MOUSE_RE.exec(s))) {
@@ -1354,6 +1370,7 @@ async function runAgentTurn(history) {
1354
1370
  fenceThought = "";
1355
1371
  fullLiveThought = ""; // #96: the fuller thought is spent too…
1356
1372
  thoughtExpanded = false; // …and each new thinking phase starts collapsed.
1373
+ lastDsrAt = 0; // …content just scrolled → re-probe the thought row next tick.
1357
1374
  lastContentAt = Date.now();
1358
1375
  };
1359
1376
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tiens.nguyen/gonext-local-worker",
3
- "version": "1.0.263",
3
+ "version": "1.0.264",
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",