@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,119 @@
1
+ /**
2
+ * rich-js template bindings — public entry point.
3
+ *
4
+ * [LAW:one-source-of-truth] This module is the public entry point for
5
+ * rich-js's styling vocabulary as `@promptctl/go-template-js` template
6
+ * functions. The split between the two exported registrations is exactly the
7
+ * split between what needs a theme and what does not:
8
+ *
9
+ * - `richTextFuncs()` — the colour sinks (`fg`/`bg`), the palette-free colour
10
+ * math (`darken`, `mix`, `contrastOn`, the OKLCH axes …), text attributes,
11
+ * and `link`. Needs no configuration; safe to register unconditionally. A
12
+ * consumer with no theme system still gets the complete colour vocabulary by
13
+ * feeding it hex literals.
14
+ * - `paletteFuncs(getPalette)` — `color` and `ramp`, the two functions that
15
+ * resolve a palette name. This is the *only* registration that knows a
16
+ * palette exists, which is why it is the only one that takes an argument.
17
+ * [LAW:one-way-deps]
18
+ *
19
+ * `createRichTextEngine()` wires up `richTextFuncs()` only — it cannot supply a
20
+ * palette. Consumers that name theme colours call `paletteFuncs(getPalette)`
21
+ * and merge it into their own engine config.
22
+ *
23
+ * Colours compose by nesting, like every other function:
24
+ * `{{ fg (darken (color "primary") 2) (bold "x") }}`. There is no second
25
+ * markup grammar and no colour-spec mini-language — the template *is* the
26
+ * composition mechanism.
27
+ *
28
+ * Fragment type: `RichText`. Chosen because it is the library's primary
29
+ * text type (implements `Renderable` + `Measurable`), composes via
30
+ * `append`, and flows directly into the existing render pipeline with no
31
+ * adapter. Top-level template expressions emit as `RichText[]` — natural
32
+ * for cases where a single template produces multiple independently
33
+ * addressable fragments (e.g. multiple top-level `link` calls, each
34
+ * carrying its own URL).
35
+ */
36
+ import { type Engine, type FuncMap } from "@promptctl/go-template-js";
37
+ import { RichText } from "../core/text.js";
38
+ import { Segment } from "../core/segment.js";
39
+ import type { RenderOptions } from "../core/protocol.js";
40
+ import { type DrawnAt } from "./color-funcs.js";
41
+ export { paletteFuncs } from "./palette-funcs.js";
42
+ export { colorFuncs, readableOnFunc, type DrawnAt } from "./color-funcs.js";
43
+ /**
44
+ * Funcs registered by the rich-js binding — the colour sinks, the palette-free
45
+ * colour math, text attributes, and the `link` cell-splitter. Nothing here
46
+ * needs a theme; the two that do (`color`, `ramp`) ship separately via
47
+ * `paletteFuncs(getPalette)`, merged consumer-side. `drawnAt` is forwarded to
48
+ * `colorFuncs` — the depth `readableOn` measures at, truecolor when omitted.
49
+ *
50
+ * `FuncMap` is not parameterised over `T` in `@promptctl/go-template-js` — the engine's
51
+ * `T` lives on the `Engine`/`EngineConfig`, and per-function input/output
52
+ * types are carried as runtime `argTypes` on each `TemplateFunc`. The map
53
+ * therefore *type-checks* against any `EngineConfig<T>.funcs`, but the
54
+ * functions returned here are only *runtime-compatible* with
55
+ * `Engine<RichText>`: each style function returns a `RichText` and
56
+ * requires its lifted child to be a `RichText` (enforced by an
57
+ * `instanceof` check that throws on misuse). Consumers merging this map
58
+ * into a wider engine must keep `T = RichText`; merging into an engine
59
+ * whose `T` is something else will compile but fail at evaluation time.
60
+ */
61
+ export declare function richTextFuncs(drawnAt?: () => DrawnAt): FuncMap;
62
+ /**
63
+ * Construct an `Engine<RichText>` with the rich-js style-function set
64
+ * registered. Consumers that already manage their own engine should call
65
+ * `richTextFuncs()` and merge the result into their own `createEngine`
66
+ * configuration; consumers that just want rich-js styling can use this
67
+ * factory directly.
68
+ *
69
+ * `fromString` lifts text literals into `RichText`. `toString` flattens a
70
+ * `RichText` to its plain text — the engine's no-silent-flatten guard uses
71
+ * this only for `printf "%s"` / `"%q"` and `print*` slots, where ANSI is
72
+ * not desired anyway.
73
+ */
74
+ export declare function createRichTextEngine(): Engine<RichText>;
75
+ export interface RenderTemplateOptions extends Partial<RenderOptions> {
76
+ /**
77
+ * Defaults to 400 — large enough that downstream `splitLines` /
78
+ * `adjustLineLength` clipping decides actual width, matching the typical
79
+ * "render wide, fit on output" pipeline. The error line is fitted to it too,
80
+ * so only a caller that passes the width it draws at sees a cut error end in
81
+ * `…` rather than be cropped by whatever draws it.
82
+ */
83
+ maxWidth?: number;
84
+ /** The error line's style, a `Style.parse` spec (default `"red dim"`). */
85
+ errorStyle?: string;
86
+ }
87
+ /**
88
+ * Compile a template source against `engine` and render the result to a
89
+ * flat `Segment[]`. The 90%-case convenience over chaining `engine.compile`,
90
+ * `RichText.fromFragments`, and `.render` by hand:
91
+ *
92
+ * - Runs `engine.compile(source)(scope)` to get the engine's `RichText[]`.
93
+ * - Flattens that fragment list into a single styled `RichText` via
94
+ * `RichText.fromFragments` so every fragment's wrapping style survives.
95
+ * - Renders to a `Segment[]` with the given render options — `maxWidth`,
96
+ * `theme`, `onStyleError` and the rest — as a terminal unless they say not.
97
+ * - On a parse/evaluate failure, emits a single dim styled
98
+ * `[error: <message>]` segment, one line fitted to `maxWidth`, the caller
99
+ * can drop into their layout. No bespoke fallback wiring required at every
100
+ * call site.
101
+ *
102
+ * Only compile, evaluate and flatten are inside the `try`: the error line reports a
103
+ * template that failed, nothing else. Whatever the render throws — a style
104
+ * error an `onStyleError` handler rethrew, or a fault in the render itself —
105
+ * leaves this function rather than being folded into an `[error: …]` line.
106
+ *
107
+ * [LAW:single-enforcer] One place owns "render a template to segments,
108
+ * degrade gracefully when the template fails" — every consumer that wants this exact
109
+ * shape reads from here rather than re-implementing the same try/catch +
110
+ * error-formatting glue.
111
+ *
112
+ * For the 10% — custom error UX, intermediate access to the `RichText`,
113
+ * pre-compiled templates re-used many times — call the engine directly
114
+ * and use `RichText.fromFragments` to flatten. This helper is sugar for
115
+ * the live-render case (e.g. a preview pane), not a replacement for the
116
+ * compile-once-evaluate-many pattern.
117
+
118
+ */
119
+ export declare function renderTemplate(engine: Engine<RichText>, source: string, scope?: unknown, options?: RenderTemplateOptions): Segment[];
@@ -0,0 +1,162 @@
1
+ /**
2
+ * rich-js template bindings — public entry point.
3
+ *
4
+ * [LAW:one-source-of-truth] This module is the public entry point for
5
+ * rich-js's styling vocabulary as `@promptctl/go-template-js` template
6
+ * functions. The split between the two exported registrations is exactly the
7
+ * split between what needs a theme and what does not:
8
+ *
9
+ * - `richTextFuncs()` — the colour sinks (`fg`/`bg`), the palette-free colour
10
+ * math (`darken`, `mix`, `contrastOn`, the OKLCH axes …), text attributes,
11
+ * and `link`. Needs no configuration; safe to register unconditionally. A
12
+ * consumer with no theme system still gets the complete colour vocabulary by
13
+ * feeding it hex literals.
14
+ * - `paletteFuncs(getPalette)` — `color` and `ramp`, the two functions that
15
+ * resolve a palette name. This is the *only* registration that knows a
16
+ * palette exists, which is why it is the only one that takes an argument.
17
+ * [LAW:one-way-deps]
18
+ *
19
+ * `createRichTextEngine()` wires up `richTextFuncs()` only — it cannot supply a
20
+ * palette. Consumers that name theme colours call `paletteFuncs(getPalette)`
21
+ * and merge it into their own engine config.
22
+ *
23
+ * Colours compose by nesting, like every other function:
24
+ * `{{ fg (darken (color "primary") 2) (bold "x") }}`. There is no second
25
+ * markup grammar and no colour-spec mini-language — the template *is* the
26
+ * composition mechanism.
27
+ *
28
+ * Fragment type: `RichText`. Chosen because it is the library's primary
29
+ * text type (implements `Renderable` + `Measurable`), composes via
30
+ * `append`, and flows directly into the existing render pipeline with no
31
+ * adapter. Top-level template expressions emit as `RichText[]` — natural
32
+ * for cases where a single template produces multiple independently
33
+ * addressable fragments (e.g. multiple top-level `link` calls, each
34
+ * carrying its own URL).
35
+ */
36
+ import { createEngine } from "@promptctl/go-template-js";
37
+ import { RichText } from "../core/text.js";
38
+ import { Style } from "../core/style.js";
39
+ import { Segment } from "../core/segment.js";
40
+ import { richTextStyleFuncs } from "./style-funcs.js";
41
+ import { colorFuncs } from "./color-funcs.js";
42
+ export { paletteFuncs } from "./palette-funcs.js";
43
+ export { colorFuncs, readableOnFunc } from "./color-funcs.js";
44
+ /**
45
+ * Funcs registered by the rich-js binding — the colour sinks, the palette-free
46
+ * colour math, text attributes, and the `link` cell-splitter. Nothing here
47
+ * needs a theme; the two that do (`color`, `ramp`) ship separately via
48
+ * `paletteFuncs(getPalette)`, merged consumer-side. `drawnAt` is forwarded to
49
+ * `colorFuncs` — the depth `readableOn` measures at, truecolor when omitted.
50
+ *
51
+ * `FuncMap` is not parameterised over `T` in `@promptctl/go-template-js` — the engine's
52
+ * `T` lives on the `Engine`/`EngineConfig`, and per-function input/output
53
+ * types are carried as runtime `argTypes` on each `TemplateFunc`. The map
54
+ * therefore *type-checks* against any `EngineConfig<T>.funcs`, but the
55
+ * functions returned here are only *runtime-compatible* with
56
+ * `Engine<RichText>`: each style function returns a `RichText` and
57
+ * requires its lifted child to be a `RichText` (enforced by an
58
+ * `instanceof` check that throws on misuse). Consumers merging this map
59
+ * into a wider engine must keep `T = RichText`; merging into an engine
60
+ * whose `T` is something else will compile but fail at evaluation time.
61
+ */
62
+ // [LAW:one-source-of-truth] The truecolor default lives on `colorFuncs` alone;
63
+ // an omitted `drawnAt` is forwarded as omitted.
64
+ export function richTextFuncs(drawnAt) {
65
+ return { ...richTextStyleFuncs(), ...colorFuncs(drawnAt) };
66
+ }
67
+ /**
68
+ * Construct an `Engine<RichText>` with the rich-js style-function set
69
+ * registered. Consumers that already manage their own engine should call
70
+ * `richTextFuncs()` and merge the result into their own `createEngine`
71
+ * configuration; consumers that just want rich-js styling can use this
72
+ * factory directly.
73
+ *
74
+ * `fromString` lifts text literals into `RichText`. `toString` flattens a
75
+ * `RichText` to its plain text — the engine's no-silent-flatten guard uses
76
+ * this only for `printf "%s"` / `"%q"` and `print*` slots, where ANSI is
77
+ * not desired anyway.
78
+ */
79
+ export function createRichTextEngine() {
80
+ return createEngine({
81
+ fromString: (s) => new RichText(s),
82
+ toString: (rt) => rt.plain,
83
+ funcs: richTextFuncs(),
84
+ });
85
+ }
86
+ /**
87
+ * Compile a template source against `engine` and render the result to a
88
+ * flat `Segment[]`. The 90%-case convenience over chaining `engine.compile`,
89
+ * `RichText.fromFragments`, and `.render` by hand:
90
+ *
91
+ * - Runs `engine.compile(source)(scope)` to get the engine's `RichText[]`.
92
+ * - Flattens that fragment list into a single styled `RichText` via
93
+ * `RichText.fromFragments` so every fragment's wrapping style survives.
94
+ * - Renders to a `Segment[]` with the given render options — `maxWidth`,
95
+ * `theme`, `onStyleError` and the rest — as a terminal unless they say not.
96
+ * - On a parse/evaluate failure, emits a single dim styled
97
+ * `[error: <message>]` segment, one line fitted to `maxWidth`, the caller
98
+ * can drop into their layout. No bespoke fallback wiring required at every
99
+ * call site.
100
+ *
101
+ * Only compile, evaluate and flatten are inside the `try`: the error line reports a
102
+ * template that failed, nothing else. Whatever the render throws — a style
103
+ * error an `onStyleError` handler rethrew, or a fault in the render itself —
104
+ * leaves this function rather than being folded into an `[error: …]` line.
105
+ *
106
+ * [LAW:single-enforcer] One place owns "render a template to segments,
107
+ * degrade gracefully when the template fails" — every consumer that wants this exact
108
+ * shape reads from here rather than re-implementing the same try/catch +
109
+ * error-formatting glue.
110
+ *
111
+ * For the 10% — custom error UX, intermediate access to the `RichText`,
112
+ * pre-compiled templates re-used many times — call the engine directly
113
+ * and use `RichText.fromFragments` to flatten. This helper is sugar for
114
+ * the live-render case (e.g. a preview pane), not a replacement for the
115
+ * compile-once-evaluate-many pattern.
116
+
117
+ */
118
+ export function renderTemplate(engine, source, scope = {}, options) {
119
+ const { errorStyle, ...renderOptions } = options ?? {};
120
+ const maxWidth = renderOptions.maxWidth ?? 400;
121
+ // Flattening belongs to the template: a scope value the engine passes
122
+ // through unlifted fails here, and that is a template that failed.
123
+ let text;
124
+ try {
125
+ text = RichText.fromFragments(engine.compile(source)(scope));
126
+ }
127
+ catch (e) {
128
+ return [new Segment(errorLine(e, maxWidth), safeErrorStyle(errorStyle))];
129
+ }
130
+ return Array.from(text.render({ isTerminal: true, ...renderOptions, maxWidth }));
131
+ }
132
+ /**
133
+ * A caught error as the one line `renderTemplate` promises. The summary is the
134
+ * error's `message` — `String(e)` on an engine error appends a multi-line code
135
+ * frame — and even a `message` can carry the character it is complaining about
136
+ * (`bad escape sequence \⏎`). Any whitespace that is not a plain space — a line
137
+ * break, or a tab, whose stop depends on the column the line is set at and so
138
+ * cannot be measured here — folds, with the spaces around it, to one space.
139
+ *
140
+ * [LAW:one-source-of-truth] The line fits the same `maxWidth` the success path
141
+ * draws at, not a width of its own, and a cut ends in an ellipsis so it never
142
+ * reads as the whole message.
143
+ */
144
+ function errorLine(e, maxWidth) {
145
+ const summary = (e instanceof Error ? e.message : String(e)).replace(/\s*[^\S ]\s*/g, " ");
146
+ return new RichText(`[error: ${summary}]`).truncate(maxWidth).plain;
147
+ }
148
+ // [LAW:single-enforcer] The error-path Style must not itself throw, or
149
+ // the whole "degrade gracefully" promise of renderTemplate is broken. A
150
+ // bogus user-supplied `errorStyle` spec falls back to a hard-coded safe
151
+ // Style — never propagates the parse failure to the caller.
152
+ const FALLBACK_ERROR_STYLE = new Style({ color: "red", dim: true });
153
+ function safeErrorStyle(spec) {
154
+ if (spec === undefined)
155
+ return FALLBACK_ERROR_STYLE;
156
+ try {
157
+ return Style.parse(spec);
158
+ }
159
+ catch {
160
+ return FALLBACK_ERROR_STYLE;
161
+ }
162
+ }
@@ -0,0 +1,96 @@
1
+ /**
2
+ * The palette-dependent template functions: `color`, and `ramp` whose stops
3
+ * are color references.
4
+ *
5
+ * ### Why `color` is the one name resolver
6
+ *
7
+ * This module used to register four kinds of thing: one function per
8
+ * identifier-safe palette variable (`{{ primary child }}`, `{{ accent child }}`,
9
+ * …), a general `palette "spec" child`, a `paletteOver "spec" "#bg" child` for
10
+ * specs needing a background, and an `auto "#bg" child` sugar. That surface had
11
+ * two defects worth recording, because both are easy to reintroduce.
12
+ *
13
+ * **The name family could not cover its own domain.** A themed palette carries
14
+ * ~150 variables; roughly 14 of them are legal Go-template identifiers. So the
15
+ * generated functions reached under a tenth of the palette, and the other nine
16
+ * tenths needed `palette "text-primary" child` — a *different expression shape*
17
+ * for the same intent. An author who learned `{{ primary x }}` and reasonably
18
+ * tried `{{ text_primary x }}` got a FuncNotFound. [LAW:composability] — the
19
+ * `filterByStatus`/`filterByOwner` shape: names cannot enumerate a domain, and
20
+ * the N+1 case always needs a form you have to learn separately.
21
+ *
22
+ * **Resolution and application were fused.** Every one of those functions
23
+ * consumed its color instantly into a styled fragment, so a color could never
24
+ * be held or passed. Composition therefore had to happen inside the spec
25
+ * *string* — hence the old `name-darken-N alpha%` grammar. See
26
+ * `color-funcs.ts` for the full argument; the short version is that a string
27
+ * grammar is function application with the function calls spelled as
28
+ * punctuation, and it grows a new production for every operation.
29
+ *
30
+ * What replaces all of it: `color "name-or-hex"` produces a color value, the
31
+ * functions in `color-funcs.ts` transform colors, and `fg`/`bg` in
32
+ * `style-funcs.ts` paint them. One shape, total over the palette, open to
33
+ * arbitrary composition. [LAW:one-type-per-behavior]
34
+ *
35
+ * ### Why `ramp` lives here and not with the color math
36
+ *
37
+ * `ramp` is the one function whose input is a *number*, and its stops are
38
+ * spelled as color references — `ramp .pct "step" 0 "panel" 50 "warning" 80
39
+ * "error"` — resolved through the same `resolveColorRef` that `color` crosses.
40
+ * Requiring `(color "panel")` around every stop would put the boilerplate
41
+ * back that this binding exists to remove, and an author who writes a hex
42
+ * literal loses nothing: the resolver is idempotent on hex. The arithmetic
43
+ * itself is `ColorRamp` in `themes/ramp.ts`, palette-free; only the
44
+ * reference resolution is here. [LAW:one-way-deps]
45
+ *
46
+ * ### Why a getter, not a palette
47
+ *
48
+ * `paletteFuncs` takes `() => Palette` rather than a `Palette`. A consumer
49
+ * whose theme can change at runtime (a live preview, a theme picker, a
50
+ * status-line that recolors on click) would otherwise be frozen to whichever
51
+ * palette happened to be current when the engine was constructed — and since
52
+ * templates are parsed once and evaluated many times, that freeze outlives
53
+ * every subsequent theme change while the *rest* of the consumer's colors move
54
+ * on. Two palettes, one render: [LAW:one-source-of-truth] violated by a
55
+ * captured reference.
56
+ *
57
+ * The getter costs nothing structurally. `FuncMap` entries are data in the
58
+ * engine and their bodies run at *evaluate* time, so reading the palette
59
+ * through a getter leaves parse-once/evaluate-many completely intact. What the
60
+ * getter must not change is *which functions exist* — and it cannot, because
61
+ * the two names here do not depend on the palette's contents. That was not
62
+ * true of the generated per-variable functions, which is the second reason
63
+ * they are gone.
64
+ */
65
+ import type { FuncMap } from "@promptctl/go-template-js";
66
+ import type { Palette } from "../themes/palette.js";
67
+ /**
68
+ * Register `color "name-or-hex"` against a live palette.
69
+ *
70
+ * `color` resolves a palette variable name to its hex string — `#RRGGBB`, or
71
+ * `#00000000` for a theme's "no background", which `bg` paints as no
72
+ * background and the colour functions refuse — and passes
73
+ * an already-literal color through unchanged. That second half is not a
74
+ * convenience — it makes `color` **idempotent**, which is what lets consumers
75
+ * apply it unconditionally to any author-written color string without first
76
+ * asking whether it is a name or already a color. [LAW:dataflow-not-control-flow]
77
+ *
78
+ * An unknown name throws, carrying near-miss suggestions from the live
79
+ * palette. In a template that surfaces as an evaluation error at the exact
80
+ * call site, which is the signal an author (or an agent editing a config) needs
81
+ * to fix it. [LAW:no-silent-failure]
82
+ *
83
+ * @example
84
+ * ```ts
85
+ * const engine = createEngine({
86
+ * fromString: (s) => new RichText(s),
87
+ * toString: (rt) => rt.plain,
88
+ * funcs: {
89
+ * ...richTextFuncs(),
90
+ * ...colorFuncs(),
91
+ * ...paletteFuncs(() => currentTheme.palette),
92
+ * },
93
+ * });
94
+ * ```
95
+ */
96
+ export declare function paletteFuncs(getPalette: () => Palette): FuncMap;
@@ -0,0 +1,145 @@
1
+ /**
2
+ * The palette-dependent template functions: `color`, and `ramp` whose stops
3
+ * are color references.
4
+ *
5
+ * ### Why `color` is the one name resolver
6
+ *
7
+ * This module used to register four kinds of thing: one function per
8
+ * identifier-safe palette variable (`{{ primary child }}`, `{{ accent child }}`,
9
+ * …), a general `palette "spec" child`, a `paletteOver "spec" "#bg" child` for
10
+ * specs needing a background, and an `auto "#bg" child` sugar. That surface had
11
+ * two defects worth recording, because both are easy to reintroduce.
12
+ *
13
+ * **The name family could not cover its own domain.** A themed palette carries
14
+ * ~150 variables; roughly 14 of them are legal Go-template identifiers. So the
15
+ * generated functions reached under a tenth of the palette, and the other nine
16
+ * tenths needed `palette "text-primary" child` — a *different expression shape*
17
+ * for the same intent. An author who learned `{{ primary x }}` and reasonably
18
+ * tried `{{ text_primary x }}` got a FuncNotFound. [LAW:composability] — the
19
+ * `filterByStatus`/`filterByOwner` shape: names cannot enumerate a domain, and
20
+ * the N+1 case always needs a form you have to learn separately.
21
+ *
22
+ * **Resolution and application were fused.** Every one of those functions
23
+ * consumed its color instantly into a styled fragment, so a color could never
24
+ * be held or passed. Composition therefore had to happen inside the spec
25
+ * *string* — hence the old `name-darken-N alpha%` grammar. See
26
+ * `color-funcs.ts` for the full argument; the short version is that a string
27
+ * grammar is function application with the function calls spelled as
28
+ * punctuation, and it grows a new production for every operation.
29
+ *
30
+ * What replaces all of it: `color "name-or-hex"` produces a color value, the
31
+ * functions in `color-funcs.ts` transform colors, and `fg`/`bg` in
32
+ * `style-funcs.ts` paint them. One shape, total over the palette, open to
33
+ * arbitrary composition. [LAW:one-type-per-behavior]
34
+ *
35
+ * ### Why `ramp` lives here and not with the color math
36
+ *
37
+ * `ramp` is the one function whose input is a *number*, and its stops are
38
+ * spelled as color references — `ramp .pct "step" 0 "panel" 50 "warning" 80
39
+ * "error"` — resolved through the same `resolveColorRef` that `color` crosses.
40
+ * Requiring `(color "panel")` around every stop would put the boilerplate
41
+ * back that this binding exists to remove, and an author who writes a hex
42
+ * literal loses nothing: the resolver is idempotent on hex. The arithmetic
43
+ * itself is `ColorRamp` in `themes/ramp.ts`, palette-free; only the
44
+ * reference resolution is here. [LAW:one-way-deps]
45
+ *
46
+ * ### Why a getter, not a palette
47
+ *
48
+ * `paletteFuncs` takes `() => Palette` rather than a `Palette`. A consumer
49
+ * whose theme can change at runtime (a live preview, a theme picker, a
50
+ * status-line that recolors on click) would otherwise be frozen to whichever
51
+ * palette happened to be current when the engine was constructed — and since
52
+ * templates are parsed once and evaluated many times, that freeze outlives
53
+ * every subsequent theme change while the *rest* of the consumer's colors move
54
+ * on. Two palettes, one render: [LAW:one-source-of-truth] violated by a
55
+ * captured reference.
56
+ *
57
+ * The getter costs nothing structurally. `FuncMap` entries are data in the
58
+ * engine and their bodies run at *evaluate* time, so reading the palette
59
+ * through a getter leaves parse-once/evaluate-many completely intact. What the
60
+ * getter must not change is *which functions exist* — and it cannot, because
61
+ * the two names here do not depend on the palette's contents. That was not
62
+ * true of the generated per-variable functions, which is the second reason
63
+ * they are gone.
64
+ */
65
+ import { resolveColorRef } from "../themes/colorRef.js";
66
+ import { parseEase } from "../core/easing.js";
67
+ import { ColorRamp } from "../themes/ramp.js";
68
+ import { computable } from "./color-funcs.js";
69
+ /**
70
+ * The `(position, color-ref)` pairs a `ramp` call's tail spells, as stops.
71
+ *
72
+ * The engine's `alternating` gate has already typed every even slot as a
73
+ * float and every odd slot as a string; what it cannot see is the *pairing*
74
+ * — a trailing position with no color — nor that the string is a color
75
+ * reference. Both are settled here, once, and `ColorRamp` receives resolved
76
+ * stops it never re-checks. [LAW:parse-dont-validate]
77
+ *
78
+ * Each reference resolves against the palette of *this* evaluation, so a
79
+ * ramp over palette names follows the theme exactly as `color` does.
80
+ */
81
+ function colorStops(tail, palette) {
82
+ if (tail.length % 2 !== 0) {
83
+ throw new RangeError(`ramp's last stop (position ${String(tail[tail.length - 1])}) has no color — ` +
84
+ `stops are <position> <color> pairs`);
85
+ }
86
+ const stops = [];
87
+ for (let i = 0; i < tail.length; i += 2) {
88
+ stops.push({
89
+ at: tail[i],
90
+ color: computable(resolveColorRef(palette, tail[i + 1]), "ramp", JSON.stringify(tail[i + 1])),
91
+ });
92
+ }
93
+ return stops;
94
+ }
95
+ /**
96
+ * Register `color "name-or-hex"` against a live palette.
97
+ *
98
+ * `color` resolves a palette variable name to its hex string — `#RRGGBB`, or
99
+ * `#00000000` for a theme's "no background", which `bg` paints as no
100
+ * background and the colour functions refuse — and passes
101
+ * an already-literal color through unchanged. That second half is not a
102
+ * convenience — it makes `color` **idempotent**, which is what lets consumers
103
+ * apply it unconditionally to any author-written color string without first
104
+ * asking whether it is a name or already a color. [LAW:dataflow-not-control-flow]
105
+ *
106
+ * An unknown name throws, carrying near-miss suggestions from the live
107
+ * palette. In a template that surfaces as an evaluation error at the exact
108
+ * call site, which is the signal an author (or an agent editing a config) needs
109
+ * to fix it. [LAW:no-silent-failure]
110
+ *
111
+ * @example
112
+ * ```ts
113
+ * const engine = createEngine({
114
+ * fromString: (s) => new RichText(s),
115
+ * toString: (rt) => rt.plain,
116
+ * funcs: {
117
+ * ...richTextFuncs(),
118
+ * ...colorFuncs(),
119
+ * ...paletteFuncs(() => currentTheme.palette),
120
+ * },
121
+ * });
122
+ * ```
123
+ */
124
+ export function paletteFuncs(getPalette) {
125
+ const colorFunc = {
126
+ fn: ((ref) => resolveColorRef(getPalette(), ref).hex),
127
+ argTypes: ["string"],
128
+ arity: { kind: "exact" },
129
+ returnType: "string",
130
+ };
131
+ // `ramp <value> <ease> <position> <color> …` — the argument list is one
132
+ // float/string cycle end to end (value, ease, then each stop's position
133
+ // and color), so the engine's `alternating` gate types every slot; the
134
+ // pairing and the references are parsed in `colorStops`. Its minimum is the
135
+ // value, the ease, and one stop, so the gate refuses a stopless call.
136
+ // [LAW:types-are-the-program]
137
+ const rampFunc = {
138
+ fn: ((value, ease, ...tail) => new ColorRamp(parseEase(ease), colorStops(tail, getPalette())).at(value)
139
+ .hex),
140
+ argTypes: ["float", "string"],
141
+ arity: { kind: "alternating", minimum: 4 },
142
+ returnType: "string",
143
+ };
144
+ return { color: colorFunc, ramp: rampFunc };
145
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Style-function registrations for the rich-js template binding.
3
+ *
4
+ * [LAW:one-source-of-truth] The function inventory below mirrors the
5
+ * string-syntax style vocabulary documented in `docs/style.md` — the two
6
+ * colour slots (`fg`/`bg`), text attributes (positive + negated), short
7
+ * aliases, and the hyperlink. Each registration is a templating-time analogue
8
+ * of a piece of `Style.parse`, so a template fragment composed by these
9
+ * functions round-trips through `Style` without semantic drift.
10
+ *
11
+ * [LAW:dataflow-not-control-flow] Every function follows the same
12
+ * shape: child `RichText` in, `RichText` out. The styling difference
13
+ * lives in a captured `Style` value (or constructor closure), not in
14
+ * branches inside a single dispatch. That is why this file is mostly
15
+ * factories — the data (the colour, the attribute name) is the
16
+ * variability; the operation is fixed.
17
+ */
18
+ import type { FuncMap } from "@promptctl/go-template-js";
19
+ /**
20
+ * Text-styling registrations: the two colour sinks (`fg`, `bg`), text
21
+ * attributes (canonical names, short aliases, and `not_*` negations), the
22
+ * hyperlink cell-splitter (`link`), and the multi-attribute `style` spec.
23
+ *
24
+ * Colours themselves come from elsewhere: `colorFuncs()` for the palette-free
25
+ * math, `paletteFuncs()` for naming a theme colour. This module only paints.
26
+ * [LAW:one-way-deps] — nothing here imports a palette.
27
+ */
28
+ export declare function richTextStyleFuncs(): FuncMap;