@promptctl/rich-js 0.7.0 → 0.9.0
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/README.md +99 -31
- package/dist/core/box.d.ts +96 -42
- package/dist/core/box.d.ts.map +1 -1
- package/dist/core/box.js +348 -490
- package/dist/core/box.js.map +1 -1
- package/dist/core/cells.d.ts +46 -1
- package/dist/core/cells.d.ts.map +1 -1
- package/dist/core/cells.js +55 -14
- package/dist/core/cells.js.map +1 -1
- package/dist/core/color.d.ts.map +1 -1
- package/dist/core/color.js +22 -8
- package/dist/core/color.js.map +1 -1
- package/dist/core/console.d.ts +30 -14
- package/dist/core/console.d.ts.map +1 -1
- package/dist/core/console.js +176 -104
- package/dist/core/console.js.map +1 -1
- package/dist/core/highlighter.d.ts +2 -1
- package/dist/core/highlighter.d.ts.map +1 -1
- package/dist/core/highlighter.js +28 -6
- package/dist/core/highlighter.js.map +1 -1
- package/dist/{renderables → core}/json.d.ts +8 -3
- package/dist/core/json.d.ts.map +1 -0
- package/dist/{renderables → core}/json.js +7 -2
- package/dist/core/json.js.map +1 -0
- package/dist/core/markup.d.ts +6 -9
- package/dist/core/markup.d.ts.map +1 -1
- package/dist/core/markup.js +153 -47
- package/dist/core/markup.js.map +1 -1
- package/dist/core/measure.d.ts +11 -0
- package/dist/core/measure.d.ts.map +1 -1
- package/dist/core/measure.js +15 -3
- package/dist/core/measure.js.map +1 -1
- package/dist/core/oklch.d.ts +22 -0
- package/dist/core/oklch.d.ts.map +1 -1
- package/dist/core/oklch.js +62 -18
- package/dist/core/oklch.js.map +1 -1
- package/dist/core/pretty.d.ts +122 -0
- package/dist/core/pretty.d.ts.map +1 -0
- package/dist/core/pretty.js +398 -0
- package/dist/core/pretty.js.map +1 -0
- package/dist/core/protocol.d.ts +80 -0
- package/dist/core/protocol.d.ts.map +1 -1
- package/dist/core/protocol.js +85 -0
- package/dist/core/protocol.js.map +1 -1
- package/dist/core/segment.d.ts +26 -0
- package/dist/core/segment.d.ts.map +1 -1
- package/dist/core/segment.js +45 -0
- package/dist/core/segment.js.map +1 -1
- package/dist/core/strip.d.ts +9 -5
- package/dist/core/strip.d.ts.map +1 -1
- package/dist/core/strip.js +39 -41
- package/dist/core/strip.js.map +1 -1
- package/dist/core/style.d.ts +19 -0
- package/dist/core/style.d.ts.map +1 -1
- package/dist/core/style.js +46 -38
- package/dist/core/style.js.map +1 -1
- package/dist/core/subscription.d.ts +13 -0
- package/dist/core/subscription.d.ts.map +1 -0
- package/dist/core/subscription.js +13 -0
- package/dist/core/subscription.js.map +1 -0
- package/dist/core/text.d.ts +102 -4
- package/dist/core/text.d.ts.map +1 -1
- package/dist/core/text.js +332 -162
- package/dist/core/text.js.map +1 -1
- package/dist/core/wrap.d.ts +42 -0
- package/dist/core/wrap.d.ts.map +1 -0
- package/dist/core/wrap.js +128 -0
- package/dist/core/wrap.js.map +1 -0
- package/dist/host/host-stream.d.ts.map +1 -0
- package/dist/host/host-stream.js.map +1 -0
- package/dist/host/index.d.ts +4 -0
- package/dist/host/index.d.ts.map +1 -0
- package/dist/host/index.js +3 -0
- package/dist/host/index.js.map +1 -0
- package/dist/{widgets → host}/terminal-host.d.ts +20 -64
- package/dist/host/terminal-host.d.ts.map +1 -0
- package/dist/host/terminal-host.js +133 -0
- package/dist/host/terminal-host.js.map +1 -0
- package/dist/index.d.ts +13 -36
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +35 -22
- package/dist/index.js.map +1 -1
- package/dist/node/terminal-host.d.ts +85 -0
- package/dist/node/terminal-host.d.ts.map +1 -0
- package/dist/node/terminal-host.js +161 -0
- package/dist/node/terminal-host.js.map +1 -0
- package/dist/node/traceback.d.ts +28 -0
- package/dist/node/traceback.d.ts.map +1 -0
- package/dist/node/traceback.js +113 -0
- package/dist/node/traceback.js.map +1 -0
- package/dist/renderables/columns.d.ts +24 -2
- package/dist/renderables/columns.d.ts.map +1 -1
- package/dist/renderables/columns.js +91 -32
- package/dist/renderables/columns.js.map +1 -1
- package/dist/renderables/layout.d.ts +61 -5
- package/dist/renderables/layout.d.ts.map +1 -1
- package/dist/renderables/layout.js +191 -19
- package/dist/renderables/layout.js.map +1 -1
- package/dist/renderables/markdown.d.ts +1 -1
- package/dist/renderables/markdown.d.ts.map +1 -1
- package/dist/renderables/markdown.js +7 -14
- package/dist/renderables/markdown.js.map +1 -1
- package/dist/renderables/padding.d.ts +16 -3
- package/dist/renderables/padding.d.ts.map +1 -1
- package/dist/renderables/padding.js +96 -47
- package/dist/renderables/padding.js.map +1 -1
- package/dist/renderables/panel.d.ts +48 -7
- package/dist/renderables/panel.d.ts.map +1 -1
- package/dist/renderables/panel.js +175 -106
- package/dist/renderables/panel.js.map +1 -1
- package/dist/renderables/progress.d.ts +3 -3
- package/dist/renderables/progress.d.ts.map +1 -1
- package/dist/renderables/progress.js +11 -9
- package/dist/renderables/progress.js.map +1 -1
- package/dist/renderables/progressBar.d.ts +3 -3
- package/dist/renderables/progressBar.d.ts.map +1 -1
- package/dist/renderables/progressBar.js +9 -15
- package/dist/renderables/progressBar.js.map +1 -1
- package/dist/renderables/prompt.js +1 -1
- package/dist/renderables/prompt.js.map +1 -1
- package/dist/renderables/rule.d.ts +1 -1
- package/dist/renderables/rule.d.ts.map +1 -1
- package/dist/renderables/rule.js +5 -10
- package/dist/renderables/rule.js.map +1 -1
- package/dist/renderables/spinner.d.ts +2 -2
- package/dist/renderables/spinner.d.ts.map +1 -1
- package/dist/renderables/spinner.js +6 -11
- package/dist/renderables/spinner.js.map +1 -1
- package/dist/renderables/status.d.ts.map +1 -1
- package/dist/renderables/status.js +5 -11
- package/dist/renderables/status.js.map +1 -1
- package/dist/renderables/table.d.ts +90 -16
- package/dist/renderables/table.d.ts.map +1 -1
- package/dist/renderables/table.js +481 -223
- package/dist/renderables/table.js.map +1 -1
- package/dist/renderables/traceback.d.ts +7 -3
- package/dist/renderables/traceback.d.ts.map +1 -1
- package/dist/renderables/traceback.js +15 -18
- package/dist/renderables/traceback.js.map +1 -1
- package/dist/renderables/tree.d.ts +29 -4
- package/dist/renderables/tree.d.ts.map +1 -1
- package/dist/renderables/tree.js +86 -33
- package/dist/renderables/tree.js.map +1 -1
- package/dist/template-bindings/color-funcs.d.ts +3 -3
- package/dist/template-bindings/color-funcs.js +3 -3
- package/dist/template-bindings/helpers.d.ts.map +1 -1
- package/dist/template-bindings/helpers.js +19 -1
- package/dist/template-bindings/helpers.js.map +1 -1
- package/dist/template-bindings/index.d.ts +6 -7
- package/dist/template-bindings/index.d.ts.map +1 -1
- package/dist/template-bindings/index.js +6 -7
- package/dist/template-bindings/index.js.map +1 -1
- package/dist/template-bindings/palette-funcs.d.ts +17 -5
- package/dist/template-bindings/palette-funcs.d.ts.map +1 -1
- package/dist/template-bindings/palette-funcs.js +69 -6
- package/dist/template-bindings/palette-funcs.js.map +1 -1
- package/dist/template-bindings/style-funcs.d.ts +1 -1
- package/dist/template-bindings/style-funcs.js +1 -1
- package/dist/themes/ramp.d.ts +84 -0
- package/dist/themes/ramp.d.ts.map +1 -0
- package/dist/themes/ramp.js +123 -0
- package/dist/themes/ramp.js.map +1 -0
- package/dist/widgets/dropdown.d.ts +1 -1
- package/dist/widgets/dropdown.js +1 -1
- package/dist/widgets/event-router.d.ts +3 -2
- package/dist/widgets/event-router.d.ts.map +1 -1
- package/dist/widgets/focus-manager.d.ts +2 -1
- package/dist/widgets/focus-manager.d.ts.map +1 -1
- package/dist/widgets/index.d.ts +3 -6
- package/dist/widgets/index.d.ts.map +1 -1
- package/dist/widgets/index.js +4 -3
- package/dist/widgets/index.js.map +1 -1
- package/dist/widgets/screen.d.ts +1 -1
- package/dist/widgets/screen.d.ts.map +1 -1
- package/dist/widgets/text-input.d.ts +2 -3
- package/dist/widgets/text-input.d.ts.map +1 -1
- package/dist/widgets/text-input.js +4 -12
- package/dist/widgets/text-input.js.map +1 -1
- package/dist/widgets/types.d.ts +1 -1
- package/dist/widgets/types.d.ts.map +1 -1
- package/dist/widgets/types.js.map +1 -1
- package/dist/widgets/widget-base.d.ts +2 -1
- package/dist/widgets/widget-base.d.ts.map +1 -1
- package/package.json +38 -6
- package/dist/renderables/json.d.ts.map +0 -1
- package/dist/renderables/json.js.map +0 -1
- package/dist/renderables/pretty.d.ts +0 -29
- package/dist/renderables/pretty.d.ts.map +0 -1
- package/dist/renderables/pretty.js +0 -141
- package/dist/renderables/pretty.js.map +0 -1
- package/dist/themes/paletteResolver.d.ts +0 -35
- package/dist/themes/paletteResolver.d.ts.map +0 -1
- package/dist/themes/paletteResolver.js +0 -88
- package/dist/themes/paletteResolver.js.map +0 -1
- package/dist/widgets/host-stream.d.ts.map +0 -1
- package/dist/widgets/host-stream.js.map +0 -1
- package/dist/widgets/terminal-host.d.ts.map +0 -1
- package/dist/widgets/terminal-host.js +0 -258
- package/dist/widgets/terminal-host.js.map +0 -1
- /package/dist/{widgets → host}/host-stream.d.ts +0 -0
- /package/dist/{widgets → host}/host-stream.js +0 -0
package/README.md
CHANGED
|
@@ -16,6 +16,25 @@ Works on Linux, macOS, and Windows. Requires Node.js >= 20. ESM-only.
|
|
|
16
16
|
npm install @promptctl/rich-js
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
+
### Entry points
|
|
20
|
+
|
|
21
|
+
Most of the library comes from the package name itself — `Console`, `Table`, `Panel`, `Tree`, and everything else in the snippets below. Four areas sit behind package subpaths instead — worth knowing before you go looking for one of them in the main entry point and find nothing there:
|
|
22
|
+
|
|
23
|
+
| Import from | What lives there | What you install yourself | Why it's separate |
|
|
24
|
+
|---|---|---|---|
|
|
25
|
+
| `@promptctl/rich-js/widgets` | Button, Checkbox, Toggle, TextInput, Dropdown, Slider, and the screen that mounts them | `mobx` | Carries a third-party runtime dependency of its own — MobX, for widget state |
|
|
26
|
+
| `@promptctl/rich-js/template-bindings` | The styling vocabulary as Go-template functions, so styled text can be authored as a template | `@promptctl/go-template-js` | Carries a third-party runtime dependency of its own — the Go-template engine, for parsing and evaluating the templates |
|
|
27
|
+
| `@promptctl/rich-js/host` | `TerminalHost`, `BrowserTerminalHost`, `hostStream` — the seam between rendering and a terminal | Nothing | A program that just wants to write bytes through a host shouldn't pay for the widget set to do it |
|
|
28
|
+
| `@promptctl/rich-js/node/save`, `/node/prompt`, `/node/traceback`, `/node/terminal-host` | File export, readline input, the crash handler, and the node TTY host | Nothing | Each one reads node built-ins, and keeping them off the main entry point is what keeps that entry point browser-safe |
|
|
29
|
+
|
|
30
|
+
Those two peer dependencies are yours to install — `npm install @promptctl/rich-js` deliberately fetches neither, because a program that prints a table shouldn't acquire a state library or a template engine to do it. Add the ones you need:
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
npm install @promptctl/rich-js mobx @promptctl/go-template-js
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Import `@promptctl/rich-js/widgets` without MobX, or `@promptctl/rich-js/template-bindings` without the engine, and the import itself fails, with `ERR_MODULE_NOT_FOUND`. The main entry point is unaffected — every snippet on this page that imports from `@promptctl/rich-js` runs on the plain install.
|
|
37
|
+
|
|
19
38
|
## Using the Console
|
|
20
39
|
|
|
21
40
|
Import and construct a `Console` object:
|
|
@@ -145,7 +164,7 @@ await progress.run(async () => {
|
|
|
145
164
|
For situations where it is hard to calculate progress, use `Status` to display a spinner animation with a message:
|
|
146
165
|
|
|
147
166
|
```typescript
|
|
148
|
-
import { Console } from "@promptctl/rich-js";
|
|
167
|
+
import { Console, Status } from "@promptctl/rich-js";
|
|
149
168
|
|
|
150
169
|
const console = new Console();
|
|
151
170
|
|
|
@@ -256,6 +275,64 @@ try {
|
|
|
256
275
|
}
|
|
257
276
|
```
|
|
258
277
|
|
|
278
|
+
To format every crash — uncaught exceptions and unhandled promise rejections alike — install the handler at your entry point:
|
|
279
|
+
|
|
280
|
+
```typescript
|
|
281
|
+
import { installTraceback } from "@promptctl/rich-js/node/traceback";
|
|
282
|
+
|
|
283
|
+
installTraceback();
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
`installTraceback` lives on the `node/traceback` subpath because it calls `process.on` and `process.exit`; the `Traceback` renderable itself stays in the main barrel, which remains browser-safe.
|
|
287
|
+
|
|
288
|
+
</details>
|
|
289
|
+
|
|
290
|
+
<details>
|
|
291
|
+
<summary>Interactive Widgets</summary>
|
|
292
|
+
|
|
293
|
+
Everything above draws once and returns. Widgets stay on screen and respond — a button that highlights under the cursor, a text field with a cursor you can move, a dropdown you filter by typing. They come from the `widgets` subpath and need MobX installed alongside the package (see [Entry points](#entry-points)).
|
|
294
|
+
|
|
295
|
+
```typescript
|
|
296
|
+
import { Button, TextInput, DefaultScreen, EventRouter } from "@promptctl/rich-js/widgets";
|
|
297
|
+
import { NodeTerminalHost } from "@promptctl/rich-js/node/terminal-host";
|
|
298
|
+
|
|
299
|
+
const host = new NodeTerminalHost();
|
|
300
|
+
const screen = new DefaultScreen({ host });
|
|
301
|
+
const router = new EventRouter({ screen, host });
|
|
302
|
+
|
|
303
|
+
const name = new TextInput({ placeholder: "your name" });
|
|
304
|
+
const submit = new Button({ label: "Submit", variant: "primary" });
|
|
305
|
+
|
|
306
|
+
const quit = (): void => {
|
|
307
|
+
router.stop();
|
|
308
|
+
screen.stop();
|
|
309
|
+
host.write("\n");
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
submit.onSubmit(() => {
|
|
313
|
+
quit();
|
|
314
|
+
host.write(`hello, ${name.value}\n`);
|
|
315
|
+
process.exit(0);
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
// Raw mode swallows Ctrl+C, so the app has to handle it itself.
|
|
319
|
+
router.onKey(
|
|
320
|
+
(event) => {
|
|
321
|
+
if (event.ctrl && event.key === "c") {
|
|
322
|
+
quit();
|
|
323
|
+
process.exit(0);
|
|
324
|
+
}
|
|
325
|
+
},
|
|
326
|
+
{ priority: "high" },
|
|
327
|
+
);
|
|
328
|
+
|
|
329
|
+
screen.mount(name, submit);
|
|
330
|
+
screen.start();
|
|
331
|
+
router.start();
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
`DefaultScreen` builds a focus manager when you don't pass one, so the `TextInput` has focus before the user touches anything and Tab moves between the two widgets. The screen re-renders through a MobX reaction: change a widget's state and the frame redraws itself, with no explicit repaint call anywhere. See [docs/widgets.md](docs/widgets.md) for the full widget set, key dispatch, layout placements, and how to write your own.
|
|
335
|
+
|
|
259
336
|
</details>
|
|
260
337
|
|
|
261
338
|
## Custom Renderables
|
|
@@ -302,14 +379,12 @@ saveHtml(console, "output.html");
|
|
|
302
379
|
|
|
303
380
|
## Demos
|
|
304
381
|
|
|
305
|
-
|
|
382
|
+
Every demo below also runs in your browser on the [live demo gallery](https://brandon-fryslie.github.io/rich-js/master/demos/) — the same code mounted against an xterm.js terminal, nothing to install. To drive one against your own terminal instead, run its npm script:
|
|
306
383
|
|
|
307
384
|
```sh
|
|
308
|
-
#
|
|
309
|
-
npm run demo # rich-explore — TUI file browser + markdown/code reader
|
|
310
|
-
npm run sessions # claude-sessions — Claude Code session browser
|
|
311
|
-
|
|
312
|
-
# Other interactive demos
|
|
385
|
+
# Interactive
|
|
386
|
+
npm run demo # rich-explore — TUI file browser + markdown/code reader
|
|
387
|
+
npm run sessions # claude-sessions — Claude Code session browser
|
|
313
388
|
npm run demo-inputs # rich-config — TextInput / palette search
|
|
314
389
|
npm run demo:dropdown # dropdown-demo — Dropdown widget showcase
|
|
315
390
|
npm run dash # rich-dash — Live dashboard
|
|
@@ -321,6 +396,8 @@ npm run strip # rich-strip — side-by-side joiner showcase
|
|
|
321
396
|
npm run markup-plugins # rich-markup-plugins — plugin-tag examples
|
|
322
397
|
```
|
|
323
398
|
|
|
399
|
+
`rich-explore`, `claude-sessions`, and `themes-and-color-studio` are covered in detail below. `package.json` holds the authoritative script list — `jq .scripts package.json` to see it.
|
|
400
|
+
|
|
324
401
|
### rich-explore — TUI file browser + markdown/code reader
|
|
325
402
|
|
|
326
403
|
A two-pane file browser with a directory tree on the left and a file preview on the right. Navigate with vim-style keys, Tab to switch focus, Enter/arrow keys to expand/collapse directories.
|
|
@@ -373,7 +450,7 @@ npm run sessions
|
|
|
373
450
|
|
|
374
451
|
### themes-and-color-studio — color, palette, theme, and contrast tour
|
|
375
452
|
|
|
376
|
-
A one-shot non-interactive demo that walks every public surface of the color subsystem in eight sections: ColorRgba values and parsing, ColorSpec and downgrade tables, color-system detection, the theme registry,
|
|
453
|
+
A one-shot non-interactive demo that walks every public surface of the color subsystem in eight sections: ColorRgba values and parsing, ColorSpec and downgrade tables, color-system detection, the theme registry, colour references through `resolveColorRef`, every bundled `TerminalTheme` constant, OKLCH transposition (hue circle / chroma sweep / lightness invert / themeKeyForRoot), and the WCAG contrast toolkit.
|
|
377
454
|
|
|
378
455
|
```sh
|
|
379
456
|
npm run themes-and-color-studio # terminal output
|
|
@@ -387,7 +464,7 @@ EXPORT_HTML=out.html npm run themes-and-color-studio # also write a styled HTML
|
|
|
387
464
|
| `ColorRgba` / `parseRgbHex` / `parseRgbaHex` / `blendRgb` | Pixel-level values, two hex parsers, linear blend, alpha compositing |
|
|
388
465
|
| `ColorSpec` / `ColorDepth` | Every factory; downgrade across `STANDARD_TABLE` / `EIGHT_BIT_TABLE` / `WINDOWS_TABLE`; `ANSI_COLOR_NAMES` lookups; `ColorParseError` |
|
|
389
466
|
| `detectColorSystem` / `resolveColorSystem` | Env-driven color-system detection with `DetectColorOptions` fixtures; spec-string resolution |
|
|
390
|
-
| `Palette` / `
|
|
467
|
+
| `Palette` / `resolveColorRef` / `parseHexColor` / `buildPalette` | Palette names and `#RRGGBB` literals through one checkpoint against gruvbox, including the hex round-trip that shows it is idempotent and the `ColorRefError` miss; `BaseColors` → derived `text-*` / `on-*` / `*-muted` vars |
|
|
391
468
|
| Theme registry | `getThemePalette` / `listThemePalettes` / `getThemeBaseColors` walking every bundled theme; raw `THEMES` / `ThemePaletteData` via subpath |
|
|
392
469
|
| `TerminalTheme` constants | All bundled constants (`DEFAULT`, `SVG_EXPORT`, `MONOKAI`, `NORD`, `GRUVBOX`, `DRACULA`, `TOKYO_NIGHT`, `FLEXOKI`, `CYBERPUNK`, `CATPPUCCIN_*`, `SOLARIZED_*`, `ROSE_PINE*`, `ATOM_ONE_*`, `TEXTUAL_*`) |
|
|
393
470
|
| `Oklch` / `transposePalette` / `themeKeyForRoot` | Round-trip + `IDENTITY` / `INVERT_LIGHTNESS`; hue circle; chroma sweep; light↔dark invert; `ANCHORED_ROOTS` / `isAnchored` |
|
|
@@ -396,35 +473,26 @@ EXPORT_HTML=out.html npm run themes-and-color-studio # also write a styled HTML
|
|
|
396
473
|
|
|
397
474
|
---
|
|
398
475
|
|
|
399
|
-
###
|
|
476
|
+
### Demo coverage is checked, not claimed
|
|
477
|
+
|
|
478
|
+
`test/coverage/coverage.test.ts` asserts that every public export is referenced by at least one file under `examples/`. Add a public export without demoing it and CI fails.
|
|
479
|
+
|
|
480
|
+
The check builds its universe from `package.json#exports` when the test loads, so there is no hand-written list of covered symbols to maintain — and none to drift out of date. Coverage is counted per *symbol origin*, meaning the declaring file plus the declared name, so one declaration re-exported under two names is still one thing to demonstrate. Renaming a symbol on import inside a demo still counts; `import * as rich` does not, because a namespace import never names what it pulls in.
|
|
400
481
|
|
|
401
|
-
|
|
482
|
+
An export that genuinely cannot be demonstrated at runtime belongs in `test/coverage/coverage-allowlist.ts` with a written reason. The allowlist is validated in both directions: an entry pointing at no real export fails the suite, and so does an entry for something a demo now covers. You can neither widen the exemption quietly nor leave a stale one lying around.
|
|
402
483
|
|
|
403
|
-
|
|
484
|
+
Two things to know before adding a demo. `examples/shared/` is a helper module rather than a demo, and references from it count — the verifier walks every file under `examples/`, not just the entry points. And a demo reaches the browser gallery only if it has an `examples/<name>/wire.ts`; `npm run demos:build` fails loudly when a `wire.ts` has no compiled output rather than dropping the demo from the site.
|
|
404
485
|
|
|
405
|
-
|
|
486
|
+
That check is a floor, not a goal. A script that imports thirty exports and prints them in sequence passes it; a small interactive TUI that lets you drive eight of them in a real composition is the better demo, and no test can tell you which one you wrote.
|
|
406
487
|
|
|
407
|
-
**Bugs found and fixed via demo integration
|
|
488
|
+
**Bugs found and fixed via demo integration** — the argument for exercising the library this way rather than only in unit tests:
|
|
408
489
|
|
|
409
490
|
| Bug | Location | Impact | Fix |
|
|
410
491
|
|---|---|---|---|
|
|
411
|
-
| `Live.refresh()` strips all ANSI styles | `src/renderables/live.ts
|
|
412
|
-
| `Progress.render()` drops column styles | `src/renderables/progress.ts
|
|
413
|
-
| `Tree` emits double blank lines | `src/renderables/tree.ts
|
|
414
|
-
| `Spinner` constructor rejects `undefined` name | `src/renderables/spinner.ts
|
|
415
|
-
|
|
416
|
-
**Not yet exercised — candidates for new demos or demo additions:**
|
|
417
|
-
|
|
418
|
-
| Module | Notes | Suggested coverage |
|
|
419
|
-
|---|---|---|
|
|
420
|
-
| `Status` | Spinner + message display | Loading indicator for large sessions in claude-sessions |
|
|
421
|
-
| `Prompt` / `IntPrompt` / `FloatPrompt` / `Confirm` | Interactive input via readline | Add a go-to-path prompt in rich-explore; incompatible with raw-mode loops so needs a modal switch |
|
|
422
|
-
| `emoji` | Shortcode substitution (`:smiley:` → 😃) | Enable in markup-rendered block text in claude-sessions |
|
|
423
|
-
| `NullHighlighter` / `RegexHighlighter` | Specialized highlighters not yet exercised | `RegexHighlighter` for search-term highlighting in claude-sessions |
|
|
424
|
-
| `StyleStack` / `Theme` / `DEFAULT_STYLES` | Theme customization | Add a Console style-theme switcher demo |
|
|
425
|
-
| `Console` recording | `record`, `exportText`, `exportHtml`, `saveHtml` | Add an export-to-HTML feature to claude-sessions |
|
|
426
|
-
| Most `Box` variants | `ASCII`, `SQUARE`, `MINIMAL`, `HEAVY`, `DOUBLE`, `MARKDOWN`, etc. | Add a box-style picker to rich-explore's Panel borders |
|
|
427
|
-
| `Measurement.get()` / `measureRenderables` | Explicit width measurement | Used internally; could add a measurement debug overlay |
|
|
492
|
+
| `Live.refresh()` strips all ANSI styles | `Live.refresh` in `src/renderables/live.ts` | Every renderable flowing through `Live` (including `Status`, `Progress`, `Spinner`) appeared unstyled | Apply `style.render(text, colorSystem)` instead of bare `s.text` |
|
|
493
|
+
| `Progress.render()` drops column styles | `Progress.render` in `src/renderables/progress.ts` | Progress percentage, timing, and spinner styles were stripped when building table cells | Use `RichText.append(text, style)` to preserve segment styles |
|
|
494
|
+
| `Tree` emits double blank lines | `Tree.render` in `src/renderables/tree.ts` | Label rendering and the explicit `Segment.line()` both contributed a newline, producing blank lines between tree entries | Make `RichText` stop emitting a trailing newline so `Tree`'s explicit `yield Segment.line()` remains the only line break |
|
|
495
|
+
| `Spinner` constructor rejects `undefined` name | `Spinner` constructor in `src/renderables/spinner.ts` | `SpinnerColumn` (used by `Progress`) passed optional `string \| undefined` to required `string` parameter | Make `name` optional, default to `DEFAULT_SPINNER` |
|
|
428
496
|
|
|
429
497
|
## Environment Variables
|
|
430
498
|
|
package/dist/core/box.d.ts
CHANGED
|
@@ -1,27 +1,46 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Box-drawing character sets for borders and table grids.
|
|
3
|
+
*
|
|
4
|
+
* A style is declared as an 8x4 character grid — one line per row a table can
|
|
5
|
+
* draw, one column per position within that row:
|
|
6
|
+
*
|
|
7
|
+
* ┌─┬┐ top border
|
|
8
|
+
* │ ││ header content
|
|
9
|
+
* ├─┼┤ header separator
|
|
10
|
+
* │ ││ body content
|
|
11
|
+
* ├─┼┤ row separator
|
|
12
|
+
* ├─┼┤ footer separator
|
|
13
|
+
* │ ││ footer content
|
|
14
|
+
* └─┴┘ bottom border
|
|
15
|
+
*
|
|
16
|
+
* [LAW:one-source-of-truth] The grid is the shape the reference implementation
|
|
17
|
+
* (Python Rich's `box.py`) publishes these glyphs in, so a constant below can
|
|
18
|
+
* be diffed against it character for character. The eighteen named fields this
|
|
19
|
+
* replaced were a second, differently-shaped map of the same territory, and it
|
|
20
|
+
* had drifted: seven of the nineteen constants carried wrong glyphs, and the
|
|
21
|
+
* field named `mid` held the reference's line 3 while the reference's `mid_*`
|
|
22
|
+
* is line 4 — so reading the reference name-for-name swapped a separator for a
|
|
23
|
+
* content row. Named row fields are gone for that reason; a row is reached by
|
|
24
|
+
* what it is (`getRow`, `getContentChars`), never by a name that can be
|
|
25
|
+
* mismatched to a line.
|
|
3
26
|
*/
|
|
4
27
|
import { Segment } from "./segment.js";
|
|
5
28
|
import type { Style } from "./style.js";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
top: string;
|
|
9
|
-
topDivider: string;
|
|
10
|
-
topRight: string;
|
|
11
|
-
headLeft: string;
|
|
12
|
-
headVertical: string;
|
|
13
|
-
headRight: string;
|
|
14
|
-
midLeft: string;
|
|
15
|
-
mid: string;
|
|
16
|
-
midVertical: string;
|
|
17
|
-
midRight: string;
|
|
18
|
-
bottomLeft: string;
|
|
19
|
-
bottom: string;
|
|
20
|
-
bottomDivider: string;
|
|
21
|
-
bottomRight: string;
|
|
29
|
+
/** A rule spanning the table: the top and bottom borders, and every separator. */
|
|
30
|
+
export interface EdgeChars {
|
|
22
31
|
left: string;
|
|
32
|
+
horizontal: string;
|
|
33
|
+
cross: string;
|
|
23
34
|
right: string;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* The verticals framing a row of cells. A content line of the grid has no
|
|
38
|
+
* horizontal of its own — its fill column is a placeholder the cells occupy.
|
|
39
|
+
*/
|
|
40
|
+
export interface ContentChars {
|
|
41
|
+
left: string;
|
|
24
42
|
vertical: string;
|
|
43
|
+
right: string;
|
|
25
44
|
}
|
|
26
45
|
export type RowLevel = "head" | "row" | "foot" | "mid";
|
|
27
46
|
export interface SubstituteOptions {
|
|
@@ -29,44 +48,79 @@ export interface SubstituteOptions {
|
|
|
29
48
|
safe?: boolean;
|
|
30
49
|
}
|
|
31
50
|
export declare class Box {
|
|
32
|
-
readonly
|
|
33
|
-
readonly
|
|
34
|
-
readonly
|
|
35
|
-
readonly
|
|
36
|
-
readonly
|
|
37
|
-
readonly
|
|
38
|
-
readonly
|
|
39
|
-
readonly
|
|
40
|
-
readonly
|
|
41
|
-
readonly
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
readonly right: string;
|
|
49
|
-
readonly vertical: string;
|
|
50
|
-
constructor(chars: BoxChars);
|
|
51
|
+
readonly top: EdgeChars;
|
|
52
|
+
readonly bottom: EdgeChars;
|
|
53
|
+
private readonly grid;
|
|
54
|
+
private readonly ascii;
|
|
55
|
+
private readonly headContent;
|
|
56
|
+
private readonly headSeparator;
|
|
57
|
+
private readonly bodyContent;
|
|
58
|
+
private readonly rowSeparator;
|
|
59
|
+
private readonly footSeparator;
|
|
60
|
+
private readonly footContent;
|
|
61
|
+
/**
|
|
62
|
+
* [LAW:parse-dont-validate] The one crossing between a grid string and box
|
|
63
|
+
* glyphs. A `Box` cannot exist without eight rows of four characters, so no
|
|
64
|
+
* consumer below ever re-checks the shape of the data it reads.
|
|
65
|
+
*/
|
|
66
|
+
constructor(grid: string);
|
|
51
67
|
/**
|
|
52
68
|
* Renders the top border row for given column widths.
|
|
53
69
|
*/
|
|
54
|
-
getTop(widths: number[], style?: Style, edge?: boolean): Segment[];
|
|
70
|
+
getTop(widths: readonly number[], style?: Style, edge?: boolean): Segment[];
|
|
55
71
|
/**
|
|
56
|
-
* Renders
|
|
72
|
+
* Renders the separator drawn *above* a row at `level` — the head separator
|
|
73
|
+
* under the header, the row separator between body rows, the foot separator
|
|
74
|
+
* above the footer.
|
|
57
75
|
*/
|
|
58
|
-
getRow(widths: number[], level: RowLevel, style?: Style, edge?: boolean): Segment[];
|
|
76
|
+
getRow(widths: readonly number[], level: RowLevel, style?: Style, edge?: boolean): Segment[];
|
|
77
|
+
/**
|
|
78
|
+
* The verticals that frame a content row at `level` — the counterpart to
|
|
79
|
+
* `getRow`, which draws the separator between two such rows.
|
|
80
|
+
*/
|
|
81
|
+
getContentChars(level: RowLevel): ContentChars;
|
|
59
82
|
/**
|
|
60
83
|
* Renders the bottom border row.
|
|
61
84
|
*/
|
|
62
|
-
getBottom(widths: number[], style?: Style, edge?: boolean): Segment[];
|
|
85
|
+
getBottom(widths: readonly number[], style?: Style, edge?: boolean): Segment[];
|
|
63
86
|
/**
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
87
|
+
* The box to draw with when the platform cannot render this one as written.
|
|
88
|
+
*
|
|
89
|
+
* `asciiOnly` gives up a box that spends non-ASCII glyphs for `ASCII`, and
|
|
90
|
+
* leaves the four already-ASCII styles as they are — a caller that chose
|
|
91
|
+
* MARKDOWN or ASCII_DOUBLE_HEAD asked for that frame and it is already
|
|
92
|
+
* drawable, so answering with `ASCII` would trade a frame the terminal
|
|
93
|
+
* supports for a different one it equally supports. `safe` squares off the
|
|
94
|
+
* rounded corners a legacy Windows terminal draws as blanks.
|
|
67
95
|
*/
|
|
68
96
|
substitute(options?: SubstituteOptions): Box;
|
|
69
97
|
private safeSubstitute;
|
|
98
|
+
/**
|
|
99
|
+
* The nearest box that spends no special glyphs on a header — what a table
|
|
100
|
+
* with `showHeader: false` draws with. A box whose head row already matches
|
|
101
|
+
* its body is its own answer, so this is the identity for fourteen of the
|
|
102
|
+
* nineteen shipped styles.
|
|
103
|
+
*
|
|
104
|
+
* It stands beside `substitute` rather than joining it because the two ask
|
|
105
|
+
* unrelated questions: `substitute` asks what the *platform* can draw, this
|
|
106
|
+
* asks what the *table* contains. Nothing correlates them, so a caller that
|
|
107
|
+
* wants both wants both, and a shared options bag would only multiply the
|
|
108
|
+
* combinations either one has to reason about. [LAW:no-mode-explosion]
|
|
109
|
+
*
|
|
110
|
+
* [LAW:types-are-the-program] The relation is keyed on the grid, not on
|
|
111
|
+
* object identity as the reference's dict is. A `Box` is wholly determined by
|
|
112
|
+
* its grid — that is what the constructor takes and all eight rows derive
|
|
113
|
+
* from — so two boxes with one grid must answer this alike. Identity keying
|
|
114
|
+
* would say otherwise the moment a box arrived by any route but the shipped
|
|
115
|
+
* constant, and `safeSubstitute` above builds exactly such a box: a fresh
|
|
116
|
+
* instance carrying an unchanged grid.
|
|
117
|
+
*/
|
|
118
|
+
plainHeaded(): Box;
|
|
119
|
+
/**
|
|
120
|
+
* [LAW:dataflow-not-control-flow] Every full-width rule the box can draw is
|
|
121
|
+
* this one loop; which rule it is arrives as four characters, not a branch.
|
|
122
|
+
*/
|
|
123
|
+
private getEdge;
|
|
70
124
|
private getRowChars;
|
|
71
125
|
}
|
|
72
126
|
export declare const ASCII: Box;
|
package/dist/core/box.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"box.d.ts","sourceRoot":"","sources":["../../src/core/box.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"box.d.ts","sourceRoot":"","sources":["../../src/core/box.ts"],"names":[],"mappings":"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"}
|