@promptctl/rich-js 0.6.0 → 0.7.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 (33) hide show
  1. package/dist/index.d.ts +1 -2
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +3 -1
  4. package/dist/index.js.map +1 -1
  5. package/dist/template-bindings/color-funcs.d.ts +78 -0
  6. package/dist/template-bindings/color-funcs.d.ts.map +1 -0
  7. package/dist/template-bindings/color-funcs.js +212 -0
  8. package/dist/template-bindings/color-funcs.js.map +1 -0
  9. package/dist/template-bindings/index.d.ts +25 -18
  10. package/dist/template-bindings/index.d.ts.map +1 -1
  11. package/dist/template-bindings/index.js +27 -19
  12. package/dist/template-bindings/index.js.map +1 -1
  13. package/dist/template-bindings/palette-funcs.d.ts +60 -53
  14. package/dist/template-bindings/palette-funcs.d.ts.map +1 -1
  15. package/dist/template-bindings/palette-funcs.js +64 -132
  16. package/dist/template-bindings/palette-funcs.js.map +1 -1
  17. package/dist/template-bindings/style-funcs.d.ts +11 -12
  18. package/dist/template-bindings/style-funcs.d.ts.map +1 -1
  19. package/dist/template-bindings/style-funcs.js +48 -74
  20. package/dist/template-bindings/style-funcs.js.map +1 -1
  21. package/dist/themes/buildPalette.d.ts +7 -1
  22. package/dist/themes/buildPalette.d.ts.map +1 -1
  23. package/dist/themes/buildPalette.js +15 -1
  24. package/dist/themes/buildPalette.js.map +1 -1
  25. package/dist/themes/colorRef.d.ts +52 -0
  26. package/dist/themes/colorRef.d.ts.map +1 -0
  27. package/dist/themes/colorRef.js +97 -0
  28. package/dist/themes/colorRef.js.map +1 -0
  29. package/dist/themes/palette.d.ts +4 -2
  30. package/dist/themes/palette.d.ts.map +1 -1
  31. package/dist/themes/palette.js +4 -2
  32. package/dist/themes/palette.js.map +1 -1
  33. package/package.json +1 -1
@@ -1,76 +1,83 @@
1
1
  /**
2
- * Palette / theme / auto-contrast function registrations for the rich-js
3
- * template binding.
2
+ * The one palette-dependent template function: `color`.
4
3
  *
5
- * [LAW:one-source-of-truth] All palette resolution flows through the existing
6
- * `PaletteResolver.resolve(spec, ctx)` API — this module adds no resolution
7
- * logic of its own. The template functions here are thin adaptors that translate
8
- * function call arguments into a `(spec, ctx)` call and wrap the resulting
9
- * `ColorRgba` as a `Style` applied to the child fragment.
4
+ * ### Why exactly one
10
5
  *
11
- * [LAW:dataflow-not-control-flow] Every function follows the same shape:
12
- * resolve spec → apply as foreground color → return styled fragment. The
13
- * variability is in the spec string and the optional against context; the
14
- * operation is fixed.
6
+ * This module used to register four kinds of thing: one function per
7
+ * identifier-safe palette variable (`{{ primary child }}`, `{{ accent child }}`,
8
+ * …), a general `palette "spec" child`, a `paletteOver "spec" "#bg" child` for
9
+ * specs needing a background, and an `auto "#bg" child` sugar. That surface had
10
+ * two defects worth recording, because both are easy to reintroduce.
15
11
  *
16
- * ### Surface design rationale
12
+ * **The name family could not cover its own domain.** A themed palette carries
13
+ * ~150 variables; roughly 14 of them are legal Go-template identifiers. So the
14
+ * generated functions reached under a tenth of the palette, and the other nine
15
+ * tenths needed `palette "text-primary" child` — a *different expression shape*
16
+ * for the same intent. An author who learned `{{ primary x }}` and reasonably
17
+ * tried `{{ text_primary x }}` got a FuncNotFound. [LAW:composability] — the
18
+ * `filterByStatus`/`filterByOwner` shape: names cannot enumerate a domain, and
19
+ * the N+1 case always needs a form you have to learn separately.
17
20
  *
18
- * Four layers of ergonomics:
21
+ * **Resolution and application were fused.** Every one of those functions
22
+ * consumed its color instantly into a styled fragment, so a color could never
23
+ * be held or passed. Composition therefore had to happen inside the spec
24
+ * *string* — hence the old `name-darken-N alpha%` grammar. See
25
+ * `color-funcs.ts` for the full argument; the short version is that a string
26
+ * grammar is function application with the function calls spelled as
27
+ * punctuation, and it grows a new production for every operation.
19
28
  *
20
- * 1. **Semantic-name functions** — one function per palette variable whose
21
- * name is a valid Go template identifier (no hyphens). `{{ primary child }}`,
22
- * `{{ accent child }}`, etc. The happy path: common semantic names read
23
- * cleanly in templates. Hyphenated names (`primary-muted`, `text-primary`)
24
- * cannot be Go template identifiers and are accessed via `palette`.
29
+ * What replaces all of it: `color "name-or-hex"` produces a color value, the
30
+ * functions in `color-funcs.ts` transform colors, and `fg`/`bg` in
31
+ * `style-funcs.ts` paint them. One shape, total over the palette, open to
32
+ * arbitrary composition. [LAW:one-type-per-behavior]
25
33
  *
26
- * 2. **`palette "spec" child`** — the general-purpose function for any spec
27
- * that does not need a background context (bare names and darken/lighten
28
- * modifiers). Covers hyphenated names and modifier chains.
34
+ * ### Why a getter, not a palette
29
35
  *
30
- * 3. **`paletteOver "spec" "#bgHex" child`** — for specs that require a
31
- * background context: alpha compositing (`"primary 50%"`) and auto-contrast
32
- * (`"auto"`, `"auto 33%"`). The bg color is threaded as an explicit hex
33
- * argument rather than via a scope side-channel — no mutable state, no
34
- * closure magic; the data flows through the function call.
36
+ * `paletteFuncs` takes `() => Palette` rather than a `Palette`. A consumer
37
+ * whose theme can change at runtime (a live preview, a theme picker, a
38
+ * status-line that recolors on click) would otherwise be frozen to whichever
39
+ * palette happened to be current when the engine was constructed — and since
40
+ * templates are parsed once and evaluated many times, that freeze outlives
41
+ * every subsequent theme change while the *rest* of the consumer's colors move
42
+ * on. Two palettes, one render: [LAW:one-source-of-truth] violated by a
43
+ * captured reference.
35
44
  *
36
- * 4. **`auto "#bgHex" child`** — syntactic sugar for
37
- * `{{ paletteOver "auto" "#bgHex" child }}`, the common auto-contrast case.
38
- *
39
- * ### Theme switching
40
- *
41
- * `paletteFuncs(resolver)` captures `resolver` at construction time. Consumers
42
- * that need runtime theme switching create a new `paletteFuncs()` from the new
43
- * theme's resolver and rebuild their engine (or merge into a fresh `FuncMap`).
44
- * The same template *source* produces different colors because the functions in
45
- * the engine changed — the template text is the same, the resolver differs.
45
+ * The getter costs nothing structurally. `FuncMap` entries are data in the
46
+ * engine and their bodies run at *evaluate* time, so reading the palette
47
+ * through a getter leaves parse-once/evaluate-many completely intact. What the
48
+ * getter must not change is *which functions exist* — and it cannot, because
49
+ * there is now exactly one, whose name does not depend on the palette's
50
+ * contents. That was not true of the generated per-variable functions, which
51
+ * is the second reason they are gone.
46
52
  */
47
53
  import type { FuncMap } from "@promptctl/go-template-js";
48
- import { PaletteResolver } from "../themes/paletteResolver.js";
54
+ import type { Palette } from "../themes/palette.js";
49
55
  /**
50
- * Build a `FuncMap` exposing the semantic palette of `resolver` as template
51
- * functions. Merge into `richTextFuncs()` (or pass to `createEngine`) to make
52
- * palette colors available in templates.
56
+ * Register `color "name-or-hex"` against a live palette.
53
57
  *
54
- * Registered functions:
55
- * - One function per palette variable whose name is a valid Go template
56
- * identifier: `{{ primary child }}`, `{{ accent child }}`, etc.
57
- * - `palette "spec" child` — any spec without background context.
58
- * - `paletteOver "spec" "#bgHex" child` — any spec needing a background (alpha,
59
- * auto-contrast).
60
- * - `auto "#bgHex" child` — sugar for `paletteOver "auto" bgHex child`.
58
+ * `color` resolves a palette variable name to a `#RRGGBB` string, and passes
59
+ * an already-literal color through unchanged. That second half is not a
60
+ * convenience — it makes `color` **idempotent**, which is what lets consumers
61
+ * apply it unconditionally to any author-written color string without first
62
+ * asking whether it is a name or already a color. [LAW:dataflow-not-control-flow]
63
+ *
64
+ * An unknown name throws, carrying near-miss suggestions from the live
65
+ * palette. In a template that surfaces as an evaluation error at the exact
66
+ * call site, which is the signal an author (or an agent editing a config) needs
67
+ * to fix it. [LAW:no-silent-failure]
61
68
  *
62
69
  * @example
63
70
  * ```ts
64
- * import { createEngine } from "@promptctl/go-template-js";
65
- * import { GRUVBOX, PaletteResolver, RichText } from "rich-js";
66
- * import { richTextFuncs, paletteFuncs } from "rich-js/template-bindings";
67
- *
68
71
  * const engine = createEngine({
69
72
  * fromString: (s) => new RichText(s),
70
73
  * toString: (rt) => rt.plain,
71
- * funcs: { ...richTextFuncs(), ...paletteFuncs(new PaletteResolver(GRUVBOX.palette)) },
74
+ * funcs: {
75
+ * ...richTextFuncs(),
76
+ * ...colorFuncs(),
77
+ * ...paletteFuncs(() => currentTheme.palette),
78
+ * },
72
79
  * });
73
80
  * ```
74
81
  */
75
- export declare function paletteFuncs(resolver: PaletteResolver): FuncMap;
82
+ export declare function paletteFuncs(getPalette: () => Palette): FuncMap;
76
83
  //# sourceMappingURL=palette-funcs.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"palette-funcs.d.ts","sourceRoot":"","sources":["../../src/template-bindings/palette-funcs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAgB,MAAM,2BAA2B,CAAC;AAIvE,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAiG/D;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,eAAe,GAAG,OAAO,CAO/D"}
1
+ {"version":3,"file":"palette-funcs.d.ts","sourceRoot":"","sources":["../../src/template-bindings/palette-funcs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAgB,MAAM,2BAA2B,CAAC;AACvE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAGpD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,OAAO,GAAG,OAAO,CAO/D"}
@@ -1,157 +1,89 @@
1
1
  /**
2
- * Palette / theme / auto-contrast function registrations for the rich-js
3
- * template binding.
2
+ * The one palette-dependent template function: `color`.
4
3
  *
5
- * [LAW:one-source-of-truth] All palette resolution flows through the existing
6
- * `PaletteResolver.resolve(spec, ctx)` API — this module adds no resolution
7
- * logic of its own. The template functions here are thin adaptors that translate
8
- * function call arguments into a `(spec, ctx)` call and wrap the resulting
9
- * `ColorRgba` as a `Style` applied to the child fragment.
4
+ * ### Why exactly one
10
5
  *
11
- * [LAW:dataflow-not-control-flow] Every function follows the same shape:
12
- * resolve spec → apply as foreground color → return styled fragment. The
13
- * variability is in the spec string and the optional against context; the
14
- * operation is fixed.
6
+ * This module used to register four kinds of thing: one function per
7
+ * identifier-safe palette variable (`{{ primary child }}`, `{{ accent child }}`,
8
+ * …), a general `palette "spec" child`, a `paletteOver "spec" "#bg" child` for
9
+ * specs needing a background, and an `auto "#bg" child` sugar. That surface had
10
+ * two defects worth recording, because both are easy to reintroduce.
15
11
  *
16
- * ### Surface design rationale
12
+ * **The name family could not cover its own domain.** A themed palette carries
13
+ * ~150 variables; roughly 14 of them are legal Go-template identifiers. So the
14
+ * generated functions reached under a tenth of the palette, and the other nine
15
+ * tenths needed `palette "text-primary" child` — a *different expression shape*
16
+ * for the same intent. An author who learned `{{ primary x }}` and reasonably
17
+ * tried `{{ text_primary x }}` got a FuncNotFound. [LAW:composability] — the
18
+ * `filterByStatus`/`filterByOwner` shape: names cannot enumerate a domain, and
19
+ * the N+1 case always needs a form you have to learn separately.
17
20
  *
18
- * Four layers of ergonomics:
21
+ * **Resolution and application were fused.** Every one of those functions
22
+ * consumed its color instantly into a styled fragment, so a color could never
23
+ * be held or passed. Composition therefore had to happen inside the spec
24
+ * *string* — hence the old `name-darken-N alpha%` grammar. See
25
+ * `color-funcs.ts` for the full argument; the short version is that a string
26
+ * grammar is function application with the function calls spelled as
27
+ * punctuation, and it grows a new production for every operation.
19
28
  *
20
- * 1. **Semantic-name functions** — one function per palette variable whose
21
- * name is a valid Go template identifier (no hyphens). `{{ primary child }}`,
22
- * `{{ accent child }}`, etc. The happy path: common semantic names read
23
- * cleanly in templates. Hyphenated names (`primary-muted`, `text-primary`)
24
- * cannot be Go template identifiers and are accessed via `palette`.
29
+ * What replaces all of it: `color "name-or-hex"` produces a color value, the
30
+ * functions in `color-funcs.ts` transform colors, and `fg`/`bg` in
31
+ * `style-funcs.ts` paint them. One shape, total over the palette, open to
32
+ * arbitrary composition. [LAW:one-type-per-behavior]
25
33
  *
26
- * 2. **`palette "spec" child`** — the general-purpose function for any spec
27
- * that does not need a background context (bare names and darken/lighten
28
- * modifiers). Covers hyphenated names and modifier chains.
34
+ * ### Why a getter, not a palette
29
35
  *
30
- * 3. **`paletteOver "spec" "#bgHex" child`** — for specs that require a
31
- * background context: alpha compositing (`"primary 50%"`) and auto-contrast
32
- * (`"auto"`, `"auto 33%"`). The bg color is threaded as an explicit hex
33
- * argument rather than via a scope side-channel — no mutable state, no
34
- * closure magic; the data flows through the function call.
36
+ * `paletteFuncs` takes `() => Palette` rather than a `Palette`. A consumer
37
+ * whose theme can change at runtime (a live preview, a theme picker, a
38
+ * status-line that recolors on click) would otherwise be frozen to whichever
39
+ * palette happened to be current when the engine was constructed — and since
40
+ * templates are parsed once and evaluated many times, that freeze outlives
41
+ * every subsequent theme change while the *rest* of the consumer's colors move
42
+ * on. Two palettes, one render: [LAW:one-source-of-truth] violated by a
43
+ * captured reference.
35
44
  *
36
- * 4. **`auto "#bgHex" child`** — syntactic sugar for
37
- * `{{ paletteOver "auto" "#bgHex" child }}`, the common auto-contrast case.
38
- *
39
- * ### Theme switching
40
- *
41
- * `paletteFuncs(resolver)` captures `resolver` at construction time. Consumers
42
- * that need runtime theme switching create a new `paletteFuncs()` from the new
43
- * theme's resolver and rebuild their engine (or merge into a fresh `FuncMap`).
44
- * The same template *source* produces different colors because the functions in
45
- * the engine changed — the template text is the same, the resolver differs.
46
- */
47
- import { ColorSpec } from "../core/color.js";
48
- import { Style } from "../core/style.js";
49
- import { applyStyleToFragment } from "./helpers.js";
50
- // Regex for validating hex bg strings accepted by `paletteOver` and `auto`.
51
- // Accepts #RRGGBB and #RRGGBBAA — same gate as the `hex` style function.
52
- const HEX_BG_RE = /^#[0-9a-fA-F]{6}([0-9a-fA-F]{2})?$/;
53
- /**
54
- * Convert a hex string to `ColorRgba`. Only #RRGGBB / #RRGGBBAA accepted.
55
- * Throws `RangeError` on invalid input — surfaces as an `EvalError` in the
56
- * template engine, giving the author a precise failure message.
45
+ * The getter costs nothing structurally. `FuncMap` entries are data in the
46
+ * engine and their bodies run at *evaluate* time, so reading the palette
47
+ * through a getter leaves parse-once/evaluate-many completely intact. What the
48
+ * getter must not change is *which functions exist* — and it cannot, because
49
+ * there is now exactly one, whose name does not depend on the palette's
50
+ * contents. That was not true of the generated per-variable functions, which
51
+ * is the second reason they are gone.
57
52
  */
58
- function hexToColorRgba(hex) {
59
- if (!HEX_BG_RE.test(hex)) {
60
- throw new RangeError(`palette background expected #RRGGBB or #RRGGBBAA, got ${JSON.stringify(hex)}`);
61
- }
62
- // ColorSpec.parse("#RRGGBB") → TRUECOLOR spec; getTruecolor() returns value directly.
63
- return ColorSpec.parse(hex).getTruecolor();
64
- }
53
+ import { resolveColorRef } from "../themes/colorRef.js";
65
54
  /**
66
- * Resolve `spec` against the resolver (with optional background context) and
67
- * apply the resulting color as a foreground `Style` on `child`.
55
+ * Register `color "name-or-hex"` against a live palette.
68
56
  *
69
- * Throws on resolution failure with a contextual message — including a hint to
70
- * use `paletteOver` when the spec needs a background but none was provided.
71
- */
72
- function resolveAndApply(resolver, spec, against, child) {
73
- const color = resolver.resolve(spec, against !== undefined ? { against } : undefined);
74
- if (color === null) {
75
- const hint = against === undefined
76
- ? "; for specs with alpha or auto-contrast, use paletteOver"
77
- : "";
78
- throw new Error(`palette spec ${JSON.stringify(spec)} did not resolve — check the spec string is valid and the variable exists${hint}`);
79
- }
80
- return applyStyleToFragment(child, new Style({ color: ColorSpec.fromRgba(color) }));
81
- }
82
- // [LAW:types-are-the-program] Only palette var names that are valid Go template
83
- // identifiers (letter/underscore start, alphanumeric/underscore body) get
84
- // individual functions. Hyphenated names like "primary-muted" parse as subtraction
85
- // in Go template syntax and cannot be function names.
86
- const IDENTIFIER_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
87
- function semanticNameFuncs(resolver) {
88
- const out = {};
89
- for (const name of resolver.palette.vars.keys()) {
90
- if (!IDENTIFIER_RE.test(name))
91
- continue;
92
- // [LAW:dataflow-not-control-flow] Capture name; same fn shape for every entry.
93
- const captured = name;
94
- out[captured] = {
95
- fn: ((child) => resolveAndApply(resolver, captured, undefined, child)),
96
- argTypes: ["liftable"],
97
- returnType: "T",
98
- };
99
- }
100
- return out;
101
- }
102
- function makePaletteFunc(resolver) {
103
- return {
104
- fn: ((spec, child) => resolveAndApply(resolver, spec, undefined, child)),
105
- argTypes: ["string", "liftable"],
106
- returnType: "T",
107
- };
108
- }
109
- function makePaletteOverFunc(resolver) {
110
- return {
111
- fn: ((spec, bgHex, child) => resolveAndApply(resolver, spec, hexToColorRgba(bgHex), child)),
112
- argTypes: ["string", "string", "liftable"],
113
- returnType: "T",
114
- };
115
- }
116
- function makeAutoFunc(resolver) {
117
- return {
118
- fn: ((bgHex, child) => resolveAndApply(resolver, "auto", hexToColorRgba(bgHex), child)),
119
- argTypes: ["string", "liftable"],
120
- returnType: "T",
121
- };
122
- }
123
- /**
124
- * Build a `FuncMap` exposing the semantic palette of `resolver` as template
125
- * functions. Merge into `richTextFuncs()` (or pass to `createEngine`) to make
126
- * palette colors available in templates.
57
+ * `color` resolves a palette variable name to a `#RRGGBB` string, and passes
58
+ * an already-literal color through unchanged. That second half is not a
59
+ * convenience — it makes `color` **idempotent**, which is what lets consumers
60
+ * apply it unconditionally to any author-written color string without first
61
+ * asking whether it is a name or already a color. [LAW:dataflow-not-control-flow]
127
62
  *
128
- * Registered functions:
129
- * - One function per palette variable whose name is a valid Go template
130
- * identifier: `{{ primary child }}`, `{{ accent child }}`, etc.
131
- * - `palette "spec" child` — any spec without background context.
132
- * - `paletteOver "spec" "#bgHex" child` — any spec needing a background (alpha,
133
- * auto-contrast).
134
- * - `auto "#bgHex" child` — sugar for `paletteOver "auto" bgHex child`.
63
+ * An unknown name throws, carrying near-miss suggestions from the live
64
+ * palette. In a template that surfaces as an evaluation error at the exact
65
+ * call site, which is the signal an author (or an agent editing a config) needs
66
+ * to fix it. [LAW:no-silent-failure]
135
67
  *
136
68
  * @example
137
69
  * ```ts
138
- * import { createEngine } from "@promptctl/go-template-js";
139
- * import { GRUVBOX, PaletteResolver, RichText } from "rich-js";
140
- * import { richTextFuncs, paletteFuncs } from "rich-js/template-bindings";
141
- *
142
70
  * const engine = createEngine({
143
71
  * fromString: (s) => new RichText(s),
144
72
  * toString: (rt) => rt.plain,
145
- * funcs: { ...richTextFuncs(), ...paletteFuncs(new PaletteResolver(GRUVBOX.palette)) },
73
+ * funcs: {
74
+ * ...richTextFuncs(),
75
+ * ...colorFuncs(),
76
+ * ...paletteFuncs(() => currentTheme.palette),
77
+ * },
146
78
  * });
147
79
  * ```
148
80
  */
149
- export function paletteFuncs(resolver) {
150
- return {
151
- ...semanticNameFuncs(resolver),
152
- palette: makePaletteFunc(resolver),
153
- paletteOver: makePaletteOverFunc(resolver),
154
- auto: makeAutoFunc(resolver),
81
+ export function paletteFuncs(getPalette) {
82
+ const colorFunc = {
83
+ fn: ((ref) => resolveColorRef(getPalette(), ref).hex),
84
+ argTypes: ["string"],
85
+ returnType: "string",
155
86
  };
87
+ return { color: colorFunc };
156
88
  }
157
89
  //# sourceMappingURL=palette-funcs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"palette-funcs.js","sourceRoot":"","sources":["../../src/template-bindings/palette-funcs.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"palette-funcs.js","sourceRoot":"","sources":["../../src/template-bindings/palette-funcs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AAIH,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,UAAU,YAAY,CAAC,UAAyB;IACpD,MAAM,SAAS,GAAiB;QAC9B,EAAE,EAAE,CAAC,CAAC,GAAW,EAAE,EAAE,CAAC,eAAe,CAAC,UAAU,EAAE,EAAE,GAAG,CAAC,CAAC,GAAG,CAAuB;QACnF,QAAQ,EAAE,CAAC,QAAQ,CAAC;QACpB,UAAU,EAAE,QAAQ;KACrB,CAAC;IACF,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;AAC9B,CAAC"}
@@ -2,12 +2,11 @@
2
2
  * Style-function registrations for the rich-js template binding.
3
3
  *
4
4
  * [LAW:one-source-of-truth] The function inventory below mirrors the
5
- * string-syntax style vocabulary documented in `spec/style.md` —
6
- * foreground colours (named, palette index, hex, RGB), background
7
- * (`on`), text attributes (positive + negated), and short aliases.
8
- * Each registration is a templating-time analogue of a piece of
9
- * `Style.parse`, so a template fragment composed by these functions
10
- * round-trips through `Style` without semantic drift.
5
+ * string-syntax style vocabulary documented in `spec/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.
11
10
  *
12
11
  * [LAW:dataflow-not-control-flow] Every function follows the same
13
12
  * shape: child `RichText` in, `RichText` out. The styling difference
@@ -18,13 +17,13 @@
18
17
  */
19
18
  import type { FuncMap } from "@promptctl/go-template-js";
20
19
  /**
21
- * The full binding registration set populated by the template-bindings
22
- * style epics — foreground colours (named + generic forms), background
23
- * (`on`), text attributes (canonical names, short aliases, and `not_*`
24
- * negations), and the hyperlink cell-splitter (`link`).
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.
25
23
  *
26
- * Palette/theme/auto-contrast and per-position hue rotation are
27
- * deliberately absent — they ship in `rich-template-bindings-83q`.
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.
28
27
  */
29
28
  export declare function richTextStyleFuncs(): FuncMap;
30
29
  //# sourceMappingURL=style-funcs.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"style-funcs.d.ts","sourceRoot":"","sources":["../../src/template-bindings/style-funcs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAgB,MAAM,2BAA2B,CAAC;AAqKvE;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,IAAI,OAAO,CAW5C"}
1
+ {"version":3,"file":"style-funcs.d.ts","sourceRoot":"","sources":["../../src/template-bindings/style-funcs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAgB,MAAM,2BAA2B,CAAC;AA0IvE;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,IAAI,OAAO,CAQ5C"}
@@ -2,12 +2,11 @@
2
2
  * Style-function registrations for the rich-js template binding.
3
3
  *
4
4
  * [LAW:one-source-of-truth] The function inventory below mirrors the
5
- * string-syntax style vocabulary documented in `spec/style.md` —
6
- * foreground colours (named, palette index, hex, RGB), background
7
- * (`on`), text attributes (positive + negated), and short aliases.
8
- * Each registration is a templating-time analogue of a piece of
9
- * `Style.parse`, so a template fragment composed by these functions
10
- * round-trips through `Style` without semantic drift.
5
+ * string-syntax style vocabulary documented in `spec/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.
11
10
  *
12
11
  * [LAW:dataflow-not-control-flow] Every function follows the same
13
12
  * shape: child `RichText` in, `RichText` out. The styling difference
@@ -17,66 +16,44 @@
17
16
  * variability; the operation is fixed.
18
17
  */
19
18
  import { Style, ATTRIBUTE_NAMES, ATTRIBUTE_SHORT_ALIASES, } from "../core/style.js";
20
- import { ColorSpec, ANSI_COLOR_NAMES } from "../core/color.js";
19
+ import { ColorSpec } from "../core/color.js";
21
20
  import { applyStyleToFragment } from "./helpers.js";
22
- function fgFunc(style) {
21
+ function attrFunc(style) {
23
22
  return {
24
23
  fn: ((child) => applyStyleToFragment(child, style)),
25
24
  argTypes: ["liftable"],
26
25
  returnType: "T",
27
26
  };
28
27
  }
29
- // --- Foreground colours: named ---
30
- function namedColorFuncs() {
31
- const out = {};
32
- for (const name of Object.keys(ANSI_COLOR_NAMES)) {
33
- const colorSpec = ColorSpec.parse(name);
34
- out[name] = fgFunc(new Style({ color: colorSpec }));
35
- }
36
- return out;
28
+ // --- Colour sinks ---
29
+ //
30
+ // `fg` and `bg` are the only two colour-applying functions, and they are the
31
+ // terminal step of the colour pipeline: `color` names a colour, the functions
32
+ // in `color-funcs.ts` transform it, these paint it onto text.
33
+ //
34
+ // [LAW:composability] They replace five separate families — one function per
35
+ // ANSI colour name (`red`, `bright_blue`, …), `hex`, `rgb`, `color` (256-index),
36
+ // and `on` (background). Every one of those encoded *which colour* in the
37
+ // function's identity, so the vocabulary could only grow by adding names, and
38
+ // a colour computed at render time could not be applied at all. Here the colour
39
+ // is an argument, so the sink admits every colour that exists and every colour
40
+ // that will ever exist. Two functions, unbounded reach.
41
+ //
42
+ // [LAW:types-are-the-program] The slot accepts the full `ColorSpec.parse`
43
+ // vocabulary, which is wider than the hex the colour math produces — and that
44
+ // width is deliberate, not laxity. `#7aa2f7` is a *concrete* colour; `"red"`
45
+ // and `"color(42)"` are *symbolic* ones the terminal resolves against its own
46
+ // theme. Only concrete colours can be darkened or blended, which is why the
47
+ // colour-math functions take hex alone; but both kinds can be painted, so the
48
+ // sink takes the union. The type of each slot is exactly the set of values that
49
+ // slot can mean something for.
50
+ function colorSinkFunc(slot) {
51
+ return {
52
+ fn: ((spec, child) => applyStyleToFragment(child, new Style({ [slot]: ColorSpec.parse(spec) }))),
53
+ argTypes: ["string", "liftable"],
54
+ returnType: "T",
55
+ };
37
56
  }
38
- // --- Foreground colours: generic forms ---
39
- const colorPaletteFunc = {
40
- fn: ((index, child) => {
41
- if (!Number.isInteger(index) || index < 0 || index > 255) {
42
- throw new RangeError(`color index ${index} is out of range (0-255)`);
43
- }
44
- return applyStyleToFragment(child, new Style({ color: ColorSpec.fromAnsi(index) }));
45
- }),
46
- argTypes: ["int", "liftable"],
47
- returnType: "T",
48
- };
49
- // [LAW:types-are-the-program] `hex` advertises a narrower domain than the
50
- // general colour-spec parser — only `#RRGGBB` / `#RRGGBBAA` is admitted.
51
- // Without this gate, `ColorSpec.parse` would silently accept any colour-spec
52
- // string (named colours, `rgb(...)`, `color(N)`), letting `hex "red"` succeed
53
- // and masking author mistakes.
54
- const HEX_INPUT_RE = /^#[0-9a-fA-F]{6}([0-9a-fA-F]{2})?$/;
55
- const colorHexFunc = {
56
- fn: ((hex, child) => {
57
- if (!HEX_INPUT_RE.test(hex)) {
58
- throw new RangeError(`hex expected #RRGGBB or #RRGGBBAA, got ${JSON.stringify(hex)}`);
59
- }
60
- return applyStyleToFragment(child, new Style({ color: ColorSpec.parse(hex) }));
61
- }),
62
- argTypes: ["string", "liftable"],
63
- returnType: "T",
64
- };
65
- const colorRgbFunc = {
66
- fn: ((r, g, b, child) => {
67
- return applyStyleToFragment(child, new Style({ color: ColorSpec.fromRgb(r, g, b) }));
68
- }),
69
- argTypes: ["int", "int", "int", "liftable"],
70
- returnType: "T",
71
- };
72
- // --- Background ---
73
- const onFunc = {
74
- fn: ((spec, child) => {
75
- return applyStyleToFragment(child, new Style({ bgcolor: ColorSpec.parse(spec) }));
76
- }),
77
- argTypes: ["string", "liftable"],
78
- returnType: "T",
79
- };
80
57
  // --- Text attributes ---
81
58
  //
82
59
  // [LAW:one-source-of-truth] The attribute and short-alias inventories
@@ -89,11 +66,11 @@ function attrStyle(name, value) {
89
66
  function attributeFuncs() {
90
67
  const out = {};
91
68
  for (const name of ATTRIBUTE_NAMES) {
92
- out[name] = fgFunc(attrStyle(name, true));
93
- out[`not_${name}`] = fgFunc(attrStyle(name, false));
69
+ out[name] = attrFunc(attrStyle(name, true));
70
+ out[`not_${name}`] = attrFunc(attrStyle(name, false));
94
71
  }
95
72
  for (const [alias, canonical] of Object.entries(ATTRIBUTE_SHORT_ALIASES)) {
96
- out[alias] = fgFunc(attrStyle(canonical, true));
73
+ out[alias] = attrFunc(attrStyle(canonical, true));
97
74
  }
98
75
  return out;
99
76
  }
@@ -105,7 +82,7 @@ function attributeFuncs() {
105
82
  // byte-equivalent to the same spec inside markup, and one that `Style.parse`
106
83
  // rejects raises the same `StyleSyntaxError` surface.
107
84
  //
108
- // Motivation: the per-attribute functions (`bold`, `underline`, `hex`, …)
85
+ // Motivation: the per-attribute functions (`bold`, `underline`, `fg`, …)
109
86
  // compose by nesting. For "apply a fixed set of styles to this child" or
110
87
  // "apply this named style set everywhere", nesting is awkward and the
111
88
  // style description is fragmented across multiple call sites. `style`
@@ -138,7 +115,7 @@ const styleSpecFunc = {
138
115
  //
139
116
  // The cell-boundary signal that consumers (cc-candybar et al.) walk is
140
117
  // `fragment.style.link` being truthy. `Style.add` propagates `link`
141
- // through any outer wrapping call, so `{{ red (link "u" "x") }}` and
118
+ // through any outer wrapping call, so `{{ fg "red" (link "u" "x") }}` and
142
119
  // `{{ link "u" "x" }}` produce shapes that both qualify as cells from
143
120
  // the consumer's perspective. Outer-wins on nested links comes for free
144
121
  // from `Style.add`'s right-wins-on-conflict rule.
@@ -155,21 +132,18 @@ const linkFunc = {
155
132
  };
156
133
  // --- Public assembly ---
157
134
  /**
158
- * The full binding registration set populated by the template-bindings
159
- * style epics — foreground colours (named + generic forms), background
160
- * (`on`), text attributes (canonical names, short aliases, and `not_*`
161
- * negations), and the hyperlink cell-splitter (`link`).
135
+ * Text-styling registrations: the two colour sinks (`fg`, `bg`), text
136
+ * attributes (canonical names, short aliases, and `not_*` negations), the
137
+ * hyperlink cell-splitter (`link`), and the multi-attribute `style` spec.
162
138
  *
163
- * Palette/theme/auto-contrast and per-position hue rotation are
164
- * deliberately absent — they ship in `rich-template-bindings-83q`.
139
+ * Colours themselves come from elsewhere: `colorFuncs()` for the palette-free
140
+ * math, `paletteFuncs()` for naming a theme colour. This module only paints.
141
+ * [LAW:one-way-deps] — nothing here imports a palette.
165
142
  */
166
143
  export function richTextStyleFuncs() {
167
144
  return {
168
- ...namedColorFuncs(),
169
- color: colorPaletteFunc,
170
- hex: colorHexFunc,
171
- rgb: colorRgbFunc,
172
- on: onFunc,
145
+ fg: colorSinkFunc("color"),
146
+ bg: colorSinkFunc("bgcolor"),
173
147
  ...attributeFuncs(),
174
148
  link: linkFunc,
175
149
  style: styleSpecFunc,