@promptctl/rich-js 0.18.0 → 0.19.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 (244) hide show
  1. package/README.md +9 -7
  2. package/dist/core/anchor.d.ts +45 -0
  3. package/dist/core/anchor.js +39 -0
  4. package/dist/core/ansi.d.ts +65 -0
  5. package/dist/core/ansi.js +231 -0
  6. package/dist/core/box.d.ts +145 -0
  7. package/dist/core/box.js +372 -0
  8. package/dist/core/cells.d.ts +192 -0
  9. package/dist/core/cells.js +334 -0
  10. package/dist/core/clock.d.ts +48 -0
  11. package/dist/core/clock.js +42 -0
  12. package/dist/core/color.d.ts +326 -0
  13. package/dist/core/color.js +1225 -0
  14. package/dist/core/console.d.ts +196 -0
  15. package/dist/core/console.js +596 -0
  16. package/dist/core/easing.d.ts +86 -0
  17. package/dist/core/easing.js +171 -0
  18. package/dist/core/emoji.d.ts +24 -0
  19. package/dist/core/emoji.js +391 -0
  20. package/dist/core/env.d.ts +16 -0
  21. package/dist/core/env.js +16 -0
  22. package/dist/core/export-html.d.ts +59 -0
  23. package/dist/core/export-html.js +197 -0
  24. package/dist/core/export-lines.d.ts +130 -0
  25. package/dist/core/export-lines.js +159 -0
  26. package/dist/core/export-svg.d.ts +49 -0
  27. package/dist/core/export-svg.js +213 -0
  28. package/dist/core/fnv1a.d.ts +10 -0
  29. package/dist/core/fnv1a.js +17 -0
  30. package/dist/core/highlighter.d.ts +33 -0
  31. package/dist/core/highlighter.js +190 -0
  32. package/dist/core/json.d.ts +27 -0
  33. package/dist/core/json.js +53 -0
  34. package/dist/core/markup.d.ts +140 -0
  35. package/dist/core/markup.js +654 -0
  36. package/dist/core/measure.d.ts +27 -0
  37. package/dist/core/measure.js +67 -0
  38. package/dist/core/memo.d.ts +19 -0
  39. package/dist/core/memo.js +34 -0
  40. package/dist/core/oklch.d.ts +161 -0
  41. package/dist/core/oklch.js +312 -0
  42. package/dist/core/osc8.d.ts +68 -0
  43. package/dist/core/osc8.js +78 -0
  44. package/dist/core/paint.d.ts +107 -0
  45. package/dist/core/paint.js +150 -0
  46. package/dist/core/place.d.ts +30 -0
  47. package/dist/core/place.js +49 -0
  48. package/dist/core/pretty.d.ts +247 -0
  49. package/dist/core/pretty.js +906 -0
  50. package/dist/core/protocol.d.ts +305 -0
  51. package/dist/core/protocol.js +183 -0
  52. package/dist/core/render.d.ts +87 -0
  53. package/dist/core/render.js +105 -0
  54. package/dist/core/segment.d.ts +162 -0
  55. package/dist/core/segment.js +488 -0
  56. package/dist/core/spinnerData.d.ts +12 -0
  57. package/dist/core/spinnerData.js +117 -0
  58. package/dist/core/strip.d.ts +152 -0
  59. package/dist/core/strip.js +326 -0
  60. package/dist/core/style.d.ts +189 -0
  61. package/dist/core/style.js +758 -0
  62. package/dist/core/subscription.d.ts +12 -0
  63. package/dist/core/subscription.js +12 -0
  64. package/dist/core/text.d.ts +283 -0
  65. package/dist/core/text.js +1097 -0
  66. package/dist/core/wrap.d.ts +46 -0
  67. package/dist/core/wrap.js +140 -0
  68. package/dist/host/app.d.ts +133 -0
  69. package/dist/host/app.js +285 -0
  70. package/dist/host/host-environment.d.ts +26 -0
  71. package/dist/host/host-environment.js +44 -0
  72. package/dist/host/index.d.ts +6 -0
  73. package/dist/host/index.js +3 -0
  74. package/dist/host/terminal-host.d.ts +180 -0
  75. package/dist/host/terminal-host.js +186 -0
  76. package/dist/index.d.ts +98 -0
  77. package/dist/index.js +114 -0
  78. package/dist/node/prompt.d.ts +34 -0
  79. package/dist/node/prompt.js +51 -0
  80. package/dist/node/save.d.ts +18 -0
  81. package/dist/node/save.js +24 -0
  82. package/dist/node/terminal-host.d.ts +114 -0
  83. package/dist/node/terminal-host.js +227 -0
  84. package/dist/node/traceback.d.ts +38 -0
  85. package/dist/node/traceback.js +98 -0
  86. package/dist/renderables/align.d.ts +16 -0
  87. package/dist/renderables/align.js +26 -0
  88. package/dist/renderables/columns.d.ts +77 -0
  89. package/dist/renderables/columns.js +191 -0
  90. package/dist/renderables/constrain.d.ts +15 -0
  91. package/dist/renderables/constrain.js +31 -0
  92. package/dist/renderables/effect.d.ts +98 -0
  93. package/dist/renderables/effect.js +160 -0
  94. package/dist/renderables/embed.d.ts +107 -0
  95. package/dist/renderables/embed.js +160 -0
  96. package/dist/renderables/flexStrip.d.ts +41 -0
  97. package/dist/renderables/flexStrip.js +200 -0
  98. package/dist/renderables/group.d.ts +14 -0
  99. package/dist/renderables/group.js +22 -0
  100. package/dist/renderables/layout.d.ts +75 -0
  101. package/dist/renderables/layout.js +223 -0
  102. package/dist/renderables/live.d.ts +74 -0
  103. package/dist/renderables/live.js +188 -0
  104. package/dist/renderables/markdown.d.ts +38 -0
  105. package/dist/renderables/markdown.js +587 -0
  106. package/dist/renderables/padding.d.ts +59 -0
  107. package/dist/renderables/padding.js +147 -0
  108. package/dist/renderables/panel.d.ts +114 -0
  109. package/dist/renderables/panel.js +303 -0
  110. package/dist/renderables/progress.d.ts +178 -0
  111. package/dist/renderables/progress.js +362 -0
  112. package/dist/renderables/progressBar.d.ts +31 -0
  113. package/dist/renderables/progressBar.js +64 -0
  114. package/dist/renderables/prompt.d.ts +71 -0
  115. package/dist/renderables/prompt.js +165 -0
  116. package/dist/renderables/ratio.d.ts +123 -0
  117. package/dist/renderables/ratio.js +213 -0
  118. package/dist/renderables/rule.d.ts +26 -0
  119. package/dist/renderables/rule.js +86 -0
  120. package/dist/renderables/spinner.d.ts +57 -0
  121. package/dist/renderables/spinner.js +103 -0
  122. package/dist/renderables/status.d.ts +29 -0
  123. package/dist/renderables/status.js +46 -0
  124. package/dist/renderables/syntax.d.ts +65 -0
  125. package/dist/renderables/syntax.js +166 -0
  126. package/dist/renderables/table.d.ts +232 -0
  127. package/dist/renderables/table.js +973 -0
  128. package/dist/renderables/traceback.d.ts +32 -0
  129. package/dist/renderables/traceback.js +279 -0
  130. package/dist/renderables/tree.d.ts +76 -0
  131. package/dist/renderables/tree.js +177 -0
  132. package/dist/renderables/viewport.d.ts +114 -0
  133. package/dist/renderables/viewport.js +196 -0
  134. package/dist/template-bindings/color-funcs.d.ts +109 -0
  135. package/dist/template-bindings/color-funcs.js +243 -0
  136. package/dist/template-bindings/helpers.d.ts +26 -0
  137. package/dist/template-bindings/helpers.js +33 -0
  138. package/dist/template-bindings/index.d.ts +119 -0
  139. package/dist/template-bindings/index.js +162 -0
  140. package/dist/template-bindings/palette-funcs.d.ts +96 -0
  141. package/dist/template-bindings/palette-funcs.js +145 -0
  142. package/dist/template-bindings/style-funcs.d.ts +28 -0
  143. package/dist/template-bindings/style-funcs.js +157 -0
  144. package/dist/themes/buildPalette.d.ts +57 -0
  145. package/dist/themes/buildPalette.js +100 -0
  146. package/dist/themes/colorMath.d.ts +94 -0
  147. package/dist/themes/colorMath.js +280 -0
  148. package/dist/themes/colorRef.d.ts +36 -0
  149. package/dist/themes/colorRef.js +78 -0
  150. package/dist/themes/data/ansi-tables.d.ts +14 -0
  151. package/dist/themes/data/ansi-tables.js +49 -0
  152. package/dist/themes/data/atom-one-dark.d.ts +3 -0
  153. package/dist/themes/data/atom-one-dark.js +177 -0
  154. package/dist/themes/data/atom-one-light.d.ts +3 -0
  155. package/dist/themes/data/atom-one-light.js +177 -0
  156. package/dist/themes/data/catppuccin-frappe.d.ts +3 -0
  157. package/dist/themes/data/catppuccin-frappe.js +177 -0
  158. package/dist/themes/data/catppuccin-latte.d.ts +3 -0
  159. package/dist/themes/data/catppuccin-latte.js +177 -0
  160. package/dist/themes/data/catppuccin-macchiato.d.ts +3 -0
  161. package/dist/themes/data/catppuccin-macchiato.js +177 -0
  162. package/dist/themes/data/catppuccin-mocha.d.ts +3 -0
  163. package/dist/themes/data/catppuccin-mocha.js +177 -0
  164. package/dist/themes/data/cyberpunk.d.ts +17 -0
  165. package/dist/themes/data/cyberpunk.js +193 -0
  166. package/dist/themes/data/default.d.ts +3 -0
  167. package/dist/themes/data/default.js +160 -0
  168. package/dist/themes/data/dracula.d.ts +3 -0
  169. package/dist/themes/data/dracula.js +177 -0
  170. package/dist/themes/data/flexoki.d.ts +3 -0
  171. package/dist/themes/data/flexoki.js +177 -0
  172. package/dist/themes/data/gruvbox.d.ts +3 -0
  173. package/dist/themes/data/gruvbox.js +177 -0
  174. package/dist/themes/data/index.d.ts +35 -0
  175. package/dist/themes/data/index.js +64 -0
  176. package/dist/themes/data/monokai.d.ts +3 -0
  177. package/dist/themes/data/monokai.js +177 -0
  178. package/dist/themes/data/nord.d.ts +3 -0
  179. package/dist/themes/data/nord.js +177 -0
  180. package/dist/themes/data/rose-pine-dawn.d.ts +3 -0
  181. package/dist/themes/data/rose-pine-dawn.js +177 -0
  182. package/dist/themes/data/rose-pine-moon.d.ts +3 -0
  183. package/dist/themes/data/rose-pine-moon.js +177 -0
  184. package/dist/themes/data/rose-pine.d.ts +3 -0
  185. package/dist/themes/data/rose-pine.js +177 -0
  186. package/dist/themes/data/solarized-dark.d.ts +3 -0
  187. package/dist/themes/data/solarized-dark.js +177 -0
  188. package/dist/themes/data/solarized-light.d.ts +3 -0
  189. package/dist/themes/data/solarized-light.js +177 -0
  190. package/dist/themes/data/svg-export.d.ts +3 -0
  191. package/dist/themes/data/svg-export.js +177 -0
  192. package/dist/themes/data/textual-ansi.d.ts +3 -0
  193. package/dist/themes/data/textual-ansi.js +160 -0
  194. package/dist/themes/data/textual-dark.d.ts +3 -0
  195. package/dist/themes/data/textual-dark.js +160 -0
  196. package/dist/themes/data/textual-light.d.ts +3 -0
  197. package/dist/themes/data/textual-light.js +160 -0
  198. package/dist/themes/data/tokyo-night.d.ts +3 -0
  199. package/dist/themes/data/tokyo-night.js +177 -0
  200. package/dist/themes/data/types.d.ts +31 -0
  201. package/dist/themes/data/types.js +10 -0
  202. package/dist/themes/palette.d.ts +45 -0
  203. package/dist/themes/palette.js +71 -0
  204. package/dist/themes/ramp.d.ts +70 -0
  205. package/dist/themes/ramp.js +103 -0
  206. package/dist/themes/registry.d.ts +44 -0
  207. package/dist/themes/registry.js +105 -0
  208. package/dist/themes/terminalThemes.d.ts +49 -0
  209. package/dist/themes/terminalThemes.js +63 -0
  210. package/dist/themes/transpose.d.ts +87 -0
  211. package/dist/themes/transpose.js +138 -0
  212. package/dist/widgets/button.d.ts +45 -0
  213. package/dist/widgets/button.js +177 -0
  214. package/dist/widgets/checkbox.d.ts +38 -0
  215. package/dist/widgets/checkbox.js +135 -0
  216. package/dist/widgets/dropdown.d.ts +64 -0
  217. package/dist/widgets/dropdown.js +416 -0
  218. package/dist/widgets/event-router.d.ts +71 -0
  219. package/dist/widgets/event-router.js +629 -0
  220. package/dist/widgets/focus-manager.d.ts +45 -0
  221. package/dist/widgets/focus-manager.js +191 -0
  222. package/dist/widgets/hit.d.ts +64 -0
  223. package/dist/widgets/hit.js +113 -0
  224. package/dist/widgets/index.d.ts +20 -0
  225. package/dist/widgets/index.js +14 -0
  226. package/dist/widgets/ink.d.ts +22 -0
  227. package/dist/widgets/ink.js +31 -0
  228. package/dist/widgets/slider.d.ts +54 -0
  229. package/dist/widgets/slider.js +247 -0
  230. package/dist/widgets/static-item.d.ts +34 -0
  231. package/dist/widgets/static-item.js +54 -0
  232. package/dist/widgets/text-input.d.ts +283 -0
  233. package/dist/widgets/text-input.js +1174 -0
  234. package/dist/widgets/themed-widget.d.ts +20 -0
  235. package/dist/widgets/themed-widget.js +89 -0
  236. package/dist/widgets/toggle.d.ts +41 -0
  237. package/dist/widgets/toggle.js +157 -0
  238. package/dist/widgets/types.d.ts +87 -0
  239. package/dist/widgets/types.js +30 -0
  240. package/dist/widgets/widget-app.d.ts +65 -0
  241. package/dist/widgets/widget-app.js +206 -0
  242. package/dist/widgets/widget-base.d.ts +42 -0
  243. package/dist/widgets/widget-base.js +172 -0
  244. package/package.json +16 -2
@@ -0,0 +1,280 @@
1
+ import { ColorDepth, ColorRgba, ColorSpec, blendRgb, contrastRatio, downgradeTable, relativeLuminance, resolveTerminal, SURFACE_BLACK, } from "../core/color.js";
2
+ import { Oklch } from "../core/oklch.js";
3
+ const LEVEL_STEP = 0.1;
4
+ function rgbToHsl(c) {
5
+ const r = c.red / 255;
6
+ const g = c.green / 255;
7
+ const b = c.blue / 255;
8
+ const max = Math.max(r, g, b);
9
+ const min = Math.min(r, g, b);
10
+ const l = (max + min) / 2;
11
+ const d = max - min;
12
+ if (d === 0)
13
+ return { h: 0, s: 0, l };
14
+ const s = d / (l > 0.5 ? 2 - max - min : max + min);
15
+ let h;
16
+ if (max === r)
17
+ h = ((g - b) / d) % 6;
18
+ else if (max === g)
19
+ h = (b - r) / d + 2;
20
+ else
21
+ h = (r - g) / d + 4;
22
+ h = (h * 60 + 360) % 360;
23
+ return { h, s, l };
24
+ }
25
+ function clampChannel(v) {
26
+ // Float HSL math + Math.round can land at -1 or 256 at the boundaries; clamp
27
+ // so an invalid ColorRgba never escapes this function.
28
+ const r = Math.round(v);
29
+ return r < 0 ? 0 : r > 255 ? 255 : r;
30
+ }
31
+ function hslToRgb(hsl) {
32
+ const { h, s, l } = hsl;
33
+ if (s === 0) {
34
+ const v = clampChannel(l * 255);
35
+ return new ColorRgba(v, v, v);
36
+ }
37
+ const c = (1 - Math.abs(2 * l - 1)) * s;
38
+ const hp = h / 60;
39
+ const x = c * (1 - Math.abs((hp % 2) - 1));
40
+ let r = 0, g = 0, b = 0;
41
+ if (hp < 1)
42
+ [r, g, b] = [c, x, 0];
43
+ else if (hp < 2)
44
+ [r, g, b] = [x, c, 0];
45
+ else if (hp < 3)
46
+ [r, g, b] = [0, c, x];
47
+ else if (hp < 4)
48
+ [r, g, b] = [0, x, c];
49
+ else if (hp < 5)
50
+ [r, g, b] = [x, 0, c];
51
+ else
52
+ [r, g, b] = [c, 0, x];
53
+ const m = l - c / 2;
54
+ return new ColorRgba(clampChannel((r + m) * 255), clampChannel((g + m) * 255), clampChannel((b + m) * 255));
55
+ }
56
+ function clamp01(v) {
57
+ return v < 0 ? 0 : v > 1 ? 1 : v;
58
+ }
59
+ /**
60
+ * Darken a color by N levels, where each level reduces HSL lightness by 10%.
61
+ * Negative levels lighten. Level 0 returns an equivalent triplet (after the
62
+ * RGB↔HSL roundtrip; values may differ by ±1 due to rounding).
63
+ */
64
+ export function darken(color, levels) {
65
+ const hsl = rgbToHsl(color);
66
+ hsl.l = clamp01(hsl.l - LEVEL_STEP * levels);
67
+ return hslToRgb(hsl);
68
+ }
69
+ /**
70
+ * Lighten a color by N levels. Equivalent to `darken(color, -levels)`.
71
+ */
72
+ export function lighten(color, levels) {
73
+ return darken(color, -levels);
74
+ }
75
+ /**
76
+ * Composite `fg` over `bg` with the given alpha (0..1). At alpha=0 returns bg;
77
+ * at alpha=1 returns fg.
78
+ */
79
+ export function alphaBlend(fg, bg, alpha) {
80
+ return blendRgb(bg, fg, clamp01(alpha));
81
+ }
82
+ /**
83
+ * Pick a contrasting foreground (black or white) for a background, using the
84
+ * WCAG relative-luminance threshold of 0.179 (the perceptually correct cutoff
85
+ * where black and white are equally readable). A translucent `bg` is judged
86
+ * as drawn: composited over `substrate` (see `drawnBackground`).
87
+ */
88
+ export function contrastFor(bg, substrate = SURFACE_BLACK) {
89
+ const lum = relativeLuminance(drawnBackground(bg, substrate));
90
+ return lum > 0.179
91
+ ? new ColorRgba(0, 0, 0)
92
+ : new ColorRgba(255, 255, 255);
93
+ }
94
+ // [LAW:one-way-deps] The WCAG measures live in core/color.ts, beside the
95
+ // ColorTable whose `matchReadable` needs them to pick a drawn text colour, and
96
+ // below the theme math here. Re-exported so this module stays the colour-math
97
+ // surface.
98
+ export { relativeLuminance, contrastRatio };
99
+ // Iterations for the lightness bisection below. 20 resolves L to ~1e-6 — far
100
+ // finer than 8-bit quantization or the eye.
101
+ const CONTRAST_ITERS = 20;
102
+ /**
103
+ * Return a foreground guaranteed to clear `minRatio` against `bg`, keeping the
104
+ * color *recognizably itself*. If the themed `fg` already passes it is returned
105
+ * untouched. Otherwise its OKLCH lightness is slid toward the pole that raises
106
+ * contrast — holding hue, and chroma where it stays in gamut (near the poles
107
+ * gamut clamping may reduce chroma, but hue is preserved) — until the ratio is
108
+ * met, so a blue on a dark-blue background becomes a lighter blue, not white.
109
+ * Only when no lightness of that hue can meet the ratio (a mid-toned
110
+ * background where even pure black-or-white tops out below the target) does it
111
+ * fall back to `contrastFor`'s black/white — the true maximum-contrast pick.
112
+ *
113
+ * A translucent `bg` is measured as it is drawn — composited over
114
+ * `substrate`, the SGR writer's black by default — and a translucent `fg` is
115
+ * then flattened over that drawn background, the order the writer composites
116
+ * in, so the ratio is measured on what the eye actually sees and the returned
117
+ * color is opaque.
118
+ *
119
+ * `drawnAt` is the depth the terminal will draw the pair at. At 256 colours
120
+ * the terminal rounds text and background independently, and two roundings
121
+ * can meet in the middle, so the ratio is measured on the drawn pair: a
122
+ * colour that loses the floor there is replaced by the nearest cube/grey entry
123
+ * that clears it (whose own rounding is itself). At ANSI the terminal draws
124
+ * each index in its own theme's colour, so the ratio is measured on
125
+ * `terminal`'s sixteen: a theme whose black is light and white dark (Rosé Pine
126
+ * Dawn) turns the nominal table's sides over. A replacement is the colour the
127
+ * writer emits as the index nearest `fg` that clears the floor as `terminal`
128
+ * draws it. Truecolor draws the colour chosen.
129
+ *
130
+ * [LAW:single-enforcer] The one place "is this text readable, and if not fix
131
+ * it" is decided. Callers route every fg/bg pair through here and the
132
+ * unreadable state never reaches output. [LAW:dataflow-not-control-flow] the
133
+ * function always runs; the measured ratio (data) decides how far the
134
+ * lightness moves — there is no caller-side "should I check contrast" branch.
135
+ */
136
+ export function ensureContrast(fg, bg, minRatio = 4.5, // WCAG AA for normal text
137
+ drawnAt = ColorDepth.TRUECOLOR, substrate = SURFACE_BLACK, terminal) {
138
+ const ground = drawnBackground(bg, substrate);
139
+ const chosen = ensureContrastToward(fg, ground, minRatio, contrastFor(ground));
140
+ // [LAW:dataflow-not-control-flow] The depth names the table the terminal
141
+ // draws from; every table is measured the same way.
142
+ const table = drawnTable(drawnAt, terminal);
143
+ if (table === undefined)
144
+ return chosen;
145
+ const shownBg = shownAs(table, ground);
146
+ if (contrastRatio(shownAs(table, chosen), shownBg) >= minRatio)
147
+ return chosen;
148
+ return table.written.get(table.shown.matchReadable(fg.compositeOver(ground), shownBg, minRatio));
149
+ }
150
+ /**
151
+ * `chosen` (composited opaque over `substrate`), or — when the depth the
152
+ * terminal draws at rounds it to a colour `accept` refuses — the colour the
153
+ * writer emits as the nearest entry `accept` takes. `accept` sees the
154
+ * candidate as drawn, and `drawn`, the same rounding for any other colour it
155
+ * measures against, so the caller states a floor once and it holds on the
156
+ * colours the terminal shows — at ANSI, `terminal`'s own sixteen, as
157
+ * `ensureContrast` measures there. Truecolor
158
+ * draws from no table, so a colour refused there has no replacement;
159
+ * `undefined` means nothing the depth draws is accepted.
160
+ *
161
+ * `ensureContrast` is this with a contrast ratio as the floor; this is for a
162
+ * floor that is not text on its background (an open state standing off every
163
+ * closed cell, two planes standing off each other).
164
+ */
165
+ export function ensureDrawn(chosen, drawnAt, accept, substrate = SURFACE_BLACK, terminal) {
166
+ // The floor was measured on `chosen` composited over `substrate`, so that
167
+ // opaque colour is what is returned — never a translucent one a different
168
+ // ground would composite into a colour `accept` never saw.
169
+ const opaque = drawnBackground(chosen, substrate);
170
+ const table = drawnTable(drawnAt, terminal);
171
+ const drawn = (c) => shownAs(table, drawnBackground(c, substrate));
172
+ if (accept(drawn(opaque), drawn))
173
+ return opaque;
174
+ // [LAW:dataflow-not-control-flow] Truecolor draws from no table, so it has
175
+ // no candidates: a refusal there is `undefined` like any exhausted search.
176
+ const index = table?.shown.matchWhere(opaque, (entry) => accept(entry, drawn));
177
+ return index === undefined ? undefined : table.written.get(index);
178
+ }
179
+ /**
180
+ * The colour a ground is shown as at `drawnAt`: composited over `substrate`
181
+ * (the SGR writer's black by default), rounded to the index the writer emits
182
+ * at that depth, and shown as the terminal shows that index — at ANSI, in
183
+ * `terminal`'s own colour. The one account `ensureDrawn` and `ensureContrast`
184
+ * measure by, for a caller that measures a floor of its own.
185
+ */
186
+ export function drawnColour(colour, drawnAt, substrate = SURFACE_BLACK, terminal) {
187
+ return shownAs(drawnTable(drawnAt, terminal), drawnBackground(colour, substrate));
188
+ }
189
+ /**
190
+ * An opaque colour as the terminal shows it: downgraded to the index the
191
+ * writer emits and drawn as the terminal draws that index.
192
+ * [LAW:one-source-of-truth] That rounding is `ColorSpec.downgrade`'s, the one
193
+ * `Style.drawnColors` writes; the tables here only list the candidates.
194
+ */
195
+ function shownAs(table, opaque) {
196
+ return table === undefined ? opaque : ColorSpec.fromRgba(opaque).downgrade(table.depth).getTruecolor(table.terminal);
197
+ }
198
+ /**
199
+ * A background as it is drawn: composited over the surface beneath it. That
200
+ * surface is a fact about where the pair is drawn, so it arrives as a value:
201
+ * the SGR writer (`Style.toSgrCodes`) and the exports that picture its output
202
+ * composite over `SURFACE_BLACK`, the default here; a caller that composites
203
+ * its colours onto a different surface itself names that one.
204
+ * [LAW:no-silent-failure] A surface has nothing under it, so a translucent one
205
+ * has no drawn colour to offer; `compositeOver` would read its raw RGB as if
206
+ * it were opaque, so it is refused here rather than measured wrong.
207
+ * [LAW:one-source-of-truth] Text is chosen against the colour the surface will
208
+ * show — measuring the raw RGBA reads a colour that is drawn nowhere, and text
209
+ * that "clears" it can land below the floor. Opaque colours composite to
210
+ * themselves.
211
+ */
212
+ function drawnBackground(bg, substrate) {
213
+ if (substrate.alpha !== 1) {
214
+ throw new RangeError(`a contrast substrate is the opaque surface under a translucent background; got ${substrate.hex}`);
215
+ }
216
+ return bg.compositeOver(substrate);
217
+ }
218
+ /**
219
+ * The table the terminal draws from at `drawnAt`. The 256-colour cube and
220
+ * grey ramp are fixed by xterm, so the colour written is the colour shown.
221
+ * ANSI 0–15 are the terminal theme's own: the writer still picks the index by
222
+ * the nominal table — the Windows console's at WINDOWS — and the terminal
223
+ * shows `terminal`'s colour there.
224
+ * Truecolor draws the chosen colour itself, as do the depths no table stands
225
+ * behind.
226
+ */
227
+ function drawnTable(drawnAt, terminal) {
228
+ const drawnBy = resolveTerminal(terminal, drawnAt);
229
+ switch (drawnAt) {
230
+ case ColorDepth.EIGHT_BIT:
231
+ return { depth: drawnAt, terminal: drawnBy, written: downgradeTable(drawnAt), shown: downgradeTable(drawnAt) };
232
+ case ColorDepth.STANDARD:
233
+ case ColorDepth.WINDOWS:
234
+ return { depth: drawnAt, terminal: drawnBy, written: downgradeTable(drawnAt), shown: drawnBy.ansiColors };
235
+ case ColorDepth.TRUECOLOR:
236
+ case ColorDepth.DEFAULT:
237
+ return undefined;
238
+ }
239
+ }
240
+ /**
241
+ * `fg` held to `minRatio` on `bg` in truecolor, moving in lightness toward
242
+ * `toward` (black or white). `ensureContrast` moves toward the pole `bg`
243
+ * itself contrasts with; a colour that already has a side (a palette's
244
+ * `text-*` is its text, tinted) passes that side, since on a ground near the
245
+ * 0.179 cutoff both poles clear and only one also reads on everything else
246
+ * the colour is drawn on. Where `toward` cannot clear `minRatio` on `bg`,
247
+ * the floor wins over the side: the answer is `contrastFor(bg)`.
248
+ */
249
+ export function ensureContrastToward(fg, bg, minRatio, toward) {
250
+ // Flatten translucency so the guarantee holds for the displayed color, not
251
+ // the raw bytes (e.g. a hand-written "#FFFFFF60" over a light surface).
252
+ const opaqueFg = fg.compositeOver(bg);
253
+ if (contrastRatio(opaqueFg, bg) >= minRatio)
254
+ return opaqueFg;
255
+ const lab = Oklch.fromRgba(opaqueFg);
256
+ // `toward` is black or white, the pole the lightness moves to.
257
+ const poleL = relativeLuminance(toward) > 0.179 ? 1 : 0;
258
+ // If even the pole of this hue can't reach the ratio, the hue physically
259
+ // can't — return the true maximum-contrast pick (pure black/white from
260
+ // contrastFor). The gamut-clamped OKLCH pole is only *near* b/w, so
261
+ // contrastFor is at least as strong and is the honest maximum.
262
+ const pole = new Oklch(poleL, lab.c, lab.h, lab.alpha).toRgba();
263
+ if (contrastRatio(pole, bg) < minRatio)
264
+ return contrastFor(bg);
265
+ // Bisect for the lightness nearest the original that still clears the ratio:
266
+ // the smallest perceptual change that achieves accessibility. Contrast is
267
+ // monotone in L over [lab.l, poleL] (everything below the crossing fails),
268
+ // so the search is well-posed.
269
+ let fail = lab.l;
270
+ let pass = poleL;
271
+ for (let i = 0; i < CONTRAST_ITERS; i++) {
272
+ const mid = (fail + pass) / 2;
273
+ const candidate = new Oklch(mid, lab.c, lab.h, lab.alpha).toRgba();
274
+ if (contrastRatio(candidate, bg) >= minRatio)
275
+ pass = mid;
276
+ else
277
+ fail = mid;
278
+ }
279
+ return new Oklch(pass, lab.c, lab.h, lab.alpha).toRgba();
280
+ }
@@ -0,0 +1,36 @@
1
+ import { type ColorRgba } from "../core/color.js";
2
+ import type { Palette } from "./palette.js";
3
+ /**
4
+ * A **color reference** is the one string form an author uses to name a
5
+ * concrete color: a palette variable name (`"primary"`, `"surface-active"`,
6
+ * `"text-primary"`) or a literal `#RRGGBB` / `#RRGGBBAA`.
7
+ *
8
+ * [LAW:parse-dont-validate] `resolveColorRef` is the single checkpoint that
9
+ * turns an untrusted reference string into a `ColorRgba`. Everything
10
+ * downstream — color math, `Style` construction, serialization — takes
11
+ * `ColorRgba` and therefore cannot ask again whether the name existed. There
12
+ * is no `isValidColorRef` companion, deliberately: a validator would hand back
13
+ * the same `string` it was given and every consumer would re-check.
14
+ *
15
+ * [LAW:no-silent-failure] A miss throws. An unknown palette name is a broken
16
+ * config, and the author needs to see which name and what was available — not
17
+ * a silently substituted default that makes the bar merely look wrong.
18
+ */
19
+ export declare class ColorRefError extends Error {
20
+ readonly ref: string;
21
+ constructor(ref: string, detail: string, options?: ErrorOptions);
22
+ }
23
+ /**
24
+ * Resolve a color reference against `palette`.
25
+ *
26
+ * Accepting hex alongside names is what makes this **idempotent**:
27
+ * `resolveColorRef(p, resolveColorRef(p, r).hex)` equals
28
+ * `resolveColorRef(p, r)` for every `r` this function accepts. Callers
29
+ * therefore never branch on "is this a palette name or already a color" —
30
+ * they call it unconditionally on whatever the author wrote, whether that is
31
+ * `"surface-active"` or the output of a chain of color math.
32
+ * [LAW:dataflow-not-control-flow]
33
+ *
34
+ * @throws {ColorRefError} on malformed hex or an unknown palette name.
35
+ */
36
+ export declare function resolveColorRef(palette: Palette, ref: string): ColorRgba;
@@ -0,0 +1,78 @@
1
+ import { ColorParseError, parseHexColor } from "../core/color.js";
2
+ /**
3
+ * A **color reference** is the one string form an author uses to name a
4
+ * concrete color: a palette variable name (`"primary"`, `"surface-active"`,
5
+ * `"text-primary"`) or a literal `#RRGGBB` / `#RRGGBBAA`.
6
+ *
7
+ * [LAW:parse-dont-validate] `resolveColorRef` is the single checkpoint that
8
+ * turns an untrusted reference string into a `ColorRgba`. Everything
9
+ * downstream — color math, `Style` construction, serialization — takes
10
+ * `ColorRgba` and therefore cannot ask again whether the name existed. There
11
+ * is no `isValidColorRef` companion, deliberately: a validator would hand back
12
+ * the same `string` it was given and every consumer would re-check.
13
+ *
14
+ * [LAW:no-silent-failure] A miss throws. An unknown palette name is a broken
15
+ * config, and the author needs to see which name and what was available — not
16
+ * a silently substituted default that makes the bar merely look wrong.
17
+ */
18
+ export class ColorRefError extends Error {
19
+ ref;
20
+ constructor(ref, detail, options) {
21
+ super(`color reference ${JSON.stringify(ref)} did not resolve — ${detail}`, options);
22
+ this.ref = ref;
23
+ this.name = "ColorRefError";
24
+ }
25
+ }
26
+ // A reference *shaped* like hex is resolved as hex; anything else is a palette
27
+ // name. This is a lexical distinction with no overlap — palette variable names
28
+ // never begin with `#` — so it is a parse, not a mode. A `#`-leading string
29
+ // that is malformed hex fails loudly here rather than falling through to a
30
+ // palette lookup that would report a confusing "unknown name" instead of
31
+ // "malformed hex".
32
+ const HEX_LEAD = "#";
33
+ // Cap the "did you mean" list. Palettes carry ~150 variables; dumping all of
34
+ // them buries the message. Nearby names are what a mistyped reference needs.
35
+ const SUGGESTION_LIMIT = 8;
36
+ /**
37
+ * Resolve a color reference against `palette`.
38
+ *
39
+ * Accepting hex alongside names is what makes this **idempotent**:
40
+ * `resolveColorRef(p, resolveColorRef(p, r).hex)` equals
41
+ * `resolveColorRef(p, r)` for every `r` this function accepts. Callers
42
+ * therefore never branch on "is this a palette name or already a color" —
43
+ * they call it unconditionally on whatever the author wrote, whether that is
44
+ * `"surface-active"` or the output of a chain of color math.
45
+ * [LAW:dataflow-not-control-flow]
46
+ *
47
+ * @throws {ColorRefError} on malformed hex or an unknown palette name.
48
+ */
49
+ export function resolveColorRef(palette, ref) {
50
+ const trimmed = ref.trim();
51
+ if (trimmed.startsWith(HEX_LEAD)) {
52
+ try {
53
+ return parseHexColor(trimmed);
54
+ }
55
+ catch (cause) {
56
+ if (!(cause instanceof ColorParseError))
57
+ throw cause;
58
+ throw new ColorRefError(ref, cause.message, { cause });
59
+ }
60
+ }
61
+ const hit = palette.get(trimmed);
62
+ if (hit === undefined) {
63
+ throw new ColorRefError(ref, `no such variable in palette ${JSON.stringify(palette.name)}` +
64
+ suggest(palette, trimmed));
65
+ }
66
+ return hit;
67
+ }
68
+ // Names sharing a prefix or suffix with the miss — enough to catch the two
69
+ // mistakes that actually happen (a wrong qualifier, `surface-active` vs
70
+ // `surface-lighten`; a wrong root, `text-primary` vs `primary-text`) without
71
+ // printing the whole palette.
72
+ function suggest(palette, miss) {
73
+ const parts = miss.split("-");
74
+ const near = [...palette.vars.keys()]
75
+ .filter((name) => parts.some((part) => part !== "" && name.includes(part)))
76
+ .slice(0, SUGGESTION_LIMIT);
77
+ return near.length === 0 ? "" : `; did you mean: ${near.join(", ")}`;
78
+ }
@@ -0,0 +1,14 @@
1
+ import { type AnsiColorsData } from "./types.js";
2
+ /**
3
+ * The VGA colours a terminal draws with no theme of its own: Rich's
4
+ * `DEFAULT_TERMINAL_THEME`, and what `textual-ansi` spells its palette in.
5
+ */
6
+ export declare const VGA_ANSI: AnsiColorsData;
7
+ /**
8
+ * The ANSI tables Textual draws named colours in for a theme that has no
9
+ * terminal scheme of its own: `MONOKAI` under a dark theme and `ALABASTER`
10
+ * under a light one (`ansi_theme_dark` / `ansi_theme_light` in Textual's
11
+ * `app.py`, values from its `_ansi_theme.py`).
12
+ */
13
+ export declare const TEXTUAL_DARK_ANSI: AnsiColorsData;
14
+ export declare const TEXTUAL_LIGHT_ANSI: AnsiColorsData;
@@ -0,0 +1,49 @@
1
+ import { STANDARD_TABLE } from "../../core/color.js";
2
+ import { ANSI_SLOTS } from "./types.js";
3
+ /**
4
+ * The VGA colours a terminal draws with no theme of its own: Rich's
5
+ * `DEFAULT_TERMINAL_THEME`, and what `textual-ansi` spells its palette in.
6
+ */
7
+ export const VGA_ANSI = Object.fromEntries(ANSI_SLOTS.map((slot, n) => [slot, STANDARD_TABLE.get(n).hex]));
8
+ /**
9
+ * The ANSI tables Textual draws named colours in for a theme that has no
10
+ * terminal scheme of its own: `MONOKAI` under a dark theme and `ALABASTER`
11
+ * under a light one (`ansi_theme_dark` / `ansi_theme_light` in Textual's
12
+ * `app.py`, values from its `_ansi_theme.py`).
13
+ */
14
+ export const TEXTUAL_DARK_ANSI = {
15
+ black: "#1A1A1A",
16
+ red: "#F4005F",
17
+ green: "#98E024",
18
+ yellow: "#FD971F",
19
+ blue: "#9D65FF",
20
+ magenta: "#F4005F",
21
+ cyan: "#58D1EB",
22
+ white: "#C4C5B5",
23
+ brightBlack: "#625E4C",
24
+ brightRed: "#F4005F",
25
+ brightGreen: "#98E024",
26
+ brightYellow: "#E0D561",
27
+ brightBlue: "#9D65FF",
28
+ brightMagenta: "#F4005F",
29
+ brightCyan: "#58D1EB",
30
+ brightWhite: "#F6F6EF",
31
+ };
32
+ export const TEXTUAL_LIGHT_ANSI = {
33
+ black: "#000000",
34
+ red: "#AA3731",
35
+ green: "#448C27",
36
+ yellow: "#CB9000",
37
+ blue: "#325CC0",
38
+ magenta: "#7A3E9D",
39
+ cyan: "#0083B2",
40
+ white: "#F7F7F7",
41
+ brightBlack: "#777777",
42
+ brightRed: "#F05050",
43
+ brightGreen: "#60CB00",
44
+ brightYellow: "#FFBC5D",
45
+ brightBlue: "#007ACC",
46
+ brightMagenta: "#E64CE6",
47
+ brightCyan: "#00AACB",
48
+ brightWhite: "#F7F7F7",
49
+ };
@@ -0,0 +1,3 @@
1
+ import type { ThemePaletteData } from "./types.js";
2
+ declare const theme: ThemePaletteData;
3
+ export default theme;
@@ -0,0 +1,177 @@
1
+ const theme = {
2
+ name: "atom-one-dark",
3
+ dark: true,
4
+ // iTerm2-Color-Schemes "One Half Dark", a terminal port of Atom One
5
+ ansi: {
6
+ black: "#282C34",
7
+ red: "#E06C75",
8
+ green: "#98C379",
9
+ yellow: "#E5C07B",
10
+ blue: "#61AFEF",
11
+ magenta: "#C678DD",
12
+ cyan: "#56B6C2",
13
+ white: "#DCDFE4",
14
+ brightBlack: "#5D677A",
15
+ brightRed: "#E06C75",
16
+ brightGreen: "#98C379",
17
+ brightYellow: "#E5C07B",
18
+ brightBlue: "#61AFEF",
19
+ brightMagenta: "#C678DD",
20
+ brightCyan: "#56B6C2",
21
+ brightWhite: "#DCDFE4",
22
+ },
23
+ vars: {
24
+ "accent": "#A378C2",
25
+ "accent-darken-1": "#8E65AD",
26
+ "accent-darken-2": "#7A5299",
27
+ "accent-darken-3": "#674085",
28
+ "accent-lighten-1": "#B78BD6",
29
+ "accent-lighten-2": "#CC9FEC",
30
+ "accent-lighten-3": "#E2B3FF",
31
+ "accent-muted": "#4C425E",
32
+ "background": "#282C34",
33
+ "background-darken-1": "#181C23",
34
+ "background-darken-2": "#050A14",
35
+ "background-darken-3": "#000000",
36
+ "background-lighten-1": "#383C44",
37
+ "background-lighten-2": "#494D56",
38
+ "background-lighten-3": "#5B5F68",
39
+ "block-cursor-background": "#61AFEF",
40
+ "block-cursor-blurred-background": "#61AFEF4C",
41
+ "block-cursor-blurred-foreground": "#ABB2BF",
42
+ "block-cursor-foreground": "#FFFFFFDD",
43
+ "block-hover-background": "#FFFFFF19",
44
+ "boost": "#FFFFFF0A",
45
+ "boost-darken-1": "#E9E9E90A",
46
+ "boost-darken-2": "#D4D4D40A",
47
+ "boost-darken-3": "#BFBFBF0A",
48
+ "boost-lighten-1": "#FFFFFF0A",
49
+ "boost-lighten-2": "#FFFFFF0A",
50
+ "boost-lighten-3": "#FFFFFF0A",
51
+ "border": "#61AFEF",
52
+ "border-blurred": "#353B47",
53
+ "button-color-foreground": "#FFFFFFDD",
54
+ "button-foreground": "#ABB2BF",
55
+ "error": "#EF6262",
56
+ "error-darken-1": "#D84D50",
57
+ "error-darken-2": "#C1373E",
58
+ "error-darken-3": "#AA1F2E",
59
+ "error-lighten-1": "#FF7674",
60
+ "error-lighten-2": "#FF8B87",
61
+ "error-lighten-3": "#FFA09B",
62
+ "error-muted": "#643C41",
63
+ "footer-background": "#4F5666",
64
+ "footer-description-background": "#00000000",
65
+ "footer-description-foreground": "#ABB2BF",
66
+ "footer-foreground": "#ABB2BF",
67
+ "footer-item-background": "#00000000",
68
+ "footer-key-background": "#00000000",
69
+ "footer-key-foreground": "#A378C2",
70
+ "foreground": "#ABB2BF",
71
+ "foreground-darken-1": "#979DAA",
72
+ "foreground-darken-2": "#838A96",
73
+ "foreground-darken-3": "#707783",
74
+ "foreground-disabled": "#ABB2BF60",
75
+ "foreground-lighten-1": "#BFC6D3",
76
+ "foreground-lighten-2": "#D4DBE8",
77
+ "foreground-lighten-3": "#E9F0FE",
78
+ "foreground-muted": "#ABB2BF99",
79
+ "input-cursor-background": "#ABB2BF",
80
+ "input-cursor-foreground": "#282C34",
81
+ "input-selection-background": "#77C3FF66",
82
+ "link-background": "#00000000",
83
+ "link-background-hover": "#61AFEF",
84
+ "link-color": "#FFFFFFDD",
85
+ "link-color-hover": "#FFFFFFDD",
86
+ "markdown-h1-background": "#00000000",
87
+ "markdown-h1-color": "#61AFEF",
88
+ "markdown-h2-background": "#00000000",
89
+ "markdown-h2-color": "#61AFEF",
90
+ "markdown-h3-background": "#00000000",
91
+ "markdown-h3-color": "#61AFEF",
92
+ "markdown-h4-background": "#00000000",
93
+ "markdown-h4-color": "#ABB2BF",
94
+ "markdown-h5-background": "#00000000",
95
+ "markdown-h5-color": "#ABB2BF",
96
+ "markdown-h6-background": "#00000000",
97
+ "markdown-h6-color": "#ABB2BF99",
98
+ "panel": "#4F5666",
99
+ "panel-darken-1": "#3D4453",
100
+ "panel-darken-2": "#2C3342",
101
+ "panel-darken-3": "#1C2331",
102
+ "panel-lighten-1": "#616878",
103
+ "panel-lighten-2": "#737A8B",
104
+ "panel-lighten-3": "#868E9F",
105
+ "primary": "#61AFEF",
106
+ "primary-background": "#4F5B6A",
107
+ "primary-background-darken-1": "#3F4D5D",
108
+ "primary-background-darken-2": "#303F50",
109
+ "primary-background-darken-3": "#303F50",
110
+ "primary-background-lighten-1": "#5E6A77",
111
+ "primary-background-lighten-2": "#6E7884",
112
+ "primary-background-lighten-3": "#7D8791",
113
+ "primary-darken-1": "#489BD9",
114
+ "primary-darken-2": "#2C87C4",
115
+ "primary-darken-3": "#0074AF",
116
+ "primary-lighten-1": "#77C3FF",
117
+ "primary-lighten-2": "#8ED8FF",
118
+ "primary-lighten-3": "#A4EDFF",
119
+ "primary-muted": "#39536C",
120
+ "scrollbar": "#355674",
121
+ "scrollbar-active": "#61AFEF",
122
+ "scrollbar-background": "#181C23",
123
+ "scrollbar-background-active": "#181C23",
124
+ "scrollbar-background-hover": "#181C23",
125
+ "scrollbar-corner-color": "#181C23",
126
+ "scrollbar-hover": "#3C6589",
127
+ "secondary": "#C678DD",
128
+ "secondary-background": "#5B5567",
129
+ "secondary-background-darken-1": "#4D465A",
130
+ "secondary-background-darken-2": "#3F374D",
131
+ "secondary-background-darken-3": "#3F374D",
132
+ "secondary-background-lighten-1": "#6A6475",
133
+ "secondary-background-lighten-2": "#787382",
134
+ "secondary-background-lighten-3": "#87828F",
135
+ "secondary-darken-1": "#B064C7",
136
+ "secondary-darken-2": "#9B50B3",
137
+ "secondary-darken-3": "#873D9E",
138
+ "secondary-lighten-1": "#DB8CF2",
139
+ "secondary-lighten-2": "#F1A0FF",
140
+ "secondary-lighten-3": "#FFB5FF",
141
+ "secondary-muted": "#574266",
142
+ "success": "#62F062",
143
+ "success-darken-1": "#48DA4D",
144
+ "success-darken-2": "#29C438",
145
+ "success-darken-3": "#00AF22",
146
+ "success-lighten-1": "#7AFF76",
147
+ "success-lighten-2": "#91FF8A",
148
+ "success-lighten-3": "#A8FF9F",
149
+ "success-muted": "#396641",
150
+ "surface": "#3B414D",
151
+ "surface-active": "#484E5B",
152
+ "surface-darken-1": "#2A303B",
153
+ "surface-darken-2": "#1A202B",
154
+ "surface-darken-3": "#09101B",
155
+ "surface-lighten-1": "#4C525E",
156
+ "surface-lighten-2": "#5E6471",
157
+ "surface-lighten-3": "#707684",
158
+ "text": "#FFFFFFDD",
159
+ "text-accent": "#C2A5D6",
160
+ "text-disabled": "#FFFFFF60",
161
+ "text-error": "#F59797",
162
+ "text-muted": "#FFFFFF99",
163
+ "text-primary": "#96CAF4",
164
+ "text-secondary": "#D9A5E8",
165
+ "text-success": "#97F597",
166
+ "text-warning": "#E9CC92",
167
+ "warning": "#DDB25B",
168
+ "warning-darken-1": "#C79D47",
169
+ "warning-darken-2": "#B28A34",
170
+ "warning-darken-3": "#9C7721",
171
+ "warning-lighten-1": "#F4C66E",
172
+ "warning-lighten-2": "#FFDB82",
173
+ "warning-lighten-3": "#FFF096",
174
+ "warning-muted": "#5E543F",
175
+ },
176
+ };
177
+ export default theme;
@@ -0,0 +1,3 @@
1
+ import type { ThemePaletteData } from "./types.js";
2
+ declare const theme: ThemePaletteData;
3
+ export default theme;