@promptctl/rich-js 0.18.0 → 0.18.1
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.
- package/dist/core/anchor.d.ts +45 -0
- package/dist/core/anchor.js +39 -0
- package/dist/core/ansi.d.ts +65 -0
- package/dist/core/ansi.js +231 -0
- package/dist/core/box.d.ts +143 -0
- package/dist/core/box.js +379 -0
- package/dist/core/cells.d.ts +159 -0
- package/dist/core/cells.js +287 -0
- package/dist/core/color.d.ts +246 -0
- package/dist/core/color.js +1038 -0
- package/dist/core/console.d.ts +165 -0
- package/dist/core/console.js +527 -0
- package/dist/core/emoji.d.ts +24 -0
- package/dist/core/emoji.js +391 -0
- package/dist/core/export-html.d.ts +59 -0
- package/dist/core/export-html.js +200 -0
- package/dist/core/export-lines.d.ts +121 -0
- package/dist/core/export-lines.js +141 -0
- package/dist/core/highlighter.d.ts +33 -0
- package/dist/core/highlighter.js +124 -0
- package/dist/core/json.d.ts +27 -0
- package/dist/core/json.js +53 -0
- package/dist/core/markup.d.ts +119 -0
- package/dist/core/markup.js +731 -0
- package/dist/core/measure.d.ts +27 -0
- package/dist/core/measure.js +62 -0
- package/dist/core/oklch.d.ts +161 -0
- package/dist/core/oklch.js +312 -0
- package/dist/core/osc8.d.ts +68 -0
- package/dist/core/osc8.js +90 -0
- package/dist/core/place.d.ts +30 -0
- package/dist/core/place.js +51 -0
- package/dist/core/pretty.d.ts +144 -0
- package/dist/core/pretty.js +476 -0
- package/dist/core/protocol.d.ts +267 -0
- package/dist/core/protocol.js +163 -0
- package/dist/core/render.d.ts +84 -0
- package/dist/core/render.js +145 -0
- package/dist/core/segment.d.ts +161 -0
- package/dist/core/segment.js +492 -0
- package/dist/core/spinnerData.d.ts +12 -0
- package/dist/core/spinnerData.js +117 -0
- package/dist/core/strip.d.ts +141 -0
- package/dist/core/strip.js +305 -0
- package/dist/core/style.d.ts +169 -0
- package/dist/core/style.js +695 -0
- package/dist/core/subscription.d.ts +12 -0
- package/dist/core/subscription.js +12 -0
- package/dist/core/text.d.ts +256 -0
- package/dist/core/text.js +976 -0
- package/dist/core/wrap.d.ts +41 -0
- package/dist/core/wrap.js +127 -0
- package/dist/host/app.d.ts +121 -0
- package/dist/host/app.js +255 -0
- package/dist/host/host-environment.d.ts +26 -0
- package/dist/host/host-environment.js +44 -0
- package/dist/host/index.d.ts +5 -0
- package/dist/host/index.js +3 -0
- package/dist/host/terminal-host.d.ts +179 -0
- package/dist/host/terminal-host.js +186 -0
- package/dist/index.d.ts +91 -0
- package/dist/index.js +107 -0
- package/dist/node/prompt.d.ts +21 -0
- package/dist/node/prompt.js +30 -0
- package/dist/node/save.d.ts +17 -0
- package/dist/node/save.js +21 -0
- package/dist/node/terminal-host.d.ts +113 -0
- package/dist/node/terminal-host.js +227 -0
- package/dist/node/traceback.d.ts +37 -0
- package/dist/node/traceback.js +134 -0
- package/dist/renderables/align.d.ts +16 -0
- package/dist/renderables/align.js +33 -0
- package/dist/renderables/columns.d.ts +61 -0
- package/dist/renderables/columns.js +152 -0
- package/dist/renderables/constrain.d.ts +15 -0
- package/dist/renderables/constrain.js +37 -0
- package/dist/renderables/embed.d.ts +50 -0
- package/dist/renderables/embed.js +71 -0
- package/dist/renderables/flexStrip.d.ts +41 -0
- package/dist/renderables/flexStrip.js +200 -0
- package/dist/renderables/group.d.ts +11 -0
- package/dist/renderables/group.js +17 -0
- package/dist/renderables/layout.d.ts +92 -0
- package/dist/renderables/layout.js +312 -0
- package/dist/renderables/live.d.ts +50 -0
- package/dist/renderables/live.js +175 -0
- package/dist/renderables/markdown.d.ts +24 -0
- package/dist/renderables/markdown.js +184 -0
- package/dist/renderables/padding.d.ts +38 -0
- package/dist/renderables/padding.js +141 -0
- package/dist/renderables/panel.d.ts +116 -0
- package/dist/renderables/panel.js +307 -0
- package/dist/renderables/progress.d.ts +90 -0
- package/dist/renderables/progress.js +238 -0
- package/dist/renderables/progressBar.d.ts +31 -0
- package/dist/renderables/progressBar.js +53 -0
- package/dist/renderables/prompt.d.ts +49 -0
- package/dist/renderables/prompt.js +125 -0
- package/dist/renderables/rule.d.ts +25 -0
- package/dist/renderables/rule.js +90 -0
- package/dist/renderables/spinner.d.ts +31 -0
- package/dist/renderables/spinner.js +70 -0
- package/dist/renderables/status.d.ts +23 -0
- package/dist/renderables/status.js +60 -0
- package/dist/renderables/syntax.d.ts +35 -0
- package/dist/renderables/syntax.js +115 -0
- package/dist/renderables/table.d.ts +194 -0
- package/dist/renderables/table.js +795 -0
- package/dist/renderables/traceback.d.ts +31 -0
- package/dist/renderables/traceback.js +108 -0
- package/dist/renderables/tree.d.ts +68 -0
- package/dist/renderables/tree.js +150 -0
- package/dist/renderables/viewport.d.ts +114 -0
- package/dist/renderables/viewport.js +198 -0
- package/dist/template-bindings/color-funcs.d.ts +87 -0
- package/dist/template-bindings/color-funcs.js +217 -0
- package/dist/template-bindings/helpers.d.ts +24 -0
- package/dist/template-bindings/helpers.js +48 -0
- package/dist/template-bindings/index.d.ts +107 -0
- package/dist/template-bindings/index.js +146 -0
- package/dist/template-bindings/palette-funcs.d.ts +94 -0
- package/dist/template-bindings/palette-funcs.js +141 -0
- package/dist/template-bindings/style-funcs.d.ts +28 -0
- package/dist/template-bindings/style-funcs.js +155 -0
- package/dist/themes/buildPalette.d.ts +36 -0
- package/dist/themes/buildPalette.js +56 -0
- package/dist/themes/colorMath.d.ts +82 -0
- package/dist/themes/colorMath.js +254 -0
- package/dist/themes/colorRef.d.ts +51 -0
- package/dist/themes/colorRef.js +96 -0
- package/dist/themes/data/ansi-tables.d.ts +14 -0
- package/dist/themes/data/ansi-tables.js +49 -0
- package/dist/themes/data/atom-one-dark.d.ts +3 -0
- package/dist/themes/data/atom-one-dark.js +177 -0
- package/dist/themes/data/atom-one-light.d.ts +3 -0
- package/dist/themes/data/atom-one-light.js +177 -0
- package/dist/themes/data/catppuccin-frappe.d.ts +3 -0
- package/dist/themes/data/catppuccin-frappe.js +177 -0
- package/dist/themes/data/catppuccin-latte.d.ts +3 -0
- package/dist/themes/data/catppuccin-latte.js +177 -0
- package/dist/themes/data/catppuccin-macchiato.d.ts +3 -0
- package/dist/themes/data/catppuccin-macchiato.js +177 -0
- package/dist/themes/data/catppuccin-mocha.d.ts +3 -0
- package/dist/themes/data/catppuccin-mocha.js +177 -0
- package/dist/themes/data/cyberpunk.d.ts +17 -0
- package/dist/themes/data/cyberpunk.js +193 -0
- package/dist/themes/data/default.d.ts +3 -0
- package/dist/themes/data/default.js +160 -0
- package/dist/themes/data/dracula.d.ts +3 -0
- package/dist/themes/data/dracula.js +177 -0
- package/dist/themes/data/flexoki.d.ts +3 -0
- package/dist/themes/data/flexoki.js +177 -0
- package/dist/themes/data/gruvbox.d.ts +3 -0
- package/dist/themes/data/gruvbox.js +177 -0
- package/dist/themes/data/index.d.ts +35 -0
- package/dist/themes/data/index.js +64 -0
- package/dist/themes/data/monokai.d.ts +3 -0
- package/dist/themes/data/monokai.js +177 -0
- package/dist/themes/data/nord.d.ts +3 -0
- package/dist/themes/data/nord.js +177 -0
- package/dist/themes/data/rose-pine-dawn.d.ts +3 -0
- package/dist/themes/data/rose-pine-dawn.js +177 -0
- package/dist/themes/data/rose-pine-moon.d.ts +3 -0
- package/dist/themes/data/rose-pine-moon.js +177 -0
- package/dist/themes/data/rose-pine.d.ts +3 -0
- package/dist/themes/data/rose-pine.js +177 -0
- package/dist/themes/data/solarized-dark.d.ts +3 -0
- package/dist/themes/data/solarized-dark.js +177 -0
- package/dist/themes/data/solarized-light.d.ts +3 -0
- package/dist/themes/data/solarized-light.js +177 -0
- package/dist/themes/data/svg-export.d.ts +3 -0
- package/dist/themes/data/svg-export.js +177 -0
- package/dist/themes/data/textual-ansi.d.ts +3 -0
- package/dist/themes/data/textual-ansi.js +160 -0
- package/dist/themes/data/textual-dark.d.ts +3 -0
- package/dist/themes/data/textual-dark.js +160 -0
- package/dist/themes/data/textual-light.d.ts +3 -0
- package/dist/themes/data/textual-light.js +160 -0
- package/dist/themes/data/tokyo-night.d.ts +3 -0
- package/dist/themes/data/tokyo-night.js +177 -0
- package/dist/themes/data/types.d.ts +31 -0
- package/dist/themes/data/types.js +10 -0
- package/dist/themes/palette.d.ts +21 -0
- package/dist/themes/palette.js +28 -0
- package/dist/themes/ramp.d.ts +83 -0
- package/dist/themes/ramp.js +122 -0
- package/dist/themes/registry.d.ts +44 -0
- package/dist/themes/registry.js +94 -0
- package/dist/themes/terminalThemes.d.ts +49 -0
- package/dist/themes/terminalThemes.js +63 -0
- package/dist/themes/transpose.d.ts +87 -0
- package/dist/themes/transpose.js +138 -0
- package/dist/widgets/button.d.ts +49 -0
- package/dist/widgets/button.js +208 -0
- package/dist/widgets/checkbox.d.ts +41 -0
- package/dist/widgets/checkbox.js +157 -0
- package/dist/widgets/dropdown.d.ts +67 -0
- package/dist/widgets/dropdown.js +450 -0
- package/dist/widgets/event-router.d.ts +71 -0
- package/dist/widgets/event-router.js +629 -0
- package/dist/widgets/focus-manager.d.ts +45 -0
- package/dist/widgets/focus-manager.js +191 -0
- package/dist/widgets/hit.d.ts +64 -0
- package/dist/widgets/hit.js +113 -0
- package/dist/widgets/index.d.ts +20 -0
- package/dist/widgets/index.js +14 -0
- package/dist/widgets/slider.d.ts +57 -0
- package/dist/widgets/slider.js +270 -0
- package/dist/widgets/static-item.d.ts +34 -0
- package/dist/widgets/static-item.js +54 -0
- package/dist/widgets/text-input.d.ts +274 -0
- package/dist/widgets/text-input.js +1165 -0
- package/dist/widgets/toggle.d.ts +44 -0
- package/dist/widgets/toggle.js +180 -0
- package/dist/widgets/types.d.ts +87 -0
- package/dist/widgets/types.js +30 -0
- package/dist/widgets/widget-app.d.ts +63 -0
- package/dist/widgets/widget-app.js +199 -0
- package/dist/widgets/widget-base.d.ts +40 -0
- package/dist/widgets/widget-base.js +165 -0
- package/package.json +6 -2
|
@@ -0,0 +1,94 @@
|
|
|
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 a `#RRGGBB` string, and passes
|
|
71
|
+
* an already-literal color through unchanged. That second half is not a
|
|
72
|
+
* convenience — it makes `color` **idempotent**, which is what lets consumers
|
|
73
|
+
* apply it unconditionally to any author-written color string without first
|
|
74
|
+
* asking whether it is a name or already a color. [LAW:dataflow-not-control-flow]
|
|
75
|
+
*
|
|
76
|
+
* An unknown name throws, carrying near-miss suggestions from the live
|
|
77
|
+
* palette. In a template that surfaces as an evaluation error at the exact
|
|
78
|
+
* call site, which is the signal an author (or an agent editing a config) needs
|
|
79
|
+
* to fix it. [LAW:no-silent-failure]
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* ```ts
|
|
83
|
+
* const engine = createEngine({
|
|
84
|
+
* fromString: (s) => new RichText(s),
|
|
85
|
+
* toString: (rt) => rt.plain,
|
|
86
|
+
* funcs: {
|
|
87
|
+
* ...richTextFuncs(),
|
|
88
|
+
* ...colorFuncs(),
|
|
89
|
+
* ...paletteFuncs(() => currentTheme.palette),
|
|
90
|
+
* },
|
|
91
|
+
* });
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
94
|
+
export declare function paletteFuncs(getPalette: () => Palette): FuncMap;
|
|
@@ -0,0 +1,141 @@
|
|
|
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 { ColorRamp, parseRampEasing } from "../themes/ramp.js";
|
|
67
|
+
/**
|
|
68
|
+
* The `(position, color-ref)` pairs a `ramp` call's tail spells, as stops.
|
|
69
|
+
*
|
|
70
|
+
* The engine's `alternating` gate has already typed every even slot as a
|
|
71
|
+
* float and every odd slot as a string; what it cannot see is the *pairing*
|
|
72
|
+
* — a trailing position with no color — nor that the string is a color
|
|
73
|
+
* reference. Both are settled here, once, and `ColorRamp` receives resolved
|
|
74
|
+
* stops it never re-checks. [LAW:parse-dont-validate]
|
|
75
|
+
*
|
|
76
|
+
* Each reference resolves against the palette of *this* evaluation, so a
|
|
77
|
+
* ramp over palette names follows the theme exactly as `color` does.
|
|
78
|
+
*/
|
|
79
|
+
function colorStops(tail, palette) {
|
|
80
|
+
if (tail.length % 2 !== 0) {
|
|
81
|
+
throw new RangeError(`ramp's last stop (position ${String(tail[tail.length - 1])}) has no color — ` +
|
|
82
|
+
`stops are <position> <color> pairs`);
|
|
83
|
+
}
|
|
84
|
+
const stops = [];
|
|
85
|
+
for (let i = 0; i < tail.length; i += 2) {
|
|
86
|
+
stops.push({
|
|
87
|
+
at: tail[i],
|
|
88
|
+
color: resolveColorRef(palette, tail[i + 1]),
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
return stops;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Register `color "name-or-hex"` against a live palette.
|
|
95
|
+
*
|
|
96
|
+
* `color` resolves a palette variable name to a `#RRGGBB` string, and passes
|
|
97
|
+
* an already-literal color through unchanged. That second half is not a
|
|
98
|
+
* convenience — it makes `color` **idempotent**, which is what lets consumers
|
|
99
|
+
* apply it unconditionally to any author-written color string without first
|
|
100
|
+
* asking whether it is a name or already a color. [LAW:dataflow-not-control-flow]
|
|
101
|
+
*
|
|
102
|
+
* An unknown name throws, carrying near-miss suggestions from the live
|
|
103
|
+
* palette. In a template that surfaces as an evaluation error at the exact
|
|
104
|
+
* call site, which is the signal an author (or an agent editing a config) needs
|
|
105
|
+
* to fix it. [LAW:no-silent-failure]
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* ```ts
|
|
109
|
+
* const engine = createEngine({
|
|
110
|
+
* fromString: (s) => new RichText(s),
|
|
111
|
+
* toString: (rt) => rt.plain,
|
|
112
|
+
* funcs: {
|
|
113
|
+
* ...richTextFuncs(),
|
|
114
|
+
* ...colorFuncs(),
|
|
115
|
+
* ...paletteFuncs(() => currentTheme.palette),
|
|
116
|
+
* },
|
|
117
|
+
* });
|
|
118
|
+
* ```
|
|
119
|
+
*/
|
|
120
|
+
export function paletteFuncs(getPalette) {
|
|
121
|
+
const colorFunc = {
|
|
122
|
+
fn: ((ref) => resolveColorRef(getPalette(), ref).hex),
|
|
123
|
+
argTypes: ["string"],
|
|
124
|
+
arity: { kind: "exact" },
|
|
125
|
+
returnType: "string",
|
|
126
|
+
};
|
|
127
|
+
// `ramp <value> <easing> <position> <color> …` — the argument list is one
|
|
128
|
+
// float/string cycle end to end (value, easing, then each stop's position
|
|
129
|
+
// and color), so the engine's `alternating` gate types every slot; the
|
|
130
|
+
// pairing and the references are parsed in `colorStops`. Its minimum is the
|
|
131
|
+
// value, the easing, and one stop, so the gate refuses a stopless call.
|
|
132
|
+
// [LAW:types-are-the-program]
|
|
133
|
+
const rampFunc = {
|
|
134
|
+
fn: ((value, easing, ...tail) => new ColorRamp(parseRampEasing(easing), colorStops(tail, getPalette())).at(value)
|
|
135
|
+
.hex),
|
|
136
|
+
argTypes: ["float", "string"],
|
|
137
|
+
arity: { kind: "alternating", minimum: 4 },
|
|
138
|
+
returnType: "string",
|
|
139
|
+
};
|
|
140
|
+
return { color: colorFunc, ramp: rampFunc };
|
|
141
|
+
}
|
|
@@ -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;
|
|
@@ -0,0 +1,155 @@
|
|
|
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 { Style, ATTRIBUTE_NAMES, ATTRIBUTE_SHORT_ALIASES, } from "../core/style.js";
|
|
19
|
+
import { ColorSpec } from "../core/color.js";
|
|
20
|
+
import { applyStyleToFragment } from "./helpers.js";
|
|
21
|
+
function attrFunc(style) {
|
|
22
|
+
return {
|
|
23
|
+
fn: ((child) => applyStyleToFragment(child, style)),
|
|
24
|
+
argTypes: ["liftable"],
|
|
25
|
+
arity: { kind: "exact" },
|
|
26
|
+
returnType: "T",
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
// --- Colour sinks ---
|
|
30
|
+
//
|
|
31
|
+
// `fg` and `bg` are the only two colour-applying functions, and they are the
|
|
32
|
+
// terminal step of the colour pipeline: `color` names a colour, the functions
|
|
33
|
+
// in `color-funcs.ts` transform it, these paint it onto text.
|
|
34
|
+
//
|
|
35
|
+
// [LAW:composability] They replace five separate families — one function per
|
|
36
|
+
// ANSI colour name (`red`, `bright_blue`, …), `hex`, `rgb`, `color` (256-index),
|
|
37
|
+
// and `on` (background). Every one of those encoded *which colour* in the
|
|
38
|
+
// function's identity, so the vocabulary could only grow by adding names, and
|
|
39
|
+
// a colour computed at render time could not be applied at all. Here the colour
|
|
40
|
+
// is an argument, so the sink admits every colour that exists and every colour
|
|
41
|
+
// that will ever exist. Two functions, unbounded reach.
|
|
42
|
+
//
|
|
43
|
+
// [LAW:types-are-the-program] The slot accepts the full `ColorSpec.parse`
|
|
44
|
+
// vocabulary, which is wider than the hex the colour math produces — and that
|
|
45
|
+
// width is deliberate, not laxity. `#7aa2f7` is a *concrete* colour; `"red"`
|
|
46
|
+
// and `"color(42)"` are *symbolic* ones the terminal resolves against its own
|
|
47
|
+
// theme. Only concrete colours can be darkened or blended, which is why the
|
|
48
|
+
// colour-math functions take hex alone; but both kinds can be painted, so the
|
|
49
|
+
// sink takes the union. The type of each slot is exactly the set of values that
|
|
50
|
+
// slot can mean something for.
|
|
51
|
+
function colorSinkFunc(slot) {
|
|
52
|
+
return {
|
|
53
|
+
fn: ((spec, child) => applyStyleToFragment(child, new Style({ [slot]: ColorSpec.parse(spec) }))),
|
|
54
|
+
argTypes: ["string", "liftable"],
|
|
55
|
+
arity: { kind: "exact" },
|
|
56
|
+
returnType: "T",
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
// --- Text attributes ---
|
|
60
|
+
//
|
|
61
|
+
// [LAW:one-source-of-truth] The attribute and short-alias inventories
|
|
62
|
+
// come from `core/style.ts`. This file derives the template registration
|
|
63
|
+
// set from the same data `Style.parse` consults — adding an attribute or
|
|
64
|
+
// alias in one place propagates here automatically.
|
|
65
|
+
function attrStyle(name, value) {
|
|
66
|
+
return new Style({ [name]: value });
|
|
67
|
+
}
|
|
68
|
+
function attributeFuncs() {
|
|
69
|
+
const out = {};
|
|
70
|
+
for (const name of ATTRIBUTE_NAMES) {
|
|
71
|
+
out[name] = attrFunc(attrStyle(name, true));
|
|
72
|
+
out[`not_${name}`] = attrFunc(attrStyle(name, false));
|
|
73
|
+
}
|
|
74
|
+
for (const [alias, canonical] of Object.entries(ATTRIBUTE_SHORT_ALIASES)) {
|
|
75
|
+
out[alias] = attrFunc(attrStyle(canonical, true));
|
|
76
|
+
}
|
|
77
|
+
return out;
|
|
78
|
+
}
|
|
79
|
+
// --- Style spec (multi-attribute one-shot) ---
|
|
80
|
+
//
|
|
81
|
+
// [LAW:one-source-of-truth] `style` accepts the same space-separated grammar
|
|
82
|
+
// `Style.parse` consults — i.e. the inside of `[...]` markup. There is no
|
|
83
|
+
// second parser: a spec that `Style.parse` accepts produces a fragment
|
|
84
|
+
// byte-equivalent to the same spec inside markup, and one that `Style.parse`
|
|
85
|
+
// rejects raises the same `StyleSyntaxError` surface.
|
|
86
|
+
//
|
|
87
|
+
// Motivation: the per-attribute functions (`bold`, `underline`, `fg`, …)
|
|
88
|
+
// compose by nesting. For "apply a fixed set of styles to this child" or
|
|
89
|
+
// "apply this named style set everywhere", nesting is awkward and the
|
|
90
|
+
// style description is fragmented across multiple call sites. `style`
|
|
91
|
+
// collapses that to a single call, and because the spec is a string it
|
|
92
|
+
// flows through Go-template `$vars` and through scope without further
|
|
93
|
+
// machinery:
|
|
94
|
+
//
|
|
95
|
+
// {{ $alert := "bold underline #ff6b6b" }}
|
|
96
|
+
// {{ style $alert "alarm!" }}
|
|
97
|
+
// {{ style $alert .otherField }}
|
|
98
|
+
//
|
|
99
|
+
// [LAW:dataflow-not-control-flow] Same shape as every other style function:
|
|
100
|
+
// a `Style` value (here built by `Style.parse(spec)`) plus a child, in,
|
|
101
|
+
// styled child out. The variability is the spec string; the operation is
|
|
102
|
+
// fixed.
|
|
103
|
+
const styleSpecFunc = {
|
|
104
|
+
fn: ((spec, child) => {
|
|
105
|
+
return applyStyleToFragment(child, Style.parse(spec));
|
|
106
|
+
}),
|
|
107
|
+
argTypes: ["string", "liftable"],
|
|
108
|
+
arity: { kind: "exact" },
|
|
109
|
+
returnType: "T",
|
|
110
|
+
};
|
|
111
|
+
// --- Hyperlink ---
|
|
112
|
+
//
|
|
113
|
+
// `link` is the cell-splitter for the multi-cell consumer contract.
|
|
114
|
+
// Implementation-wise it is the same shape as any other style function:
|
|
115
|
+
// it sets the `link` slot of `Style` exactly as the existing string-form
|
|
116
|
+
// `link URL` does, so a template-built fragment is byte-equivalent to
|
|
117
|
+
// `RichText("x", { style: Style.parse("link u") })`.
|
|
118
|
+
//
|
|
119
|
+
// The cell-boundary signal that consumers (cc-candybar et al.) walk is
|
|
120
|
+
// `fragment.style.link` being truthy. `Style.add` propagates `link`
|
|
121
|
+
// through any outer wrapping call, so `{{ fg "red" (link "u" "x") }}` and
|
|
122
|
+
// `{{ link "u" "x" }}` produce shapes that both qualify as cells from
|
|
123
|
+
// the consumer's perspective. Outer-wins on nested links comes for free
|
|
124
|
+
// from `Style.add`'s right-wins-on-conflict rule.
|
|
125
|
+
//
|
|
126
|
+
// [LAW:dataflow-not-control-flow] No special-case AST node, no
|
|
127
|
+
// `LinkFragment` subclass, no second emitter path — the variability is
|
|
128
|
+
// the value of `style.link`, not whether some control-flow branch ran.
|
|
129
|
+
const linkFunc = {
|
|
130
|
+
fn: ((url, child) => {
|
|
131
|
+
return applyStyleToFragment(child, new Style({ link: url }));
|
|
132
|
+
}),
|
|
133
|
+
argTypes: ["string", "liftable"],
|
|
134
|
+
arity: { kind: "exact" },
|
|
135
|
+
returnType: "T",
|
|
136
|
+
};
|
|
137
|
+
// --- Public assembly ---
|
|
138
|
+
/**
|
|
139
|
+
* Text-styling registrations: the two colour sinks (`fg`, `bg`), text
|
|
140
|
+
* attributes (canonical names, short aliases, and `not_*` negations), the
|
|
141
|
+
* hyperlink cell-splitter (`link`), and the multi-attribute `style` spec.
|
|
142
|
+
*
|
|
143
|
+
* Colours themselves come from elsewhere: `colorFuncs()` for the palette-free
|
|
144
|
+
* math, `paletteFuncs()` for naming a theme colour. This module only paints.
|
|
145
|
+
* [LAW:one-way-deps] — nothing here imports a palette.
|
|
146
|
+
*/
|
|
147
|
+
export function richTextStyleFuncs() {
|
|
148
|
+
return {
|
|
149
|
+
fg: colorSinkFunc("color"),
|
|
150
|
+
bg: colorSinkFunc("bgcolor"),
|
|
151
|
+
...attributeFuncs(),
|
|
152
|
+
link: linkFunc,
|
|
153
|
+
style: styleSpecFunc,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { ColorRgba } from "../core/color.js";
|
|
2
|
+
import { Palette } from "./palette.js";
|
|
3
|
+
/**
|
|
4
|
+
* Base colors required to build a full semantic palette.
|
|
5
|
+
* All are ColorRgba — callers construct from whatever source (hex, HSL, etc.).
|
|
6
|
+
*/
|
|
7
|
+
export interface BaseColors {
|
|
8
|
+
primary: ColorRgba;
|
|
9
|
+
secondary: ColorRgba;
|
|
10
|
+
accent: ColorRgba;
|
|
11
|
+
success: ColorRgba;
|
|
12
|
+
warning: ColorRgba;
|
|
13
|
+
error: ColorRgba;
|
|
14
|
+
background: ColorRgba;
|
|
15
|
+
foreground: ColorRgba;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Build a full semantic palette from base colors.
|
|
19
|
+
*
|
|
20
|
+
* Derived entries follow Textual's formulas:
|
|
21
|
+
* - `*-muted` = color blended 70% toward its opposite base role. Defined for
|
|
22
|
+
* every accent AND for the two base roles themselves
|
|
23
|
+
* (`foreground-muted` blends toward `background`,
|
|
24
|
+
* `background-muted` blends toward `foreground`) — a caller
|
|
25
|
+
* de-emphasizing body/structural text reaches for
|
|
26
|
+
* `foreground-muted` the same way it reaches for
|
|
27
|
+
* `primary-muted` to de-emphasize an accent.
|
|
28
|
+
* - `text-*` = contrast text tinted 66% with the accent color (use as
|
|
29
|
+
* foreground in muted/background-tinted contexts)
|
|
30
|
+
* - `on-*` = WCAG-correct contrast colour (black or white) for use as
|
|
31
|
+
* foreground when the FULL accent is the background. Picked
|
|
32
|
+
* by relative luminance — single source of truth so widgets
|
|
33
|
+
* never need to invert / reverse fg/bg to get readable text.
|
|
34
|
+
* - `surface` = background blended 5% toward foreground
|
|
35
|
+
*/
|
|
36
|
+
export declare function buildPalette(name: string, dark: boolean, base: BaseColors): Palette;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { blendRgb } from "../core/color.js";
|
|
2
|
+
import { alphaBlend, contrastFor } from "./colorMath.js";
|
|
3
|
+
import { Palette } from "./palette.js";
|
|
4
|
+
const MUTED_BLEND = 0.7;
|
|
5
|
+
const TEXT_ALPHA = 0.66;
|
|
6
|
+
const SURFACE_LIFT = 0.05;
|
|
7
|
+
const ACCENT_KEYS = ["primary", "secondary", "accent", "success", "warning", "error"];
|
|
8
|
+
/**
|
|
9
|
+
* Build a full semantic palette from base colors.
|
|
10
|
+
*
|
|
11
|
+
* Derived entries follow Textual's formulas:
|
|
12
|
+
* - `*-muted` = color blended 70% toward its opposite base role. Defined for
|
|
13
|
+
* every accent AND for the two base roles themselves
|
|
14
|
+
* (`foreground-muted` blends toward `background`,
|
|
15
|
+
* `background-muted` blends toward `foreground`) — a caller
|
|
16
|
+
* de-emphasizing body/structural text reaches for
|
|
17
|
+
* `foreground-muted` the same way it reaches for
|
|
18
|
+
* `primary-muted` to de-emphasize an accent.
|
|
19
|
+
* - `text-*` = contrast text tinted 66% with the accent color (use as
|
|
20
|
+
* foreground in muted/background-tinted contexts)
|
|
21
|
+
* - `on-*` = WCAG-correct contrast colour (black or white) for use as
|
|
22
|
+
* foreground when the FULL accent is the background. Picked
|
|
23
|
+
* by relative luminance — single source of truth so widgets
|
|
24
|
+
* never need to invert / reverse fg/bg to get readable text.
|
|
25
|
+
* - `surface` = background blended 5% toward foreground
|
|
26
|
+
*/
|
|
27
|
+
export function buildPalette(name, dark, base) {
|
|
28
|
+
const vars = new Map();
|
|
29
|
+
// Base entries
|
|
30
|
+
vars.set("background", base.background);
|
|
31
|
+
vars.set("foreground", base.foreground);
|
|
32
|
+
for (const key of ACCENT_KEYS) {
|
|
33
|
+
vars.set(key, base[key]);
|
|
34
|
+
}
|
|
35
|
+
// Muted variants of the two base roles, same "blend 70% toward
|
|
36
|
+
// background" formula as the accent `*-muted` family below — every
|
|
37
|
+
// base color the palette defines gets a de-emphasized variant, not just
|
|
38
|
+
// the accents. `foreground-muted` is what a caller reaches for to draw
|
|
39
|
+
// structural/contextual text (labels, punctuation, ids) at reduced visual
|
|
40
|
+
// weight without a terminal-support-dependent SGR attribute.
|
|
41
|
+
vars.set("foreground-muted", blendRgb(base.foreground, base.background, MUTED_BLEND));
|
|
42
|
+
vars.set("background-muted", blendRgb(base.background, base.foreground, MUTED_BLEND));
|
|
43
|
+
// Surface — subtle lift from background
|
|
44
|
+
vars.set("surface", blendRgb(base.background, base.foreground, SURFACE_LIFT));
|
|
45
|
+
// Derived: muted, text-, and on- for each accent.
|
|
46
|
+
// [LAW:single-enforcer] contrastFor is the only place that decides
|
|
47
|
+
// black-vs-white for "text on this accent" — widgets read on-${accent}.
|
|
48
|
+
const contrastText = contrastFor(base.background);
|
|
49
|
+
for (const key of ACCENT_KEYS) {
|
|
50
|
+
const color = base[key];
|
|
51
|
+
vars.set(`${key}-muted`, blendRgb(color, base.background, MUTED_BLEND));
|
|
52
|
+
vars.set(`text-${key}`, alphaBlend(color, contrastText, TEXT_ALPHA));
|
|
53
|
+
vars.set(`on-${key}`, contrastFor(color));
|
|
54
|
+
}
|
|
55
|
+
return new Palette(name, dark, vars);
|
|
56
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { ColorDepth, ColorRgba, contrastRatio, relativeLuminance } from "../core/color.js";
|
|
2
|
+
/**
|
|
3
|
+
* Darken a color by N levels, where each level reduces HSL lightness by 10%.
|
|
4
|
+
* Negative levels lighten. Level 0 returns an equivalent triplet (after the
|
|
5
|
+
* RGB↔HSL roundtrip; values may differ by ±1 due to rounding).
|
|
6
|
+
*/
|
|
7
|
+
export declare function darken(color: ColorRgba, levels: number): ColorRgba;
|
|
8
|
+
/**
|
|
9
|
+
* Lighten a color by N levels. Equivalent to `darken(color, -levels)`.
|
|
10
|
+
*/
|
|
11
|
+
export declare function lighten(color: ColorRgba, levels: number): ColorRgba;
|
|
12
|
+
/**
|
|
13
|
+
* Composite `fg` over `bg` with the given alpha (0..1). At alpha=0 returns bg;
|
|
14
|
+
* at alpha=1 returns fg.
|
|
15
|
+
*/
|
|
16
|
+
export declare function alphaBlend(fg: ColorRgba, bg: ColorRgba, alpha: number): ColorRgba;
|
|
17
|
+
/**
|
|
18
|
+
* Pick a contrasting foreground (black or white) for a background, using the
|
|
19
|
+
* WCAG relative-luminance threshold of 0.179 (the perceptually correct cutoff
|
|
20
|
+
* where black and white are equally readable). A translucent `bg` is judged
|
|
21
|
+
* as drawn: composited over `substrate` (see `drawnBackground`).
|
|
22
|
+
*/
|
|
23
|
+
export declare function contrastFor(bg: ColorRgba, substrate?: ColorRgba): ColorRgba;
|
|
24
|
+
export { relativeLuminance, contrastRatio };
|
|
25
|
+
/**
|
|
26
|
+
* Return a foreground guaranteed to clear `minRatio` against `bg`, keeping the
|
|
27
|
+
* color *recognizably itself*. If the themed `fg` already passes it is returned
|
|
28
|
+
* untouched. Otherwise its OKLCH lightness is slid toward the pole that raises
|
|
29
|
+
* contrast — holding hue, and chroma where it stays in gamut (near the poles
|
|
30
|
+
* gamut clamping may reduce chroma, but hue is preserved) — until the ratio is
|
|
31
|
+
* met, so a blue on a dark-blue background becomes a lighter blue, not white.
|
|
32
|
+
* Only when no lightness of that hue can meet the ratio (a mid-toned
|
|
33
|
+
* background where even pure black-or-white tops out below the target) does it
|
|
34
|
+
* fall back to `contrastFor`'s black/white — the true maximum-contrast pick.
|
|
35
|
+
*
|
|
36
|
+
* A translucent `bg` is measured as it is drawn — composited over
|
|
37
|
+
* `substrate`, the SGR writer's black by default — and a translucent `fg` is
|
|
38
|
+
* then flattened over that drawn background, the order the writer composites
|
|
39
|
+
* in, so the ratio is measured on what the eye actually sees and the returned
|
|
40
|
+
* color is opaque.
|
|
41
|
+
*
|
|
42
|
+
* `drawnAt` is the depth the terminal will draw the pair at. At 256 colours
|
|
43
|
+
* the terminal rounds text and background independently, and two roundings
|
|
44
|
+
* can meet in the middle, so the ratio is measured on the drawn pair: a
|
|
45
|
+
* colour that loses the floor there is replaced by the nearest cube/grey entry
|
|
46
|
+
* that clears it (whose own rounding is itself). At ANSI the terminal draws
|
|
47
|
+
* its own theme's colours, so the ratio is measured on the table's nominal
|
|
48
|
+
* ones (`DRAWN_FROM`): they say which side of the ground text belongs on, and
|
|
49
|
+
* text on its background's index measures 1:1 and is always replaced.
|
|
50
|
+
* Truecolor draws the colour chosen.
|
|
51
|
+
*
|
|
52
|
+
* [LAW:single-enforcer] The one place "is this text readable, and if not fix
|
|
53
|
+
* it" is decided. Callers route every fg/bg pair through here and the
|
|
54
|
+
* unreadable state never reaches output. [LAW:dataflow-not-control-flow] the
|
|
55
|
+
* function always runs; the measured ratio (data) decides how far the
|
|
56
|
+
* lightness moves — there is no caller-side "should I check contrast" branch.
|
|
57
|
+
*/
|
|
58
|
+
export declare function ensureContrast(fg: ColorRgba, bg: ColorRgba, minRatio?: number, // WCAG AA for normal text
|
|
59
|
+
drawnAt?: ColorDepth, substrate?: ColorRgba): ColorRgba;
|
|
60
|
+
/**
|
|
61
|
+
* `chosen` (composited opaque over `substrate`), or — when the depth the
|
|
62
|
+
* terminal draws at rounds it to a colour `accept` refuses — the nearest colour that depth draws as itself which
|
|
63
|
+
* `accept` takes. `accept` sees the candidate as drawn, and `drawn`, the same
|
|
64
|
+
* rounding for any other colour it measures against, so the caller states a
|
|
65
|
+
* floor once and it holds on the colours the terminal shows — at ANSI, on
|
|
66
|
+
* the table's nominal colours, as `ensureContrast` measures there. Truecolor
|
|
67
|
+
* draws from no table, so a colour refused there has no replacement;
|
|
68
|
+
* `undefined` means nothing the depth draws is accepted.
|
|
69
|
+
*
|
|
70
|
+
* `ensureContrast` is this with a contrast ratio as the floor; this is for a
|
|
71
|
+
* floor that is not text on its background (an open state standing off every
|
|
72
|
+
* closed cell, two planes standing off each other).
|
|
73
|
+
*/
|
|
74
|
+
export declare function ensureDrawn(chosen: ColorRgba, drawnAt: ColorDepth, accept: (candidate: ColorRgba, drawn: (c: ColorRgba) => ColorRgba) => boolean, substrate?: ColorRgba): ColorRgba | undefined;
|
|
75
|
+
/**
|
|
76
|
+
* The colour a ground is shown as at `drawnAt`: composited over `substrate`
|
|
77
|
+
* (the SGR writer's black by default), then rounded to the table that depth
|
|
78
|
+
* draws from — at ANSI, that table's nominal colour stands in for the
|
|
79
|
+
* theme's. The one account `ensureDrawn` and `ensureContrast` measure by, for
|
|
80
|
+
* a caller that measures a floor of its own.
|
|
81
|
+
*/
|
|
82
|
+
export declare function drawnColour(colour: ColorRgba, drawnAt: ColorDepth, substrate?: ColorRgba): ColorRgba;
|