@promptctl/rich-js 0.18.1 → 0.20.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.
- package/README.md +11 -8
- package/dist/core/box.d.ts +3 -1
- package/dist/core/box.js +6 -13
- package/dist/core/cells.d.ts +61 -28
- package/dist/core/cells.js +171 -124
- package/dist/core/clock.d.ts +48 -0
- package/dist/core/clock.js +42 -0
- package/dist/core/color.d.ts +99 -18
- package/dist/core/color.js +334 -146
- package/dist/core/console.d.ts +38 -7
- package/dist/core/console.js +159 -90
- package/dist/core/easing.d.ts +86 -0
- package/dist/core/easing.js +171 -0
- package/dist/core/env.d.ts +16 -0
- package/dist/core/env.js +16 -0
- package/dist/core/export-html.js +1 -4
- package/dist/core/export-lines.d.ts +22 -13
- package/dist/core/export-lines.js +37 -19
- package/dist/core/export-svg.d.ts +49 -0
- package/dist/core/export-svg.js +213 -0
- package/dist/core/fnv1a.d.ts +10 -0
- package/dist/core/fnv1a.js +17 -0
- package/dist/core/highlighter.d.ts +2 -2
- package/dist/core/highlighter.js +113 -47
- package/dist/core/json.js +1 -1
- package/dist/core/markup.d.ts +28 -7
- package/dist/core/markup.js +235 -312
- package/dist/core/measure.d.ts +3 -3
- package/dist/core/measure.js +12 -7
- package/dist/core/memo.d.ts +19 -0
- package/dist/core/memo.js +34 -0
- package/dist/core/noise.d.ts +31 -0
- package/dist/core/noise.js +94 -0
- package/dist/core/osc8.d.ts +6 -6
- package/dist/core/osc8.js +9 -21
- package/dist/core/paint.d.ts +107 -0
- package/dist/core/paint.js +161 -0
- package/dist/core/place.d.ts +1 -1
- package/dist/core/place.js +4 -6
- package/dist/core/pretty.d.ts +126 -2
- package/dist/core/pretty.js +571 -105
- package/dist/core/protocol.d.ts +43 -3
- package/dist/core/protocol.js +23 -2
- package/dist/core/render.d.ts +33 -30
- package/dist/core/render.js +43 -83
- package/dist/core/segment.d.ts +6 -5
- package/dist/core/segment.js +11 -15
- package/dist/core/selection.d.ts +12 -0
- package/dist/core/selection.js +14 -0
- package/dist/core/strip.d.ts +116 -74
- package/dist/core/strip.js +266 -217
- package/dist/core/style.d.ts +27 -7
- package/dist/core/style.js +106 -41
- package/dist/core/text.d.ts +43 -16
- package/dist/core/text.js +180 -59
- package/dist/core/wrap.d.ts +5 -0
- package/dist/core/wrap.js +16 -3
- package/dist/host/app.d.ts +95 -17
- package/dist/host/app.js +201 -61
- package/dist/host/index.d.ts +4 -1
- package/dist/host/index.js +1 -0
- package/dist/host/input.d.ts +84 -0
- package/dist/host/input.js +498 -0
- package/dist/host/terminal-host.d.ts +15 -2
- package/dist/host/terminal-host.js +2 -0
- package/dist/index.d.ts +26 -14
- package/dist/index.js +17 -7
- package/dist/node/prompt.d.ts +13 -0
- package/dist/node/prompt.js +30 -9
- package/dist/node/save.d.ts +4 -3
- package/dist/node/save.js +6 -3
- package/dist/node/terminal-host.d.ts +10 -2
- package/dist/node/terminal-host.js +7 -0
- package/dist/node/traceback.d.ts +2 -1
- package/dist/node/traceback.js +3 -39
- package/dist/renderables/align.js +2 -9
- package/dist/renderables/columns.d.ts +45 -29
- package/dist/renderables/columns.js +142 -103
- package/dist/renderables/constrain.js +6 -12
- package/dist/renderables/effect.d.ts +113 -0
- package/dist/renderables/effect.js +182 -0
- package/dist/renderables/effects.d.ts +252 -0
- package/dist/renderables/effects.js +565 -0
- package/dist/renderables/embed.d.ts +94 -31
- package/dist/renderables/embed.js +141 -45
- package/dist/renderables/group.d.ts +6 -3
- package/dist/renderables/group.js +5 -0
- package/dist/renderables/layout.d.ts +6 -23
- package/dist/renderables/layout.js +24 -113
- package/dist/renderables/list-view.d.ts +42 -0
- package/dist/renderables/list-view.js +78 -0
- package/dist/renderables/live.d.ts +40 -16
- package/dist/renderables/live.js +96 -83
- package/dist/renderables/markdown.d.ts +16 -2
- package/dist/renderables/markdown.js +521 -118
- package/dist/renderables/padding.d.ts +22 -1
- package/dist/renderables/padding.js +30 -24
- package/dist/renderables/panel.d.ts +31 -33
- package/dist/renderables/panel.js +137 -141
- package/dist/renderables/progress.d.ts +110 -16
- package/dist/renderables/progress.js +212 -81
- package/dist/renderables/progressBar.d.ts +24 -4
- package/dist/renderables/progressBar.js +78 -23
- package/dist/renderables/prompt.d.ts +37 -15
- package/dist/renderables/prompt.js +125 -85
- package/dist/renderables/ratio.d.ts +123 -0
- package/dist/renderables/ratio.js +213 -0
- package/dist/renderables/rule.d.ts +3 -2
- package/dist/renderables/rule.js +50 -52
- package/dist/renderables/spinner.d.ts +28 -2
- package/dist/renderables/spinner.js +52 -19
- package/dist/renderables/status.d.ts +13 -7
- package/dist/renderables/status.js +15 -29
- package/dist/renderables/syntax.d.ts +37 -7
- package/dist/renderables/syntax.js +134 -83
- package/dist/renderables/table.d.ts +58 -20
- package/dist/renderables/table.js +373 -197
- package/dist/renderables/traceback.d.ts +5 -4
- package/dist/renderables/traceback.js +239 -68
- package/dist/renderables/tree.d.ts +10 -2
- package/dist/renderables/tree.js +61 -34
- package/dist/renderables/viewport.d.ts +30 -0
- package/dist/renderables/viewport.js +61 -7
- package/dist/template-bindings/color-funcs.d.ts +31 -9
- package/dist/template-bindings/color-funcs.js +40 -14
- package/dist/template-bindings/helpers.d.ts +4 -2
- package/dist/template-bindings/helpers.js +5 -20
- package/dist/template-bindings/index.d.ts +29 -17
- package/dist/template-bindings/index.js +36 -20
- package/dist/template-bindings/palette-funcs.d.ts +3 -1
- package/dist/template-bindings/palette-funcs.js +11 -7
- package/dist/template-bindings/style-funcs.js +4 -2
- package/dist/themes/buildPalette.d.ts +24 -3
- package/dist/themes/buildPalette.js +52 -8
- package/dist/themes/colorMath.d.ts +29 -17
- package/dist/themes/colorMath.js +74 -48
- package/dist/themes/colorRef.d.ts +2 -17
- package/dist/themes/colorRef.js +13 -31
- package/dist/themes/palette.d.ts +24 -0
- package/dist/themes/palette.js +46 -3
- package/dist/themes/ramp.d.ts +21 -34
- package/dist/themes/ramp.js +23 -42
- package/dist/themes/registry.js +39 -28
- package/dist/widgets/button.d.ts +2 -6
- package/dist/widgets/button.js +9 -40
- package/dist/widgets/checkbox.d.ts +2 -5
- package/dist/widgets/checkbox.js +9 -31
- package/dist/widgets/dropdown.d.ts +2 -5
- package/dist/widgets/dropdown.js +17 -51
- package/dist/widgets/event-router.d.ts +31 -40
- package/dist/widgets/event-router.js +52 -483
- package/dist/widgets/index.d.ts +2 -1
- package/dist/widgets/ink.d.ts +22 -0
- package/dist/widgets/ink.js +31 -0
- package/dist/widgets/slider.d.ts +2 -5
- package/dist/widgets/slider.js +11 -34
- package/dist/widgets/text-input.d.ts +19 -10
- package/dist/widgets/text-input.js +156 -147
- package/dist/widgets/themed-widget.d.ts +20 -0
- package/dist/widgets/themed-widget.js +89 -0
- package/dist/widgets/toggle.d.ts +2 -5
- package/dist/widgets/toggle.js +8 -31
- package/dist/widgets/types.d.ts +8 -25
- package/dist/widgets/types.js +8 -0
- package/dist/widgets/widget-app.d.ts +24 -7
- package/dist/widgets/widget-app.js +35 -18
- package/dist/widgets/widget-base.d.ts +2 -0
- package/dist/widgets/widget-base.js +9 -2
- package/package.json +31 -17
|
@@ -35,7 +35,7 @@ import { Segment } from "../core/segment.js";
|
|
|
35
35
|
import { Measurement } from "../core/measure.js";
|
|
36
36
|
import { cellCount, cellLen } from "../core/cells.js";
|
|
37
37
|
import { NULL_STYLE } from "../core/style.js";
|
|
38
|
-
import { drawable, fitHeight, getStyle,
|
|
38
|
+
import { drawable, fitHeight, getStyle, regionRows, withBoundedWidth, withCellWidth, } from "../core/protocol.js";
|
|
39
39
|
/** A heavy line for the thumb on a light line for the track. */
|
|
40
40
|
export const SCROLLBAR = {
|
|
41
41
|
thumb: { glyph: "┃", style: "scrollbar.thumb" },
|
|
@@ -58,6 +58,8 @@ export class Viewport {
|
|
|
58
58
|
// Nothing rendered, nothing shown: no move can take until the first render.
|
|
59
59
|
_extent = { rows: 0, lines: 0 };
|
|
60
60
|
_moves = [];
|
|
61
|
+
// No range followed yet: NaN equals nothing, so the first `follow` moves.
|
|
62
|
+
_followed = { start: NaN, end: NaN, rows: NaN };
|
|
61
63
|
constructor(content, options = {}) {
|
|
62
64
|
this.content = content;
|
|
63
65
|
this.rows = options.rows;
|
|
@@ -96,20 +98,47 @@ export class Viewport {
|
|
|
96
98
|
ensureVisible(start, end) {
|
|
97
99
|
this._moves.push((offset, { rows }) => Math.min(start, Math.max(offset, end - rows)));
|
|
98
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* `ensureVisible(start, end)`, when the range differs from the one the last
|
|
103
|
+
* `follow` named. A view rebuilt every frame calls this in every frame with
|
|
104
|
+
* its selection's lines, and the selection is brought into view when it
|
|
105
|
+
* moves — not on every frame, which would undo on its very render each
|
|
106
|
+
* scroll the mouse wheel queues. A followed range the last render showed
|
|
107
|
+
* whole stays shown when the row count changes: the same lines can fall off
|
|
108
|
+
* the bottom of a viewport that got shorter. One the wheel had scrolled
|
|
109
|
+
* away from stays away.
|
|
110
|
+
*/
|
|
111
|
+
follow(start, end) {
|
|
112
|
+
if (start === this._followed.start && end === this._followed.end)
|
|
113
|
+
return;
|
|
114
|
+
this._followed = { start, end, rows: NaN };
|
|
115
|
+
this.ensureVisible(start, end);
|
|
116
|
+
}
|
|
99
117
|
/**
|
|
100
118
|
* The width its content renders at, out of `width`: what the scrollbar's
|
|
101
119
|
* gutter leaves. Lines handed to `ensureVisible` are lines at this width.
|
|
102
120
|
*/
|
|
103
121
|
contentWidth(width) {
|
|
104
|
-
return cellCount(width -
|
|
122
|
+
return cellCount(width - this.gutter);
|
|
123
|
+
}
|
|
124
|
+
/** The cells the scrollbar's gutter takes beside the content. */
|
|
125
|
+
get gutter() {
|
|
126
|
+
return gutterWidth(this.scrollbar);
|
|
105
127
|
}
|
|
106
128
|
*render(rawOptions) {
|
|
107
129
|
const { height, ...options } = withBoundedWidth(rawOptions, this);
|
|
108
|
-
const gutter =
|
|
130
|
+
const gutter = this.gutter;
|
|
109
131
|
const contentWidth = this.contentWidth(options.maxWidth);
|
|
110
132
|
const lines = Segment.splitLines(this.content.render({ ...options, maxWidth: contentWidth }));
|
|
111
133
|
const extent = { rows: viewRows(height, this.rows, lines.length), lines: lines.length };
|
|
134
|
+
const followed = this._followed;
|
|
135
|
+
const wasShown = followed.start >= this._offset && followed.end <= this._offset + this._extent.rows;
|
|
112
136
|
this._extent = extent;
|
|
137
|
+
if (!Number.isNaN(followed.rows) && followed.rows !== extent.rows && wasShown) {
|
|
138
|
+
this.ensureVisible(followed.start, followed.end);
|
|
139
|
+
}
|
|
140
|
+
if (!Number.isNaN(followed.start))
|
|
141
|
+
this._followed = { ...followed, rows: extent.rows };
|
|
113
142
|
this._offset = this.resolve(extent);
|
|
114
143
|
this._moves = [];
|
|
115
144
|
const shown = fitHeight(lines.slice(this._offset, this._offset + extent.rows), { rows: extent.rows, exact: true });
|
|
@@ -153,14 +182,39 @@ export class Viewport {
|
|
|
153
182
|
// `withBoundedWidth`: the bounded parse asks this very method for the
|
|
154
183
|
// natural width.
|
|
155
184
|
const options = withCellWidth(rawOptions);
|
|
156
|
-
const gutter =
|
|
185
|
+
const gutter = this.gutter;
|
|
157
186
|
const inner = { ...options, maxWidth: this.contentWidth(options.maxWidth) };
|
|
158
|
-
const content =
|
|
159
|
-
? Measurement.get(inner, this.content)
|
|
160
|
-
: new Measurement(Math.min(1, inner.maxWidth), inner.maxWidth);
|
|
187
|
+
const content = Measurement.get(inner, this.content);
|
|
161
188
|
return new Measurement(content.minimum + gutter, content.maximum + gutter).withMaximum(options.maxWidth);
|
|
162
189
|
}
|
|
163
190
|
}
|
|
191
|
+
/**
|
|
192
|
+
* One viewport per thing a pane shows, named by a key. The same viewport comes
|
|
193
|
+
* back while the key holds, so its scroll position does too; a new key gets a
|
|
194
|
+
* fresh one, so a new subject opens at its top rather than wherever the last
|
|
195
|
+
* one was left. A frame is rebuilt on every key press and a scroll position is
|
|
196
|
+
* a viewport's, so this is what outlives the frame for a pane whose subject
|
|
197
|
+
* changes under it.
|
|
198
|
+
*/
|
|
199
|
+
export class ViewportPerKey {
|
|
200
|
+
options;
|
|
201
|
+
held;
|
|
202
|
+
constructor(options = {}) {
|
|
203
|
+
this.options = options;
|
|
204
|
+
this.held = { key: "", viewport: this.fresh() };
|
|
205
|
+
}
|
|
206
|
+
/** The viewport for `key`: the held one while the key holds, a fresh one otherwise. */
|
|
207
|
+
of(key) {
|
|
208
|
+
if (key !== this.held.key)
|
|
209
|
+
this.held = { key, viewport: this.fresh() };
|
|
210
|
+
return this.held.viewport;
|
|
211
|
+
}
|
|
212
|
+
fresh() {
|
|
213
|
+
// Empty until a view hands it content: the content is built where the frame is.
|
|
214
|
+
return new Viewport(EMPTY, this.options);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
const EMPTY = { *render() { } };
|
|
164
218
|
const scrollbarGlyphs = ({ thumb, track }) => thumb.glyph + track.glyph;
|
|
165
219
|
/** `scrollbar` in its own styles, drawn in ASCII. */
|
|
166
220
|
function asciiScrollbar({ thumb, track }) {
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
*
|
|
27
27
|
* ### The carrier is a hex string, deliberately
|
|
28
28
|
*
|
|
29
|
-
* A color crosses the template seam as `#RRGGBB
|
|
29
|
+
* A color crosses the template seam as `#RRGGBB` (or `#RRGGBBAA`), not as an opaque object,
|
|
30
30
|
* for three reasons:
|
|
31
31
|
*
|
|
32
32
|
* 1. **The type separates.** `"string"` is the strictest arg slot the template
|
|
@@ -56,12 +56,34 @@
|
|
|
56
56
|
* the full color vocabulary by feeding it hex literals. [LAW:one-way-deps]
|
|
57
57
|
*/
|
|
58
58
|
import type { FuncMap, TemplateFunc } from "@promptctl/go-template-js";
|
|
59
|
-
import { ColorDepth } from "../core/color.js";
|
|
59
|
+
import { ColorDepth, type ColorRgba, type TerminalTheme } from "../core/color.js";
|
|
60
60
|
/**
|
|
61
|
-
*
|
|
62
|
-
*
|
|
61
|
+
* A colour the colour functions can compute with. A fully transparent one — a
|
|
62
|
+
* theme's "no background", `#00000000` — has no colour of its own: what shows
|
|
63
|
+
* through it is whatever surface it lands on, which a template cannot know.
|
|
64
|
+
* Darkening, mixing or contrasting it would answer for a surface nobody named,
|
|
65
|
+
* so it is refused here, for every colour function and every `ramp` stop.
|
|
66
|
+
* [LAW:single-enforcer]
|
|
63
67
|
*/
|
|
64
|
-
export declare
|
|
68
|
+
export declare function computable(colour: ColorRgba, func: string, written: string): ColorRgba;
|
|
69
|
+
/**
|
|
70
|
+
* Where `readableOn`'s text will be drawn: the depth the output is encoded at,
|
|
71
|
+
* and the terminal that draws it when that is known. At sixteen colours a
|
|
72
|
+
* terminal paints each slot in its own theme's colour, so the floor is measured
|
|
73
|
+
* in `terminal`'s sixteen; without one, in the VGA defaults (the Windows
|
|
74
|
+
* console's at `WINDOWS`). Both are facts about the one place the text lands,
|
|
75
|
+
* so they arrive together.
|
|
76
|
+
*/
|
|
77
|
+
export interface DrawnAt {
|
|
78
|
+
readonly depth: ColorDepth;
|
|
79
|
+
readonly terminal?: TerminalTheme;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* The `readableOn` binding, measuring where `drawnAt` says the text lands when
|
|
83
|
+
* it is evaluated — a consumer that renders to a per-call destination
|
|
84
|
+
* registers its own.
|
|
85
|
+
*/
|
|
86
|
+
export declare const readableOnFunc: (drawnAt: () => DrawnAt) => TemplateFunc;
|
|
65
87
|
/**
|
|
66
88
|
* The palette-free color vocabulary: every function takes colors and returns a
|
|
67
89
|
* color, so they compose by nesting.
|
|
@@ -80,8 +102,8 @@ export declare const readableOnFunc: (drawnAt: () => ColorDepth) => TemplateFunc
|
|
|
80
102
|
* Pair with `paletteFuncs()` to name colors from a theme, and with
|
|
81
103
|
* `richTextStyleFuncs()`'s `fg`/`bg` to paint them onto text.
|
|
82
104
|
*
|
|
83
|
-
* @param drawnAt
|
|
84
|
-
* `readableOn` evaluation so a host that learns
|
|
85
|
-
* one getter; defaults to truecolor.
|
|
105
|
+
* @param drawnAt where the text will be drawn — the depth, and the terminal
|
|
106
|
+
* when known — read on every `readableOn` evaluation so a host that learns
|
|
107
|
+
* its destination per render passes one getter; defaults to truecolor.
|
|
86
108
|
*/
|
|
87
|
-
export declare function colorFuncs(drawnAt?: () =>
|
|
109
|
+
export declare function colorFuncs(drawnAt?: () => DrawnAt): FuncMap;
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
*
|
|
27
27
|
* ### The carrier is a hex string, deliberately
|
|
28
28
|
*
|
|
29
|
-
* A color crosses the template seam as `#RRGGBB
|
|
29
|
+
* A color crosses the template seam as `#RRGGBB` (or `#RRGGBBAA`), not as an opaque object,
|
|
30
30
|
* for three reasons:
|
|
31
31
|
*
|
|
32
32
|
* 1. **The type separates.** `"string"` is the strictest arg slot the template
|
|
@@ -55,9 +55,8 @@
|
|
|
55
55
|
* from that point on the math is pure. A consumer with no theme system at all still gets
|
|
56
56
|
* the full color vocabulary by feeding it hex literals. [LAW:one-way-deps]
|
|
57
57
|
*/
|
|
58
|
-
import { blendRgb, ColorDepth } from "../core/color.js";
|
|
58
|
+
import { blendRgb, ColorDepth, ColorParseError, parseHexColor } from "../core/color.js";
|
|
59
59
|
import { Oklch, IDENTITY } from "../core/oklch.js";
|
|
60
|
-
import { HEX_COLOR_RE, parseHexColor } from "../themes/colorRef.js";
|
|
61
60
|
import { darken, contrastFor, ensureContrast } from "../themes/colorMath.js";
|
|
62
61
|
/**
|
|
63
62
|
* The gate every color argument crosses. [LAW:parse-dont-validate] — the
|
|
@@ -67,15 +66,37 @@ import { darken, contrastFor, ensureContrast } from "../themes/colorMath.js";
|
|
|
67
66
|
* its error *wording*, because the mistake it catches is almost always the
|
|
68
67
|
* same one — a palette name passed where a color belongs (`darken "primary" 2`)
|
|
69
68
|
* — and an author who reads "wrap it: darken (color "primary")" is corrected in
|
|
70
|
-
* one step, where "expected #RRGGBB" leaves them guessing.
|
|
69
|
+
* one step, where "expected #RRGGBB" leaves them guessing. What it parses
|
|
70
|
+
* must then be a colour these functions can compute with (`computable`).
|
|
71
71
|
* [LAW:no-silent-failure]
|
|
72
72
|
*/
|
|
73
73
|
function asColor(value, func) {
|
|
74
|
-
|
|
74
|
+
let colour;
|
|
75
|
+
try {
|
|
76
|
+
colour = parseHexColor(value.trim());
|
|
77
|
+
}
|
|
78
|
+
catch (cause) {
|
|
79
|
+
if (!(cause instanceof ColorParseError))
|
|
80
|
+
throw cause;
|
|
75
81
|
throw new TypeError(`${func} expected a color (#RRGGBB or #RRGGBBAA), got ${JSON.stringify(value)}` +
|
|
76
|
-
` — to use a palette name here, wrap it: ${func} (color ${JSON.stringify(value)})
|
|
82
|
+
` — to use a palette name here, wrap it: ${func} (color ${JSON.stringify(value)}) …`, { cause });
|
|
83
|
+
}
|
|
84
|
+
return computable(colour, func, JSON.stringify(value));
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* A colour the colour functions can compute with. A fully transparent one — a
|
|
88
|
+
* theme's "no background", `#00000000` — has no colour of its own: what shows
|
|
89
|
+
* through it is whatever surface it lands on, which a template cannot know.
|
|
90
|
+
* Darkening, mixing or contrasting it would answer for a surface nobody named,
|
|
91
|
+
* so it is refused here, for every colour function and every `ramp` stop.
|
|
92
|
+
* [LAW:single-enforcer]
|
|
93
|
+
*/
|
|
94
|
+
export function computable(colour, func, written) {
|
|
95
|
+
if (colour.alpha === 0) {
|
|
96
|
+
throw new RangeError(`${func} got ${written}, which is fully transparent: it draws no colour of its own, ` +
|
|
97
|
+
`so there is nothing to compute with — use the colour of the surface it sits on, e.g. (color "background")`);
|
|
77
98
|
}
|
|
78
|
-
return
|
|
99
|
+
return colour;
|
|
79
100
|
}
|
|
80
101
|
// [LAW:single-enforcer] Every colour function is fixed-arity, so the engine's
|
|
81
102
|
// arity gate refuses a missing or surplus argument before the body runs.
|
|
@@ -145,11 +166,16 @@ const RATIO_RANGE = {
|
|
|
145
166
|
note: "4.5 = AA body text, 3 = AA large text",
|
|
146
167
|
};
|
|
147
168
|
const contrastOnFunc = colorFunc(["string"], ((bgHex) => contrastFor(asColor(bgHex, "contrastOn")).hex));
|
|
169
|
+
const TRUECOLOR = { depth: ColorDepth.TRUECOLOR };
|
|
148
170
|
/**
|
|
149
|
-
* The `readableOn` binding, measuring
|
|
150
|
-
* evaluated — a consumer that renders
|
|
171
|
+
* The `readableOn` binding, measuring where `drawnAt` says the text lands when
|
|
172
|
+
* it is evaluated — a consumer that renders to a per-call destination
|
|
173
|
+
* registers its own.
|
|
151
174
|
*/
|
|
152
|
-
export const readableOnFunc = (drawnAt) => colorFunc(["string", "string", "float"], ((fgHex, bgHex, ratio) =>
|
|
175
|
+
export const readableOnFunc = (drawnAt) => colorFunc(["string", "string", "float"], ((fgHex, bgHex, ratio) => {
|
|
176
|
+
const { depth, terminal } = drawnAt();
|
|
177
|
+
return ensureContrast(asColor(fgHex, "readableOn"), asColor(bgHex, "readableOn"), asAmount(ratio, "readableOn", "ratio", RATIO_RANGE), depth, undefined, terminal).hex;
|
|
178
|
+
}));
|
|
153
179
|
// --- OKLCH axes ---
|
|
154
180
|
//
|
|
155
181
|
// [LAW:one-source-of-truth] One function per `ThemeKey` axis, and the table is
|
|
@@ -201,11 +227,11 @@ function oklchAxisFuncs() {
|
|
|
201
227
|
* Pair with `paletteFuncs()` to name colors from a theme, and with
|
|
202
228
|
* `richTextStyleFuncs()`'s `fg`/`bg` to paint them onto text.
|
|
203
229
|
*
|
|
204
|
-
* @param drawnAt
|
|
205
|
-
* `readableOn` evaluation so a host that learns
|
|
206
|
-
* one getter; defaults to truecolor.
|
|
230
|
+
* @param drawnAt where the text will be drawn — the depth, and the terminal
|
|
231
|
+
* when known — read on every `readableOn` evaluation so a host that learns
|
|
232
|
+
* its destination per render passes one getter; defaults to truecolor.
|
|
207
233
|
*/
|
|
208
|
-
export function colorFuncs(drawnAt = () =>
|
|
234
|
+
export function colorFuncs(drawnAt = () => TRUECOLOR) {
|
|
209
235
|
return {
|
|
210
236
|
darken: darkenFunc,
|
|
211
237
|
lighten: lightenFunc,
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* registrations and the palette-function registrations use this same operation;
|
|
7
7
|
* keeping one copy prevents the two from drifting.
|
|
8
8
|
*/
|
|
9
|
-
import { Style } from "../core/style.js";
|
|
9
|
+
import { type Style } from "../core/style.js";
|
|
10
10
|
import { RichText } from "../core/text.js";
|
|
11
11
|
/**
|
|
12
12
|
* Apply a style on top of an already-styled RichText fragment.
|
|
@@ -19,6 +19,8 @@ import { RichText } from "../core/text.js";
|
|
|
19
19
|
* is the binding's own `T`. Misuse (`{{ red someMap }}`) fails loudly here
|
|
20
20
|
* rather than producing a malformed fragment.
|
|
21
21
|
*
|
|
22
|
-
* Conflict resolution: `Style.add` — the outer (newly applied) style wins.
|
|
22
|
+
* Conflict resolution: `Style.add` — the outer (newly applied) style wins. A
|
|
23
|
+
* base style that is a name is layered under, not resolved: a template runs
|
|
24
|
+
* before the render whose theme says what the name stands for.
|
|
23
25
|
*/
|
|
24
26
|
export declare function applyStyleToFragment(child: unknown, style: Style): RichText;
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* registrations and the palette-function registrations use this same operation;
|
|
7
7
|
* keeping one copy prevents the two from drifting.
|
|
8
8
|
*/
|
|
9
|
-
import {
|
|
9
|
+
import { layerStyle } from "../core/style.js";
|
|
10
10
|
import { RichText } from "../core/text.js";
|
|
11
11
|
/**
|
|
12
12
|
* Apply a style on top of an already-styled RichText fragment.
|
|
@@ -19,30 +19,15 @@ import { RichText } from "../core/text.js";
|
|
|
19
19
|
* is the binding's own `T`. Misuse (`{{ red someMap }}`) fails loudly here
|
|
20
20
|
* rather than producing a malformed fragment.
|
|
21
21
|
*
|
|
22
|
-
* Conflict resolution: `Style.add` — the outer (newly applied) style wins.
|
|
22
|
+
* Conflict resolution: `Style.add` — the outer (newly applied) style wins. A
|
|
23
|
+
* base style that is a name is layered under, not resolved: a template runs
|
|
24
|
+
* before the render whose theme says what the name stands for.
|
|
23
25
|
*/
|
|
24
26
|
export function applyStyleToFragment(child, style) {
|
|
25
27
|
if (!(child instanceof RichText)) {
|
|
26
28
|
throw new TypeError(`template function expected a RichText fragment, got ${typeof child === "object" ? Object.prototype.toString.call(child) : typeof child}`);
|
|
27
29
|
}
|
|
28
30
|
const result = child.copy();
|
|
29
|
-
result.style =
|
|
31
|
+
result.style = layerStyle(child.style, style);
|
|
30
32
|
return result;
|
|
31
33
|
}
|
|
32
|
-
/**
|
|
33
|
-
* A fragment's base style as a `Style`. A string must parse as a definition: a
|
|
34
|
-
* name resolves only against the theme of the render that draws it, and a
|
|
35
|
-
* template runs before any render exists.
|
|
36
|
-
*/
|
|
37
|
-
function baseStyleOf(style) {
|
|
38
|
-
if (style instanceof Style)
|
|
39
|
-
return style;
|
|
40
|
-
try {
|
|
41
|
-
return Style.parse(style);
|
|
42
|
-
}
|
|
43
|
-
catch (err) {
|
|
44
|
-
if (!(err instanceof StyleSyntaxError))
|
|
45
|
-
throw err;
|
|
46
|
-
throw new TypeError(`template function cannot style a fragment whose base style "${style}" is not a style definition: a style name resolves only against the theme of a render`, { cause: err });
|
|
47
|
-
}
|
|
48
|
-
}
|
|
@@ -36,9 +36,10 @@
|
|
|
36
36
|
import { type Engine, type FuncMap } from "@promptctl/go-template-js";
|
|
37
37
|
import { RichText } from "../core/text.js";
|
|
38
38
|
import { Segment } from "../core/segment.js";
|
|
39
|
-
import {
|
|
39
|
+
import type { RenderOptions } from "../core/protocol.js";
|
|
40
|
+
import { type DrawnAt } from "./color-funcs.js";
|
|
40
41
|
export { paletteFuncs } from "./palette-funcs.js";
|
|
41
|
-
export { colorFuncs, readableOnFunc } from "./color-funcs.js";
|
|
42
|
+
export { colorFuncs, readableOnFunc, type DrawnAt } from "./color-funcs.js";
|
|
42
43
|
/**
|
|
43
44
|
* Funcs registered by the rich-js binding — the colour sinks, the palette-free
|
|
44
45
|
* colour math, text attributes, and the `link` cell-splitter. Nothing here
|
|
@@ -57,7 +58,7 @@ export { colorFuncs, readableOnFunc } from "./color-funcs.js";
|
|
|
57
58
|
* into a wider engine must keep `T = RichText`; merging into an engine
|
|
58
59
|
* whose `T` is something else will compile but fail at evaluation time.
|
|
59
60
|
*/
|
|
60
|
-
export declare function richTextFuncs(drawnAt?: () =>
|
|
61
|
+
export declare function richTextFuncs(drawnAt?: () => DrawnAt): FuncMap;
|
|
61
62
|
/**
|
|
62
63
|
* Construct an `Engine<RichText>` with the rich-js style-function set
|
|
63
64
|
* registered. Consumers that already manage their own engine should call
|
|
@@ -71,6 +72,18 @@ export declare function richTextFuncs(drawnAt?: () => ColorDepth): FuncMap;
|
|
|
71
72
|
* not desired anyway.
|
|
72
73
|
*/
|
|
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
|
+
}
|
|
74
87
|
/**
|
|
75
88
|
* Compile a template source against `engine` and render the result to a
|
|
76
89
|
* flat `Segment[]`. The 90%-case convenience over chaining `engine.compile`,
|
|
@@ -79,14 +92,20 @@ export declare function createRichTextEngine(): Engine<RichText>;
|
|
|
79
92
|
* - Runs `engine.compile(source)(scope)` to get the engine's `RichText[]`.
|
|
80
93
|
* - Flattens that fragment list into a single styled `RichText` via
|
|
81
94
|
* `RichText.fromFragments` so every fragment's wrapping style survives.
|
|
82
|
-
* - Renders to a `Segment[]`
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
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
|
|
86
100
|
* call site.
|
|
87
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
|
+
*
|
|
88
107
|
* [LAW:single-enforcer] One place owns "render a template to segments,
|
|
89
|
-
* degrade gracefully
|
|
108
|
+
* degrade gracefully when the template fails" — every consumer that wants this exact
|
|
90
109
|
* shape reads from here rather than re-implementing the same try/catch +
|
|
91
110
|
* error-formatting glue.
|
|
92
111
|
*
|
|
@@ -95,13 +114,6 @@ export declare function createRichTextEngine(): Engine<RichText>;
|
|
|
95
114
|
* and use `RichText.fromFragments` to flatten. This helper is sugar for
|
|
96
115
|
* the live-render case (e.g. a preview pane), not a replacement for the
|
|
97
116
|
* compile-once-evaluate-many pattern.
|
|
98
|
-
|
|
99
|
-
* @param maxWidth defaults to 400 — large enough that downstream `splitLines`
|
|
100
|
-
* / `adjustLineLength` clipping decides actual width, matching the typical
|
|
101
|
-
* "render wide, fit on output" pipeline.
|
|
102
|
-
* @param errorStyle is a `Style.parse` spec (default `"red dim"`).
|
|
117
|
+
|
|
103
118
|
*/
|
|
104
|
-
export declare function renderTemplate(engine: Engine<RichText>, source: string, scope?: unknown, options?:
|
|
105
|
-
maxWidth?: number;
|
|
106
|
-
errorStyle?: string;
|
|
107
|
-
}): Segment[];
|
|
119
|
+
export declare function renderTemplate(engine: Engine<RichText>, source: string, scope?: unknown, options?: RenderTemplateOptions): Segment[];
|
|
@@ -91,14 +91,20 @@ export function createRichTextEngine() {
|
|
|
91
91
|
* - Runs `engine.compile(source)(scope)` to get the engine's `RichText[]`.
|
|
92
92
|
* - Flattens that fragment list into a single styled `RichText` via
|
|
93
93
|
* `RichText.fromFragments` so every fragment's wrapping style survives.
|
|
94
|
-
* - Renders to a `Segment[]`
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
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
|
|
98
99
|
* call site.
|
|
99
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
|
+
*
|
|
100
106
|
* [LAW:single-enforcer] One place owns "render a template to segments,
|
|
101
|
-
* degrade gracefully
|
|
107
|
+
* degrade gracefully when the template fails" — every consumer that wants this exact
|
|
102
108
|
* shape reads from here rather than re-implementing the same try/catch +
|
|
103
109
|
* error-formatting glue.
|
|
104
110
|
*
|
|
@@ -107,27 +113,37 @@ export function createRichTextEngine() {
|
|
|
107
113
|
* and use `RichText.fromFragments` to flatten. This helper is sugar for
|
|
108
114
|
* the live-render case (e.g. a preview pane), not a replacement for the
|
|
109
115
|
* compile-once-evaluate-many pattern.
|
|
110
|
-
|
|
111
|
-
* @param maxWidth defaults to 400 — large enough that downstream `splitLines`
|
|
112
|
-
* / `adjustLineLength` clipping decides actual width, matching the typical
|
|
113
|
-
* "render wide, fit on output" pipeline.
|
|
114
|
-
* @param errorStyle is a `Style.parse` spec (default `"red dim"`).
|
|
116
|
+
|
|
115
117
|
*/
|
|
116
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;
|
|
117
124
|
try {
|
|
118
|
-
|
|
119
|
-
const rt = RichText.fromFragments(frags);
|
|
120
|
-
return Array.from(rt.render({
|
|
121
|
-
maxWidth: options?.maxWidth ?? 400,
|
|
122
|
-
isTerminal: true,
|
|
123
|
-
}));
|
|
125
|
+
text = RichText.fromFragments(engine.compile(source)(scope));
|
|
124
126
|
}
|
|
125
127
|
catch (e) {
|
|
126
|
-
|
|
127
|
-
// multi-line code frame, which would break this single segment across lines.
|
|
128
|
-
const summary = e instanceof Error ? e.message : String(e);
|
|
129
|
-
return [new Segment(`[error: ${summary.slice(0, 80)}]`, safeErrorStyle(options?.errorStyle))];
|
|
128
|
+
return [new Segment(errorLine(e, maxWidth), safeErrorStyle(errorStyle))];
|
|
130
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;
|
|
131
147
|
}
|
|
132
148
|
// [LAW:single-enforcer] The error-path Style must not itself throw, or
|
|
133
149
|
// the whole "degrade gracefully" promise of renderTemplate is broken. A
|
|
@@ -67,7 +67,9 @@ import type { Palette } from "../themes/palette.js";
|
|
|
67
67
|
/**
|
|
68
68
|
* Register `color "name-or-hex"` against a live palette.
|
|
69
69
|
*
|
|
70
|
-
* `color` resolves a palette variable name to
|
|
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
|
|
71
73
|
* an already-literal color through unchanged. That second half is not a
|
|
72
74
|
* convenience — it makes `color` **idempotent**, which is what lets consumers
|
|
73
75
|
* apply it unconditionally to any author-written color string without first
|
|
@@ -63,7 +63,9 @@
|
|
|
63
63
|
* they are gone.
|
|
64
64
|
*/
|
|
65
65
|
import { resolveColorRef } from "../themes/colorRef.js";
|
|
66
|
-
import {
|
|
66
|
+
import { parseEase } from "../core/easing.js";
|
|
67
|
+
import { ColorRamp } from "../themes/ramp.js";
|
|
68
|
+
import { computable } from "./color-funcs.js";
|
|
67
69
|
/**
|
|
68
70
|
* The `(position, color-ref)` pairs a `ramp` call's tail spells, as stops.
|
|
69
71
|
*
|
|
@@ -85,7 +87,7 @@ function colorStops(tail, palette) {
|
|
|
85
87
|
for (let i = 0; i < tail.length; i += 2) {
|
|
86
88
|
stops.push({
|
|
87
89
|
at: tail[i],
|
|
88
|
-
color: resolveColorRef(palette, tail[i + 1]),
|
|
90
|
+
color: computable(resolveColorRef(palette, tail[i + 1]), "ramp", JSON.stringify(tail[i + 1])),
|
|
89
91
|
});
|
|
90
92
|
}
|
|
91
93
|
return stops;
|
|
@@ -93,7 +95,9 @@ function colorStops(tail, palette) {
|
|
|
93
95
|
/**
|
|
94
96
|
* Register `color "name-or-hex"` against a live palette.
|
|
95
97
|
*
|
|
96
|
-
* `color` resolves a palette variable name to
|
|
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
|
|
97
101
|
* an already-literal color through unchanged. That second half is not a
|
|
98
102
|
* convenience — it makes `color` **idempotent**, which is what lets consumers
|
|
99
103
|
* apply it unconditionally to any author-written color string without first
|
|
@@ -124,14 +128,14 @@ export function paletteFuncs(getPalette) {
|
|
|
124
128
|
arity: { kind: "exact" },
|
|
125
129
|
returnType: "string",
|
|
126
130
|
};
|
|
127
|
-
// `ramp <value> <
|
|
128
|
-
// float/string cycle end to end (value,
|
|
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
|
|
129
133
|
// and color), so the engine's `alternating` gate types every slot; the
|
|
130
134
|
// pairing and the references are parsed in `colorStops`. Its minimum is the
|
|
131
|
-
// value, the
|
|
135
|
+
// value, the ease, and one stop, so the gate refuses a stopless call.
|
|
132
136
|
// [LAW:types-are-the-program]
|
|
133
137
|
const rampFunc = {
|
|
134
|
-
fn: ((value,
|
|
138
|
+
fn: ((value, ease, ...tail) => new ColorRamp(parseEase(ease), colorStops(tail, getPalette())).at(value)
|
|
135
139
|
.hex),
|
|
136
140
|
argTypes: ["float", "string"],
|
|
137
141
|
arity: { kind: "alternating", minimum: 4 },
|
|
@@ -113,11 +113,13 @@ const styleSpecFunc = {
|
|
|
113
113
|
// `link` is the cell-splitter for the multi-cell consumer contract.
|
|
114
114
|
// Implementation-wise it is the same shape as any other style function:
|
|
115
115
|
// it sets the `link` slot of `Style` exactly as the existing string-form
|
|
116
|
-
// `link URL` does, so
|
|
116
|
+
// `link URL` does, so `{{ link "u" "x" }}` is byte-equivalent to
|
|
117
117
|
// `RichText("x", { style: Style.parse("link u") })`.
|
|
118
118
|
//
|
|
119
119
|
// The cell-boundary signal that consumers (cc-candybar et al.) walk is
|
|
120
|
-
// `fragment.style.link` being truthy
|
|
120
|
+
// `theme.resolve(fragment.style).link` being truthy: a fragment whose base
|
|
121
|
+
// style is a string keeps the link in a `LayeredStyle` until a theme says
|
|
122
|
+
// what the string stands for. `Style.add` propagates `link`
|
|
121
123
|
// through any outer wrapping call, so `{{ fg "red" (link "u" "x") }}` and
|
|
122
124
|
// `{{ link "u" "x" }}` produce shapes that both qualify as cells from
|
|
123
125
|
// the consumer's perspective. Outer-wins on nested links comes for free
|
|
@@ -14,6 +14,8 @@ export interface BaseColors {
|
|
|
14
14
|
background: ColorRgba;
|
|
15
15
|
foreground: ColorRgba;
|
|
16
16
|
}
|
|
17
|
+
type AccentKey = "primary" | "secondary" | "accent" | "success" | "warning" | "error";
|
|
18
|
+
export declare const ACCENT_KEYS: readonly AccentKey[];
|
|
17
19
|
/**
|
|
18
20
|
* Build a full semantic palette from base colors.
|
|
19
21
|
*
|
|
@@ -25,12 +27,31 @@ export interface BaseColors {
|
|
|
25
27
|
* de-emphasizing body/structural text reaches for
|
|
26
28
|
* `foreground-muted` the same way it reaches for
|
|
27
29
|
* `primary-muted` to de-emphasize an accent.
|
|
28
|
-
* - `text-*` = contrast text tinted 66% with the accent color
|
|
29
|
-
*
|
|
30
|
+
* - `text-*` = contrast text tinted 66% with the accent color, then held
|
|
31
|
+
* to WCAG AA (4.5:1) on `*-muted` — the foreground for a
|
|
32
|
+
* `*-muted` ground
|
|
30
33
|
* - `on-*` = WCAG-correct contrast colour (black or white) for use as
|
|
31
34
|
* foreground when the FULL accent is the background. Picked
|
|
32
35
|
* by relative luminance — single source of truth so widgets
|
|
33
36
|
* never need to invert / reverse fg/bg to get readable text.
|
|
34
37
|
* - `surface` = background blended 5% toward foreground
|
|
38
|
+
*
|
|
39
|
+
* A translucent base colour is drawn onto `background` before anything is
|
|
40
|
+
* derived from it, so `on-*` is chosen against the colour the accent is drawn
|
|
41
|
+
* as and every derived entry is opaque.
|
|
42
|
+
*/
|
|
43
|
+
export declare function buildPalette(name: string, dark: boolean, given: BaseColors): Palette;
|
|
44
|
+
/**
|
|
45
|
+
* A palette's `text-*`: its tint held to WCAG AA on `*-muted`, moved toward
|
|
46
|
+
* the palette's own text side so it also reads on `background`, where widgets
|
|
47
|
+
* draw it too. The muted floor is the promise: where no lightness on the text
|
|
48
|
+
* side clears it, the colour that does is on the other side, and the
|
|
49
|
+
* background pair is the one given up. Every bundled palette clears both
|
|
50
|
+
* (test/themes/text-on-muted.test.ts).
|
|
51
|
+
*
|
|
52
|
+
* [LAW:single-enforcer] The one rule for the pair, for the palettes derived
|
|
53
|
+
* here and the authored ones the registry hydrates: the tint is the theme's,
|
|
54
|
+
* whether it reads is decided here.
|
|
35
55
|
*/
|
|
36
|
-
export declare function
|
|
56
|
+
export declare function accentText(tint: ColorRgba, muted: ColorRgba, background: ColorRgba): ColorRgba;
|
|
57
|
+
export {};
|