@promptctl/rich-js 0.18.1 → 0.20.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.
Files changed (169) hide show
  1. package/README.md +11 -8
  2. package/dist/core/box.d.ts +3 -1
  3. package/dist/core/box.js +6 -13
  4. package/dist/core/cells.d.ts +61 -28
  5. package/dist/core/cells.js +171 -124
  6. package/dist/core/clock.d.ts +48 -0
  7. package/dist/core/clock.js +42 -0
  8. package/dist/core/color.d.ts +99 -18
  9. package/dist/core/color.js +334 -146
  10. package/dist/core/console.d.ts +38 -7
  11. package/dist/core/console.js +159 -90
  12. package/dist/core/easing.d.ts +86 -0
  13. package/dist/core/easing.js +171 -0
  14. package/dist/core/env.d.ts +16 -0
  15. package/dist/core/env.js +16 -0
  16. package/dist/core/export-html.js +1 -4
  17. package/dist/core/export-lines.d.ts +22 -13
  18. package/dist/core/export-lines.js +37 -19
  19. package/dist/core/export-svg.d.ts +49 -0
  20. package/dist/core/export-svg.js +213 -0
  21. package/dist/core/fnv1a.d.ts +10 -0
  22. package/dist/core/fnv1a.js +17 -0
  23. package/dist/core/highlighter.d.ts +2 -2
  24. package/dist/core/highlighter.js +113 -47
  25. package/dist/core/json.js +1 -1
  26. package/dist/core/markup.d.ts +28 -7
  27. package/dist/core/markup.js +235 -312
  28. package/dist/core/measure.d.ts +3 -3
  29. package/dist/core/measure.js +12 -7
  30. package/dist/core/memo.d.ts +19 -0
  31. package/dist/core/memo.js +34 -0
  32. package/dist/core/noise.d.ts +31 -0
  33. package/dist/core/noise.js +94 -0
  34. package/dist/core/osc8.d.ts +6 -6
  35. package/dist/core/osc8.js +9 -21
  36. package/dist/core/paint.d.ts +107 -0
  37. package/dist/core/paint.js +161 -0
  38. package/dist/core/place.d.ts +1 -1
  39. package/dist/core/place.js +4 -6
  40. package/dist/core/pretty.d.ts +126 -2
  41. package/dist/core/pretty.js +571 -105
  42. package/dist/core/protocol.d.ts +43 -3
  43. package/dist/core/protocol.js +23 -2
  44. package/dist/core/render.d.ts +33 -30
  45. package/dist/core/render.js +43 -83
  46. package/dist/core/segment.d.ts +6 -5
  47. package/dist/core/segment.js +11 -15
  48. package/dist/core/selection.d.ts +12 -0
  49. package/dist/core/selection.js +14 -0
  50. package/dist/core/strip.d.ts +116 -74
  51. package/dist/core/strip.js +266 -217
  52. package/dist/core/style.d.ts +27 -7
  53. package/dist/core/style.js +106 -41
  54. package/dist/core/text.d.ts +43 -16
  55. package/dist/core/text.js +180 -59
  56. package/dist/core/wrap.d.ts +5 -0
  57. package/dist/core/wrap.js +16 -3
  58. package/dist/host/app.d.ts +95 -17
  59. package/dist/host/app.js +201 -61
  60. package/dist/host/index.d.ts +4 -1
  61. package/dist/host/index.js +1 -0
  62. package/dist/host/input.d.ts +84 -0
  63. package/dist/host/input.js +498 -0
  64. package/dist/host/terminal-host.d.ts +15 -2
  65. package/dist/host/terminal-host.js +2 -0
  66. package/dist/index.d.ts +26 -14
  67. package/dist/index.js +17 -7
  68. package/dist/node/prompt.d.ts +13 -0
  69. package/dist/node/prompt.js +30 -9
  70. package/dist/node/save.d.ts +4 -3
  71. package/dist/node/save.js +6 -3
  72. package/dist/node/terminal-host.d.ts +10 -2
  73. package/dist/node/terminal-host.js +7 -0
  74. package/dist/node/traceback.d.ts +2 -1
  75. package/dist/node/traceback.js +3 -39
  76. package/dist/renderables/align.js +2 -9
  77. package/dist/renderables/columns.d.ts +45 -29
  78. package/dist/renderables/columns.js +142 -103
  79. package/dist/renderables/constrain.js +6 -12
  80. package/dist/renderables/effect.d.ts +113 -0
  81. package/dist/renderables/effect.js +182 -0
  82. package/dist/renderables/effects.d.ts +252 -0
  83. package/dist/renderables/effects.js +565 -0
  84. package/dist/renderables/embed.d.ts +94 -31
  85. package/dist/renderables/embed.js +141 -45
  86. package/dist/renderables/group.d.ts +6 -3
  87. package/dist/renderables/group.js +5 -0
  88. package/dist/renderables/layout.d.ts +6 -23
  89. package/dist/renderables/layout.js +24 -113
  90. package/dist/renderables/list-view.d.ts +42 -0
  91. package/dist/renderables/list-view.js +78 -0
  92. package/dist/renderables/live.d.ts +40 -16
  93. package/dist/renderables/live.js +96 -83
  94. package/dist/renderables/markdown.d.ts +16 -2
  95. package/dist/renderables/markdown.js +521 -118
  96. package/dist/renderables/padding.d.ts +22 -1
  97. package/dist/renderables/padding.js +30 -24
  98. package/dist/renderables/panel.d.ts +31 -33
  99. package/dist/renderables/panel.js +137 -141
  100. package/dist/renderables/progress.d.ts +110 -16
  101. package/dist/renderables/progress.js +212 -81
  102. package/dist/renderables/progressBar.d.ts +24 -4
  103. package/dist/renderables/progressBar.js +78 -23
  104. package/dist/renderables/prompt.d.ts +37 -15
  105. package/dist/renderables/prompt.js +125 -85
  106. package/dist/renderables/ratio.d.ts +123 -0
  107. package/dist/renderables/ratio.js +213 -0
  108. package/dist/renderables/rule.d.ts +3 -2
  109. package/dist/renderables/rule.js +50 -52
  110. package/dist/renderables/spinner.d.ts +28 -2
  111. package/dist/renderables/spinner.js +52 -19
  112. package/dist/renderables/status.d.ts +13 -7
  113. package/dist/renderables/status.js +15 -29
  114. package/dist/renderables/syntax.d.ts +37 -7
  115. package/dist/renderables/syntax.js +134 -83
  116. package/dist/renderables/table.d.ts +58 -20
  117. package/dist/renderables/table.js +373 -197
  118. package/dist/renderables/traceback.d.ts +5 -4
  119. package/dist/renderables/traceback.js +239 -68
  120. package/dist/renderables/tree.d.ts +10 -2
  121. package/dist/renderables/tree.js +61 -34
  122. package/dist/renderables/viewport.d.ts +30 -0
  123. package/dist/renderables/viewport.js +61 -7
  124. package/dist/template-bindings/color-funcs.d.ts +31 -9
  125. package/dist/template-bindings/color-funcs.js +40 -14
  126. package/dist/template-bindings/helpers.d.ts +4 -2
  127. package/dist/template-bindings/helpers.js +5 -20
  128. package/dist/template-bindings/index.d.ts +29 -17
  129. package/dist/template-bindings/index.js +36 -20
  130. package/dist/template-bindings/palette-funcs.d.ts +3 -1
  131. package/dist/template-bindings/palette-funcs.js +11 -7
  132. package/dist/template-bindings/style-funcs.js +4 -2
  133. package/dist/themes/buildPalette.d.ts +24 -3
  134. package/dist/themes/buildPalette.js +52 -8
  135. package/dist/themes/colorMath.d.ts +29 -17
  136. package/dist/themes/colorMath.js +74 -48
  137. package/dist/themes/colorRef.d.ts +2 -17
  138. package/dist/themes/colorRef.js +13 -31
  139. package/dist/themes/palette.d.ts +24 -0
  140. package/dist/themes/palette.js +46 -3
  141. package/dist/themes/ramp.d.ts +21 -34
  142. package/dist/themes/ramp.js +23 -42
  143. package/dist/themes/registry.js +39 -28
  144. package/dist/widgets/button.d.ts +2 -6
  145. package/dist/widgets/button.js +9 -40
  146. package/dist/widgets/checkbox.d.ts +2 -5
  147. package/dist/widgets/checkbox.js +9 -31
  148. package/dist/widgets/dropdown.d.ts +2 -5
  149. package/dist/widgets/dropdown.js +17 -51
  150. package/dist/widgets/event-router.d.ts +31 -40
  151. package/dist/widgets/event-router.js +52 -483
  152. package/dist/widgets/index.d.ts +2 -1
  153. package/dist/widgets/ink.d.ts +22 -0
  154. package/dist/widgets/ink.js +31 -0
  155. package/dist/widgets/slider.d.ts +2 -5
  156. package/dist/widgets/slider.js +11 -34
  157. package/dist/widgets/text-input.d.ts +19 -10
  158. package/dist/widgets/text-input.js +156 -147
  159. package/dist/widgets/themed-widget.d.ts +20 -0
  160. package/dist/widgets/themed-widget.js +89 -0
  161. package/dist/widgets/toggle.d.ts +2 -5
  162. package/dist/widgets/toggle.js +8 -31
  163. package/dist/widgets/types.d.ts +8 -25
  164. package/dist/widgets/types.js +8 -0
  165. package/dist/widgets/widget-app.d.ts +24 -7
  166. package/dist/widgets/widget-app.js +35 -18
  167. package/dist/widgets/widget-base.d.ts +2 -0
  168. package/dist/widgets/widget-base.js +9 -2
  169. package/package.json +31 -17
@@ -9,46 +9,22 @@
9
9
  * can no longer transpose with the palette it was meant to belong to. A ramp
10
10
  * is the one function whose input is a number, so the decision stays inside.
11
11
  *
12
- * A threshold cascade is a ramp too: the same stops with a `step` easing hold
12
+ * A threshold cascade is a ramp too: the same stops with the `step` ease hold
13
13
  * each color until the next position, which is exactly `≥ threshold → hotter`
14
14
  * written as data. [LAW:one-type-per-behavior] One primitive; the easing is a
15
- * value, so a gradient and a cascade differ by one word, not by which function
16
- * was called.
15
+ * value — any `Ease` from `core/easing`, the vocabulary every effect shares —
16
+ * so a gradient and a cascade differ by one word, not by which function was
17
+ * called. [LAW:dataflow-not-control-flow] `at` applies it unconditionally.
17
18
  *
18
19
  * Interpolation is in OKLCH (`Oklch.mix`), so a `linear` ramp between two
19
20
  * theme colors passes through perceptually even steps rather than the muddy
20
21
  * midpoints of an sRGB blend.
21
22
  *
22
- * [LAW:one-way-deps] Imports `core/color` and `core/oklch` only; nothing here
23
- * knows what a palette is. Resolving stop *names* is the template binding's
23
+ * [LAW:one-way-deps] Imports `core/color`, `core/easing` and `core/oklch`
24
+ * only; nothing here knows what a palette is. Resolving stop *names* is the template binding's
24
25
  * job (`paletteFuncs`), which hands this module resolved colors.
25
26
  */
26
27
  import { Oklch } from "../core/oklch.js";
27
- /**
28
- * How a value between two stops maps to progress along them. Each easing is
29
- * a function on segment progress `t ∈ [0, 1)` — `linear` keeps it, `step`
30
- * holds the left stop for the whole segment.
31
- *
32
- * [LAW:dataflow-not-control-flow] The easing is looked up by name and applied
33
- * unconditionally; `at` runs the same code for a gradient and a cascade.
34
- */
35
- export const RAMP_EASINGS = {
36
- linear: (t) => t,
37
- step: () => 0,
38
- };
39
- export const RAMP_EASING_NAMES = Object.keys(RAMP_EASINGS);
40
- /**
41
- * The gate a spelled easing crosses. [LAW:parse-dont-validate] — returns the
42
- * narrowed name, so `ColorRamp` never re-checks. Unknown names throw naming
43
- * every legal one. [LAW:no-silent-failure]
44
- */
45
- export function parseRampEasing(name) {
46
- if (!Object.hasOwn(RAMP_EASINGS, name)) {
47
- throw new RangeError(`unknown ramp easing ${JSON.stringify(name)}; expected one of ` +
48
- RAMP_EASING_NAMES.map((n) => JSON.stringify(n)).join(", "));
49
- }
50
- return name;
51
- }
52
28
  /**
53
29
  * An immutable ramp over stops sorted by position.
54
30
  *
@@ -65,10 +41,10 @@ export function parseRampEasing(name) {
65
41
  * same way).
66
42
  */
67
43
  export class ColorRamp {
68
- easing;
44
+ ease;
69
45
  stops;
70
- constructor(easing, stops) {
71
- this.easing = easing;
46
+ constructor(ease, stops) {
47
+ this.ease = ease;
72
48
  this.stops = stops;
73
49
  if (stops.length === 0) {
74
50
  throw new RangeError("a ColorRamp needs at least one stop");
@@ -86,14 +62,19 @@ export class ColorRamp {
86
62
  }
87
63
  /**
88
64
  * The color at `value`. Below the first stop it is the first color; at or
89
- * above the last stop it is the last; between two stops it is `easing`
90
- * of the way from the lower to the upper, so a value exactly on a stop is
91
- * that stop's color, byte for byte.
65
+ * above the last stop it is the last; between two stops the raw progress
66
+ * from the lower to the upper goes through `ease`, and the eased progress
67
+ * says how far to mix.
92
68
  *
93
- * The two endpoint returns are the exactness contract, not a shortcut:
94
- * the sRGB → OKLCH → sRGB round-trip can land a channel one unit off, and a
95
- * ramp that does not hit its own stops exactly would make a `step` ramp
96
- * paint a color the author never wrote.
69
+ * Eased progress 0 is the lower stop's color and 1 the upper's, byte for
70
+ * byte — the exactness contract, not a shortcut: the sRGB → OKLCH → sRGB
71
+ * round-trip can land a channel one unit off, and a ramp that did not hit
72
+ * its own stops exactly would make a `step` ramp paint a color the author
73
+ * never wrote. So a value on a stop is that stop's color whenever the ease
74
+ * starts at 0, as every built-in does but `step-start` and the
75
+ * `jump-start`/`jump-both` steps, which jump at the start of the interval as
76
+ * CSS defines them. An ease that overshoots [0, 1] holds the end colors
77
+ * where it does: a ramp never paints outside its stops.
97
78
  */
98
79
  at(value) {
99
80
  if (!Number.isFinite(value)) {
@@ -110,9 +91,9 @@ export class ColorRamp {
110
91
  if (lower === stops.length - 1)
111
92
  return from.color;
112
93
  // `to.at > value >= from.at` by construction of `lower`, so the segment
113
- // has positive width and `t ∈ [0, 1)`.
94
+ // has positive width and raw progress is in [0, 1).
114
95
  const to = stops[lower + 1];
115
- const t = RAMP_EASINGS[this.easing]((value - from.at) / (to.at - from.at));
96
+ const t = this.ease((value - from.at) / (to.at - from.at));
116
97
  if (t <= 0)
117
98
  return from.color;
118
99
  if (t >= 1)
@@ -1,5 +1,6 @@
1
- import { ColorTable, parseRgbHex, parseRgbaHex } from "../core/color.js";
2
- import { Palette } from "./palette.js";
1
+ import { ColorParseError, ColorTable, parseHexColor } from "../core/color.js";
2
+ import { Palette, drawnOn } from "./palette.js";
3
+ import { ACCENT_KEYS, accentText } from "./buildPalette.js";
3
4
  import { THEMES } from "./data/index.js";
4
5
  import { ANSI_SLOTS } from "./data/types.js";
5
6
  const THEME_NAMES = Object.freeze(Object.keys(THEMES).sort());
@@ -31,17 +32,22 @@ function isThemeName(name) {
31
32
  */
32
33
  export function getThemeBaseColors(name) {
33
34
  const data = THEMES[name];
35
+ const bg = requireBaseVar(data, "background");
36
+ // The colours `getThemePalette(name)` hands out for the same names: drawn on
37
+ // the theme's own background, as a Palette draws every colour it holds.
38
+ const draw = drawnOn(data.name, bg);
39
+ const base = (key) => draw(requireBaseVar(data, key));
34
40
  return {
35
41
  name: data.name,
36
42
  dark: data.dark,
37
- bg: requireBaseVar(data, "background"),
38
- fg: requireBaseVar(data, "foreground"),
39
- primary: requireBaseVar(data, "primary"),
40
- secondary: requireBaseVar(data, "secondary"),
41
- accent: requireBaseVar(data, "accent"),
42
- success: requireBaseVar(data, "success"),
43
- warning: requireBaseVar(data, "warning"),
44
- error: requireBaseVar(data, "error"),
43
+ bg,
44
+ fg: base("foreground"),
45
+ primary: base("primary"),
46
+ secondary: base("secondary"),
47
+ accent: base("accent"),
48
+ success: base("success"),
49
+ warning: base("warning"),
50
+ error: base("error"),
45
51
  ansi: new ColorTable(ANSI_SLOTS.map((slot) => parseOpaqueHex(data.ansi[slot], data.name, `ansi.${slot}`))),
46
52
  };
47
53
  }
@@ -64,31 +70,36 @@ function requireBaseVar(data, key) {
64
70
  function hydrate(data) {
65
71
  const map = new Map();
66
72
  for (const [k, v] of Object.entries(data.vars)) {
67
- map.set(k, parseHex(v, data.name, k));
73
+ map.set(k, parseHex(v, data.name, `var ${k}`));
74
+ }
75
+ // The data is Textual's as authored; its `text-*` is held to the pair this
76
+ // library promises by the same rule `buildPalette` derives it with.
77
+ for (const key of ACCENT_KEYS) {
78
+ const text = requireBaseVar(data, `text-${key}`);
79
+ map.set(`text-${key}`, accentText(text, requireBaseVar(data, `${key}-muted`), requireBaseVar(data, "background")));
68
80
  }
69
81
  return new Palette(data.name, data.dark, map);
70
82
  }
71
- // [LAW:single-enforcer] This is the loud-failure boundary for theme hex
72
- // data. `parseRgbHex`/`parseRgbaHex` in core/color.ts trust their input
73
- // shape (they assume the caller validated); here we are the caller from
74
- // the untrusted side (authored data files) and the place that has to
75
- // reject `"0G"`, `""`, `"#GGGGGG"`, etc.
76
- const HEX_RE = /^[0-9a-fA-F]+$/;
83
+ // Theme data is authored, so a malformed colour names the theme and the key it
84
+ // sits under; the grammar itself is `parseHexColor`'s.
85
+ function parseHex(value, theme, key) {
86
+ try {
87
+ return parseHexColor(value);
88
+ }
89
+ catch (cause) {
90
+ if (!(cause instanceof ColorParseError))
91
+ throw cause;
92
+ throw new Error(`Theme ${theme}: ${key}: ${cause.message}`, { cause });
93
+ }
94
+ }
77
95
  /**
78
96
  * An ANSI slot is a colour a terminal paints, never a blend, so it takes no
79
97
  * alpha: `flattenAlpha` leaves a STANDARD spec alone and would pass one through.
80
98
  */
81
99
  function parseOpaqueHex(value, theme, key) {
82
- if (!/^#[0-9A-Fa-f]{6}$/.test(value)) {
83
- throw new Error(`Theme ${theme}: ${key} has invalid hex ${JSON.stringify(value)} (expected #RRGGBB)`);
84
- }
85
- return parseRgbHex(value.slice(1));
86
- }
87
- function parseHex(value, theme, key) {
88
- const hex = value.startsWith("#") ? value.slice(1) : value;
89
- if ((hex.length !== 6 && hex.length !== 8) || !HEX_RE.test(hex)) {
90
- throw new Error(`Theme ${theme}: var ${key} has invalid hex ${JSON.stringify(value)} ` +
91
- `(expected #RRGGBB or #RRGGBBAA)`);
100
+ const color = parseHex(value, theme, key);
101
+ if (color.alpha !== 1) {
102
+ throw new Error(`Theme ${theme}: ${key} has alpha in ${JSON.stringify(value)} (expected #RRGGBB)`);
92
103
  }
93
- return hex.length === 6 ? parseRgbHex(hex) : parseRgbaHex(hex);
104
+ return color;
94
105
  }
@@ -18,7 +18,7 @@
18
18
  import { Segment } from "../core/segment.js";
19
19
  import type { RenderOptions } from "../core/protocol.js";
20
20
  import type { TerminalTheme } from "../core/color.js";
21
- import { WidgetBase } from "./widget-base.js";
21
+ import { ThemedWidget } from "./themed-widget.js";
22
22
  import type { KeyEvent, WidgetMouseEvent } from "./types.js";
23
23
  export type ButtonVariant = "default" | "primary" | "success" | "warning" | "danger";
24
24
  export interface ButtonOptions {
@@ -28,14 +28,12 @@ export interface ButtonOptions {
28
28
  disabled?: boolean;
29
29
  theme?: TerminalTheme;
30
30
  }
31
- export declare class Button extends WidgetBase {
31
+ export declare class Button extends ThemedWidget {
32
32
  readonly id: string;
33
33
  readonly focusable = true;
34
34
  accessor label: string;
35
35
  accessor variant: ButtonVariant;
36
- private accessor _theme;
37
36
  constructor(options: ButtonOptions);
38
- setTheme(theme: TerminalTheme): void;
39
37
  handleKey(event: KeyEvent): void;
40
38
  handleMouse(event: WidgetMouseEvent): void;
41
39
  setActive(value: boolean): void;
@@ -44,6 +42,4 @@ export declare class Button extends WidgetBase {
44
42
  minimum: number;
45
43
  maximum: number;
46
44
  };
47
- private resolveColors;
48
- private resolvePalette;
49
45
  }
@@ -52,10 +52,9 @@ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn,
52
52
  import { observable, action, observableRef } from "mobx";
53
53
  import { Segment } from "../core/segment.js";
54
54
  import { Style } from "../core/style.js";
55
- import { ColorSpec } from "../core/color.js";
56
55
  import { cellLen } from "../core/cells.js";
57
- import { DEFAULT_TERMINAL_THEME } from "../themes/terminalThemes.js";
58
- import { WidgetBase } from "./widget-base.js";
56
+ import { ThemedWidget } from "./themed-widget.js";
57
+ import { ink } from "./ink.js";
59
58
  // Variant → palette key mapping. Per variant:
60
59
  // bg / fg — normal state. Muted accent bg + mostly-accent fg → readable.
61
60
  // hover — full accent bg; pair with hoverFg, NOT with `fg`. The
@@ -70,7 +69,7 @@ const VARIANT_KEYS = {
70
69
  danger: { bg: "error-muted", fg: "text-error", hover: "error", hoverFg: "on-error" },
71
70
  };
72
71
  let Button = (() => {
73
- let _classSuper = WidgetBase;
72
+ let _classSuper = ThemedWidget;
74
73
  let _instanceExtraInitializers = [];
75
74
  let _label_decorators;
76
75
  let _label_initializers = [];
@@ -78,10 +77,6 @@ let Button = (() => {
78
77
  let _variant_decorators;
79
78
  let _variant_initializers = [];
80
79
  let _variant_extraInitializers = [];
81
- let __theme_decorators;
82
- let __theme_initializers = [];
83
- let __theme_extraInitializers = [];
84
- let _setTheme_decorators;
85
80
  let _handleKey_decorators;
86
81
  let _handleMouse_decorators;
87
82
  let _setActive_decorators;
@@ -90,15 +85,11 @@ let Button = (() => {
90
85
  const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
91
86
  _label_decorators = [observable];
92
87
  _variant_decorators = [observableRef];
93
- __theme_decorators = [observableRef];
94
- _setTheme_decorators = [action];
95
88
  _handleKey_decorators = [action];
96
89
  _handleMouse_decorators = [action];
97
90
  _setActive_decorators = [action];
98
91
  __esDecorate(this, null, _label_decorators, { kind: "accessor", name: "label", static: false, private: false, access: { has: obj => "label" in obj, get: obj => obj.label, set: (obj, value) => { obj.label = value; } }, metadata: _metadata }, _label_initializers, _label_extraInitializers);
99
92
  __esDecorate(this, null, _variant_decorators, { kind: "accessor", name: "variant", static: false, private: false, access: { has: obj => "variant" in obj, get: obj => obj.variant, set: (obj, value) => { obj.variant = value; } }, metadata: _metadata }, _variant_initializers, _variant_extraInitializers);
100
- __esDecorate(this, null, __theme_decorators, { kind: "accessor", name: "_theme", static: false, private: false, access: { has: obj => "_theme" in obj, get: obj => obj._theme, set: (obj, value) => { obj._theme = value; } }, metadata: _metadata }, __theme_initializers, __theme_extraInitializers);
101
- __esDecorate(this, null, _setTheme_decorators, { kind: "method", name: "setTheme", static: false, private: false, access: { has: obj => "setTheme" in obj, get: obj => obj.setTheme }, metadata: _metadata }, null, _instanceExtraInitializers);
102
93
  __esDecorate(this, null, _handleKey_decorators, { kind: "method", name: "handleKey", static: false, private: false, access: { has: obj => "handleKey" in obj, get: obj => obj.handleKey }, metadata: _metadata }, null, _instanceExtraInitializers);
103
94
  __esDecorate(this, null, _handleMouse_decorators, { kind: "method", name: "handleMouse", static: false, private: false, access: { has: obj => "handleMouse" in obj, get: obj => obj.handleMouse }, metadata: _metadata }, null, _instanceExtraInitializers);
104
95
  __esDecorate(this, null, _setActive_decorators, { kind: "method", name: "setActive", static: false, private: false, access: { has: obj => "setActive" in obj, get: obj => obj.setActive }, metadata: _metadata }, null, _instanceExtraInitializers);
@@ -112,21 +103,14 @@ let Button = (() => {
112
103
  #variant_accessor_storage = (__runInitializers(this, _label_extraInitializers), __runInitializers(this, _variant_initializers, void 0));
113
104
  get variant() { return this.#variant_accessor_storage; }
114
105
  set variant(value) { this.#variant_accessor_storage = value; }
115
- #_theme_accessor_storage = (__runInitializers(this, _variant_extraInitializers), __runInitializers(this, __theme_initializers, void 0));
116
- // [LAW:types-are-the-program] @observableRef so setTheme() triggers a
117
- // re-render — see slider.ts.
118
- get _theme() { return this.#_theme_accessor_storage; }
119
- set _theme(value) { this.#_theme_accessor_storage = value; }
120
106
  constructor(options) {
121
- super();
122
- __runInitializers(this, __theme_extraInitializers);
107
+ super(options.theme);
108
+ __runInitializers(this, _variant_extraInitializers);
123
109
  this.id = options.id ?? `button-${options.label.toLowerCase().replace(/\s+/g, "-")}`;
124
110
  this.label = options.label;
125
111
  this.variant = options.variant ?? "default";
126
112
  this.disabled = options.disabled ?? false;
127
- this._theme = options.theme ?? DEFAULT_TERMINAL_THEME;
128
113
  }
129
- setTheme(theme) { this._theme = theme; }
130
114
  // --- Event handlers ---
131
115
  handleKey(event) {
132
116
  if (this.disabled)
@@ -170,19 +154,17 @@ let Button = (() => {
170
154
  const right = focused ? "]" : " ";
171
155
  const text = `${left} ${this.label} ${right}`;
172
156
  if (this.disabled) {
173
- return [new Segment(text, new Style({ color: "#666666", bgcolor: "#333333", dim: true }))];
157
+ return [new Segment(text, new Style(this.disabledInk))];
174
158
  }
175
159
  // Active and hover share the same colour pair (full accent bg + on-accent fg).
176
160
  // Active is differentiated by bold, not by inverting fg/bg — inversion gives
177
161
  // mostly-accent text on mostly-bg-tinted background, which is unreadable for
178
162
  // accents whose contrast partner depends on luminance.
163
+ const keys = VARIANT_KEYS[this.variant];
179
164
  if (this.active || this.hovered) {
180
- const fg = this.resolvePalette(VARIANT_KEYS[this.variant].hoverFg);
181
- const bg = this.resolvePalette(VARIANT_KEYS[this.variant].hover);
182
- return [new Segment(text, new Style({ color: fg, bgcolor: bg, bold: this.active }))];
165
+ return [new Segment(text, new Style({ ...ink(this.theme, keys.hoverFg, keys.hover), bold: this.active }))];
183
166
  }
184
- const { fg, bg } = this.resolveColors("bg");
185
- return [new Segment(text, new Style({ color: fg, bgcolor: bg }))];
167
+ return [new Segment(text, new Style(ink(this.theme, keys.fg, keys.bg)))];
186
168
  }
187
169
  measure(_options) {
188
170
  // [LAW:one-source-of-truth] cellLen is the cell-width authority; .length
@@ -190,19 +172,6 @@ let Button = (() => {
190
172
  const width = cellLen(this.label) + 4;
191
173
  return { minimum: width, maximum: width };
192
174
  }
193
- // --- Palette resolution ---
194
- resolveColors(bgKey) {
195
- const keys = VARIANT_KEYS[this.variant];
196
- return {
197
- fg: this.resolvePalette(keys.fg),
198
- bg: this.resolvePalette(bgKey === "hover" ? keys.hover : keys.bg),
199
- };
200
- }
201
- resolvePalette(key) {
202
- const rgba = this._theme.palette.get(key);
203
- // [LAW:no-defensive-null-guards] palette is required and must contain all keys.
204
- return ColorSpec.fromRgba(rgba);
205
- }
206
175
  };
207
176
  })();
208
177
  export { Button };
@@ -13,7 +13,7 @@
13
13
  import { Segment } from "../core/segment.js";
14
14
  import { type RenderOptions } from "../core/protocol.js";
15
15
  import type { TerminalTheme } from "../core/color.js";
16
- import { WidgetBase } from "./widget-base.js";
16
+ import { ThemedWidget } from "./themed-widget.js";
17
17
  import type { KeyEvent, WidgetMouseEvent } from "./types.js";
18
18
  export interface CheckboxOptions {
19
19
  label: string;
@@ -22,14 +22,12 @@ export interface CheckboxOptions {
22
22
  disabled?: boolean;
23
23
  theme?: TerminalTheme;
24
24
  }
25
- export declare class Checkbox extends WidgetBase {
25
+ export declare class Checkbox extends ThemedWidget {
26
26
  readonly id: string;
27
27
  readonly focusable = true;
28
28
  accessor label: string;
29
29
  accessor checked: boolean;
30
- private accessor _theme;
31
30
  constructor(options: CheckboxOptions);
32
- setTheme(theme: TerminalTheme): void;
33
31
  handleKey(event: KeyEvent): void;
34
32
  handleMouse(event: WidgetMouseEvent): void;
35
33
  protected draw(options: RenderOptions): Iterable<Segment>;
@@ -37,5 +35,4 @@ export declare class Checkbox extends WidgetBase {
37
35
  minimum: number;
38
36
  maximum: number;
39
37
  };
40
- private resolvePalette;
41
38
  }
@@ -44,16 +44,15 @@ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn,
44
44
  if (target) Object.defineProperty(target, contextIn.name, descriptor);
45
45
  done = true;
46
46
  };
47
- import { observable, action, observableRef } from "mobx";
47
+ import { observable, action } from "mobx";
48
48
  import { Segment } from "../core/segment.js";
49
49
  import { Style } from "../core/style.js";
50
- import { ColorSpec } from "../core/color.js";
51
50
  import { cellLen } from "../core/cells.js";
52
- import { DEFAULT_TERMINAL_THEME } from "../themes/terminalThemes.js";
53
51
  import { drawable } from "../core/protocol.js";
54
- import { WidgetBase } from "./widget-base.js";
52
+ import { ThemedWidget } from "./themed-widget.js";
53
+ import { ink } from "./ink.js";
55
54
  let Checkbox = (() => {
56
- let _classSuper = WidgetBase;
55
+ let _classSuper = ThemedWidget;
57
56
  let _instanceExtraInitializers = [];
58
57
  let _label_decorators;
59
58
  let _label_initializers = [];
@@ -61,10 +60,6 @@ let Checkbox = (() => {
61
60
  let _checked_decorators;
62
61
  let _checked_initializers = [];
63
62
  let _checked_extraInitializers = [];
64
- let __theme_decorators;
65
- let __theme_initializers = [];
66
- let __theme_extraInitializers = [];
67
- let _setTheme_decorators;
68
63
  let _handleKey_decorators;
69
64
  let _handleMouse_decorators;
70
65
  return class Checkbox extends _classSuper {
@@ -72,14 +67,10 @@ let Checkbox = (() => {
72
67
  const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
73
68
  _label_decorators = [observable];
74
69
  _checked_decorators = [observable];
75
- __theme_decorators = [observableRef];
76
- _setTheme_decorators = [action];
77
70
  _handleKey_decorators = [action];
78
71
  _handleMouse_decorators = [action];
79
72
  __esDecorate(this, null, _label_decorators, { kind: "accessor", name: "label", static: false, private: false, access: { has: obj => "label" in obj, get: obj => obj.label, set: (obj, value) => { obj.label = value; } }, metadata: _metadata }, _label_initializers, _label_extraInitializers);
80
73
  __esDecorate(this, null, _checked_decorators, { kind: "accessor", name: "checked", static: false, private: false, access: { has: obj => "checked" in obj, get: obj => obj.checked, set: (obj, value) => { obj.checked = value; } }, metadata: _metadata }, _checked_initializers, _checked_extraInitializers);
81
- __esDecorate(this, null, __theme_decorators, { kind: "accessor", name: "_theme", static: false, private: false, access: { has: obj => "_theme" in obj, get: obj => obj._theme, set: (obj, value) => { obj._theme = value; } }, metadata: _metadata }, __theme_initializers, __theme_extraInitializers);
82
- __esDecorate(this, null, _setTheme_decorators, { kind: "method", name: "setTheme", static: false, private: false, access: { has: obj => "setTheme" in obj, get: obj => obj.setTheme }, metadata: _metadata }, null, _instanceExtraInitializers);
83
74
  __esDecorate(this, null, _handleKey_decorators, { kind: "method", name: "handleKey", static: false, private: false, access: { has: obj => "handleKey" in obj, get: obj => obj.handleKey }, metadata: _metadata }, null, _instanceExtraInitializers);
84
75
  __esDecorate(this, null, _handleMouse_decorators, { kind: "method", name: "handleMouse", static: false, private: false, access: { has: obj => "handleMouse" in obj, get: obj => obj.handleMouse }, metadata: _metadata }, null, _instanceExtraInitializers);
85
76
  if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
@@ -92,21 +83,14 @@ let Checkbox = (() => {
92
83
  #checked_accessor_storage = (__runInitializers(this, _label_extraInitializers), __runInitializers(this, _checked_initializers, void 0));
93
84
  get checked() { return this.#checked_accessor_storage; }
94
85
  set checked(value) { this.#checked_accessor_storage = value; }
95
- #_theme_accessor_storage = (__runInitializers(this, _checked_extraInitializers), __runInitializers(this, __theme_initializers, void 0));
96
- // [LAW:types-are-the-program] @observableRef so setTheme() triggers a
97
- // re-render — see slider.ts.
98
- get _theme() { return this.#_theme_accessor_storage; }
99
- set _theme(value) { this.#_theme_accessor_storage = value; }
100
86
  constructor(options) {
101
- super();
102
- __runInitializers(this, __theme_extraInitializers);
87
+ super(options.theme);
88
+ __runInitializers(this, _checked_extraInitializers);
103
89
  this.id = options.id ?? `checkbox-${options.label.toLowerCase().replace(/\s+/g, "-")}`;
104
90
  this.label = options.label;
105
91
  this.checked = options.checked ?? false;
106
92
  this.disabled = options.disabled ?? false;
107
- this._theme = options.theme ?? DEFAULT_TERMINAL_THEME;
108
93
  }
109
- setTheme(theme) { this._theme = theme; }
110
94
  // --- Event handlers ---
111
95
  handleKey(event) {
112
96
  if (this.disabled)
@@ -136,22 +120,16 @@ let Checkbox = (() => {
136
120
  const indicator = this.checked ? drawable(options, "✓", "x") : " ";
137
121
  const text = `[${indicator}] ${this.label}`;
138
122
  if (this.disabled) {
139
- return [new Segment(text, new Style({ color: "#666666", bgcolor: "#333333", dim: true }))];
123
+ return [new Segment(text, new Style(this.disabledInk))];
140
124
  }
141
- const fg = this.checked ? this.resolvePalette("primary") : this.resolvePalette("foreground");
142
- return [new Segment(text, new Style({ color: fg, underline: this.focused }))];
125
+ const colors = ink(this.theme, this.checked ? "text-primary" : "foreground", "background");
126
+ return [new Segment(text, new Style({ ...colors, underline: this.focused }))];
143
127
  }
144
128
  measure(_options) {
145
129
  // [LAW:one-source-of-truth] cellLen — see button.ts.
146
130
  const width = cellLen(this.label) + 4;
147
131
  return { minimum: width, maximum: width };
148
132
  }
149
- // --- Palette resolution ---
150
- resolvePalette(key) {
151
- const rgba = this._theme.palette.get(key);
152
- // [LAW:no-defensive-null-guards] palette must contain all keys; missing is a construction bug.
153
- return ColorSpec.fromRgba(rgba);
154
- }
155
133
  };
156
134
  })();
157
135
  export { Checkbox };
@@ -25,7 +25,7 @@
25
25
  import { Segment } from "../core/segment.js";
26
26
  import { type RenderOptions } from "../core/protocol.js";
27
27
  import type { TerminalTheme } from "../core/color.js";
28
- import { WidgetBase } from "./widget-base.js";
28
+ import { ThemedWidget } from "./themed-widget.js";
29
29
  import type { KeyEvent, WidgetMouseEvent, WidgetFocusEvent, OverlayRenderable } from "./types.js";
30
30
  export interface DropdownOptions {
31
31
  options: string[];
@@ -34,7 +34,7 @@ export interface DropdownOptions {
34
34
  disabled?: boolean;
35
35
  theme?: TerminalTheme;
36
36
  }
37
- export declare class Dropdown extends WidgetBase implements OverlayRenderable {
37
+ export declare class Dropdown extends ThemedWidget implements OverlayRenderable {
38
38
  readonly id: string;
39
39
  readonly focusable = true;
40
40
  accessor options: string[];
@@ -46,10 +46,8 @@ export declare class Dropdown extends WidgetBase implements OverlayRenderable {
46
46
  label: string;
47
47
  idx: number;
48
48
  }[];
49
- private accessor _theme;
50
49
  constructor(options: DropdownOptions);
51
50
  private clampIndex;
52
- setTheme(theme: TerminalTheme): void;
53
51
  handleKey(event: KeyEvent): void;
54
52
  handleMouse(event: WidgetMouseEvent): void;
55
53
  handleFocus(event: WidgetFocusEvent): void;
@@ -63,5 +61,4 @@ export declare class Dropdown extends WidgetBase implements OverlayRenderable {
63
61
  minimum: number;
64
62
  maximum: number;
65
63
  };
66
- private resolvePalette;
67
64
  }