bmweb-cli 0.1.4 → 0.1.6

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 (3) hide show
  1. package/README.md +9 -1
  2. package/dist/bmweb.js +100 -13
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -315,7 +315,10 @@ cancels. The home starts with or without a cable.
315
315
 
316
316
  The TUI runs on the terminal's alternate screen (the buffer vim and htop
317
317
  use), so the shell's scrollback is never touched and quitting restores it;
318
- a screen redraws in place, only the lines that changed.
318
+ a screen redraws in place, only the lines that changed. A viewer longer
319
+ than the terminal (a fault protocol, a report) scrolls: Up/Down a line,
320
+ PgUp/PgDn a page, Home/End to either end, with a line under it saying
321
+ which rows are shown.
319
322
 
320
323
  Every dialog INPA opens is a prompt: a message waits for Enter, an input
321
324
  asks for the number (or hex, or text) within the declared range, the
@@ -350,6 +353,11 @@ tests drive the app's runtime against a fake car and a scripted terminal,
350
353
  with a module script written for the tests in INPA's language. The
351
354
  repository's `tools/check.sh` runs all of it.
352
355
 
356
+ Set `BMWEB_VERBOSE=1` to see what the app's runtime logs: the wire trace
357
+ the bus dumps after an error, each variant probe's verdict, the cable
358
+ events. It goes to stderr; without it the runtime is silent and a command's
359
+ output is only its own.
360
+
353
361
  ## License
354
362
 
355
363
  GPL-3.0, as the repository is. See `LICENSE`.
package/dist/bmweb.js CHANGED
@@ -111,6 +111,7 @@ function formatCount(n) {
111
111
  import { readFileSync as readFileSync2 } from "node:fs";
112
112
  import { join as join3 } from "node:path";
113
113
  import { fileURLToPath } from "node:url";
114
+ import { format, inspect } from "node:util";
114
115
  import { createContext, runInContext } from "node:vm";
115
116
 
116
117
  // src/runtime-files.json
@@ -284,14 +285,49 @@ function runtimeGlobals() {
284
285
  loadRuntime();
285
286
  return sandboxRef;
286
287
  }
288
+ function runtimeConsole() {
289
+ const on = !!process.env.BMWEB_VERBOSE;
290
+ const say = (...a) => {
291
+ if (on) process.stderr.write(`${format(...a)}
292
+ `);
293
+ };
294
+ const table = (rows) => {
295
+ if (on)
296
+ process.stderr.write(
297
+ `${inspect(rows, { depth: 3, maxArrayLength: 200, breakLength: 160 })}
298
+ `
299
+ );
300
+ };
301
+ const noop = () => {
302
+ };
303
+ return {
304
+ log: say,
305
+ info: say,
306
+ warn: say,
307
+ error: say,
308
+ debug: say,
309
+ trace: say,
310
+ table,
311
+ group: say,
312
+ groupCollapsed: say,
313
+ groupEnd: noop,
314
+ time: noop,
315
+ timeEnd: noop,
316
+ assert: noop,
317
+ dir: table
318
+ };
319
+ }
287
320
  function hostGlobals() {
288
321
  const noop = () => {
289
322
  };
290
323
  const sandbox = {
291
- // the scripts test `typeof window` and read window.<x>; the context's own
292
- // global stands in, as it does in the page (self: fflate's UMD attaches
293
- // its global to `self` when there is no CommonJS `exports`)
294
- console,
324
+ // the app's scripts talk to the browser console: the bus dumps its wire
325
+ // trace after an IFH error (console.table in a collapsed group), the
326
+ // variant resolver notes every probe's verdict, the bus reports the
327
+ // cable. In a terminal those are noise between a command's own lines
328
+ // (a scan's table had the trace printed through it), so they go to
329
+ // stderr only when BMWEB_VERBOSE is set, and never to stdout
330
+ console: runtimeConsole(),
295
331
  // timers.js's bmwSleep falls back to setTimeout where there is no
296
332
  // Worker; program.js schedules screen cycles and drains key presses
297
333
  // through setTimeout; activations.js defers a session end a microtask
@@ -2008,6 +2044,12 @@ var TuiUi = class {
2008
2044
  frame = [];
2009
2045
  /** a picker owns the keyboard: the program's key handler must stand back */
2010
2046
  modal = false;
2047
+ /** the first line of the viewer shown (INPA's viewer scrolls; so does this) */
2048
+ viewTop = 0;
2049
+ /** the view the scroll position belongs to; a new view starts at the top */
2050
+ viewShown = null;
2051
+ /** how many viewer lines the last frame had room for */
2052
+ viewRoom = 0;
2011
2053
  /** the terminal size the frame was drawn for; a change draws fresh */
2012
2054
  frameSize = "";
2013
2055
  leftResolve = null;
@@ -2422,6 +2464,32 @@ Save as [fault-memory.txt]: `);
2422
2464
  paint(p) {
2423
2465
  this.flush(this.frameLines(p));
2424
2466
  }
2467
+ /**
2468
+ * Scroll the viewer by a key: a line, a page, or to an end. Nothing
2469
+ * happens without a viewer on screen.
2470
+ * @param how - 'up' | 'down' | 'pageup' | 'pagedown' | 'home' | 'end'
2471
+ * @returns whether the key was for the viewer
2472
+ */
2473
+ scrollView(how) {
2474
+ const p = this.program;
2475
+ if (!p || !p.view) return false;
2476
+ const page = Math.max(1, this.viewRoom - 1);
2477
+ const n = (p.view.lines || []).length;
2478
+ const max = Math.max(0, n - this.viewRoom);
2479
+ const jump = {
2480
+ up: -1,
2481
+ down: 1,
2482
+ pageup: -page,
2483
+ pagedown: page,
2484
+ home: -n,
2485
+ end: n
2486
+ };
2487
+ const by = jump[how];
2488
+ if (by === void 0) return false;
2489
+ this.viewTop = Math.max(0, Math.min(max, this.viewTop + by));
2490
+ this.paint(p);
2491
+ return true;
2492
+ }
2425
2493
  /**
2426
2494
  * The frame as lines: title, rule, the view or the grid, a blank, the
2427
2495
  * key bar, then the status and progress lines. Cut to the terminal's
@@ -2434,16 +2502,34 @@ Save as [fault-memory.txt]: `);
2434
2502
  const w = this.term.columns;
2435
2503
  const title = `${p.ecu.label || p.ecu.sgbd} ${p.ecu.sgbd}.prg ${p.title || ""}`.trim();
2436
2504
  const head = [title, "-".repeat(Math.min(w, 78))];
2437
- let body = p.view ? [...p.view.lines || []] : this.gridLines(p);
2438
2505
  const tail = ["", ...this.keyLines(p), this.statusText, this.progressText];
2439
2506
  const room = Math.max(1, this.term.rows - 1 - head.length - tail.length);
2440
- if (body.length > room) body = body.filter((l, i) => l || body[i - 1]);
2441
- if (body.length > room) {
2442
- const hidden = body.length - (room - 1);
2443
- body = [
2444
- ...body.slice(0, room - 1),
2445
- `(${hidden} more rows: enlarge the terminal)`
2446
- ];
2507
+ let body;
2508
+ if (p.view) {
2509
+ const lines = [...p.view.lines || []];
2510
+ if (p.view !== this.viewShown) {
2511
+ this.viewShown = p.view;
2512
+ this.viewTop = 0;
2513
+ }
2514
+ const fits = lines.length <= room;
2515
+ const window = fits ? room : room - 1;
2516
+ this.viewRoom = window;
2517
+ this.viewTop = Math.max(0, Math.min(this.viewTop, lines.length - window));
2518
+ body = lines.slice(this.viewTop, this.viewTop + window);
2519
+ if (!fits)
2520
+ body.push(
2521
+ `\x1B[2mrows ${this.viewTop + 1}-${this.viewTop + body.length} of ${lines.length} \u2191\u2193 PgUp PgDn Home End scroll\x1B[0m`
2522
+ );
2523
+ } else {
2524
+ body = this.gridLines(p);
2525
+ if (body.length > room) body = body.filter((l, i) => l || body[i - 1]);
2526
+ if (body.length > room) {
2527
+ const hidden = body.length - (room - 1);
2528
+ body = [
2529
+ ...body.slice(0, room - 1),
2530
+ `(${hidden} more rows: enlarge the terminal)`
2531
+ ];
2532
+ }
2447
2533
  }
2448
2534
  return [...head, ...body, ...tail].map(
2449
2535
  (l) => String(l).replace(/[\r\n]/g, " ").slice(0, w)
@@ -2623,6 +2709,7 @@ async function tuiCommand(chassis, sgbd, opts = {}) {
2623
2709
  let leaving = false;
2624
2710
  const unsubscribe = term.onKey((k) => {
2625
2711
  if (ui.modal) return;
2712
+ if (ui.scrollView(k.name)) return;
2626
2713
  const what = keyToPress(k);
2627
2714
  if (what === null) return;
2628
2715
  if (what === "quit") {
@@ -2662,7 +2749,7 @@ async function tuiCommand(chassis, sgbd, opts = {}) {
2662
2749
  }
2663
2750
 
2664
2751
  // src/bmweb.ts
2665
- var VERSION = true ? "0.1.4" : "0.0.0-dev";
2752
+ var VERSION = true ? "0.1.6" : "0.0.0-dev";
2666
2753
  var INCLUDE = {
2667
2754
  include: {
2668
2755
  kind: "list",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bmweb-cli",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "BMWeb's tools as a command line: read INPA .IPO scripts, compile .IPS sources, search the corpus job index, decode and diff shared Garage reports, and, over a K+DCAN cable, run jobs, whole-car scans and INPA screens in the terminal.",
5
5
  "license": "GPL-3.0-only",
6
6
  "type": "module",