@promptctl/rich-js 0.28.0 → 0.29.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 (162) hide show
  1. package/README.md +7 -7
  2. package/dist/core/animation.d.ts +37 -37
  3. package/dist/core/animation.js +61 -61
  4. package/dist/core/ansi.d.ts +4 -4
  5. package/dist/core/ansi.js +9 -9
  6. package/dist/core/bidi.js +6 -6
  7. package/dist/core/cells.d.ts +1 -1
  8. package/dist/core/cells.js +5 -5
  9. package/dist/core/clock.d.ts +1 -1
  10. package/dist/core/color.d.ts +50 -50
  11. package/dist/core/color.js +69 -69
  12. package/dist/core/console.d.ts +8 -8
  13. package/dist/core/console.js +5 -5
  14. package/dist/core/control-codes.d.ts +3 -3
  15. package/dist/core/control-codes.js +7 -7
  16. package/dist/core/easing.d.ts +40 -40
  17. package/dist/core/easing.js +40 -40
  18. package/dist/core/env.d.ts +1 -1
  19. package/dist/core/env.js +1 -1
  20. package/dist/core/export-html.js +1 -1
  21. package/dist/core/export-lines.d.ts +11 -11
  22. package/dist/core/export-lines.js +12 -12
  23. package/dist/core/export-svg.d.ts +3 -3
  24. package/dist/core/export-svg.js +4 -4
  25. package/dist/core/markup.js +1 -1
  26. package/dist/core/memo.d.ts +1 -1
  27. package/dist/core/memo.js +1 -1
  28. package/dist/core/oklch.d.ts +14 -14
  29. package/dist/core/oklch.js +10 -10
  30. package/dist/core/orient.d.ts +3 -3
  31. package/dist/core/orient.js +3 -3
  32. package/dist/core/paint.d.ts +9 -9
  33. package/dist/core/paint.js +3 -3
  34. package/dist/core/pretty.d.ts +1 -1
  35. package/dist/core/pretty.js +3 -3
  36. package/dist/core/protocol.d.ts +26 -26
  37. package/dist/core/protocol.js +22 -22
  38. package/dist/core/render.d.ts +11 -11
  39. package/dist/core/render.js +12 -12
  40. package/dist/core/segment.d.ts +3 -3
  41. package/dist/core/segment.js +5 -5
  42. package/dist/core/strip.d.ts +83 -83
  43. package/dist/core/strip.js +70 -70
  44. package/dist/core/style.d.ts +20 -20
  45. package/dist/core/style.js +55 -55
  46. package/dist/core/text.d.ts +2 -2
  47. package/dist/core/text.js +5 -5
  48. package/dist/core/timeline.d.ts +5 -5
  49. package/dist/core/timeline.js +9 -9
  50. package/dist/core/tween.d.ts +6 -6
  51. package/dist/core/tween.js +10 -10
  52. package/dist/core/view.d.ts +4 -4
  53. package/dist/core/view.js +12 -12
  54. package/dist/host/app.d.ts +9 -9
  55. package/dist/host/app.js +12 -12
  56. package/dist/host/host-environment.d.ts +2 -2
  57. package/dist/host/host-environment.js +2 -2
  58. package/dist/host/index.d.ts +2 -2
  59. package/dist/host/input.d.ts +7 -7
  60. package/dist/host/input.js +4 -4
  61. package/dist/host/terminal-colors.d.ts +2 -2
  62. package/dist/host/terminal-colors.js +4 -4
  63. package/dist/host/terminal-host.d.ts +2 -2
  64. package/dist/host/terminal-host.js +1 -1
  65. package/dist/index.d.ts +14 -14
  66. package/dist/index.js +10 -10
  67. package/dist/node/prompt.d.ts +1 -1
  68. package/dist/node/prompt.js +1 -1
  69. package/dist/node/terminal-host.d.ts +1 -1
  70. package/dist/node/traceback.js +2 -2
  71. package/dist/renderables/change.js +2 -2
  72. package/dist/renderables/effect.d.ts +39 -39
  73. package/dist/renderables/effect.js +45 -45
  74. package/dist/renderables/effects.d.ts +81 -81
  75. package/dist/renderables/effects.js +157 -157
  76. package/dist/renderables/embed.d.ts +1 -1
  77. package/dist/renderables/embed.js +2 -2
  78. package/dist/renderables/flexStrip.d.ts +7 -7
  79. package/dist/renderables/flexStrip.js +15 -15
  80. package/dist/renderables/frames.d.ts +6 -6
  81. package/dist/renderables/frames.js +11 -11
  82. package/dist/renderables/glyph-effect.d.ts +15 -15
  83. package/dist/renderables/glyph-effect.js +17 -17
  84. package/dist/renderables/glyph-effects.d.ts +7 -7
  85. package/dist/renderables/glyph-effects.js +16 -16
  86. package/dist/renderables/html-entities.d.ts +1 -1
  87. package/dist/renderables/html-entities.js +1 -1
  88. package/dist/renderables/lifecycle.d.ts +4 -4
  89. package/dist/renderables/lifecycle.js +12 -12
  90. package/dist/renderables/live.d.ts +2 -2
  91. package/dist/renderables/live.js +7 -7
  92. package/dist/renderables/markdown-parse.js +3 -3
  93. package/dist/renderables/panel.js +3 -3
  94. package/dist/renderables/progress.d.ts +1 -1
  95. package/dist/renderables/progress.js +1 -1
  96. package/dist/renderables/progressBar.d.ts +1 -1
  97. package/dist/renderables/progressBar.js +6 -6
  98. package/dist/renderables/prompt.d.ts +1 -1
  99. package/dist/renderables/scroll.d.ts +1 -1
  100. package/dist/renderables/scroll.js +3 -3
  101. package/dist/renderables/spinner.d.ts +4 -4
  102. package/dist/renderables/spinner.js +10 -10
  103. package/dist/renderables/syntax.d.ts +2 -2
  104. package/dist/renderables/syntax.js +2 -2
  105. package/dist/renderables/table.js +3 -3
  106. package/dist/renderables/traceback.d.ts +1 -1
  107. package/dist/renderables/traceback.js +1 -1
  108. package/dist/renderables/tree.js +3 -3
  109. package/dist/template-bindings/animation-funcs.d.ts +14 -14
  110. package/dist/template-bindings/animation-funcs.js +27 -27
  111. package/dist/template-bindings/color-funcs.d.ts +14 -14
  112. package/dist/template-bindings/color-funcs.js +25 -25
  113. package/dist/template-bindings/index.d.ts +9 -9
  114. package/dist/template-bindings/index.js +9 -9
  115. package/dist/template-bindings/palette-funcs.d.ts +1 -1
  116. package/dist/template-bindings/palette-funcs.js +3 -3
  117. package/dist/template-bindings/style-funcs.d.ts +5 -5
  118. package/dist/template-bindings/style-funcs.js +16 -16
  119. package/dist/themes/{transpose.d.ts → adjust.d.ts} +23 -23
  120. package/dist/themes/{transpose.js → adjust.js} +28 -28
  121. package/dist/themes/buildPalette.d.ts +3 -3
  122. package/dist/themes/buildPalette.js +20 -20
  123. package/dist/themes/colorMath.d.ts +16 -16
  124. package/dist/themes/colorMath.js +32 -32
  125. package/dist/themes/data/ansi-tables.d.ts +2 -2
  126. package/dist/themes/data/ansi-tables.js +2 -2
  127. package/dist/themes/data/types.d.ts +4 -4
  128. package/dist/themes/data/types.js +2 -2
  129. package/dist/themes/palette.d.ts +12 -12
  130. package/dist/themes/palette.js +16 -16
  131. package/dist/themes/ramp.d.ts +17 -17
  132. package/dist/themes/ramp.js +12 -12
  133. package/dist/themes/registry.js +4 -4
  134. package/dist/themes/terminalThemes.d.ts +1 -1
  135. package/dist/themes/terminalThemes.js +1 -1
  136. package/dist/widgets/button.d.ts +2 -2
  137. package/dist/widgets/button.js +4 -4
  138. package/dist/widgets/checkbox.d.ts +2 -2
  139. package/dist/widgets/checkbox.js +2 -2
  140. package/dist/widgets/dropdown.d.ts +2 -2
  141. package/dist/widgets/dropdown.js +3 -3
  142. package/dist/widgets/event-router.d.ts +6 -6
  143. package/dist/widgets/event-router.js +2 -2
  144. package/dist/widgets/focus-manager.d.ts +3 -3
  145. package/dist/widgets/focus-manager.js +3 -3
  146. package/dist/widgets/index.d.ts +2 -2
  147. package/dist/widgets/index.js +1 -1
  148. package/dist/widgets/ink.d.ts +3 -3
  149. package/dist/widgets/ink.js +4 -4
  150. package/dist/widgets/slider.d.ts +2 -2
  151. package/dist/widgets/static-item.d.ts +2 -2
  152. package/dist/widgets/text-input.d.ts +2 -2
  153. package/dist/widgets/text-input.js +5 -5
  154. package/dist/widgets/themed-widget.js +1 -1
  155. package/dist/widgets/toggle.d.ts +3 -3
  156. package/dist/widgets/toggle.js +3 -3
  157. package/dist/widgets/types.d.ts +6 -6
  158. package/dist/widgets/types.js +2 -2
  159. package/dist/widgets/widget-app.d.ts +10 -10
  160. package/dist/widgets/widget-app.js +8 -8
  161. package/dist/widgets/widget-base.d.ts +2 -2
  162. package/package.json +1 -1
@@ -2,21 +2,21 @@
2
2
  * The effect catalogue: shimmer, pulse, sparkle and wheel, which loop for as
3
3
  * long as they are drawn, fade-in and dissolve-out, which run once and are
4
4
  * done, a blink and a bold pulse, which move an attribute on a beat, and a
5
- * gradient, which lays a colour ramp across an element and may move it. Each is an `Effect` for `Effected` (`./effect.ts`); the defaults
5
+ * gradient, which lays a color ramp across an element and may move it. Each is an `Effect` for `Effected` (`./effect.ts`); the defaults
6
6
  * in `EFFECT_CURVES` are the ones signed off by eye in the effects-feel demo
7
7
  * (`npm run effects-feel`), at 30 fps and at one frame a second, and
8
8
  * `EFFECT_CATALOGUE` makes every one of them at those, by name, one way.
9
9
  *
10
10
  * Each effect is something a first-time observer already knows from life —
11
- * a slow glow, sunlight moving on water, the colours of daylight, fireflies, ink
11
+ * a slow glow, sunlight moving on water, the colors of daylight, fireflies, ink
12
12
  * blooming in water, mist lifting — because a motion the eye recognises
13
13
  * reads as calm and alive, where a motion it has to learn reads as a widget.
14
14
  * One effect here is not ambience but a signal, and keeps none of this on
15
- * purpose: `steppedInk` steps its ink through colours on a beat, the news
15
+ * purpose: `steppedInk` steps its ink through colors on a beat, the news
16
16
  * that something is at its limit, and jumps from slot to slot where the
17
17
  * loops hold still. Nor is `gradient` a light: it paints an ink rather than
18
18
  * lighting it, so it may darken one, and a ramp scrolled across it jumps
19
- * where the ramp wraps unless its ends are one colour. Of the list below it
19
+ * where the ramp wraps unless its ends are one color. Of the list below it
20
20
  * keeps the words. What makes the loops' motions familiar is what every
21
21
  * curve of theirs keeps:
22
22
  *
@@ -32,13 +32,13 @@
32
32
  * filaments, fronts — never per-cell static.
33
33
  * - Light only ever lights, and never takes the words away. Ink and the
34
34
  * fill under it both move toward the light and never darken, each by its
35
- * own share: the share a colour can take while every cell it is drawn in
35
+ * own share: the share a color can take while every cell it is drawn in
36
36
  * still reads at its resting contrast or WCAG AA, whichever is lower. A
37
37
  * cell with contrast to spare glows brightly; one with none barely moves.
38
38
  * - Elements are apart. Each sits at its own `z` in the noise, so two
39
39
  * elements under one effect do not move in lockstep.
40
40
  *
41
- * Colour moves are in OKLCH, where equal steps look equal. A curve's `ease`
41
+ * Color moves are in OKLCH, where equal steps look equal. A curve's `ease`
42
42
  * maps its intensity in [0, 1] — how strongly the effect acts on a cell at a
43
43
  * moment — onto its swing, for the loops and the transitions alike.
44
44
  *
@@ -52,7 +52,7 @@
52
52
  */
53
53
  import { Animation } from "../core/animation.js";
54
54
  import { ColorDepth, ColorRgba, ColorSpec, contrastRatio, relativeLuminance } from "../core/color.js";
55
- import { EASES, Phase, beat } from "../core/easing.js";
55
+ import { EASES, WrapMode, stepSequence } from "../core/easing.js";
56
56
  import { fbm, hash, noise, smoothstep } from "../core/noise.js";
57
57
  import { Memo, WeakMemo } from "../core/memo.js";
58
58
  import { Oklch } from "../core/oklch.js";
@@ -64,7 +64,7 @@ const LEGIBLE = 4.5;
64
64
  /**
65
65
  * The strengths a cell is drawn at: a field is snapped to one of these, and
66
66
  * a share is checked at every one, so every strength drawn was checked. A
67
- * 256-colour cube colour changes in jumps between strengths, and no check of
67
+ * 256-color cube color changes in jumps between strengths, and no check of
68
68
  * a continuous strength between two of them could see every jump. A step is
69
69
  * a 48th of a swing, well under a just-noticeable difference.
70
70
  */
@@ -75,7 +75,7 @@ const ROUNDING = 0.01;
75
75
  /**
76
76
  * The contrast no loop draws `pair` below at `depth`: its resting contrast or
77
77
  * `LEGIBLE`, whichever is lower, less what truecolor's rounding can cost.
78
- * Below truecolor every colour written is one the depth holds exactly, so
78
+ * Below truecolor every color written is one the depth holds exactly, so
79
79
  * there is nothing to forgive. [LAW:one-source-of-truth] The floor `shares`
80
80
  * settles the light against and `wheel` writes its turn above.
81
81
  */
@@ -87,33 +87,33 @@ const SIDES = ["fg", "bg"];
87
87
  * How much of `touch` each of `colors` (hex) can take: the largest share in
88
88
  * [0, 1] at which every pair `drawn` shows it in, touched at the same
89
89
  * strength, still reads at its resting contrast or `LEGIBLE`, whichever is
90
- * lower, in the colours `Effected` writes it in at `drawn`'s depth and terminal.
91
- * A colour in a pair but not in `colors` takes none, and so does one
90
+ * lower, in the colors `Effected` writes it in at `drawn`'s depth and terminal.
91
+ * A color in a pair but not in `colors` takes none, and so does one
92
92
  * the terminal supplies, whatever its hex, as in `onColors`. One of `colors`
93
93
  * no pair writes has nothing to be measured against, and takes it all.
94
94
  *
95
- * Colours are settled lightest first, each against every partner: one
95
+ * Colors are settled lightest first, each against every partner: one
96
96
  * already settled at its share, one not yet settled as it is. So the lighter
97
- * colour of a cell takes the light first and its darker partner spends
97
+ * color of a cell takes the light first and its darker partner spends
98
98
  * whatever contrast is left — pale lettering lifting off a dark fill gives
99
- * the fill room to glow — and a darker colour left as it is always holds,
99
+ * the fill room to glow — and a darker color left as it is always holds,
100
100
  * because its lighter partner was settled against exactly that.
101
101
  */
102
102
  export function shares({ pairs, depth, terminal }, colors, touch) {
103
- // [LAW:one-source-of-truth] A slot shows a subject colour, by hex, only
103
+ // [LAW:one-source-of-truth] A slot shows a subject color, by hex, only
104
104
  // where the element writes it: the reading `onColors` lights by.
105
105
  const own = (pair, side) => (pair.terminal[side] ? undefined : pair[side].hex);
106
106
  const rgba = new Map(pairs.flatMap((pair) => SIDES.flatMap((side) => {
107
107
  const hex = own(pair, side);
108
108
  return hex === undefined ? [] : [[hex, pair[side]]];
109
109
  })));
110
- // A colour no pair writes keeps no word legible, so nothing bounds its share:
111
- // a frame drawing a lit colour only under blanks and seams, or not at all,
110
+ // A color no pair writes keeps no word legible, so nothing bounds its share:
111
+ // a frame drawing a lit color only under blanks and seams, or not at all,
112
112
  // is a frame like any other.
113
113
  const settled = new Map([...colors].filter((hex) => !rgba.has(hex)).map((hex) => [hex, 1]));
114
114
  const lightness = (hex) => Oklch.fromRgba(rgba.get(hex)).l;
115
115
  for (const hex of [...colors].filter((hex) => rgba.has(hex)).sort((a, b) => lightness(b) - lightness(a))) {
116
- // Each pair showing the colour, with the side it shows it on and the share its partner takes.
116
+ // Each pair showing the color, with the side it shows it on and the share its partner takes.
117
117
  const facing = pairs.flatMap((pair) => {
118
118
  const mine = SIDES.find((side) => own(pair, side) === hex);
119
119
  if (mine === undefined)
@@ -121,7 +121,7 @@ export function shares({ pairs, depth, terminal }, colors, touch) {
121
121
  const partner = own(pair, mine === "fg" ? "bg" : "fg");
122
122
  return [[pair, mine, partner === undefined ? 0 : (settled.get(partner) ?? 0)]];
123
123
  });
124
- // [LAW:one-source-of-truth] A pair reads in the colours `Effected` writes it in, touched at each side's share.
124
+ // [LAW:one-source-of-truth] A pair reads in the colors `Effected` writes it in, touched at each side's share.
125
125
  const holds = (share) => facing.every(([pair, mine, theirs]) => {
126
126
  const floor = floorOf(pair, depth);
127
127
  const at = (side, w) => touchedAt(touch, pair[side], (side === mine ? share : theirs) * w);
@@ -140,19 +140,19 @@ export function shares({ pairs, depth, terminal }, colors, touch) {
140
140
  return settled;
141
141
  }
142
142
  /**
143
- * A loop as an `Effect` on the element's own colours, by hex, wherever a cell
143
+ * A loop as an `Effect` on the element's own colors, by hex, wherever a cell
144
144
  * shows them, each touched at its share of the cell's strength: the share
145
145
  * `shares` settles for it against the pairs the element draws its words in
146
146
  * (`Drawn`), touched as `Effected` writes them at the frame's depth — at 256
147
- * colours a touched colour lands on a colour the slot can write, and the
148
- * contrast it spends is that colour's; on a sixteen-colour terminal nothing
149
- * moves (`stillWhereNothingMoves`). `colors` are the ones lit, by default every colour the element
147
+ * colors a touched color lands on a color the slot can write, and the
148
+ * contrast it spends is that color's; on a sixteen-color terminal nothing
149
+ * moves (`staticWhereUnchanged`). `colors` are the ones lit, by default every color the element
150
150
  * sets itself. A powerline strip's fill is the ground of its cells and the
151
151
  * ink of the seam glyphs between them, so the arrow moves with the cell it
152
- * points out of. A colour the terminal supplies (`Pair.terminal`) is
152
+ * points out of. A color the terminal supplies (`Pair.terminal`) is
153
153
  * not the element's, whatever its hex, and takes no share.
154
154
  *
155
- * Settling shares is costly, so it is done once for each set of colours an
155
+ * Settling shares is costly, so it is done once for each set of colors an
156
156
  * element draws: make the effect once and draw it in every frame, and a frame
157
157
  * drawing what an earlier one drew costs no settling.
158
158
  */
@@ -164,7 +164,7 @@ export function onColors(loop, colors) {
164
164
  const key = [drawnKey(drawn), "|", ...lit].join(" ");
165
165
  return settled.get(drawn.terminal, () => new Memo()).get(key, () => shares(drawn, lit, loop.touch));
166
166
  };
167
- return stillWhereNothingMoves(SIDES, litIn, atShares(loop, settle));
167
+ return staticWhereUnchanged(SIDES, litIn, atShares(loop, settle));
168
168
  }
169
169
  /** What a frame draws, as a key: its depth and its pairs, which its `colors` and `fills` are read off. */
170
170
  function drawnKey({ depth, pairs }) {
@@ -182,10 +182,10 @@ function pairKey({ fg, bg, terminal, reverse }) {
182
182
  return `${fg.hex}/${bg.hex}/${terminal.fg}/${terminal.bg}/${reverse}`;
183
183
  }
184
184
  /**
185
- * A ramp laid across the element: each lit cell's ink is the colour `ramp`
185
+ * A ramp laid across the element: each lit cell's ink is the color `ramp`
186
186
  * shows at `t` at the cell's column, the element's leftmost cell reading
187
187
  * the ramp's first stop and its rightmost its last, each at its cell's
188
- * centre — or, where that colour would not read on the cell's ground, the
188
+ * centre — or, where that color would not read on the cell's ground, the
189
189
  * nearest that does (`legibleInks`, as `steppedInk` writes its steps). A
190
190
  * ramp that moves (`ColorRamp.moving`) scrolls along the element and turns
191
191
  * its hues, and the frame changes when the ramp does — every frame for a
@@ -193,26 +193,26 @@ function pairKey({ fg, bg, terminal, reverse }) {
193
193
  * `Animation.constant(ramp)`. Lit cells are those whose ink is one of
194
194
  * `colors`, named as a style names them (`"#a6e3a1"`, `"bright_green"`),
195
195
  * opaque, and matched as the screen shows them at the frame's depth — so
196
- * below truecolor every ink that lands on the same palette colour is lit —
197
- * by default every colour the element sets itself — never an ink the terminal supplies: a
198
- * rule's line by its colour and not its title, a panel's border and not its
196
+ * below truecolor every ink that lands on the same palette color is lit —
197
+ * by default every color the element sets itself — never an ink the terminal supplies: a
198
+ * rule's line by its color and not its title, a panel's border and not its
199
199
  * body. The element is where its frame lists
200
200
  * cells, as the fireflies read it, so a padded or confined element has the
201
- * ramp across what it shows. On a sixteen-colour terminal nothing moves
202
- * (`stillWhereNothingMoves`): a gradient there is jumps between slots, and the cells
201
+ * ramp across what it shows. On a sixteen-color terminal nothing moves
202
+ * (`staticWhereUnchanged`): a gradient there is jumps between slots, and the cells
203
203
  * hold as drawn.
204
204
  */
205
205
  export function gradient(ramp, colors) {
206
206
  const legible = legibleInks();
207
- // [LAW:parse-dont-validate] Parsed at the call, so a misspelt colour fails
207
+ // [LAW:parse-dont-validate] Parsed at the call, so a misspelt color fails
208
208
  // where it was written and not on some later frame.
209
209
  const named = colors?.map(opaqueInk);
210
- // [LAW:one-source-of-truth] A colour named as a style spells it is lit
210
+ // [LAW:one-source-of-truth] A color named as a style spells it is lit
211
211
  // wherever a cell shows it, so it is resolved as `Effected` resolves a
212
212
  // cell's ink: drawn at the frame's depth, then read on its terminal.
213
213
  const shown = (spec, { depth, terminal }) => (Style.fromColor(spec).drawnColors(depth).color ?? ColorSpec.default()).getTruecolor(terminal, true).hex;
214
214
  const litIn = (drawn) => (named === undefined ? drawn.colors : new Set(named.map((spec) => shown(spec, drawn))));
215
- return stillWhereNothingMoves(["fg"], litIn, (drawn, lit) => {
215
+ return staticWhereUnchanged(["fg"], litIn, (drawn, lit) => {
216
216
  const inkOn = legible(drawn);
217
217
  const [left, right] = extent(drawn.cells().map((cell) => cell.col));
218
218
  return Animation.map(ramp, (now) => {
@@ -226,18 +226,18 @@ export function gradient(ramp, colors) {
226
226
  /**
227
227
  * `spec` parsed as an ink `gradient` lights. Opaque only: a translucent ink
228
228
  * is drawn over the ground of each cell it is in, so it shows as no one
229
- * colour to match, and naming one is refused here.
229
+ * color to match, and naming one is refused here.
230
230
  */
231
231
  function opaqueInk(spec) {
232
232
  const parsed = ColorSpec.parse(spec);
233
233
  if (parsed.value !== undefined && parsed.value.alpha < 1)
234
- throw new RangeError(`gradient lights opaque colours, and ${spec} is translucent`);
234
+ throw new RangeError(`gradient lights opaque colors, and ${spec} is translucent`);
235
235
  return parsed;
236
236
  }
237
237
  /**
238
- * A loop as an `Effect` on colours by hex, each touched at the share
238
+ * A loop as an `Effect` on colors by hex, each touched at the share
239
239
  * `share` gives it of the cell's strength in a frame drawing what `Drawn`
240
- * says; a colour it gives none, and one the terminal supplies, stays as it is.
240
+ * says; a color it gives none, and one the terminal supplies, stays as it is.
241
241
  */
242
242
  export function atShares(loop, share) {
243
243
  return (drawn) => {
@@ -246,7 +246,7 @@ export function atShares(loop, share) {
246
246
  const field = loop.field(t, drawn);
247
247
  return (look, cell) => {
248
248
  const w = Math.round(clamp01(field(cell)) * LEVELS) / LEVELS;
249
- // [LAW:one-source-of-truth] A share belongs to the colour the element drew; the touch falls on what is there now.
249
+ // [LAW:one-source-of-truth] A share belongs to the color the element drew; the touch falls on what is there now.
250
250
  const moved = (side) => touchedAt(loop.touch, look[side], (cell.pair.terminal[side] ? 0 : (shared.get(cell.pair[side].hex) ?? 0)) * w);
251
251
  return { ...look, fg: moved("fg"), bg: moved("bg") };
252
252
  };
@@ -254,27 +254,27 @@ export function atShares(loop, share) {
254
254
  };
255
255
  }
256
256
  /**
257
- * `loop` as drawn at the frame's depth and on the colours it moves on `sides`
257
+ * `loop` as drawn at the frame's depth and on the colors it moves on `sides`
258
258
  * (`litIn`): where the depth writes only the
259
- * sixteen ANSI slots, or no colour, a move that shows at all is a jump to
259
+ * sixteen ANSI slots, or no color, a move that shows at all is a jump to
260
260
  * another slot, and a light on an element never jumps — so there it
261
261
  * holds every cell still, as it does where no cell that shows anything has
262
- * one of those colours of its own there, and anywhere else it moves. [LAW:single-enforcer]
262
+ * one of those colors of its own there, and anywhere else it moves. [LAW:single-enforcer]
263
263
  * `onColors`, `wheel` and `gradient` draw through this, so every loop in the catalogue
264
- * meets the sixteen-colour terminal here. `atShares` does not: a progress
264
+ * meets the sixteen-color terminal here. `atShares` does not: a progress
265
265
  * bar's pulse is the news that work is going on, and flashes a slot as
266
266
  * Rich's does; nor does `steppedInk`, a signal, which steps slot to slot.
267
267
  * A frame held still never changes, and says so, so a host drawing the
268
268
  * loop on such a terminal draws it once.
269
269
  */
270
- function stillWhereNothingMoves(sides, litIn, loop) {
270
+ function staticWhereUnchanged(sides, litIn, loop) {
271
271
  return (drawn) => {
272
272
  const lit = litIn(drawn);
273
273
  const shows = ({ pair }) => sides.some((side) => !pair.terminal[side] && lit.has(pair[side].hex));
274
274
  return onlySlots(drawn.depth) || !drawn.cells().some(shows) ? Animation.constant((look) => look) : loop(drawn, lit);
275
275
  };
276
276
  }
277
- /** Whether `depth` writes nothing between the slots it has: the sixteen, at either sixteen-colour depth, or none. */
277
+ /** Whether `depth` writes nothing between the slots it has: the sixteen, at either sixteen-color depth, or none. */
278
278
  function onlySlots(depth) {
279
279
  switch (depth) {
280
280
  case ColorDepth.DEFAULT:
@@ -287,8 +287,8 @@ function onlySlots(depth) {
287
287
  }
288
288
  }
289
289
  /**
290
- * `color` under `touch` at strength `w`, and at strength 0 the colour itself,
291
- * exactly: an OKLCH round trip can land a step off, and at 16 colours a step
290
+ * `color` under `touch` at strength `w`, and at strength 0 the color itself,
291
+ * exactly: an OKLCH round trip can land a step off, and at 16 colors a step
292
292
  * off is another slot.
293
293
  */
294
294
  function touchedAt(touch, color, w) {
@@ -313,7 +313,7 @@ export function requireZ(what, z) {
313
313
  /**
314
314
  * Each effect's curve as it was signed off. Every loop's period is a prime
315
315
  * number of seconds, so no two loops come back into step within a sitting.
316
- * The periods are long on purpose: at one frame a second a loop's colour
316
+ * The periods are long on purpose: at one frame a second a loop's color
317
317
  * moves by no more than a just-noticeable step from one frame to the next.
318
318
  */
319
319
  export const EFFECT_CURVES = {
@@ -334,8 +334,8 @@ export const SHIMMER_WIDTH = 24;
334
334
  const clamp01 = (x) => Math.min(1, Math.max(0, x));
335
335
  /**
336
336
  * `w` of the way from `from` to `to` along a straight line in OKLab. Light
337
- * falling on a colour, or a colour thinning into its ground, moves straight
338
- * toward the other colour; `Oklch.mix` goes round the hue circle instead, so
337
+ * falling on a color, or a color thinning into its ground, moves straight
338
+ * toward the other color; `Oklch.mix` goes round the hue circle instead, so
339
339
  * blue lit by gold would pass through cyan on the way.
340
340
  */
341
341
  function blend(from, to, w) {
@@ -347,9 +347,9 @@ function blend(from, to, w) {
347
347
  return new Oklch((1 - w) * from.l + w * to.l, Math.hypot(a, b), Math.atan2(b, a) / rad, from.alpha);
348
348
  }
349
349
  /**
350
- * A colour under `glow`: the one way every light here falls. It moves `w` of
351
- * the way toward the light's colour, and never below its own lightness —
352
- * light can warm a colour and lift it, never darken it. A colour already
350
+ * A color under `glow`: the one way every light here falls. It moves `w` of
351
+ * the way toward the light's color, and never below its own lightness —
352
+ * light can warm a color and lift it, never darken it. A color already
353
353
  * lighter than the light only warms, and white, which has no warmer shade at
354
354
  * its lightness, stays white.
355
355
  */
@@ -399,11 +399,11 @@ function given(shape, s, k) {
399
399
  /** A firefly's brightest flash, in turns: it kindles quicker than it fades, and the rest of its turn is dark. */
400
400
  const FLASH = [[0.24, 1], [0.6, 0]];
401
401
  /**
402
- * Pulse, as a gentle glow on one element of a screen: the colours handed to
402
+ * Pulse, as a gentle glow on one element of a screen: the colors handed to
403
403
  * it, an element's own, warm into `light` and settle back, over and over, the
404
404
  * way an indicator breathes while something is waiting. Swing: how far into
405
405
  * the light it goes at the top of a beat, 0–1. The element glows as one —
406
- * every cell it is drawn in at once — and the colours not handed to it, the
406
+ * every cell it is drawn in at once — and the colors not handed to it, the
407
407
  * rest of the screen, stay as they are, so what pulses is what was chosen.
408
408
  * Each element keeps a time of its own, a little quicker or slower than
409
409
  * another's by `z`, and no beat is quite the last: the rhythm wanders ahead
@@ -428,8 +428,8 @@ export function pulse(curve, glow, z) {
428
428
  // Each beat's own depth, 70% to the whole of the swing; it changes where
429
429
  // the beat is at rest, so nothing jumps.
430
430
  const depth = 0.7 + 0.3 * smoothstep(-0.5, 0.5, noise(n * 0.37, 2.1, z));
431
- const beat = 0.5 - 0.5 * Math.cos(2 * Math.PI * (beats - n));
432
- const level = curve.swing * curve.ease(depth * beat);
431
+ const stepSequence = 0.5 - 0.5 * Math.cos(2 * Math.PI * (beats - n));
432
+ const level = curve.swing * curve.ease(depth * stepSequence);
433
433
  return () => level;
434
434
  };
435
435
  return { touch: light(glow), field };
@@ -437,7 +437,7 @@ export function pulse(curve, glow, z) {
437
437
  /**
438
438
  * How many columns a passing light crosses in its period: its pace.
439
439
  * Sunlight moves at a speed, not in a time set by how long the row is,
440
- * so a wider element is crossed for longer, never faster, and no cell's colour
440
+ * so a wider element is crossed for longer, never faster, and no cell's color
441
441
  * changes quicker on it from one frame to the next.
442
442
  */
443
443
  const STRIDE = 80;
@@ -628,11 +628,11 @@ export function sparkle(curve, glow, z) {
628
628
  * A cell mixed toward the ground of the terminal the frame is drawn on
629
629
  * (`Drawn.terminal`) by how hidden it is: its fill toward the ground, and its ink
630
630
  * toward that fill, so an element with fills of its own — a powerline strip —
631
- * arrives and leaves whole, fill and words together, never as bare coloured
631
+ * arrives and leaves whole, fill and words together, never as bare colored
632
632
  * blocks. Invisible is ink and fill equal to the ground. `swing` scales the
633
633
  * hiding — 1 reaches fully invisible. A cell wholly shown is returned as it was.
634
634
  */
635
- export function veiled(visibility, swing) {
635
+ export function mask(visibility, swing) {
636
636
  return (drawn) => {
637
637
  // [LAW:one-source-of-truth] The ground is the terminal `Effected` draws on, never a second one handed here.
638
638
  const ground = Oklch.fromRgba(drawn.terminal.backgroundColor);
@@ -669,8 +669,8 @@ function order(z) {
669
669
  * `schedule` is when its frame next changes on that clock, whatever the
670
670
  * element draws: what a caller drawing the transition inside an animation —
671
671
  * a lifecycle — says its frame does, since the frame it hands over carries
672
- * no word of its own (`stampFrame`, `../core/animation.ts`). Until done it changes
673
- * every frame, or on each hold of a beat (`onBeat`), and from then never.
672
+ * no word of its own (`stampValidityFrame`, `../core/animation.ts`). Until done it changes
673
+ * every frame, or on each hold of a beat (`sampleAndHold`), and from then never.
674
674
  * [LAW:one-source-of-truth] `done` is that schedule read as a fact — done
675
675
  * from the first moment it never changes again — so no transition carries
676
676
  * two answers to when it is over.
@@ -689,7 +689,7 @@ export class Transition {
689
689
  /**
690
690
  * `effect` run once over `seconds` as a `Transition`: what it draws until it
691
691
  * is done, then held. [LAW:single-enforcer] The one rule for when a
692
- * transition is done and what it answers after, for a colour effect and a
692
+ * transition is done and what it answers after, for a color effect and a
693
693
  * glyph effect (`./glyph-effects.ts`) alike.
694
694
  */
695
695
  export function transition(seconds, effect) {
@@ -704,21 +704,21 @@ export function transition(seconds, effect) {
704
704
  const moving = effect(drawn);
705
705
  return Animation.of({
706
706
  at: (t) => moving.at(t),
707
- nextChange: (t) => (ran.done(t) ? Number.POSITIVE_INFINITY : moving.nextChange(t)),
707
+ validUntil: (t) => (ran.done(t) ? Number.POSITIVE_INFINITY : moving.validUntil(t)),
708
708
  // Done, the frame settled when the transition did; until then it is the effect's.
709
- lastChange: (t) => (ran.done(t) ? schedule.lastChange(t) : moving.lastChange(t)),
709
+ validFrom: (t) => (ran.done(t) ? schedule.validFrom(t) : moving.validFrom(t)),
710
710
  // A one-shot, reduced, cuts from its start to its end, whatever the effect it runs would hold as a loop.
711
711
  reduced: () => Animation.cut(moving.at(0), moving.at(seconds)),
712
712
  });
713
713
  }, schedule);
714
714
  return ran;
715
715
  }
716
- export function onBeat(stepped, beat) {
716
+ export function sampleAndHold(stepped, stepSequence) {
717
717
  // [LAW:types-are-the-program] An effect is a function and a transition an object: the representation is the discriminator.
718
718
  if (typeof stepped === "function")
719
- return (drawn) => Animation.onBeat(stepped(drawn), beat);
719
+ return (drawn) => Animation.sampleAndHold(stepped(drawn), stepSequence);
720
720
  const { effect, schedule } = stepped;
721
- return new Transition((drawn) => Animation.onBeat(effect(drawn), beat), Animation.onBeat(schedule, beat));
721
+ return new Transition((drawn) => Animation.sampleAndHold(effect(drawn), stepSequence), Animation.sampleAndHold(schedule, stepSequence));
722
722
  }
723
723
  /**
724
724
  * Of a transition's `seconds`, how long each cell's own change takes. Under
@@ -749,7 +749,7 @@ function ownChange(curve, place) {
749
749
  const [first, last] = extent(cells.map(place));
750
750
  const stagger = (last > first ? 1 - OWN : 0) * curve.seconds;
751
751
  // One phase a frame, shifted per cell, so no cell builds its own.
752
- const change = Phase.once(0, curve.seconds - stagger);
752
+ const change = WrapMode.once(0, curve.seconds - stagger);
753
753
  // A share of the way from the first place to the last, held to the order for a cell the frame does not list.
754
754
  return (cell) => {
755
755
  const along = last > first ? clamp01((place(cell) - first) / (last - first)) : 0;
@@ -776,7 +776,7 @@ export function fadeIn(curve, z) {
776
776
  requireCurve(curve);
777
777
  requireZ("fadeIn", z);
778
778
  const risen = ownChange(curve, order(1.7 + z));
779
- return transition(curve.seconds, veiled((t, drawn) => {
779
+ return transition(curve.seconds, mask((t, drawn) => {
780
780
  const of = risen(t, drawn.cells());
781
781
  return (cell) => curve.ease(of(cell).progress);
782
782
  }, curve.swing));
@@ -805,7 +805,7 @@ export function dissolveOut(curve, z) {
805
805
  const eddy = order(9.3 + z);
806
806
  const faded = ownChange(curve, order(4.2 + z));
807
807
  const { depth, from, to, maskFrom, maskTo, late } = DISSOLVE_SHAPE;
808
- return transition(curve.seconds, veiled((t, drawn) => {
808
+ return transition(curve.seconds, mask((t, drawn) => {
809
809
  const of = faded(t, drawn.cells());
810
810
  return (cell) => {
811
811
  const { along, progress } = of(cell);
@@ -837,22 +837,22 @@ const STRAY_SPREAD = 24;
837
837
  /** `cell` as a place of its own, as wide as its glyph: where no region covers it. */
838
838
  const ownPlace = (cell, width) => ({ row: cell.row, centre: cell.col + width / 2 });
839
839
  /**
840
- * The segments `drawn` draws, as where each colour of each cell turns: a
840
+ * The segments `drawn` draws, as where each color of each cell turns: a
841
841
  * segment is a region of adjacent cells — beside or above one another — of
842
- * one colour of the element's own, each cell carrying it as its ground or,
842
+ * one color of the element's own, each cell carrying it as its ground or,
843
843
  * on the terminal's ground, as its ink, read off `Drawn.all` once a frame.
844
844
  * So a panel's ground is one segment whatever words lie on it, a blank
845
845
  * carrying the ink keeps a styled paragraph one segment across its spaces
846
- * and lines, a caption in a bar's colour written under the bar turns with
846
+ * and lines, a caption in a bar's color written under the bar turns with
847
847
  * it, and a blank in the terminal's ink splits a segment. A seam a joiner
848
- * drew is in none, since its colours are its neighbours' edges: each colour
849
- * of a seam cell turns with the region of that colour past the seam on
848
+ * drew is in none, since its colors are its neighbours' edges: each color
849
+ * of a seam cell turns with the region of that color past the seam on
850
850
  * either side — a two-cell rail reads past its other cell — so a powerline
851
- * cap keeps the colour of the cell it caps, a custom joiner's handed cap
851
+ * cap keeps the color of the cell it caps, a custom joiner's handed cap
852
852
  * included, and with the cell's own place where none is beside it — a
853
853
  * neighbour's edge the frame does not show, handed at the seam's cell. Any
854
- * other cell's colours turn with the region covering it: a letter in a
855
- * neighbour's fill colour is a word, not a cap. A cell the frame lists
854
+ * other cell's colors turn with the region covering it: a letter in a
855
+ * neighbour's fill color is a word, not a cap. A cell the frame lists
856
856
  * nowhere, which a hand-built `Drawn` hands, is one column wide. The place
857
857
  * is the region's centre column from the element's left edge, with its
858
858
  * centre row: a region that grows by a column moves its centre by half of
@@ -864,10 +864,10 @@ function segmentsOf(drawn) {
864
864
  const { colors } = drawn;
865
865
  const at = (row, col) => `${row}:${col}`;
866
866
  const listed = new Map();
867
- // Every column a seam cell covers: what a seam's colours look past to the region beside the seam.
867
+ // Every column a seam cell covers: what a seam's colors look past to the region beside the seam.
868
868
  const seams = new Set();
869
- // The colour a cell is a region of: its own ground, else on the terminal's ground its own ink, else none.
870
- const regionColour = (cell) => {
869
+ // The color a cell is a region of: its own ground, else on the terminal's ground its own ink, else none.
870
+ const regionColor = (cell) => {
871
871
  if (cell.seam)
872
872
  return undefined;
873
873
  const own = (terminal, hex) => (!terminal && colors.has(hex) ? hex : undefined);
@@ -878,7 +878,7 @@ function segmentsOf(drawn) {
878
878
  const index = new Map();
879
879
  for (const cell of drawn.all()) {
880
880
  listed.set(at(cell.row, cell.col), cell);
881
- const hex = regionColour(cell);
881
+ const hex = regionColor(cell);
882
882
  for (let col = cell.col; col < cell.col + cell.width; col++) {
883
883
  if (cell.seam)
884
884
  seams.add(at(cell.row, col));
@@ -888,7 +888,7 @@ function segmentsOf(drawn) {
888
888
  covered.push({ row: cell.row, col, hex });
889
889
  }
890
890
  }
891
- // Union-find over the covered columns: two of one colour beside or above one another are one region.
891
+ // Union-find over the covered columns: two of one color beside or above one another are one region.
892
892
  const parent = covered.map((_, i) => i);
893
893
  const find = (i) => {
894
894
  while (parent[i] !== i) {
@@ -920,7 +920,7 @@ function segmentsOf(drawn) {
920
920
  return i === undefined ? undefined : regions.get(find(i));
921
921
  };
922
922
  const placeOf = (region) => ({ row: (region.top + region.bottom) / 2, centre: (region.left + region.right + 1) / 2 });
923
- // The region past the seam from `col` in `step`'s direction: what a seam's colour turns with.
923
+ // The region past the seam from `col` in `step`'s direction: what a seam's color turns with.
924
924
  const pastSeam = (row, col, step) => {
925
925
  let past = col;
926
926
  while (seams.has(at(row, past)))
@@ -936,17 +936,17 @@ function segmentsOf(drawn) {
936
936
  };
937
937
  }
938
938
  /**
939
- * Wheel, as the colour of daylight going round: every hue turning the whole
939
+ * WheelEvent, as the color of daylight going round: every hue turning the whole
940
940
  * way round once a period — far too slowly to be seen moving, so a glance
941
941
  * sees a steady palette and an hour's absence a different one — each
942
942
  * segment of the element at its own pace. Swing: how far the segments stray
943
943
  * from one another, 0–1, 1 being half a turn apart. A segment is a region
944
- * of adjacent cells of one colour of the element's own, carried as their
944
+ * of adjacent cells of one color of the element's own, carried as their
945
945
  * ground or, on the terminal's ground, as their ink (`segmentsOf`), and it
946
- * paces by where it sits, not by its colour: two
947
- * regions of one fill at two places turn apart, and each colour of the seam
948
- * a joiner drew between them turns with the region of that colour beside
949
- * it, so a powerline cap keeps the colour of the cell it caps. An ink that
946
+ * paces by where it sits, not by its color: two
947
+ * regions of one fill at two places turn apart, and each color of the seam
948
+ * a joiner drew between them turns with the region of that color beside
949
+ * it, so a powerline cap keeps the color of the cell it caps. An ink that
950
950
  * is no fill turns with the ground it sits on, so a cell's words and fill
951
951
  * turn together. A segment's stray is slow noise at its place, at
952
952
  * `STRAY_PERIODS` in time and `STRAY_SPREAD` across the element, so
@@ -955,13 +955,13 @@ function segmentsOf(drawn) {
955
955
  * column moves its hue a little, and those it pushes along as little. Only the
956
956
  * stray reads the place: a place in the shared turn would jump a growing
957
957
  * run by `360·t·Δ/P`, without bound in `t`.
958
- * A colour keeps its luminance as it turns (`hueTurned`), so every pair of
959
- * words reads at the contrast it rested at; at 256 colours a turned colour
960
- * lands on a colour the depth writes, one that keeps every pair of words it
958
+ * A color keeps its luminance as it turns (`hueTurned`), so every pair of
959
+ * words reads at the contrast it rested at; at 256 colors a turned color
960
+ * lands on a color the depth writes, one that keeps every pair of words it
961
961
  * is drawn in at its floor (`floorOf`) — its resting contrast or WCAG AA,
962
962
  * whichever is lower — as `shares` keeps the light's (`turnedBounds`). The
963
- * turn only happens to the element's own colours (`Drawn.colors`), never the
964
- * terminal's. On a sixteen-colour terminal nothing turns (`stillWhereNothingMoves`).
963
+ * turn only happens to the element's own colors (`Drawn.colors`), never the
964
+ * terminal's. On a sixteen-color terminal nothing turns (`staticWhereUnchanged`).
965
965
  */
966
966
  export function wheel(curve, z) {
967
967
  requireCurve(curve);
@@ -978,35 +978,35 @@ export function wheel(curve, z) {
978
978
  // Measured from where it stood at the start, as pulse's wander is, so the
979
979
  // first frame turns nothing and the second has turned only a frame's worth.
980
980
  const turned = (place, t) => (360 * t) / P + curve.swing * STRAY * ((stray(place, t) - stray(place, 0)) / 1.5);
981
- // A turned colour's bounds rest on what a frame draws, never on `t`: settled once for each.
981
+ // A turned color's bounds rest on what a frame draws, never on `t`: settled once for each.
982
982
  const settled = new WeakMemo();
983
- return stillWhereNothingMoves(SIDES, ({ colors }) => colors, (drawn) => {
983
+ return staticWhereUnchanged(SIDES, ({ colors }) => colors, (drawn) => {
984
984
  const { colors, fills, depth, terminal } = drawn;
985
- // Whether a slot shows one of the element's colours: never the terminal's, whatever its hex.
985
+ // Whether a slot shows one of the element's colors: never the terminal's, whatever its hex.
986
986
  const own = (supplied, color) => !supplied && colors.has(color.hex);
987
- // The colour an own colour is bounded with, under which its bounds are
988
- // kept: itself if a fill or beside no own colour, else the ground beside
987
+ // The color an own color is bounded with, under which its bounds are
988
+ // kept: itself if a fill or beside no own color, else the ground beside
989
989
  // it. Where it turns is `segmentsOf`'s, not this.
990
990
  const boundWith = (color, other, theirs) => (fills.has(color.hex) || !theirs ? color.hex : other.hex);
991
- // The bounds a colour turns within, by the colour it is bounded with: one bound for every place a frame writes it at.
991
+ // The bounds a color turns within, by the color it is bounded with: one bound for every place a frame writes it at.
992
992
  const keyOf = (color, bound) => `${color.hex} ${bound}`;
993
993
  const bounds = settled.get(terminal, () => new Memo()).get(drawnKey(drawn), () => turnedBounds(drawn.pairs, depth, (pair, side) => {
994
994
  const other = side === "fg" ? "bg" : "fg";
995
995
  return own(pair.terminal[side], pair[side]) ? keyOf(pair[side], boundWith(pair[side], pair[other], own(pair.terminal[other], pair[other]))) : undefined;
996
996
  }));
997
- // 256 colours writes a turned colour as one it has an index for; truecolor
997
+ // 256 colors writes a turned color as one it has an index for; truecolor
998
998
  // as one of the two either side of its luminance.
999
999
  const indexed = measuredIndexed(depth, terminal);
1000
- // Where each cell's colours turn, read off the frame's listing once.
1000
+ // Where each cell's colors turn, read off the frame's listing once.
1001
1001
  const placeOf = segmentsOf(drawn);
1002
1002
  return Animation.continuous((t) => {
1003
- // [LAW:one-source-of-truth] A colour turned at a place within a
1003
+ // [LAW:one-source-of-truth] A color turned at a place within a
1004
1004
  // segment's bounds is written once a frame, wherever it falls: the
1005
1005
  // key is the bounds' key with the place, so one entry never serves
1006
1006
  // two bounds.
1007
1007
  const written = new Map();
1008
- // The bounds are measured from the colour the element drew; a layer
1009
- // beneath may have lit it, so they travel with it, and a lit colour
1008
+ // The bounds are measured from the color the element drew; a layer
1009
+ // beneath may have lit it, so they travel with it, and a lit color
1010
1010
  // turns round its own luminance rather than snapping back unturned.
1011
1011
  const choose = (color, rested, bound, place) => {
1012
1012
  const turn = hueTurned(color, turned(place, t));
@@ -1032,12 +1032,12 @@ export function wheel(curve, z) {
1032
1032
  /**
1033
1033
  * `color` turned `degrees` round the hue circle at its own relative
1034
1034
  * luminance, and at its own chroma where the gamut has it. WCAG contrast is
1035
- * a ratio of luminances, so a pair of colours each turned this way reads at
1035
+ * a ratio of luminances, so a pair of colors each turned this way reads at
1036
1036
  * the contrast it did; one turned at its OKLCH lightness instead does not,
1037
1037
  * since equal lightness is not equal luminance across hues. The lightness
1038
1038
  * is found by bisection, as `ensureContrastToward` finds one, and 8-bit
1039
- * rounding lands the luminance between two colours, so both are kept. At 0
1040
- * degrees the colour itself, exactly: an OKLCH round trip can land a step
1039
+ * rounding lands the luminance between two colors, so both are kept. At 0
1040
+ * degrees the color itself, exactly: an OKLCH round trip can land a step
1041
1041
  * off (`touchedAt`).
1042
1042
  */
1043
1043
  function hueTurned(color, degrees) {
@@ -1048,7 +1048,7 @@ function greyOf(color) {
1048
1048
  const { l, h, alpha } = Oklch.fromRgba(color);
1049
1049
  return new Oklch(l, 0, h, alpha).toRgba();
1050
1050
  }
1051
- /** `color` turned `degrees` round the hue circle and brought to `luminance`: the two 8-bit colours either side of it. */
1051
+ /** `color` turned `degrees` round the hue circle and brought to `luminance`: the two 8-bit colors either side of it. */
1052
1052
  function atLuminance(color, degrees, luminance) {
1053
1053
  const from = Oklch.fromRgba(color);
1054
1054
  const at = (l) => new Oklch(l, from.c, from.h + degrees, from.alpha).toRgba();
@@ -1060,34 +1060,34 @@ function atLuminance(color, degrees, luminance) {
1060
1060
  return { looks: at(bright), either: [at(dim), at(bright)] };
1061
1061
  }
1062
1062
  /**
1063
- * Where a colour sits on WCAG's contrast scale: the log of its contrast with
1064
- * black. Two colours' contrast ratio is `exp` of the gap between their
1065
- * stops, so a contrast to keep is a distance a colour may move.
1063
+ * Where a color sits on WCAG's contrast scale: the log of its contrast with
1064
+ * black. Two colors' contrast ratio is `exp` of the gap between their
1065
+ * stops, so a contrast to keep is a distance a color may move.
1066
1066
  */
1067
1067
  const stop = (color) => Math.log(contrastRatio(color, new ColorRgba(0, 0, 0)));
1068
1068
  const UNBOUNDED = { lo: -Infinity, hi: Infinity };
1069
- /** Bounds that admit no turn: the colour is written as it rests. */
1069
+ /** Bounds that admit no turn: the color is written as it rests. */
1070
1070
  const STILL = { lo: Infinity, hi: -Infinity };
1071
1071
  /**
1072
- * The widest 8-bit rounding parts `hueTurned`'s two colours, in stops. They
1072
+ * The widest 8-bit rounding parts `hueTurned`'s two colors, in stops. They
1073
1073
  * are one point of the hue rounded two ways, so they differ by at most a
1074
- * step in each channel, and a channel of a colour that dark or light can
1074
+ * step in each channel, and a channel of a color that dark or light can
1075
1075
  * take a step only so large: worst near a luminance of 0.05, at 3.03% of
1076
1076
  * contrast. Bounds at least this wide admit one of the two whatever the
1077
- * hue; narrower, a colour would turn at some hues and snap back to rest at
1077
+ * hue; narrower, a color would turn at some hues and snap back to rest at
1078
1078
  * the next, so it holds still.
1079
1079
  */
1080
1080
  const ROUNDING_GAP = Math.log(1.031);
1081
1081
  /**
1082
- * How far each turned colour's luminance may land from the one it rests at,
1082
+ * How far each turned color's luminance may land from the one it rests at,
1083
1083
  * keyed as `turnedAs` keys each side of a pair — none for a side that holds
1084
1084
  * still — so that every pair of `pairs` reads at its floor (`floorOf`) at
1085
- * `depth` however each of its colours lands within its bounds. Bounds
1085
+ * `depth` however each of its colors lands within its bounds. Bounds
1086
1086
  * narrower than `ROUNDING_GAP` are `STILL`.
1087
1087
  *
1088
1088
  * A pair's contrast to spare above its floor, as a gap between stops, is
1089
1089
  * shared out evenly between its sides that turn: the lighter may land that
1090
- * much darker, the darker that much lighter, and never further. So a colour
1090
+ * much darker, the darker that much lighter, and never further. So a color
1091
1091
  * bounded once for every pair it is drawn in is bounded by each of them at
1092
1092
  * once, and every way a frame writes it — the wheel turns it at each place
1093
1093
  * it falls — lands within them. A pair whose floor is 1:1 holds however it
@@ -1110,13 +1110,13 @@ function turnedBounds(pairs, depth, turnedAs) {
1110
1110
  return new Map([...bounds].map(([key, { lo, hi }]) => [key, hi - lo < ROUNDING_GAP ? STILL : { lo, hi }]));
1111
1111
  }
1112
1112
  const measured = (color) => ({ color, looks: Oklch.fromRgba(color), stop: stop(color) });
1113
- /** `ColorSpec.indexed`'s colours, measured once for each terminal and depth. */
1113
+ /** `ColorSpec.indexed`'s colors, measured once for each terminal and depth. */
1114
1114
  const measuredMemo = new WeakMemo();
1115
1115
  function measuredIndexed(depth, terminal) {
1116
1116
  return measuredMemo.get(ColorSpec.indexed(depth, terminal), (indexed) => indexed.map(([, color]) => measured(color)));
1117
1117
  }
1118
1118
  /**
1119
- * The colour that looks nearest `color` (OKLab's ΔE): `rest`, or one of
1119
+ * The color that looks nearest `color` (OKLab's ΔE): `rest`, or one of
1120
1120
  * `candidates` whose luminance is within `bounds`. `rest` is what the bounds
1121
1121
  * are measured from, so it is always offered; ties go to it, then to the
1122
1122
  * earlier.
@@ -1138,9 +1138,9 @@ function nearestOf(color, offered) {
1138
1138
  /** What a stop's float arithmetic can cost: a pair at its floor sits exactly on its bound. */
1139
1139
  const MEASURE = 1e-9;
1140
1140
  /**
1141
- * The stops a colour written at `depth` can land on: everything between black
1141
+ * The stops a color written at `depth` can land on: everything between black
1142
1142
  * and white in truecolor, and otherwise what the depth has an index for, so a
1143
- * sixteen-colour terminal's reach is its slots.
1143
+ * sixteen-color terminal's reach is its slots.
1144
1144
  */
1145
1145
  function reachOf(depth, terminal) {
1146
1146
  return depth === ColorDepth.TRUECOLOR ? [0, Math.log(21)] : extent(measuredIndexed(depth, terminal).map((candidate) => candidate.stop));
@@ -1150,7 +1150,7 @@ function reachOf(depth, terminal) {
1150
1150
  * room to read on the side of it the ink rested on: a light ink caps its ground
1151
1151
  * at the floor below the lightest the depth writes, a dark one bars it from the
1152
1152
  * floor above the darkest. Where a ground rests is always within: a depth also
1153
- * writes the terminal's own colour, which at sixteen colours lies past its
1153
+ * writes the terminal's own color, which at sixteen colors lies past its
1154
1154
  * terminal's reach, and a ground no layer moved comes back as it was. These are
1155
1155
  * what the depth's reach leaves, so a ground any one effect moves is inside them.
1156
1156
  */
@@ -1168,15 +1168,15 @@ function groundBounds(pairs, depth, terminal) {
1168
1168
  * Several effects on one element, as one effect: each layer is handed the look
1169
1169
  * the one before it left and the same cell, so later layers are on top, and each
1170
1170
  * recognises a cell by what the element drew (`EffectCell.pair`), never by the
1171
- * colour it is handed. A hue a layer turned onto another of the element's
1172
- * colours is not mistaken for it, and turns once. It changes when the soonest
1171
+ * color it is handed. A hue a layer turned onto another of the element's
1172
+ * colors is not mistaken for it, and turns once. It changes when the soonest
1173
1173
  * layer does, and with no layers it leaves every cell as drawn.
1174
1174
  *
1175
1175
  * Every word reads at its floor (`floorOf`), whatever the layers did and in
1176
- * whatever order. Each loop proves that for colours as the element drew them,
1177
- * and a colour another layer moved voids the proof: light lands at another
1176
+ * whatever order. Each loop proves that for colors as the element drew them,
1177
+ * and a color another layer moved voids the proof: light lands at another
1178
1178
  * luminance on a turned hue. So once the layers have moved a cell, its ground
1179
- * lands where its inks can still read (`groundBounds`), a function of the colour
1179
+ * lands where its inks can still read (`groundBounds`), a function of the color
1180
1180
  * alone so one fill stays one fill, and an ink that does not read on it slides
1181
1181
  * to the luminance that does, on the side of the ground it stands on while that
1182
1182
  * side has room. A layer that takes an ink across its ground crosses it here too:
@@ -1186,7 +1186,7 @@ function groundBounds(pairs, depth, terminal) {
1186
1186
  * it would be alone. A frame that takes a ground past its bounds is the one
1187
1187
  * exception, whatever its ink reads: a swap of ink and ground lands at the floor,
1188
1188
  * since a bound cannot ask what one cell's ink does without splitting a fill.
1189
- * The floor outranks a colour being the terminal's: a terminal
1189
+ * The floor outranks a color being the terminal's: a terminal
1190
1190
  * ink is moved here where a ground under it left it unreadable. The rule is the
1191
1191
  * same for one layer as for five: a veil (`veiled`, or a transition's `effect`)
1192
1192
  * stops at the floor here, so a transition goes in an `Effected` around this
@@ -1200,12 +1200,12 @@ export function layered(...effects) {
1200
1200
  const words = new Set(drawn.pairs.map(pairKey));
1201
1201
  const offered = depth === ColorDepth.TRUECOLOR ? undefined : measuredIndexed(depth, terminal);
1202
1202
  // `color` as itself if it is within the first of `bounds`, else the nearest
1203
- // colour the depth writes there, else the same of the next; as itself where
1203
+ // color the depth writes there, else the same of the next; as itself where
1204
1204
  // none has any. In truecolor that is its hue at the bound's luminance, or a
1205
- // grey there: a colour keeps its chroma only so near black or white, and a
1205
+ // grey there: a color keeps its chroma only so near black or white, and a
1206
1206
  // bound may be at either. `rested` is always offered, since a depth also
1207
- // writes the terminal's own colour, which it has no index for, and the
1208
- // colour a pair rested in is the one a bound was measured from.
1207
+ // writes the terminal's own color, which it has no index for, and the
1208
+ // color a pair rested in is the one a bound was measured from.
1209
1209
  const landing = (color, rested, ...bounds) => {
1210
1210
  const at = stop(color);
1211
1211
  for (const { lo, hi } of bounds) {
@@ -1220,9 +1220,9 @@ export function layered(...effects) {
1220
1220
  return color;
1221
1221
  };
1222
1222
  const floored = (pair, moved) => {
1223
- // [LAW:single-enforcer] Every measure below reads the colours as `Effected`
1223
+ // [LAW:single-enforcer] Every measure below reads the colors as `Effected`
1224
1224
  // writes them: rounded to the depth, and a translucent one laid on what is
1225
- // beneath it, so a colour that passes here cannot draw as one that fails.
1225
+ // beneath it, so a color that passes here cannot draw as one that fails.
1226
1226
  const ground = landing(Effected.shows(pair, moved, depth, terminal).bg, pair.bg, grounds.get(pair.bg.hex) ?? UNBOUNDED);
1227
1227
  const { fg } = Effected.shows(pair, { fg: moved.fg, bg: ground }, depth, terminal);
1228
1228
  const floor = floorOf(pair, depth);
@@ -1237,7 +1237,7 @@ export function layered(...effects) {
1237
1237
  return { fg: landing(fg, pair.fg, near, far), bg: ground };
1238
1238
  };
1239
1239
  return Animation.map(Animation.all(effects.map((effect) => effect(drawn))), (frames) => {
1240
- // A colour pair lands once a frame, wherever it falls. [LAW:one-source-of-truth]
1240
+ // A color pair lands once a frame, wherever it falls. [LAW:one-source-of-truth]
1241
1241
  const landed = new Map();
1242
1242
  return (look, cell) => {
1243
1243
  const moved = frames.reduce((under, frame) => frame(under, cell), look);
@@ -1250,23 +1250,23 @@ export function layered(...effects) {
1250
1250
  };
1251
1251
  }
1252
1252
  /**
1253
- * A signal: every cell's ink stepping through `steps`' colours on a beat,
1254
- * one colour a hold, looping, for the time it is handed — a gauge's
1255
- * chevrons in bright changing colours. The ground is left as the cell draws
1253
+ * A signal: every cell's ink stepping through `steps`' colors on a beat,
1254
+ * one color a hold, looping, for the time it is handed — a gauge's
1255
+ * chevrons in bright changing colors. The ground is left as the cell draws
1256
1256
  * it, and the words stay legible on it: each step's ink is written as the
1257
- * colour nearest the step's (OKLab's ΔE) that the depth draws and that
1257
+ * color nearest the step's (OKLab's ΔE) that the depth draws and that
1258
1258
  * reads on the cell's ground, as `Effected` writes it, at the cell's floor
1259
1259
  * (`floorOf`) — its resting contrast or `LEGIBLE`, whichever is lower, the
1260
1260
  * floor every loop here keeps. In truecolor that is the step itself, or its
1261
1261
  * hue and chroma at the lightness nearest it that reaches the floor
1262
- * (`ensureContrast`, the one slide a template's `readableOn` makes too);
1263
- * below truecolor, the nearest of the colours the depth has an index for, so on a
1264
- * sixteen-colour terminal it steps from slot to slot, the one effect here
1265
- * that does (`stillWhereNothingMoves`). A cell whose ground no offered colour reads
1262
+ * (`ensureContrast`, the one slide a template's `ensureContrast` makes too);
1263
+ * below truecolor, the nearest of the colors the depth has an index for, so on a
1264
+ * sixteen-color terminal it steps from slot to slot, the one effect here
1265
+ * that does (`staticWhereUnchanged`). A cell whose ground no offered color reads
1266
1266
  * on keeps the ink it rested in.
1267
1267
  *
1268
1268
  * The search is settled once for each ground and step — below truecolor,
1269
- * which colours read on a ground once for that ground — so a frame
1269
+ * which colors read on a ground once for that ground — so a frame
1270
1270
  * inside a step redoes none, and nor does a later pass of the beat: at sixty
1271
1271
  * frames a second a one-second step is sixty identical frames, and the
1272
1272
  * frame changes when the beat does: the signal is the beat mapped to inks.
@@ -1274,7 +1274,7 @@ export function layered(...effects) {
1274
1274
  * nothing here reads a clock.
1275
1275
  */
1276
1276
  export function steppedInk(steps) {
1277
- const rhythm = beat(steps.map((step) => step.seconds));
1277
+ const rhythm = stepSequence(steps.map((step) => step.seconds));
1278
1278
  // A copy, as `beat` keeps of the holds: what the signal plays stays what
1279
1279
  // `steps` said whatever the caller does to its array.
1280
1280
  const inks = steps.map((step) => step.color);
@@ -1293,10 +1293,10 @@ export function steppedInk(steps) {
1293
1293
  * what an earlier one drew redoes no search. [LAW:single-enforcer] Every
1294
1294
  * effect that writes an ink of the caller's choosing writes it through
1295
1295
  * here — `steppedInk`'s steps and `gradient`'s ramp alike — so none of them
1296
- * leaves a word unreadable. Below truecolor which of the depth's colours
1296
+ * leaves a word unreadable. Below truecolor which of the depth's colors
1297
1297
  * read on a pair does not depend on the ink, so it is settled once for each
1298
1298
  * pair: a ramp, whose every frame offers inks no frame offered before,
1299
- * then costs a nearest-colour scan a cell, not a contrast test of the whole
1299
+ * then costs a nearest-color scan a cell, not a contrast test of the whole
1300
1300
  * palette.
1301
1301
  */
1302
1302
  function legibleInks() {
@@ -1316,9 +1316,9 @@ function legibleInks() {
1316
1316
  };
1317
1317
  }
1318
1318
  /**
1319
- * Of `offered`, the colours that read on `pair`'s ground at `pair`'s floor,
1319
+ * Of `offered`, the colors that read on `pair`'s ground at `pair`'s floor,
1320
1320
  * as `Effected` writes them (`shows`). [LAW:one-source-of-truth] Measured the
1321
- * way `shares` and `wheel` measure: the written colours, at the catalogue's
1321
+ * way `shares` and `wheel` measure: the written colors, at the catalogue's
1322
1322
  * floor. An ink none of them is nearest stays `pair`'s own.
1323
1323
  */
1324
1324
  function legibleAmong(pair, offered, depth, terminal) {
@@ -1336,7 +1336,7 @@ function legibleAmong(pair, offered, depth, terminal) {
1336
1336
  * (`ATTRIBUTE_DEFAULTS`) [LAW:one-type-per-behavior].
1337
1337
  */
1338
1338
  export function blink({ attribute, off, on }) {
1339
- const rhythm = beat([off, on]);
1339
+ const rhythm = stepSequence([off, on]);
1340
1340
  const frames = [(look) => look, (look) => ({ ...look, attributes: { ...look.attributes, [attribute]: true } })];
1341
1341
  return () => Animation.map(rhythm, (i) => frames[i]);
1342
1342
  }
@@ -1353,7 +1353,7 @@ export const ATTRIBUTE_DEFAULTS = {
1353
1353
  * Every effect in the catalogue by its name in `EFFECT_CURVES`, each made the
1354
1354
  * one way: for an element, at its signed-off curve unless handed another,
1355
1355
  * with the light and the width it was signed off with. A loop comes lit on
1356
- * the element's own colours (`onColors`); a transition comes as itself, to
1356
+ * the element's own colors (`onColors`); a transition comes as itself, to
1357
1357
  * be told when it is done. [LAW:types-are-the-program] Keyed by the two
1358
1358
  * name types, which `EFFECT_CURVES` is held to as well: a curve signed off
1359
1359
  * does not compile until it is named a loop or a transition, and then not