@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 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,6 @@
1
1
  {
2
2
  "name": "@lewishowles/cli-style",
3
- "version": "0.9.0",
3
+ "version": "0.11.0",
4
4
  "description": "Shared terminal output styles for Lewis Howles tools, diagnostics, and package CLIs.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -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(["--no-color", "--no-colour", "--no-unicode", "--plain"]);
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(["--no-color", "--no-colour", "--no-unicode", "--plain"]);
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.
@@ -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
  },