@promptctl/rich-js 0.27.0 → 0.29.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 (185) hide show
  1. package/README.md +7 -6
  2. package/THIRD-PARTY-NOTICES +42 -0
  3. package/dist/core/animation.d.ts +65 -46
  4. package/dist/core/animation.js +91 -67
  5. package/dist/core/ansi.d.ts +19 -12
  6. package/dist/core/ansi.js +114 -36
  7. package/dist/core/bidi-data.d.ts +14 -0
  8. package/dist/core/bidi-data.js +385 -0
  9. package/dist/core/bidi.d.ts +89 -0
  10. package/dist/core/bidi.js +615 -0
  11. package/dist/core/box.d.ts +9 -1
  12. package/dist/core/box.js +11 -0
  13. package/dist/core/cell-widths.d.ts +3 -2
  14. package/dist/core/cell-widths.js +93 -93
  15. package/dist/core/cells.d.ts +20 -0
  16. package/dist/core/cells.js +94 -6
  17. package/dist/core/clock.d.ts +1 -1
  18. package/dist/core/color.d.ts +56 -57
  19. package/dist/core/color.js +79 -81
  20. package/dist/core/console.d.ts +24 -7
  21. package/dist/core/console.js +27 -8
  22. package/dist/core/control-codes.d.ts +61 -0
  23. package/dist/core/control-codes.js +157 -0
  24. package/dist/core/easing.d.ts +40 -40
  25. package/dist/core/easing.js +40 -40
  26. package/dist/core/env.d.ts +6 -0
  27. package/dist/core/env.js +9 -1
  28. package/dist/core/export-html.js +5 -4
  29. package/dist/core/export-lines.d.ts +13 -11
  30. package/dist/core/export-lines.js +19 -15
  31. package/dist/core/export-svg.d.ts +3 -3
  32. package/dist/core/export-svg.js +10 -9
  33. package/dist/core/json.js +9 -2
  34. package/dist/core/markup.js +5 -4
  35. package/dist/core/memo.d.ts +1 -1
  36. package/dist/core/memo.js +1 -1
  37. package/dist/core/oklch.d.ts +14 -14
  38. package/dist/core/oklch.js +10 -10
  39. package/dist/core/orient.d.ts +68 -0
  40. package/dist/core/orient.js +224 -0
  41. package/dist/core/osc8.d.ts +12 -5
  42. package/dist/core/osc8.js +14 -7
  43. package/dist/core/paint.d.ts +9 -9
  44. package/dist/core/paint.js +3 -3
  45. package/dist/core/pretty.d.ts +1 -1
  46. package/dist/core/pretty.js +23 -19
  47. package/dist/core/protocol.d.ts +47 -26
  48. package/dist/core/protocol.js +31 -23
  49. package/dist/core/render.d.ts +19 -14
  50. package/dist/core/render.js +30 -17
  51. package/dist/core/seal.d.ts +26 -0
  52. package/dist/core/seal.js +75 -0
  53. package/dist/core/segment.d.ts +12 -4
  54. package/dist/core/segment.js +16 -6
  55. package/dist/core/strip.d.ts +83 -83
  56. package/dist/core/strip.js +70 -70
  57. package/dist/core/style.d.ts +46 -18
  58. package/dist/core/style.js +92 -56
  59. package/dist/core/text-order.d.ts +51 -0
  60. package/dist/core/text-order.js +50 -0
  61. package/dist/core/text.d.ts +12 -2
  62. package/dist/core/text.js +136 -90
  63. package/dist/core/timeline.d.ts +5 -5
  64. package/dist/core/timeline.js +11 -11
  65. package/dist/core/tween.d.ts +6 -6
  66. package/dist/core/tween.js +10 -10
  67. package/dist/core/view.d.ts +5 -5
  68. package/dist/core/view.js +15 -14
  69. package/dist/core/wrap.d.ts +24 -6
  70. package/dist/core/wrap.js +82 -16
  71. package/dist/host/app.d.ts +50 -9
  72. package/dist/host/app.js +120 -15
  73. package/dist/host/host-environment.d.ts +2 -2
  74. package/dist/host/host-environment.js +2 -2
  75. package/dist/host/index.d.ts +3 -2
  76. package/dist/host/index.js +1 -0
  77. package/dist/host/input.d.ts +42 -9
  78. package/dist/host/input.js +91 -7
  79. package/dist/host/terminal-colors.d.ts +18 -0
  80. package/dist/host/terminal-colors.js +46 -0
  81. package/dist/host/terminal-host.d.ts +4 -3
  82. package/dist/host/terminal-host.js +1 -1
  83. package/dist/index.d.ts +17 -16
  84. package/dist/index.js +11 -11
  85. package/dist/node/prompt.d.ts +1 -1
  86. package/dist/node/prompt.js +1 -1
  87. package/dist/node/terminal-host.d.ts +1 -1
  88. package/dist/node/traceback.js +2 -2
  89. package/dist/renderables/change.d.ts +1 -3
  90. package/dist/renderables/change.js +6 -8
  91. package/dist/renderables/effect.d.ts +88 -87
  92. package/dist/renderables/effect.js +124 -114
  93. package/dist/renderables/effects.d.ts +105 -76
  94. package/dist/renderables/effects.js +309 -176
  95. package/dist/renderables/embed.d.ts +1 -1
  96. package/dist/renderables/embed.js +2 -2
  97. package/dist/renderables/flexStrip.d.ts +7 -7
  98. package/dist/renderables/flexStrip.js +15 -15
  99. package/dist/renderables/frames.d.ts +7 -16
  100. package/dist/renderables/frames.js +12 -27
  101. package/dist/renderables/glyph-effect.d.ts +22 -23
  102. package/dist/renderables/glyph-effect.js +43 -41
  103. package/dist/renderables/glyph-effects.d.ts +33 -14
  104. package/dist/renderables/glyph-effects.js +90 -22
  105. package/dist/renderables/html-entities.d.ts +1 -1
  106. package/dist/renderables/html-entities.js +1 -1
  107. package/dist/renderables/lifecycle.d.ts +8 -10
  108. package/dist/renderables/lifecycle.js +17 -18
  109. package/dist/renderables/live.d.ts +2 -2
  110. package/dist/renderables/live.js +7 -7
  111. package/dist/renderables/markdown-parse.js +3 -3
  112. package/dist/renderables/markdown.js +4 -2
  113. package/dist/renderables/panel.js +7 -5
  114. package/dist/renderables/progress.d.ts +1 -1
  115. package/dist/renderables/progress.js +1 -1
  116. package/dist/renderables/progressBar.d.ts +3 -3
  117. package/dist/renderables/progressBar.js +21 -18
  118. package/dist/renderables/prompt.d.ts +1 -1
  119. package/dist/renderables/scroll.d.ts +1 -1
  120. package/dist/renderables/scroll.js +3 -3
  121. package/dist/renderables/spinner.d.ts +4 -4
  122. package/dist/renderables/spinner.js +10 -10
  123. package/dist/renderables/syntax.d.ts +2 -2
  124. package/dist/renderables/syntax.js +9 -4
  125. package/dist/renderables/table.js +5 -4
  126. package/dist/renderables/traceback.d.ts +1 -1
  127. package/dist/renderables/traceback.js +1 -1
  128. package/dist/renderables/tree.js +3 -3
  129. package/dist/template-bindings/animation-funcs.d.ts +60 -29
  130. package/dist/template-bindings/animation-funcs.js +126 -43
  131. package/dist/template-bindings/color-funcs.d.ts +14 -14
  132. package/dist/template-bindings/color-funcs.js +25 -25
  133. package/dist/template-bindings/index.d.ts +16 -14
  134. package/dist/template-bindings/index.js +17 -14
  135. package/dist/template-bindings/palette-funcs.d.ts +2 -2
  136. package/dist/template-bindings/palette-funcs.js +4 -4
  137. package/dist/template-bindings/style-funcs.d.ts +5 -5
  138. package/dist/template-bindings/style-funcs.js +16 -16
  139. package/dist/themes/{transpose.d.ts → adjust.d.ts} +23 -23
  140. package/dist/themes/{transpose.js → adjust.js} +28 -28
  141. package/dist/themes/buildPalette.d.ts +6 -5
  142. package/dist/themes/buildPalette.js +22 -22
  143. package/dist/themes/colorMath.d.ts +16 -16
  144. package/dist/themes/colorMath.js +32 -32
  145. package/dist/themes/data/ansi-tables.d.ts +2 -2
  146. package/dist/themes/data/ansi-tables.js +2 -2
  147. package/dist/themes/data/index.js +1 -4
  148. package/dist/themes/data/textual-ansi.js +0 -143
  149. package/dist/themes/data/types.d.ts +4 -4
  150. package/dist/themes/data/types.js +2 -2
  151. package/dist/themes/palette.d.ts +12 -12
  152. package/dist/themes/palette.js +16 -16
  153. package/dist/themes/ramp.d.ts +17 -17
  154. package/dist/themes/ramp.js +12 -12
  155. package/dist/themes/registry.d.ts +6 -8
  156. package/dist/themes/registry.js +6 -8
  157. package/dist/themes/terminalThemes.d.ts +4 -11
  158. package/dist/themes/terminalThemes.js +31 -47
  159. package/dist/widgets/button.d.ts +3 -3
  160. package/dist/widgets/button.js +8 -8
  161. package/dist/widgets/checkbox.d.ts +2 -2
  162. package/dist/widgets/checkbox.js +4 -4
  163. package/dist/widgets/dropdown.d.ts +3 -3
  164. package/dist/widgets/dropdown.js +7 -5
  165. package/dist/widgets/event-router.d.ts +6 -6
  166. package/dist/widgets/event-router.js +2 -2
  167. package/dist/widgets/focus-manager.d.ts +3 -3
  168. package/dist/widgets/focus-manager.js +3 -3
  169. package/dist/widgets/index.d.ts +2 -2
  170. package/dist/widgets/index.js +1 -1
  171. package/dist/widgets/ink.d.ts +3 -3
  172. package/dist/widgets/ink.js +4 -4
  173. package/dist/widgets/slider.d.ts +2 -2
  174. package/dist/widgets/static-item.d.ts +2 -2
  175. package/dist/widgets/text-input.d.ts +2 -2
  176. package/dist/widgets/text-input.js +5 -5
  177. package/dist/widgets/themed-widget.js +4 -4
  178. package/dist/widgets/toggle.d.ts +4 -4
  179. package/dist/widgets/toggle.js +6 -6
  180. package/dist/widgets/types.d.ts +6 -6
  181. package/dist/widgets/types.js +2 -2
  182. package/dist/widgets/widget-app.d.ts +10 -10
  183. package/dist/widgets/widget-app.js +8 -8
  184. package/dist/widgets/widget-base.d.ts +2 -2
  185. package/package.json +5 -4
@@ -63,7 +63,7 @@
63
63
  * they are gone.
64
64
  */
65
65
  import { resolveColorRef } from "../themes/colorRef.js";
66
- import { parseEase } from "../core/easing.js";
66
+ import { parseEasing } from "../core/easing.js";
67
67
  import { ColorRamp } from "../themes/ramp.js";
68
68
  import { computable } from "./color-funcs.js";
69
69
  /**
@@ -97,7 +97,7 @@ function colorStops(tail, palette) {
97
97
  *
98
98
  * `color` resolves a palette variable name to its hex string — `#RRGGBB`, or
99
99
  * `#00000000` for a theme's "no background", which `bg` paints as no
100
- * background and the colour functions refuse — and passes
100
+ * background and the color functions refuse — and passes
101
101
  * an already-literal color through unchanged. That second half is not a
102
102
  * convenience — it makes `color` **idempotent**, which is what lets consumers
103
103
  * apply it unconditionally to any author-written color string without first
@@ -111,7 +111,7 @@ function colorStops(tail, palette) {
111
111
  * @example
112
112
  * ```ts
113
113
  * const engine = createEngine({
114
- * fromString: (s) => new RichText(s),
114
+ * fromString: (s) => new RichText(s), isT: (v) => v instanceof RichText,
115
115
  * toString: (rt) => rt.plain,
116
116
  * funcs: {
117
117
  * ...richTextFuncs(),
@@ -135,7 +135,7 @@ export function paletteFuncs(getPalette) {
135
135
  // value, the ease, and one stop, so the gate refuses a stopless call.
136
136
  // [LAW:types-are-the-program]
137
137
  const rampFunc = {
138
- fn: ((value, ease, ...tail) => new ColorRamp(parseEase(ease), colorStops(tail, getPalette())).at(value)
138
+ fn: ((value, ease, ...tail) => new ColorRamp(parseEasing(ease), colorStops(tail, getPalette())).at(value)
139
139
  .hex),
140
140
  argTypes: ["float", "string"],
141
141
  arity: { kind: "alternating", minimum: 4 },
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * [LAW:one-source-of-truth] The function inventory below mirrors the
5
5
  * string-syntax style vocabulary documented in `docs/style.md` — the two
6
- * colour slots (`fg`/`bg`), text attributes (positive + negated), short
6
+ * color slots (`fg`/`bg`), text attributes (positive + negated), short
7
7
  * aliases, and the hyperlink. Each registration is a templating-time analogue
8
8
  * of a piece of `Style.parse`, so a template fragment composed by these
9
9
  * functions round-trips through `Style` without semantic drift.
@@ -12,17 +12,17 @@
12
12
  * shape: child `RichText` in, `RichText` out. The styling difference
13
13
  * lives in a captured `Style` value (or constructor closure), not in
14
14
  * branches inside a single dispatch. That is why this file is mostly
15
- * factories — the data (the colour, the attribute name) is the
15
+ * factories — the data (the color, the attribute name) is the
16
16
  * variability; the operation is fixed.
17
17
  */
18
18
  import type { FuncMap } from "@promptctl/go-template-js";
19
19
  /**
20
- * Text-styling registrations: the two colour sinks (`fg`, `bg`), text
20
+ * Text-styling registrations: the two color sinks (`fg`, `bg`), text
21
21
  * attributes (canonical names, short aliases, and `not_*` negations), the
22
22
  * hyperlink cell-splitter (`link`), and the multi-attribute `style` spec.
23
23
  *
24
- * Colours themselves come from elsewhere: `colorFuncs()` for the palette-free
25
- * math, `paletteFuncs()` for naming a theme colour. This module only paints.
24
+ * Colors themselves come from elsewhere: `colorFuncs()` for the palette-free
25
+ * math, `paletteFuncs()` for naming a theme color. This module only paints.
26
26
  * [LAW:one-way-deps] — nothing here imports a palette.
27
27
  */
28
28
  export declare function richTextStyleFuncs(): FuncMap;
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * [LAW:one-source-of-truth] The function inventory below mirrors the
5
5
  * string-syntax style vocabulary documented in `docs/style.md` — the two
6
- * colour slots (`fg`/`bg`), text attributes (positive + negated), short
6
+ * color slots (`fg`/`bg`), text attributes (positive + negated), short
7
7
  * aliases, and the hyperlink. Each registration is a templating-time analogue
8
8
  * of a piece of `Style.parse`, so a template fragment composed by these
9
9
  * functions round-trips through `Style` without semantic drift.
@@ -12,7 +12,7 @@
12
12
  * shape: child `RichText` in, `RichText` out. The styling difference
13
13
  * lives in a captured `Style` value (or constructor closure), not in
14
14
  * branches inside a single dispatch. That is why this file is mostly
15
- * factories — the data (the colour, the attribute name) is the
15
+ * factories — the data (the color, the attribute name) is the
16
16
  * variability; the operation is fixed.
17
17
  */
18
18
  import { Style, ATTRIBUTE_NAMES, ATTRIBUTE_SHORT_ALIASES, } from "../core/style.js";
@@ -26,26 +26,26 @@ function attrFunc(style) {
26
26
  returnType: "T",
27
27
  };
28
28
  }
29
- // --- Colour sinks ---
29
+ // --- Color sinks ---
30
30
  //
31
- // `fg` and `bg` are the only two colour-applying functions, and they are the
32
- // terminal step of the colour pipeline: `color` names a colour, the functions
31
+ // `fg` and `bg` are the only two color-applying functions, and they are the
32
+ // terminal step of the color pipeline: `color` names a color, the functions
33
33
  // in `color-funcs.ts` transform it, these paint it onto text.
34
34
  //
35
35
  // [LAW:composability] They replace five separate families — one function per
36
- // ANSI colour name (`red`, `bright_blue`, …), `hex`, `rgb`, `color` (256-index),
37
- // and `on` (background). Every one of those encoded *which colour* in the
36
+ // ANSI color name (`red`, `bright_blue`, …), `hex`, `rgb`, `color` (256-index),
37
+ // and `on` (background). Every one of those encoded *which color* in the
38
38
  // function's identity, so the vocabulary could only grow by adding names, and
39
- // a colour computed at render time could not be applied at all. Here the colour
40
- // is an argument, so the sink admits every colour that exists and every colour
39
+ // a color computed at render time could not be applied at all. Here the color
40
+ // is an argument, so the sink admits every color that exists and every color
41
41
  // that will ever exist. Two functions, unbounded reach.
42
42
  //
43
43
  // [LAW:types-are-the-program] The slot accepts the full `ColorSpec.parse`
44
- // vocabulary, which is wider than the hex the colour math produces — and that
45
- // width is deliberate, not laxity. `#7aa2f7` is a *concrete* colour; `"red"`
44
+ // vocabulary, which is wider than the hex the color math produces — and that
45
+ // width is deliberate, not laxity. `#7aa2f7` is a *concrete* color; `"red"`
46
46
  // and `"color(42)"` are *symbolic* ones the terminal resolves against its own
47
- // theme. Only concrete colours can be darkened or blended, which is why the
48
- // colour-math functions take hex alone; but both kinds can be painted, so the
47
+ // theme. Only concrete colors can be darkened or blended, which is why the
48
+ // color-math functions take hex alone; but both kinds can be painted, so the
49
49
  // sink takes the union. The type of each slot is exactly the set of values that
50
50
  // slot can mean something for.
51
51
  function colorSinkFunc(slot) {
@@ -138,12 +138,12 @@ const linkFunc = {
138
138
  };
139
139
  // --- Public assembly ---
140
140
  /**
141
- * Text-styling registrations: the two colour sinks (`fg`, `bg`), text
141
+ * Text-styling registrations: the two color sinks (`fg`, `bg`), text
142
142
  * attributes (canonical names, short aliases, and `not_*` negations), the
143
143
  * hyperlink cell-splitter (`link`), and the multi-attribute `style` spec.
144
144
  *
145
- * Colours themselves come from elsewhere: `colorFuncs()` for the palette-free
146
- * math, `paletteFuncs()` for naming a theme colour. This module only paints.
145
+ * Colors themselves come from elsewhere: `colorFuncs()` for the palette-free
146
+ * math, `paletteFuncs()` for naming a theme color. This module only paints.
147
147
  * [LAW:one-way-deps] — nothing here imports a palette.
148
148
  */
149
149
  export function richTextStyleFuncs() {
@@ -1,48 +1,48 @@
1
1
  /**
2
- * Transpose a Palette to a new "key" — analogous to transposing a melody.
2
+ * Adjust a Palette to a new "key" — analogous to adjusting a melody.
3
3
  *
4
4
  * melody / chord progression :: Palette (relationships between colors)
5
- * key (signature) :: ThemeKey (hue / chroma / lightness deltas)
6
- * "in C major" / "in D…" :: the resulting transposed Palette
5
+ * key (signature) :: ColorAdjustment (hue / chroma / lightness deltas)
6
+ * "in C major" / "in D…" :: the resulting adjusted Palette
7
7
  *
8
8
  * Because OKLCH is perceptually uniform, "+30° hue" looks like a
9
9
  * consistent jump everywhere on the color wheel — the way a perfect-fifth
10
- * interval sounds the same in every musical key. Transposition is closed
11
- * under composition: `transpose(transpose(p, k1), k2)` produces the same
12
- * colors as a single transpose with the combined deltas, modulo round-trip
10
+ * interval sounds the same in every musical key. Adjustment is closed
11
+ * under composition: `adjust(adjust(p, k1), k2)` produces the same
12
+ * colors as a single adjust with the combined deltas, modulo round-trip
13
13
  * quantization.
14
14
  *
15
15
  * [LAW:dataflow-not-control-flow] A single uniform per-color transform
16
16
  * runs for every var in the palette. Anchor protection selects *which*
17
- * `ThemeKey` to apply (data), not *whether* to apply one (branch).
17
+ * `ColorAdjustment` to apply (data), not *whether* to apply one (branch).
18
18
  *
19
19
  * [LAW:one-source-of-truth] Anchor classification lives only in
20
- * `ANCHORED_ROOTS` below. Every variant of a semantic root carries the root
20
+ * `HUE_LOCKED_ROOTS` below. Every variant of a semantic root carries the root
21
21
  * as its first hyphen segment (`error`, `error-darken-1`, `error-muted`, ...)
22
22
  * or after a `text-` or `on-` that draws on it (`text-error`, `on-error`), so
23
23
  * one set covers the whole family.
24
24
  *
25
- * [LAW:one-way-deps] Imports flow `core/oklch → themes/transpose`;
25
+ * [LAW:one-way-deps] Imports flow `core/oklch → themes/adjust`;
26
26
  * nothing in core/ depends back on this file.
27
27
  */
28
- import { type ThemeKey } from "../core/oklch.js";
28
+ import { type ColorAdjustment } from "../core/oklch.js";
29
29
  import { Palette } from "./palette.js";
30
30
  /**
31
- * Semantic roots whose hue is locked under transposition. `error` must
31
+ * Semantic roots whose hue is locked under adjustment. `error` must
32
32
  * look red-ish, `success` green-ish, `warning` amber-ish — rotating the
33
33
  * hue would make the UI lie about meaning. Lightness and chroma *still*
34
34
  * transform for these roles, so they invert correctly during dark↔light
35
35
  * flips and respond to chroma scaling alongside everything else.
36
36
  */
37
- export declare const ANCHORED_ROOTS: ReadonlySet<string>;
37
+ export declare const HUE_LOCKED_ROOTS: ReadonlySet<string>;
38
38
  /**
39
- * Whether a palette var's hue is locked under transposition. The single
40
- * predicate used by `transposePalette`. Exported so callers building
39
+ * Whether a palette var's hue is locked under adjustment. The single
40
+ * predicate used by `adjustPalette`. Exported so callers building
41
41
  * higher-level theme machinery can stay consistent with the locking rule.
42
42
  */
43
- export declare function isAnchored(varName: string): boolean;
43
+ export declare function isHueLocked(varName: string): boolean;
44
44
  /**
45
- * Return a new `Palette` whose colors are the transposition of `palette`'s
45
+ * Return a new `Palette` whose colors are the adjustment of `palette`'s
46
46
  * colors by `key`. Pure. Identity (`IDENTITY`) returns a Palette with byte-exact
47
47
  * colors — fast-pathed so identity does not pay the sRGB↔OKLCH round-trip
48
48
  * quantization cost.
@@ -57,12 +57,12 @@ export declare function isAnchored(varName: string): boolean;
57
57
  * The identity fast-path preserves the source `dark` flag verbatim.
58
58
  *
59
59
  * @param name Optional override for the resulting palette name. Defaults
60
- * to the source palette's name. Callers building a family of transposed
60
+ * to the source palette's name. Callers building a family of adjusted
61
61
  * palettes (e.g. "gruvbox +60°") supply their own.
62
62
  */
63
- export declare function transposePalette(palette: Palette, key: ThemeKey, name?: string): Palette;
63
+ export declare function adjustPalette(palette: Palette, key: ColorAdjustment, name?: string): Palette;
64
64
  /**
65
- * Build the `ThemeKey` that rotates `palette` so its *tonic* var lands on
65
+ * Build the `ColorAdjustment` that rotates `palette` so its *tonic* var lands on
66
66
  * `targetHueDeg`. The musical operation directly: a key is the set of
67
67
  * intervals from the tonic, so "play this theme in the key of <hue>" means
68
68
  * "shift every color by exactly the interval that carries the tonic's
@@ -70,13 +70,13 @@ export declare function transposePalette(palette: Palette, key: ThemeKey, name?:
70
70
  *
71
71
  * Returns a hue-only key (chroma and lightness untouched). Callers that also
72
72
  * want to scale chroma or shift lightness spread their own axes over the
73
- * result — those are independent transposition dimensions, not part of
73
+ * result — those are independent adjustment dimensions, not part of
74
74
  * choosing the key. [LAW:one-type-per-behavior] a degree-shift key and a
75
75
  * root-note key are the *same* transform; this is just a second constructor
76
- * for it, so `transposePalette` stays untouched.
76
+ * for it, so `adjustPalette` stays untouched.
77
77
  *
78
78
  * Throws if `tonicVar` is absent — the interval has no anchor to measure
79
79
  * from, so there is no honest key to return. Failing loudly beats inventing
80
- * a zero shift that would silently mean "no transposition."
80
+ * a zero shift that would silently mean "no adjustment."
81
81
  */
82
- export declare function themeKeyForRoot(palette: Palette, tonicVar: string, targetHueDeg: number): ThemeKey;
82
+ export declare function adjustmentForRoot(palette: Palette, tonicVar: string, targetHueDeg: number): ColorAdjustment;
@@ -1,35 +1,35 @@
1
1
  /**
2
- * Transpose a Palette to a new "key" — analogous to transposing a melody.
2
+ * Adjust a Palette to a new "key" — analogous to adjusting a melody.
3
3
  *
4
4
  * melody / chord progression :: Palette (relationships between colors)
5
- * key (signature) :: ThemeKey (hue / chroma / lightness deltas)
6
- * "in C major" / "in D…" :: the resulting transposed Palette
5
+ * key (signature) :: ColorAdjustment (hue / chroma / lightness deltas)
6
+ * "in C major" / "in D…" :: the resulting adjusted Palette
7
7
  *
8
8
  * Because OKLCH is perceptually uniform, "+30° hue" looks like a
9
9
  * consistent jump everywhere on the color wheel — the way a perfect-fifth
10
- * interval sounds the same in every musical key. Transposition is closed
11
- * under composition: `transpose(transpose(p, k1), k2)` produces the same
12
- * colors as a single transpose with the combined deltas, modulo round-trip
10
+ * interval sounds the same in every musical key. Adjustment is closed
11
+ * under composition: `adjust(adjust(p, k1), k2)` produces the same
12
+ * colors as a single adjust with the combined deltas, modulo round-trip
13
13
  * quantization.
14
14
  *
15
15
  * [LAW:dataflow-not-control-flow] A single uniform per-color transform
16
16
  * runs for every var in the palette. Anchor protection selects *which*
17
- * `ThemeKey` to apply (data), not *whether* to apply one (branch).
17
+ * `ColorAdjustment` to apply (data), not *whether* to apply one (branch).
18
18
  *
19
19
  * [LAW:one-source-of-truth] Anchor classification lives only in
20
- * `ANCHORED_ROOTS` below. Every variant of a semantic root carries the root
20
+ * `HUE_LOCKED_ROOTS` below. Every variant of a semantic root carries the root
21
21
  * as its first hyphen segment (`error`, `error-darken-1`, `error-muted`, ...)
22
22
  * or after a `text-` or `on-` that draws on it (`text-error`, `on-error`), so
23
23
  * one set covers the whole family.
24
24
  *
25
- * [LAW:one-way-deps] Imports flow `core/oklch → themes/transpose`;
25
+ * [LAW:one-way-deps] Imports flow `core/oklch → themes/adjust`;
26
26
  * nothing in core/ depends back on this file.
27
27
  */
28
28
  import { Oklch, isIdentityKey } from "../core/oklch.js";
29
29
  import { pattern } from "../core/pattern.js";
30
30
  import { Palette } from "./palette.js";
31
31
  /**
32
- * Semantic roots whose hue is locked under transposition. `error` must
32
+ * Semantic roots whose hue is locked under adjustment. `error` must
33
33
  * look red-ish, `success` green-ish, `warning` amber-ish — rotating the
34
34
  * hue would make the UI lie about meaning. Lightness and chroma *still*
35
35
  * transform for these roles, so they invert correctly during dark↔light
@@ -39,7 +39,7 @@ import { Palette } from "./palette.js";
39
39
  // enforcement — typed code cannot call `add`/`delete`. (`Object.freeze` is
40
40
  // not used here because it is a no-op on Set membership; relying on it would
41
41
  // imply a runtime guarantee it does not provide.)
42
- export const ANCHORED_ROOTS = new Set([
42
+ export const HUE_LOCKED_ROOTS = new Set([
43
43
  "error",
44
44
  "success",
45
45
  "warning",
@@ -51,15 +51,15 @@ function rootOf(varName) {
51
51
  return dash === -1 ? role : role.slice(0, dash);
52
52
  }
53
53
  /**
54
- * Whether a palette var's hue is locked under transposition. The single
55
- * predicate used by `transposePalette`. Exported so callers building
54
+ * Whether a palette var's hue is locked under adjustment. The single
55
+ * predicate used by `adjustPalette`. Exported so callers building
56
56
  * higher-level theme machinery can stay consistent with the locking rule.
57
57
  */
58
- export function isAnchored(varName) {
59
- return ANCHORED_ROOTS.has(rootOf(varName));
58
+ export function isHueLocked(varName) {
59
+ return HUE_LOCKED_ROOTS.has(rootOf(varName));
60
60
  }
61
61
  /**
62
- * Return a new `Palette` whose colors are the transposition of `palette`'s
62
+ * Return a new `Palette` whose colors are the adjustment of `palette`'s
63
63
  * colors by `key`. Pure. Identity (`IDENTITY`) returns a Palette with byte-exact
64
64
  * colors — fast-pathed so identity does not pay the sRGB↔OKLCH round-trip
65
65
  * quantization cost.
@@ -74,23 +74,23 @@ export function isAnchored(varName) {
74
74
  * The identity fast-path preserves the source `dark` flag verbatim.
75
75
  *
76
76
  * @param name Optional override for the resulting palette name. Defaults
77
- * to the source palette's name. Callers building a family of transposed
77
+ * to the source palette's name. Callers building a family of adjusted
78
78
  * palettes (e.g. "gruvbox +60°") supply their own.
79
79
  */
80
- export function transposePalette(palette, key, name) {
80
+ export function adjustPalette(palette, key, name) {
81
81
  if (isIdentityKey(key)) {
82
- return new Palette(name ?? palette.name, palette.dark, palette.mapRoles((colour) => colour));
82
+ return new Palette(name ?? palette.name, palette.dark, palette.mapRoles((color) => color));
83
83
  }
84
84
  // Anchors share *everything except* hue with the user's key. They still
85
85
  // lightness-invert in a dark↔light flip; they still chroma-scale in a
86
86
  // saturation sweep — only their hue is held.
87
87
  const anchorKey = { ...key, hueShift: 0 };
88
- const next = palette.mapRoles((colour, role) => Oklch.fromRgba(colour).applyKey(isAnchored(role) ? anchorKey : key).toRgba());
88
+ const next = palette.mapRoles((color, role) => Oklch.fromRgba(color).applyKey(isHueLocked(role) ? anchorKey : key).toRgba());
89
89
  const newDark = Oklch.fromRgba(next.background).l < 0.5;
90
90
  return new Palette(name ?? palette.name, newDark, next);
91
91
  }
92
92
  /**
93
- * Build the `ThemeKey` that rotates `palette` so its *tonic* var lands on
93
+ * Build the `ColorAdjustment` that rotates `palette` so its *tonic* var lands on
94
94
  * `targetHueDeg`. The musical operation directly: a key is the set of
95
95
  * intervals from the tonic, so "play this theme in the key of <hue>" means
96
96
  * "shift every color by exactly the interval that carries the tonic's
@@ -98,26 +98,26 @@ export function transposePalette(palette, key, name) {
98
98
  *
99
99
  * Returns a hue-only key (chroma and lightness untouched). Callers that also
100
100
  * want to scale chroma or shift lightness spread their own axes over the
101
- * result — those are independent transposition dimensions, not part of
101
+ * result — those are independent adjustment dimensions, not part of
102
102
  * choosing the key. [LAW:one-type-per-behavior] a degree-shift key and a
103
103
  * root-note key are the *same* transform; this is just a second constructor
104
- * for it, so `transposePalette` stays untouched.
104
+ * for it, so `adjustPalette` stays untouched.
105
105
  *
106
106
  * Throws if `tonicVar` is absent — the interval has no anchor to measure
107
107
  * from, so there is no honest key to return. Failing loudly beats inventing
108
- * a zero shift that would silently mean "no transposition."
108
+ * a zero shift that would silently mean "no adjustment."
109
109
  */
110
- export function themeKeyForRoot(palette, tonicVar, targetHueDeg) {
110
+ export function adjustmentForRoot(palette, tonicVar, targetHueDeg) {
111
111
  // Validate the numeric input at the boundary: a non-finite target would
112
112
  // propagate as NaN to Oklch's constructor several calls downstream, where
113
113
  // the error is far less actionable.
114
114
  if (!Number.isFinite(targetHueDeg)) {
115
- throw new RangeError(`themeKeyForRoot: targetHueDeg must be a finite number; got ${targetHueDeg}`);
115
+ throw new RangeError(`adjustmentForRoot: targetHueDeg must be a finite number; got ${targetHueDeg}`);
116
116
  }
117
117
  const tonic = palette.get(tonicVar);
118
118
  if (tonic === undefined) {
119
- throw new Error(`themeKeyForRoot: palette "${palette.name}" has no "${tonicVar}" var ` +
120
- `to use as the tonic; cannot measure the transposition interval.`);
119
+ throw new Error(`adjustmentForRoot: palette "${palette.name}" has no "${tonicVar}" var ` +
120
+ `to use as the tonic; cannot measure the adjustment interval.`);
121
121
  }
122
122
  const tonicHue = Oklch.fromRgba(tonic).h;
123
123
  let hueShift = (targetHueDeg - tonicHue) % 360;
@@ -1,5 +1,5 @@
1
1
  import { ColorRgba } from "../core/color.js";
2
- import { Palette, type PaletteRole, type PaletteRoles } from "./palette.js";
2
+ import { Palette, type BaseRole, type PaletteRole, type PaletteRoles } from "./palette.js";
3
3
  /**
4
4
  * Base colors required to build a full semantic palette.
5
5
  * All are ColorRgba — callers construct from whatever source (hex, HSL, etc.).
@@ -16,17 +16,18 @@ export interface BaseColors {
16
16
  }
17
17
  /** The eight roles every palette is given, and every role it may also state. */
18
18
  export type StatedRoles = BaseColors & Partial<PaletteRoles>;
19
+ export declare const BASE_KEYS: readonly BaseRole[];
19
20
  /** Every role of every palette, read off the rules. */
20
21
  export declare const PALETTE_ROLES: readonly PaletteRole[];
21
22
  /**
22
23
  * Build a full semantic palette from its base colors and any other role it
23
- * states: a stated role keeps its colour, and everything derived from it
24
+ * states: a stated role keeps its color, and everything derived from it
24
25
  * follows it, but for `text-*`, which is held to read on its `*-muted`. An
25
- * authored theme is this with most roles stated; see `RULES` for what a role
26
+ * authored theme is this with roles stated; see `RULES` for what a role
26
27
  * not stated becomes.
27
28
  *
28
- * A translucent colour is drawn onto `background` before anything is derived
29
- * from it, so `on-*` is chosen against the colour the accent is drawn as.
29
+ * A translucent color is drawn onto `background` before anything is derived
30
+ * from it, so `on-*` is chosen against the color the accent is drawn as.
30
31
  * `boost` alone stays a layer: it is what `panel` composites.
31
32
  */
32
33
  export declare function buildPalette(name: string, dark: boolean, given: StatedRoles): Palette;
@@ -24,9 +24,9 @@ const TEXT_ON_MUTED = 4.5;
24
24
  const ACCENT_KEYS = ["primary", "secondary", "accent", "success", "warning", "error"];
25
25
  const TRANSPARENT = new ColorRgba(0, 0, 0, 0);
26
26
  const WHITE = new ColorRgba(255, 255, 255);
27
- const withAlpha = (colour, alpha) => new ColorRgba(colour.red, colour.green, colour.blue, alpha);
27
+ const withAlpha = (color, alpha) => new ColorRgba(color.red, color.green, color.blue, alpha);
28
28
  const textOn = (alpha) => (at) => withAlpha(contrastFor(at("background")), alpha);
29
- // [LAW:one-type-per-behavior] The roles naming another role's colour outright.
29
+ // [LAW:one-type-per-behavior] The roles naming another role's color outright.
30
30
  const same = (role) => (at) => at(role);
31
31
  const none = () => TRANSPARENT;
32
32
  /** The six rungs around `root`, each `-darken-3`…`-lighten-3` one `LADDER_STEP` apart. */
@@ -58,7 +58,7 @@ function accentRules(accent) {
58
58
  * How each role not given to `buildPalette` is derived, following Textual's
59
59
  * `ColorSystem`, which generated the bundled themes:
60
60
  *
61
- * - `*-muted` = the colour blended 70% toward its opposite base role — every
61
+ * - `*-muted` = the color blended 70% toward its opposite base role — every
62
62
  * accent AND the two base roles themselves, so a caller
63
63
  * de-emphasizing body text reaches for `foreground-muted` the
64
64
  * way it reaches for `primary-muted` to de-emphasize an accent.
@@ -67,7 +67,7 @@ function accentRules(accent) {
67
67
  * terminal-support-dependent SGR attribute.
68
68
  * - `text-*` = contrast text tinted 66% with the accent, the foreground for
69
69
  * a `*-muted` ground; `buildPalette` then holds it to that ground
70
- * - `on-*` = WCAG-correct contrast colour (black or white) for use as
70
+ * - `on-*` = WCAG-correct contrast color (black or white) for use as
71
71
  * foreground when the FULL accent is the background. Picked by
72
72
  * relative luminance — single source of truth so widgets never
73
73
  * need to invert fg/bg to get readable text. `contrastFor` is
@@ -152,19 +152,19 @@ const RULES = {
152
152
  "scrollbar-background-hover": same("scrollbar-background"),
153
153
  "scrollbar-corner-color": same("scrollbar-background"),
154
154
  };
155
- const BASE_KEYS = [...ACCENT_KEYS, "background", "foreground"];
155
+ export const BASE_KEYS = [...ACCENT_KEYS, "background", "foreground"];
156
156
  /** Every role of every palette, read off the rules. */
157
157
  export const PALETTE_ROLES = [...BASE_KEYS, ...Object.keys(RULES)];
158
158
  const ROLE_NAMES = new Set(PALETTE_ROLES);
159
159
  /**
160
160
  * Build a full semantic palette from its base colors and any other role it
161
- * states: a stated role keeps its colour, and everything derived from it
161
+ * states: a stated role keeps its color, and everything derived from it
162
162
  * follows it, but for `text-*`, which is held to read on its `*-muted`. An
163
- * authored theme is this with most roles stated; see `RULES` for what a role
163
+ * authored theme is this with roles stated; see `RULES` for what a role
164
164
  * not stated becomes.
165
165
  *
166
- * A translucent colour is drawn onto `background` before anything is derived
167
- * from it, so `on-*` is chosen against the colour the accent is drawn as.
166
+ * A translucent color is drawn onto `background` before anything is derived
167
+ * from it, so `on-*` is chosen against the color the accent is drawn as.
168
168
  * `boost` alone stays a layer: it is what `panel` composites.
169
169
  */
170
170
  export function buildPalette(name, dark, given) {
@@ -172,9 +172,9 @@ export function buildPalette(name, dark, given) {
172
172
  // [LAW:no-ambient-temporal-coupling] A rule asks for the roles it reads, so
173
173
  // no order among the rules matters. The eight base roles are always known.
174
174
  const at = (role) => {
175
- const colour = known.get(role) ?? RULES[role](at, dark);
176
- known.set(role, colour);
177
- return colour;
175
+ const color = known.get(role) ?? RULES[role](at, dark);
176
+ known.set(role, color);
177
+ return color;
178
178
  };
179
179
  // [LAW:single-enforcer] Stated and derived `text-*` alike: the tint is the
180
180
  // palette's, whether it reads is decided here. A stated tint stays translucent
@@ -193,23 +193,23 @@ export function buildPalette(name, dark, given) {
193
193
  */
194
194
  function statedRoles(name, given) {
195
195
  const drawn = drawnOn(name, given.background);
196
- const stated = Object.entries(given).flatMap(([role, colour]) => {
196
+ const stated = Object.entries(given).flatMap(([role, color]) => {
197
197
  if (!ROLE_NAMES.has(role))
198
198
  throw new RangeError(`palette ${JSON.stringify(name)}: ${JSON.stringify(role)} is not a role`);
199
- if (colour === undefined)
199
+ if (color === undefined)
200
200
  return [];
201
- return [[role, role === "boost" ? colour : drawn(colour)]];
201
+ return [[role, role === "boost" ? color : drawn(color)]];
202
202
  });
203
203
  const known = new Map(stated);
204
- // Every base colour is derived from, and a fully transparent one draws no
205
- // colour of its own to derive a muted shade or a contrast from.
204
+ // Every base color is derived from, and a fully transparent one draws no
205
+ // color of its own to derive a muted shade or a contrast from.
206
206
  // [LAW:no-silent-failure]
207
207
  for (const key of BASE_KEYS) {
208
- const colour = given[key];
209
- if (colour.alpha === 0) {
210
- throw new RangeError(`palette ${JSON.stringify(name)}: ${key} is fully transparent (${colour.hex}), so there is no colour to derive the palette from`);
208
+ const color = given[key];
209
+ if (color.alpha === 0) {
210
+ throw new RangeError(`palette ${JSON.stringify(name)}: ${key} is fully transparent (${color.hex}), so there is no color to derive the palette from`);
211
211
  }
212
- known.set(key, drawn(colour));
212
+ known.set(key, drawn(color));
213
213
  }
214
214
  return known;
215
215
  }
@@ -217,7 +217,7 @@ function statedRoles(name, given) {
217
217
  * A palette's `text-*`: its tint held to WCAG AA on `*-muted`, moved toward
218
218
  * the palette's own text side so it also reads on `background`, where widgets
219
219
  * draw it too. The muted floor is the promise: where no lightness on the text
220
- * side clears it, the colour that does is on the other side, and the
220
+ * side clears it, the color that does is on the other side, and the
221
221
  * background pair is the one given up. Every bundled palette clears both
222
222
  * (test/themes/text-on-muted.test.ts).
223
223
  *
@@ -10,7 +10,7 @@ export declare function darken(color: ColorRgba, levels: number): ColorRgba;
10
10
  */
11
11
  export declare function lighten(color: ColorRgba, levels: number): ColorRgba;
12
12
  /**
13
- * Move a colour's CIE L* by `amount` (0..1 of the L* scale), keeping its alpha.
13
+ * Move a color's CIE L* by `amount` (0..1 of the L* scale), keeping its alpha.
14
14
  * Textual's `Color.lighten`, which drew every shade ladder in the bundled
15
15
  * themes, so a ladder derived here continues the ones authored there.
16
16
  */
@@ -45,16 +45,16 @@ export { relativeLuminance, contrastRatio };
45
45
  * in, so the ratio is measured on what the eye actually sees and the returned
46
46
  * color is opaque.
47
47
  *
48
- * `drawnAt` is the depth the terminal will draw the pair at. At 256 colours
48
+ * `drawnAt` is the depth the terminal will draw the pair at. At 256 colors
49
49
  * the terminal rounds text and background independently, and two roundings
50
50
  * can meet in the middle, so the ratio is measured on the drawn pair: a
51
- * colour that loses the floor there is replaced by the nearest cube/grey entry
51
+ * color that loses the floor there is replaced by the nearest cube/grey entry
52
52
  * that clears it (whose own rounding is itself). At ANSI the terminal draws
53
- * each index in its own theme's colour, so the ratio is measured on
53
+ * each index in its own theme's color, so the ratio is measured on
54
54
  * `terminal`'s sixteen: a theme whose black is light and white dark (Rosé Pine
55
- * Dawn) turns the nominal table's sides over. A replacement is the colour the
55
+ * Dawn) turns the nominal table's sides over. A replacement is the color the
56
56
  * writer emits as the index nearest `fg` that clears the floor as `terminal`
57
- * draws it. Truecolor draws the colour chosen.
57
+ * draws it. Truecolor draws the color chosen.
58
58
  *
59
59
  * [LAW:single-enforcer] The one place "is this text readable, and if not fix
60
60
  * it" is decided. Callers route every fg/bg pair through here and the
@@ -66,35 +66,35 @@ export declare function ensureContrast(fg: ColorRgba, bg: ColorRgba, minRatio?:
66
66
  drawnAt?: ColorDepth, substrate?: ColorRgba, terminal?: TerminalColors): ColorRgba;
67
67
  /**
68
68
  * `chosen` (composited opaque over `substrate`), or — when the depth the
69
- * terminal draws at rounds it to a colour `accept` refuses — the colour the
69
+ * terminal draws at rounds it to a color `accept` refuses — the color the
70
70
  * writer emits as the nearest entry `accept` takes. `accept` sees the
71
- * candidate as drawn, and `drawn`, the same rounding for any other colour it
71
+ * candidate as drawn, and `drawn`, the same rounding for any other color it
72
72
  * measures against, so the caller states a floor once and it holds on the
73
- * colours the terminal shows — at ANSI, `terminal`'s own sixteen, as
73
+ * colors the terminal shows — at ANSI, `terminal`'s own sixteen, as
74
74
  * `ensureContrast` measures there. Truecolor
75
- * draws from no table, so a colour refused there has no replacement;
75
+ * draws from no table, so a color refused there has no replacement;
76
76
  * `undefined` means nothing the depth draws is accepted.
77
77
  *
78
78
  * `ensureContrast` is this with a contrast ratio as the floor; this is for a
79
79
  * floor that is not text on its background (an open state standing off every
80
80
  * closed cell, two planes standing off each other).
81
81
  */
82
- export declare function ensureDrawn(chosen: ColorRgba, drawnAt: ColorDepth, accept: (candidate: ColorRgba, drawn: (c: ColorRgba) => ColorRgba) => boolean, substrate?: ColorRgba, terminal?: TerminalColors): ColorRgba | undefined;
82
+ export declare function ensureMinimumDelta(chosen: ColorRgba, drawnAt: ColorDepth, accept: (candidate: ColorRgba, drawn: (c: ColorRgba) => ColorRgba) => boolean, substrate?: ColorRgba, terminal?: TerminalColors): ColorRgba | undefined;
83
83
  /**
84
- * The colour a ground is shown as at `drawnAt`: composited over `substrate`
84
+ * The color a ground is shown as at `drawnAt`: composited over `substrate`
85
85
  * (the SGR writer's black by default), rounded to the index the writer emits
86
86
  * at that depth, and shown as the terminal shows that index — at ANSI, in
87
- * `terminal`'s own colour. The one account `ensureDrawn` and `ensureContrast`
87
+ * `terminal`'s own color. The one account `ensureMinimumDelta` and `ensureContrast`
88
88
  * measure by, for a caller that measures a floor of its own.
89
89
  */
90
- export declare function drawnColour(colour: ColorRgba, drawnAt: ColorDepth, substrate?: ColorRgba, terminal?: TerminalColors): ColorRgba;
90
+ export declare function compositedColor(color: ColorRgba, drawnAt: ColorDepth, substrate?: ColorRgba, terminal?: TerminalColors): ColorRgba;
91
91
  /**
92
92
  * `fg` held to `minRatio` on `bg` in truecolor, moving in lightness toward
93
93
  * `toward` (black or white). `ensureContrast` moves toward the pole `bg`
94
- * itself contrasts with; a colour that already has a side (a palette's
94
+ * itself contrasts with; a color that already has a side (a palette's
95
95
  * `text-*` is its text, tinted) passes that side, since on a ground near the
96
96
  * 0.179 cutoff both poles clear and only one also reads on everything else
97
- * the colour is drawn on. Where `toward` cannot clear `minRatio` on `bg`,
97
+ * the color is drawn on. Where `toward` cannot clear `minRatio` on `bg`,
98
98
  * the floor wins over the side: the answer is `contrastFor(bg)`.
99
99
  */
100
100
  export declare function ensureContrastToward(fg: ColorRgba, bg: ColorRgba, minRatio: number, toward: ColorRgba): ColorRgba;