@lewishowles/cli-style 0.9.0 → 0.11.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/CHANGELOG.md +19 -0
- package/README.md +102 -1
- package/bin/cli-style.js +10 -0
- package/package.json +1 -1
- package/src/capability/terminal.js +98 -0
- package/src/catalogue/renderer-catalogue.js +62 -0
- package/src/cli/catalogue-command.js +108 -0
- package/src/cli/gallery-command.js +8 -1
- package/src/cli/help.js +5 -0
- package/src/cli/render-command.js +17 -2
- package/src/create-cli-style.js +14 -0
- package/src/formatters/ansi.js +17 -1
- package/src/gallery/render-gallery.js +163 -2
- package/src/index.js +19 -2
- package/src/interactive/spinner.js +335 -0
- package/src/patterns/diff-block.js +333 -0
- package/src/primitives/chip.js +1 -1
- package/src/primitives/panel.js +6 -1
- package/src/primitives/sparkline.js +209 -0
- package/src/theme/colours.js +172 -36
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.11.0 - 2026-07-24
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added a spinner/loading indicator for long-running JavaScript tasks: `ui.spinner.run(text, fn)` wraps a single async call, and `ui.spinner(text)` returns a handle (`update`, `succeed`, `fail`, `stop`) for cases that don't fit one call. Output always goes to stderr, animates only on an interactive terminal, and falls back to a static start/result line under CI, plain, JSON, or agent profiles. JavaScript-only; not available through the Bash, Python, or Swift adapters.
|
|
10
|
+
|
|
11
|
+
## 0.10.0 - 2026-07-20
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- Added light and dark ANSI-256 colour palettes with automatic theme detection (explicit flag/option, `COLORFGBG`, then a `COLORTERM`/`TERM_PROGRAM` best-effort signal, falling back to dark).
|
|
16
|
+
- Added `cli-style list` and `cli-style describe` for static renderer catalogue discovery.
|
|
17
|
+
- Added a `sparkline()` renderer (`sparkline` for `cli-style render`) for compact trend display with latest, minimum, and maximum values.
|
|
18
|
+
- Added a `diffBlock()` renderer (`diff-block` for `cli-style render`) for structured added/removed/context/header diff presentation.
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- Fixed panel body text losing legibility against its background fill in some theme/terminal combinations by giving panels their own explicit theme-matched text colour instead of relying on the terminal-inherited default.
|
|
23
|
+
|
|
5
24
|
## 0.9.0 - 2026-07-16
|
|
6
25
|
|
|
7
26
|
### Added
|
package/README.md
CHANGED
|
@@ -241,10 +241,12 @@ Renderer names are stable for `cli-style render`, `cli_style_render`, and Python
|
|
|
241
241
|
| `table` | `table(options)` | Use `cli_style_render` | Use `render("table", ...)` | Use `CliStyle.render` | Tabular records with narrow-width fallback. |
|
|
242
242
|
| `progress-bar` | `progressBar(options)` | Use `cli_style_render` | Use `render("progress-bar", ...)` | Use `CliStyle.render` | Single progress value with numeric text. |
|
|
243
243
|
| `bar-chart` | `barChart(options)` | Use `cli_style_render` | Use `render("bar-chart", ...)` | Use `CliStyle.render` | Multi-row bar chart. |
|
|
244
|
+
| `sparkline` | `sparkline(options)` | Use `cli_style_render` | Use `render("sparkline", ...)` | Use `CliStyle.render` | Compact trend with latest, minimum, and maximum values. |
|
|
244
245
|
| `step` | `step(label, state, options)` | Use `cli_style_render` | Use `render("step", ...)` | Use `CliStyle.render` | One workflow step. |
|
|
245
246
|
| `step-progress` | `stepProgress(options)` | Use `cli_style_render` | Use `render("step-progress", ...)` | Use `CliStyle.render` | Numbered workflow steps. |
|
|
246
247
|
| `empty-state` | `emptyState(title, detail, options)` | Use `cli_style_render` | Use `render("empty-state", ...)` | Use `CliStyle.render` | Empty result message. |
|
|
247
248
|
| `error-block` | `errorBlock(title, lines, options)` | Use `cli_style_render` | Use `render("error-block", ...)` | Use `CliStyle.render` | Structured error block. |
|
|
249
|
+
| `diff-block` | `diffBlock(diff, options)` | Use `cli_style_render` | Use `render("diff-block", ...)` | Use `CliStyle.render` | Structured added, removed, context, and header lines. |
|
|
248
250
|
|
|
249
251
|
Use `render("<renderer>", data, ...)` (Python) or `CliStyle.render(_:data:options:)` (Swift) for aggregate patterns (`diagnostic-report`, `agent-transcript`, `compact-data-table`) or any renderer with multi-item fields.
|
|
250
252
|
|
|
@@ -337,6 +339,7 @@ Pre-render a `span` when only part of the hint needs emphasis.
|
|
|
337
339
|
| `table` | `columns`, `rows`, `width` |
|
|
338
340
|
| `progress-bar` | `value`, `max`, `barWidth`, `tone` |
|
|
339
341
|
| `bar-chart` | `rows`, `barWidth` |
|
|
342
|
+
| `sparkline` | `values`, `label`, `tone`, `width` |
|
|
340
343
|
| `step` | `label`, `state` |
|
|
341
344
|
| `step-progress` | `steps`, `current` |
|
|
342
345
|
| `empty-state` | `title`, `detail` |
|
|
@@ -344,6 +347,35 @@ Pre-render a `span` when only part of the hint needs emphasis.
|
|
|
344
347
|
|
|
345
348
|
For visual examples, run `cli-style gallery --section primitives`.
|
|
346
349
|
|
|
350
|
+
### `sparkline`
|
|
351
|
+
|
|
352
|
+
Pass finite numbers in time order. The rendered trend always includes `latest`, `min`, and `max` context. Set `width` to limit plotted samples, `tone` to choose an optional semantic colour, `colour: false` to disable ANSI output, or `unicode: false` for an ASCII-only ramp.
|
|
353
|
+
|
|
354
|
+
```js
|
|
355
|
+
sparkline({ label: "Deploys", values: [1, 2, 3, 5, 8] });
|
|
356
|
+
// Deploys: ▁▂▃▅█ latest=8 min=1 max=8
|
|
357
|
+
|
|
358
|
+
sparkline({ label: "Rollback rate", values: [8, 6, 4, 2], tone: "warning" });
|
|
359
|
+
// Rollback rate: █▆▃▁ latest=2 min=2 max=8
|
|
360
|
+
|
|
361
|
+
sparkline({ label: "Flat", values: [4, 4, 4, 4] });
|
|
362
|
+
// Flat: ▄▄▄▄ latest=4 min=4 max=4
|
|
363
|
+
|
|
364
|
+
sparkline({ label: "Negative", values: [-6, -4, -2], colour: false });
|
|
365
|
+
// Negative: ▁▅█ latest=-2 min=-6 max=-2
|
|
366
|
+
|
|
367
|
+
sparkline({ label: "Narrow", values: [0, 10, -5, 5, 2, 8], width: 4 });
|
|
368
|
+
// Narrow: ▃█▁▇ latest=8 min=-5 max=10
|
|
369
|
+
|
|
370
|
+
sparkline({ label: "No colour", values: [1, 3, 2], colour: false });
|
|
371
|
+
// No colour: ▁█▅ latest=2 min=1 max=3
|
|
372
|
+
|
|
373
|
+
sparkline({ label: "No Unicode", values: [1, 3, 2], unicode: false });
|
|
374
|
+
// No Unicode: .#= latest=2 min=1 max=3
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
Empty or unusable `values` return `""`.
|
|
378
|
+
|
|
347
379
|
## Patterns
|
|
348
380
|
|
|
349
381
|
Patterns are built-in composite renderers for common report shapes. They use the same lower-level primitives internally, but accept structured objects so callers do not need to assemble panels, statuses, tables, and rows by hand.
|
|
@@ -358,6 +390,33 @@ Patterns are built-in composite renderers for common report shapes. They use the
|
|
|
358
390
|
| `confirmation-result` | `confirmationResult(confirmation, options)` | Confirmation outcome with detail. | `title`, `state`, `action`, `item`, `detail` |
|
|
359
391
|
| `next-step-block` | `nextStepBlock(nextStep, options)` | Next-step guidance. | `title`, `next`, `reason`, `commands`, `alternatives` |
|
|
360
392
|
| `compact-data-table` | `compactDataTable(data, options)` | Titled compact data table. | `title`, `summary`, `columns`, `rows` |
|
|
393
|
+
| `diff-block` | `diffBlock(diff, options)` | Structured diff presentation. | `title`, `path`, `lines` |
|
|
394
|
+
|
|
395
|
+
### `diff-block`
|
|
396
|
+
|
|
397
|
+
Pass structured lines with one of four types. The renderer adds stable textual prefixes, so colour is optional and never carries meaning alone.
|
|
398
|
+
|
|
399
|
+
```json
|
|
400
|
+
{
|
|
401
|
+
"title": "Renderer change",
|
|
402
|
+
"path": "src/render.js",
|
|
403
|
+
"lines": [
|
|
404
|
+
{ "type": "header", "text": "render()" },
|
|
405
|
+
{ "type": "added", "newLineNumber": 102, "text": "return nextStep;" },
|
|
406
|
+
{ "type": "removed", "oldLineNumber": 101, "text": "return currentStep;" },
|
|
407
|
+
{
|
|
408
|
+
"type": "context",
|
|
409
|
+
"oldLineNumber": 100,
|
|
410
|
+
"newLineNumber": 100,
|
|
411
|
+
"text": "const currentStep = getStep();"
|
|
412
|
+
}
|
|
413
|
+
]
|
|
414
|
+
}
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
`oldLineNumber` and `newLineNumber` are optional caller-supplied fields. Added lines use the new number, removed lines use the old number, and context lines use both. Numbered blocks show a stable gutter and computed `(+added -removed)` summary. Missing numbers fall back to the unnumbered output.
|
|
418
|
+
|
|
419
|
+
`diff-block` presents structured data only. It does not parse unified-diff strings, calculate patches or line numbers, or apply changes. Invalid line entries are ignored safely.
|
|
361
420
|
|
|
362
421
|
For visual examples, run `cli-style gallery --section patterns`.
|
|
363
422
|
|
|
@@ -523,6 +582,31 @@ const reporter = createReporter({
|
|
|
523
582
|
});
|
|
524
583
|
```
|
|
525
584
|
|
|
585
|
+
## Spinner
|
|
586
|
+
|
|
587
|
+
Use a spinner when a JavaScript script runs a long task and needs progress feedback. Spinner output always goes to stderr, so piped or JSON stdout stays clean. On a non-interactive target (CI, plain, JSON, agent profiles, or non-TTY stdout) it prints a static start line and result line instead of animating.
|
|
588
|
+
|
|
589
|
+
```js
|
|
590
|
+
import { createCliStyle } from "@lewishowles/cli-style";
|
|
591
|
+
|
|
592
|
+
const ui = createCliStyle();
|
|
593
|
+
|
|
594
|
+
await ui.spinner.run("Building", async () => {
|
|
595
|
+
await runBuild();
|
|
596
|
+
});
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
For cases that don't fit one awaited call, use the manual handle:
|
|
600
|
+
|
|
601
|
+
```js
|
|
602
|
+
const spinner = ui.spinner("Building");
|
|
603
|
+
|
|
604
|
+
spinner.update("Building (2/3)");
|
|
605
|
+
spinner.succeed("Build complete");
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
Only one spinner can be active at a time; starting a second while one is running throws.
|
|
609
|
+
|
|
526
610
|
## Options and flags
|
|
527
611
|
|
|
528
612
|
### JavaScript options
|
|
@@ -533,16 +617,18 @@ Most renderers accept the same output options:
|
|
|
533
617
|
| --------- | ------------------------------------------------------------------------- |
|
|
534
618
|
| `profile` | Output profile: `human`, `agent`, `diagnostic`, `ci`, `plain`, or `json`. |
|
|
535
619
|
| `colour` | Whether ANSI colour should be applied. |
|
|
620
|
+
| `theme` | `auto`, `light`, or `dark` semantic colour palette. |
|
|
536
621
|
| `unicode` | Whether Unicode symbols should be used. |
|
|
537
622
|
| `width` | Available output width for renderers that adapt layout. |
|
|
538
623
|
|
|
539
|
-
Use `createCliStyle()` to resolve these options from `argv`, `env`, `stdout`, CI, TTY, `NO_COLOR`, `FORCE_COLOR`, and terminal capability.
|
|
624
|
+
Use `createCliStyle()` to resolve these options from `argv`, `env`, `stdout`, CI, TTY, `NO_COLOR`, `FORCE_COLOR`, and terminal capability. `theme: "auto"` resolves in this order: explicit `--light`/`--dark` flags or `theme: "light"`/`"dark"`, recognised `COLORFGBG` background slot, `COLORTERM` or `TERM_PROGRAM`, then a dark fallback. The additional environment signals are best-effort terminal identifiers, so they select the conservative dark palette rather than claiming a light or dark preference. `NO_COLOR`, `TERM=dumb`, `--plain`, and explicit no-colour flags still disable colour.
|
|
540
625
|
|
|
541
626
|
### CLI and Bash flags
|
|
542
627
|
|
|
543
628
|
| Flag | Purpose |
|
|
544
629
|
| ---------------------------- | -------------------------------------- |
|
|
545
630
|
| `--profile <name>` | Render with a named profile. |
|
|
631
|
+
| `--light` / `--dark` | Override automatic terminal theme. |
|
|
546
632
|
| `--plain` | Disable colour and Unicode decoration. |
|
|
547
633
|
| `--no-colour` / `--no-color` | Disable ANSI colour. |
|
|
548
634
|
| `--no-unicode` | Use ASCII symbols. |
|
|
@@ -606,6 +692,19 @@ Tone values used by public APIs include `info`, `success`, `warning`, `danger`,
|
|
|
606
692
|
|
|
607
693
|
## Gallery
|
|
608
694
|
|
|
695
|
+
## Renderer discovery
|
|
696
|
+
|
|
697
|
+
List the available renderers or inspect one renderer before constructing a JSON payload:
|
|
698
|
+
|
|
699
|
+
```bash
|
|
700
|
+
cli-style list
|
|
701
|
+
cli-style list --json
|
|
702
|
+
cli-style describe progress-bar
|
|
703
|
+
cli-style describe progress-bar --json
|
|
704
|
+
```
|
|
705
|
+
|
|
706
|
+
`describe` includes the JavaScript API name, main input fields, and a focused gallery command when one is available.
|
|
707
|
+
|
|
609
708
|
Use the gallery to review current renderer output:
|
|
610
709
|
|
|
611
710
|
```bash
|
|
@@ -617,6 +716,8 @@ cli-style gallery --fixture audit-finding
|
|
|
617
716
|
cli-style gallery --profile agent
|
|
618
717
|
cli-style gallery --width 64
|
|
619
718
|
cli-style gallery --variants
|
|
719
|
+
cli-style gallery --light --section primitives
|
|
720
|
+
cli-style gallery --dark --section primitives
|
|
620
721
|
```
|
|
621
722
|
|
|
622
723
|
Use `--interactive` to select a section or fixture with `fzf` when it is installed.
|
package/bin/cli-style.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { Buffer } from "node:buffer";
|
|
4
4
|
import { createCliStyle, renderGallery, renderHelp } from "../src/index.js";
|
|
5
5
|
import { parseAdapterPathRequest, renderAdapterPath } from "../src/cli/adapter-path-command.js";
|
|
6
|
+
import { parseCatalogueRequest, renderCatalogue } from "../src/cli/catalogue-command.js";
|
|
6
7
|
import { parseRenderRequest, renderJsonInput } from "../src/cli/render-command.js";
|
|
7
8
|
|
|
8
9
|
import {
|
|
@@ -54,6 +55,15 @@ if (command === undefined || command === "--help" || command === "-h") {
|
|
|
54
55
|
console.error(error.message);
|
|
55
56
|
process.exitCode = 1;
|
|
56
57
|
}
|
|
58
|
+
} else if (command === "list" || command === "describe") {
|
|
59
|
+
try {
|
|
60
|
+
const request = parseCatalogueRequest(command, args.slice(1));
|
|
61
|
+
|
|
62
|
+
print(renderCatalogue(request, ui.options));
|
|
63
|
+
} catch (error) {
|
|
64
|
+
console.error(error.message);
|
|
65
|
+
process.exitCode = 1;
|
|
66
|
+
}
|
|
57
67
|
} else if (command === "render") {
|
|
58
68
|
try {
|
|
59
69
|
const request = parseRenderRequest(args.slice(1));
|
package/package.json
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
|
+
import { isTheme, themes } from "../theme/colours.js";
|
|
2
|
+
|
|
1
3
|
// Default terminal width when no stream width is available.
|
|
2
4
|
const defaultWidth = 80;
|
|
3
5
|
|
|
6
|
+
// Known terminal signals support a conservative dark palette when no theme is explicit.
|
|
7
|
+
const terminalThemeSignals = ["COLORTERM", "TERM_PROGRAM"];
|
|
8
|
+
|
|
4
9
|
/**
|
|
5
10
|
* Resolve terminal output capabilities from injected args, env, and stream data.
|
|
6
11
|
*
|
|
@@ -47,11 +52,104 @@ export function resolveTerminalCapabilities(options = {}) {
|
|
|
47
52
|
isCi,
|
|
48
53
|
isDumb,
|
|
49
54
|
isTty,
|
|
55
|
+
theme: resolveTheme({
|
|
56
|
+
argv,
|
|
57
|
+
env,
|
|
58
|
+
theme: options.theme,
|
|
59
|
+
}),
|
|
50
60
|
unicode,
|
|
51
61
|
width,
|
|
52
62
|
};
|
|
53
63
|
}
|
|
54
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Resolve a terminal theme from explicit overrides and terminal signals.
|
|
67
|
+
*
|
|
68
|
+
* @param {object} options
|
|
69
|
+
* Theme resolution inputs.
|
|
70
|
+
* @returns {string}
|
|
71
|
+
* Resolved light or dark theme.
|
|
72
|
+
*/
|
|
73
|
+
export function resolveTheme(options = {}) {
|
|
74
|
+
const flagTheme = readThemeFlag(options.argv ?? []);
|
|
75
|
+
|
|
76
|
+
if (flagTheme !== undefined) {
|
|
77
|
+
return flagTheme;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (isTheme(options.theme) && options.theme !== themes.AUTO) {
|
|
81
|
+
return options.theme;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const colourFgBgTheme = resolveColourFgBgTheme(options.env?.COLORFGBG);
|
|
85
|
+
|
|
86
|
+
if (colourFgBgTheme !== themes.AUTO) {
|
|
87
|
+
return colourFgBgTheme;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return resolveTerminalSignalTheme(options.env) ?? themes.DARK;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Resolve a conservative theme from best-effort terminal signals.
|
|
95
|
+
*
|
|
96
|
+
* @param {object} env
|
|
97
|
+
* Environment values to inspect.
|
|
98
|
+
* @returns {string|undefined}
|
|
99
|
+
* Dark theme when a known terminal signal is present.
|
|
100
|
+
*/
|
|
101
|
+
function resolveTerminalSignalTheme(env = {}) {
|
|
102
|
+
if (terminalThemeSignals.some((signal) => env[signal])) {
|
|
103
|
+
return themes.DARK;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Resolve known COLORFGBG background slots without guessing custom palettes.
|
|
111
|
+
*
|
|
112
|
+
* @param {string} colourFgBg
|
|
113
|
+
* Terminal foreground and background capability string.
|
|
114
|
+
* @returns {string}
|
|
115
|
+
* Resolved light, dark, or automatic theme.
|
|
116
|
+
*/
|
|
117
|
+
function resolveColourFgBgTheme(colourFgBg) {
|
|
118
|
+
const background = colourFgBg?.split(";").at(-1);
|
|
119
|
+
|
|
120
|
+
if (background === "0" || background === "8") {
|
|
121
|
+
return themes.DARK;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (background === "7" || background === "15") {
|
|
125
|
+
return themes.LIGHT;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return themes.AUTO;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Read the final explicit theme flag so later command arguments can override earlier ones.
|
|
133
|
+
*
|
|
134
|
+
* @param {string[]} argv
|
|
135
|
+
* CLI arguments to inspect.
|
|
136
|
+
* @returns {string|undefined}
|
|
137
|
+
* Explicit theme when present.
|
|
138
|
+
*/
|
|
139
|
+
function readThemeFlag(argv) {
|
|
140
|
+
let theme;
|
|
141
|
+
|
|
142
|
+
for (const argument of argv) {
|
|
143
|
+
if (argument === "--dark") {
|
|
144
|
+
theme = themes.DARK;
|
|
145
|
+
} else if (argument === "--light") {
|
|
146
|
+
theme = themes.LIGHT;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return theme;
|
|
151
|
+
}
|
|
152
|
+
|
|
55
153
|
/**
|
|
56
154
|
* Resolve colour support from explicit flags and terminal state.
|
|
57
155
|
*
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// Static renderer metadata shared by CLI discovery and render validation.
|
|
2
|
+
export const rendererCatalogue = [
|
|
3
|
+
["agent-transcript", "pattern", "agentTranscript", ["entries"], "agent-transcript"],
|
|
4
|
+
["audit-finding", "pattern", "auditFinding", ["finding", "result"], "audit-finding"],
|
|
5
|
+
["bar-chart", "primitive", "barChart", ["rows"], undefined],
|
|
6
|
+
["chip", "primitive", "chip", ["label", "tone"], undefined],
|
|
7
|
+
["command-result", "pattern", "commandResult", ["result", "summary"], "command-result"],
|
|
8
|
+
["compact-data-table", "pattern", "compactDataTable", ["columns", "rows"], "compact-data-table"],
|
|
9
|
+
[
|
|
10
|
+
"confirmation-result",
|
|
11
|
+
"pattern",
|
|
12
|
+
"confirmationResult",
|
|
13
|
+
["action", "state"],
|
|
14
|
+
"confirmation-result",
|
|
15
|
+
],
|
|
16
|
+
[
|
|
17
|
+
"diagnostic-report",
|
|
18
|
+
"pattern",
|
|
19
|
+
"diagnosticReport",
|
|
20
|
+
["findings", "summary"],
|
|
21
|
+
"diagnostic-report",
|
|
22
|
+
],
|
|
23
|
+
["diff-block", "pattern", "diffBlock", ["lines"], "diff-block"],
|
|
24
|
+
["divider", "primitive", "divider", ["label"], undefined],
|
|
25
|
+
["empty-state", "primitive", "emptyState", ["detail", "title"], undefined],
|
|
26
|
+
["error-block", "primitive", "errorBlock", ["lines", "title"], undefined],
|
|
27
|
+
["hint", "primitive", "hint", ["message"], undefined],
|
|
28
|
+
["next-step-block", "pattern", "nextStepBlock", ["next", "reason"], "next-step-block"],
|
|
29
|
+
["panel", "primitive", "panel", ["lines", "title", "tone"], undefined],
|
|
30
|
+
["progress-bar", "primitive", "progressBar", ["max", "value"], undefined],
|
|
31
|
+
["row", "primitive", "row", ["label", "value"], undefined],
|
|
32
|
+
["row-group", "primitive", "rowGroup", ["rows"], undefined],
|
|
33
|
+
["sparkline", "primitive", "sparkline", ["values"], "sparkline"],
|
|
34
|
+
["span", "primitive", "span", ["tone", "value"], undefined],
|
|
35
|
+
["status", "primitive", "status", ["detail", "type"], undefined],
|
|
36
|
+
["step", "primitive", "step", ["label", "state"], undefined],
|
|
37
|
+
["step-progress", "primitive", "stepProgress", ["current", "steps"], undefined],
|
|
38
|
+
["table", "primitive", "table", ["columns", "rows"], undefined],
|
|
39
|
+
["task-summary", "pattern", "taskSummary", ["result", "task"], "task-summary"],
|
|
40
|
+
].map(([name, category, api, fields, fixture]) => ({
|
|
41
|
+
api,
|
|
42
|
+
category,
|
|
43
|
+
fields,
|
|
44
|
+
fixture,
|
|
45
|
+
name,
|
|
46
|
+
purpose: `${api} renderer.`,
|
|
47
|
+
}));
|
|
48
|
+
|
|
49
|
+
// Stable CLI names derived from the catalogue.
|
|
50
|
+
export const rendererNames = rendererCatalogue.map((renderer) => renderer.name);
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Find renderer metadata by its stable CLI name.
|
|
54
|
+
*
|
|
55
|
+
* @param {string} name
|
|
56
|
+
* Renderer name to look up.
|
|
57
|
+
* @returns {object|undefined}
|
|
58
|
+
* Renderer metadata when found.
|
|
59
|
+
*/
|
|
60
|
+
export function getRendererMetadata(name) {
|
|
61
|
+
return rendererCatalogue.find((renderer) => renderer.name === name);
|
|
62
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { getRendererMetadata, rendererCatalogue } from "../catalogue/renderer-catalogue.js";
|
|
2
|
+
|
|
3
|
+
// Rendering flags are resolved before catalogue command parsing.
|
|
4
|
+
const globalRenderingFlags = new Set([
|
|
5
|
+
"--dark",
|
|
6
|
+
"--json",
|
|
7
|
+
"--light",
|
|
8
|
+
"--no-color",
|
|
9
|
+
"--no-colour",
|
|
10
|
+
"--no-unicode",
|
|
11
|
+
"--plain",
|
|
12
|
+
]);
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Parse a catalogue discovery request.
|
|
16
|
+
*
|
|
17
|
+
* @param {string} command
|
|
18
|
+
* Catalogue command name.
|
|
19
|
+
* @param {string[]} args
|
|
20
|
+
* Arguments after the command.
|
|
21
|
+
* @returns {object}
|
|
22
|
+
* Parsed catalogue request.
|
|
23
|
+
*/
|
|
24
|
+
export function parseCatalogueRequest(command, args = []) {
|
|
25
|
+
const commandArgs = args.filter((argument) => !globalRenderingFlags.has(argument));
|
|
26
|
+
|
|
27
|
+
if (command === "list" && commandArgs.length === 0) {
|
|
28
|
+
return {
|
|
29
|
+
command,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (
|
|
34
|
+
command === "describe" &&
|
|
35
|
+
commandArgs.length === 1 &&
|
|
36
|
+
getRendererMetadata(commandArgs[0]) !== undefined
|
|
37
|
+
) {
|
|
38
|
+
return {
|
|
39
|
+
command,
|
|
40
|
+
renderer: commandArgs[0],
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (command === "describe" && commandArgs.length === 1) {
|
|
45
|
+
throw new Error(`Unknown renderer: ${commandArgs[0]}`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
throw new Error(
|
|
49
|
+
command === "describe" ? "Usage: cli-style describe <renderer>" : "Usage: cli-style list",
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Render catalogue data for the requested output profile.
|
|
55
|
+
*
|
|
56
|
+
* @param {object} request
|
|
57
|
+
* Parsed catalogue request.
|
|
58
|
+
* @param {object} options
|
|
59
|
+
* Resolved rendering options.
|
|
60
|
+
* @returns {string}
|
|
61
|
+
* Text or JSON catalogue output.
|
|
62
|
+
*/
|
|
63
|
+
export function renderCatalogue(request, options = {}) {
|
|
64
|
+
const data =
|
|
65
|
+
request.command === "list" ? rendererCatalogue : getRendererMetadata(request.renderer);
|
|
66
|
+
|
|
67
|
+
if (options.profile === "json") {
|
|
68
|
+
return JSON.stringify(data, null, 2);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (request.command === "describe") {
|
|
72
|
+
return renderDescription(data);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return ["primitive", "pattern"]
|
|
76
|
+
.flatMap((category) => [
|
|
77
|
+
category,
|
|
78
|
+
...rendererCatalogue
|
|
79
|
+
.filter((renderer) => renderer.category === category)
|
|
80
|
+
.map((renderer) => ` ${renderer.name} ${renderer.api}`),
|
|
81
|
+
"",
|
|
82
|
+
])
|
|
83
|
+
.slice(0, -1)
|
|
84
|
+
.join("\n");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Render one renderer's discovery details.
|
|
89
|
+
*
|
|
90
|
+
* @param {object} renderer
|
|
91
|
+
* Renderer metadata.
|
|
92
|
+
* @returns {string}
|
|
93
|
+
* Human-readable renderer description.
|
|
94
|
+
*/
|
|
95
|
+
function renderDescription(renderer) {
|
|
96
|
+
const gallery =
|
|
97
|
+
renderer.fixture === undefined
|
|
98
|
+
? "No focused fixture."
|
|
99
|
+
: `cli-style gallery --fixture ${renderer.fixture}`;
|
|
100
|
+
|
|
101
|
+
return [
|
|
102
|
+
renderer.name,
|
|
103
|
+
`Category: ${renderer.category}`,
|
|
104
|
+
`JavaScript: ${renderer.api}`,
|
|
105
|
+
`Fields: ${renderer.fields.join(", ")}`,
|
|
106
|
+
`Gallery: ${gallery}`,
|
|
107
|
+
].join("\n");
|
|
108
|
+
}
|
|
@@ -4,7 +4,14 @@ import { galleryFixtures, gallerySections, galleryVariants } from "../gallery/re
|
|
|
4
4
|
import { isProfile, profiles } from "../profiles/profiles.js";
|
|
5
5
|
|
|
6
6
|
// Global rendering flags accepted after `gallery` for focused review commands.
|
|
7
|
-
const globalRenderingFlags = new Set([
|
|
7
|
+
const globalRenderingFlags = new Set([
|
|
8
|
+
"--dark",
|
|
9
|
+
"--light",
|
|
10
|
+
"--no-color",
|
|
11
|
+
"--no-colour",
|
|
12
|
+
"--no-unicode",
|
|
13
|
+
"--plain",
|
|
14
|
+
]);
|
|
8
15
|
|
|
9
16
|
/**
|
|
10
17
|
* Parse gallery command arguments.
|
package/src/cli/help.js
CHANGED
|
@@ -5,18 +5,23 @@ const helpLines = [
|
|
|
5
5
|
"Usage:",
|
|
6
6
|
" cli-style --help",
|
|
7
7
|
" cli-style adapter-path bash|python",
|
|
8
|
+
" cli-style list [options]",
|
|
9
|
+
" cli-style describe <renderer> [options]",
|
|
8
10
|
" cli-style gallery [variant] [options]",
|
|
9
11
|
" cli-style render <renderer> [options] < input.json",
|
|
10
12
|
"",
|
|
11
13
|
"Commands:",
|
|
12
14
|
" adapter-path Print a Bash or Python adapter path.",
|
|
13
15
|
" gallery Print the read-only style gallery.",
|
|
16
|
+
" list List available primitives and patterns.",
|
|
17
|
+
" describe Describe one renderer and its input fields.",
|
|
14
18
|
" render Render caller-provided JSON from stdin.",
|
|
15
19
|
"",
|
|
16
20
|
"Gallery variants:",
|
|
17
21
|
" current, no-colour, no-unicode, plain",
|
|
18
22
|
"",
|
|
19
23
|
"Options:",
|
|
24
|
+
" --light, --dark Override automatic terminal-theme detection.",
|
|
20
25
|
" --section <name> Show primitives or patterns.",
|
|
21
26
|
" --fixture <name> Show one named pattern fixture.",
|
|
22
27
|
" --profile <name> Render with a text profile.",
|
|
@@ -4,6 +4,7 @@ import { commandResult } from "../patterns/command-result.js";
|
|
|
4
4
|
import { compactDataTable } from "../patterns/compact-data-table.js";
|
|
5
5
|
import { confirmationResult } from "../patterns/confirmation-result.js";
|
|
6
6
|
import { diagnosticReport } from "../patterns/diagnostic-report.js";
|
|
7
|
+
import { diffBlock } from "../patterns/diff-block.js";
|
|
7
8
|
import { nextStepBlock } from "../patterns/next-step-block.js";
|
|
8
9
|
import { taskSummary } from "../patterns/task-summary.js";
|
|
9
10
|
import { barChart } from "../primitives/bar-chart.js";
|
|
@@ -14,11 +15,13 @@ import { panel } from "../primitives/panel.js";
|
|
|
14
15
|
import { progressBar } from "../primitives/progress-bar.js";
|
|
15
16
|
import { row } from "../primitives/row.js";
|
|
16
17
|
import { rowGroup } from "../primitives/row-group.js";
|
|
18
|
+
import { sparkline } from "../primitives/sparkline.js";
|
|
17
19
|
import { span } from "../primitives/span.js";
|
|
18
20
|
import { status } from "../primitives/status.js";
|
|
19
21
|
import { step, stepProgress } from "../primitives/step-progress.js";
|
|
20
22
|
import { table } from "../primitives/table.js";
|
|
21
23
|
import { isProfile, profiles } from "../profiles/profiles.js";
|
|
24
|
+
export { rendererNames } from "../catalogue/renderer-catalogue.js";
|
|
22
25
|
|
|
23
26
|
// Renderer names accepted by the custom-input render command.
|
|
24
27
|
const renderers = {
|
|
@@ -38,6 +41,7 @@ const renderers = {
|
|
|
38
41
|
"compact-data-table": (input, options) => compactDataTable(input, options),
|
|
39
42
|
"confirmation-result": (input, options) => confirmationResult(input, options),
|
|
40
43
|
"diagnostic-report": (input, options) => diagnosticReport(input, options),
|
|
44
|
+
"diff-block": (input, options) => diffBlock(input, options),
|
|
41
45
|
divider: (input, options) =>
|
|
42
46
|
divider({
|
|
43
47
|
...input,
|
|
@@ -79,6 +83,11 @@ const renderers = {
|
|
|
79
83
|
...input,
|
|
80
84
|
...options,
|
|
81
85
|
}),
|
|
86
|
+
sparkline: (input, options) =>
|
|
87
|
+
sparkline({
|
|
88
|
+
...input,
|
|
89
|
+
...options,
|
|
90
|
+
}),
|
|
82
91
|
span: (input, options) =>
|
|
83
92
|
span(input.value, input.tone, {
|
|
84
93
|
...input,
|
|
@@ -108,10 +117,16 @@ const renderers = {
|
|
|
108
117
|
};
|
|
109
118
|
|
|
110
119
|
// Global rendering flags accepted after `render <renderer>`.
|
|
111
|
-
const globalRenderingFlags = new Set([
|
|
120
|
+
const globalRenderingFlags = new Set([
|
|
121
|
+
"--dark",
|
|
122
|
+
"--light",
|
|
123
|
+
"--no-color",
|
|
124
|
+
"--no-colour",
|
|
125
|
+
"--no-unicode",
|
|
126
|
+
"--plain",
|
|
127
|
+
]);
|
|
112
128
|
|
|
113
129
|
// Renderer names exposed for help text and tests.
|
|
114
|
-
export const rendererNames = Object.keys(renderers);
|
|
115
130
|
|
|
116
131
|
/**
|
|
117
132
|
* Parse render command arguments.
|
package/src/create-cli-style.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { resolveProfile } from "./capability/resolve-profile.js";
|
|
2
2
|
import { resolveTerminalCapabilities } from "./capability/terminal.js";
|
|
3
|
+
import { runSpinner, spinner as startSpinner } from "./interactive/spinner.js";
|
|
3
4
|
import { profiles } from "./profiles/profiles.js";
|
|
4
5
|
import { createReporter } from "./reporters/create-reporter.js";
|
|
5
6
|
|
|
@@ -23,6 +24,18 @@ export function createCliStyle(options = {}) {
|
|
|
23
24
|
applyExplicitOption(resolvedOptions, options, "unicode");
|
|
24
25
|
applyExplicitOption(resolvedOptions, options, "width");
|
|
25
26
|
|
|
27
|
+
const spinner = (text, spinnerOptions = {}) =>
|
|
28
|
+
startSpinner(text, {
|
|
29
|
+
...resolvedOptions,
|
|
30
|
+
...spinnerOptions,
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
spinner.run = (text, fn, spinnerOptions = {}) =>
|
|
34
|
+
runSpinner(text, fn, {
|
|
35
|
+
...resolvedOptions,
|
|
36
|
+
...spinnerOptions,
|
|
37
|
+
});
|
|
38
|
+
|
|
26
39
|
return {
|
|
27
40
|
options: resolvedOptions,
|
|
28
41
|
print: (value) => {
|
|
@@ -33,6 +46,7 @@ export function createCliStyle(options = {}) {
|
|
|
33
46
|
...resolvedOptions,
|
|
34
47
|
...reporterOptions,
|
|
35
48
|
}),
|
|
49
|
+
spinner,
|
|
36
50
|
write: (value) => {
|
|
37
51
|
process.stdout.write(value);
|
|
38
52
|
},
|