@promptctl/rich-js 0.27.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 (185) hide show
  1. package/README.md +7 -6
  2. package/THIRD-PARTY-NOTICES +42 -0
  3. package/dist/core/animation.d.ts +65 -46
  4. package/dist/core/animation.js +91 -67
  5. package/dist/core/ansi.d.ts +19 -12
  6. package/dist/core/ansi.js +114 -36
  7. package/dist/core/bidi-data.d.ts +14 -0
  8. package/dist/core/bidi-data.js +385 -0
  9. package/dist/core/bidi.d.ts +89 -0
  10. package/dist/core/bidi.js +615 -0
  11. package/dist/core/box.d.ts +9 -1
  12. package/dist/core/box.js +11 -0
  13. package/dist/core/cell-widths.d.ts +3 -2
  14. package/dist/core/cell-widths.js +93 -93
  15. package/dist/core/cells.d.ts +20 -0
  16. package/dist/core/cells.js +94 -6
  17. package/dist/core/clock.d.ts +1 -1
  18. package/dist/core/color.d.ts +56 -57
  19. package/dist/core/color.js +79 -81
  20. package/dist/core/console.d.ts +24 -7
  21. package/dist/core/console.js +27 -8
  22. package/dist/core/control-codes.d.ts +61 -0
  23. package/dist/core/control-codes.js +157 -0
  24. package/dist/core/easing.d.ts +40 -40
  25. package/dist/core/easing.js +40 -40
  26. package/dist/core/env.d.ts +6 -0
  27. package/dist/core/env.js +9 -1
  28. package/dist/core/export-html.js +5 -4
  29. package/dist/core/export-lines.d.ts +13 -11
  30. package/dist/core/export-lines.js +19 -15
  31. package/dist/core/export-svg.d.ts +3 -3
  32. package/dist/core/export-svg.js +10 -9
  33. package/dist/core/json.js +9 -2
  34. package/dist/core/markup.js +5 -4
  35. package/dist/core/memo.d.ts +1 -1
  36. package/dist/core/memo.js +1 -1
  37. package/dist/core/oklch.d.ts +14 -14
  38. package/dist/core/oklch.js +10 -10
  39. package/dist/core/orient.d.ts +68 -0
  40. package/dist/core/orient.js +224 -0
  41. package/dist/core/osc8.d.ts +12 -5
  42. package/dist/core/osc8.js +14 -7
  43. package/dist/core/paint.d.ts +9 -9
  44. package/dist/core/paint.js +3 -3
  45. package/dist/core/pretty.d.ts +1 -1
  46. package/dist/core/pretty.js +23 -19
  47. package/dist/core/protocol.d.ts +47 -26
  48. package/dist/core/protocol.js +31 -23
  49. package/dist/core/render.d.ts +19 -14
  50. package/dist/core/render.js +30 -17
  51. package/dist/core/seal.d.ts +26 -0
  52. package/dist/core/seal.js +75 -0
  53. package/dist/core/segment.d.ts +12 -4
  54. package/dist/core/segment.js +16 -6
  55. package/dist/core/strip.d.ts +83 -83
  56. package/dist/core/strip.js +70 -70
  57. package/dist/core/style.d.ts +46 -18
  58. package/dist/core/style.js +92 -56
  59. package/dist/core/text-order.d.ts +51 -0
  60. package/dist/core/text-order.js +50 -0
  61. package/dist/core/text.d.ts +12 -2
  62. package/dist/core/text.js +136 -90
  63. package/dist/core/timeline.d.ts +5 -5
  64. package/dist/core/timeline.js +11 -11
  65. package/dist/core/tween.d.ts +6 -6
  66. package/dist/core/tween.js +10 -10
  67. package/dist/core/view.d.ts +5 -5
  68. package/dist/core/view.js +15 -14
  69. package/dist/core/wrap.d.ts +24 -6
  70. package/dist/core/wrap.js +82 -16
  71. package/dist/host/app.d.ts +50 -9
  72. package/dist/host/app.js +120 -15
  73. package/dist/host/host-environment.d.ts +2 -2
  74. package/dist/host/host-environment.js +2 -2
  75. package/dist/host/index.d.ts +3 -2
  76. package/dist/host/index.js +1 -0
  77. package/dist/host/input.d.ts +42 -9
  78. package/dist/host/input.js +91 -7
  79. package/dist/host/terminal-colors.d.ts +18 -0
  80. package/dist/host/terminal-colors.js +46 -0
  81. package/dist/host/terminal-host.d.ts +4 -3
  82. package/dist/host/terminal-host.js +1 -1
  83. package/dist/index.d.ts +17 -16
  84. package/dist/index.js +11 -11
  85. package/dist/node/prompt.d.ts +1 -1
  86. package/dist/node/prompt.js +1 -1
  87. package/dist/node/terminal-host.d.ts +1 -1
  88. package/dist/node/traceback.js +2 -2
  89. package/dist/renderables/change.d.ts +1 -3
  90. package/dist/renderables/change.js +6 -8
  91. package/dist/renderables/effect.d.ts +88 -87
  92. package/dist/renderables/effect.js +124 -114
  93. package/dist/renderables/effects.d.ts +105 -76
  94. package/dist/renderables/effects.js +309 -176
  95. package/dist/renderables/embed.d.ts +1 -1
  96. package/dist/renderables/embed.js +2 -2
  97. package/dist/renderables/flexStrip.d.ts +7 -7
  98. package/dist/renderables/flexStrip.js +15 -15
  99. package/dist/renderables/frames.d.ts +7 -16
  100. package/dist/renderables/frames.js +12 -27
  101. package/dist/renderables/glyph-effect.d.ts +22 -23
  102. package/dist/renderables/glyph-effect.js +43 -41
  103. package/dist/renderables/glyph-effects.d.ts +33 -14
  104. package/dist/renderables/glyph-effects.js +90 -22
  105. package/dist/renderables/html-entities.d.ts +1 -1
  106. package/dist/renderables/html-entities.js +1 -1
  107. package/dist/renderables/lifecycle.d.ts +8 -10
  108. package/dist/renderables/lifecycle.js +17 -18
  109. package/dist/renderables/live.d.ts +2 -2
  110. package/dist/renderables/live.js +7 -7
  111. package/dist/renderables/markdown-parse.js +3 -3
  112. package/dist/renderables/markdown.js +4 -2
  113. package/dist/renderables/panel.js +7 -5
  114. package/dist/renderables/progress.d.ts +1 -1
  115. package/dist/renderables/progress.js +1 -1
  116. package/dist/renderables/progressBar.d.ts +3 -3
  117. package/dist/renderables/progressBar.js +21 -18
  118. package/dist/renderables/prompt.d.ts +1 -1
  119. package/dist/renderables/scroll.d.ts +1 -1
  120. package/dist/renderables/scroll.js +3 -3
  121. package/dist/renderables/spinner.d.ts +4 -4
  122. package/dist/renderables/spinner.js +10 -10
  123. package/dist/renderables/syntax.d.ts +2 -2
  124. package/dist/renderables/syntax.js +9 -4
  125. package/dist/renderables/table.js +5 -4
  126. package/dist/renderables/traceback.d.ts +1 -1
  127. package/dist/renderables/traceback.js +1 -1
  128. package/dist/renderables/tree.js +3 -3
  129. package/dist/template-bindings/animation-funcs.d.ts +60 -29
  130. package/dist/template-bindings/animation-funcs.js +126 -43
  131. package/dist/template-bindings/color-funcs.d.ts +14 -14
  132. package/dist/template-bindings/color-funcs.js +25 -25
  133. package/dist/template-bindings/index.d.ts +16 -14
  134. package/dist/template-bindings/index.js +17 -14
  135. package/dist/template-bindings/palette-funcs.d.ts +2 -2
  136. package/dist/template-bindings/palette-funcs.js +4 -4
  137. package/dist/template-bindings/style-funcs.d.ts +5 -5
  138. package/dist/template-bindings/style-funcs.js +16 -16
  139. package/dist/themes/{transpose.d.ts → adjust.d.ts} +23 -23
  140. package/dist/themes/{transpose.js → adjust.js} +28 -28
  141. package/dist/themes/buildPalette.d.ts +6 -5
  142. package/dist/themes/buildPalette.js +22 -22
  143. package/dist/themes/colorMath.d.ts +16 -16
  144. package/dist/themes/colorMath.js +32 -32
  145. package/dist/themes/data/ansi-tables.d.ts +2 -2
  146. package/dist/themes/data/ansi-tables.js +2 -2
  147. package/dist/themes/data/index.js +1 -4
  148. package/dist/themes/data/textual-ansi.js +0 -143
  149. package/dist/themes/data/types.d.ts +4 -4
  150. package/dist/themes/data/types.js +2 -2
  151. package/dist/themes/palette.d.ts +12 -12
  152. package/dist/themes/palette.js +16 -16
  153. package/dist/themes/ramp.d.ts +17 -17
  154. package/dist/themes/ramp.js +12 -12
  155. package/dist/themes/registry.d.ts +6 -8
  156. package/dist/themes/registry.js +6 -8
  157. package/dist/themes/terminalThemes.d.ts +4 -11
  158. package/dist/themes/terminalThemes.js +31 -47
  159. package/dist/widgets/button.d.ts +3 -3
  160. package/dist/widgets/button.js +8 -8
  161. package/dist/widgets/checkbox.d.ts +2 -2
  162. package/dist/widgets/checkbox.js +4 -4
  163. package/dist/widgets/dropdown.d.ts +3 -3
  164. package/dist/widgets/dropdown.js +7 -5
  165. package/dist/widgets/event-router.d.ts +6 -6
  166. package/dist/widgets/event-router.js +2 -2
  167. package/dist/widgets/focus-manager.d.ts +3 -3
  168. package/dist/widgets/focus-manager.js +3 -3
  169. package/dist/widgets/index.d.ts +2 -2
  170. package/dist/widgets/index.js +1 -1
  171. package/dist/widgets/ink.d.ts +3 -3
  172. package/dist/widgets/ink.js +4 -4
  173. package/dist/widgets/slider.d.ts +2 -2
  174. package/dist/widgets/static-item.d.ts +2 -2
  175. package/dist/widgets/text-input.d.ts +2 -2
  176. package/dist/widgets/text-input.js +5 -5
  177. package/dist/widgets/themed-widget.js +4 -4
  178. package/dist/widgets/toggle.d.ts +4 -4
  179. package/dist/widgets/toggle.js +6 -6
  180. package/dist/widgets/types.d.ts +6 -6
  181. package/dist/widgets/types.js +2 -2
  182. package/dist/widgets/widget-app.d.ts +10 -10
  183. package/dist/widgets/widget-app.js +8 -8
  184. package/dist/widgets/widget-base.d.ts +2 -2
  185. package/package.json +5 -4
@@ -20,7 +20,7 @@ const HALF_BAR_LEFT = "╺";
20
20
  */
21
21
  const PULSE_CURVE = { ...EFFECT_CURVES.shimmer, seconds: 5 };
22
22
  const PULSE_REACH = 10;
23
- /** Rich's pulse back where the back style names no colour. */
23
+ /** Rich's pulse back where the back style names no color. */
24
24
  const PULSE_BACK = ColorSpec.fromRgba(new ColorRgba(0, 0, 0));
25
25
  export class ProgressBar {
26
26
  // Rich's ProgressBar is updated after it is built: `bar.completed = n`.
@@ -70,31 +70,34 @@ export class ProgressBar {
70
70
  return;
71
71
  }
72
72
  const depth = options.colorSystem ?? ColorDepth.TRUECOLOR;
73
- // Rich's pulse resolves its colours on the terminal a depth assumes, as
74
- // `get_truecolor` does with no theme; so does this.
75
- const theme = resolveTerminal(undefined, depth);
76
- // Rich writes the pulse's back as a colour of its own, black where the
77
- // back style names none, so the light always has the bar's colour to fall
73
+ // Rich's pulse resolves its colors on the terminal a depth assumes, as
74
+ // `get_truecolor` does with no theme; so does this with none named.
75
+ const terminal = resolveTerminal(options.terminal, depth);
76
+ // Rich writes the pulse's back as a color of its own, black where the
77
+ // back style names none, so the light always has the bar's color to fall
78
78
  // on: one the terminal supplies takes no light.
79
79
  const style = getStyle(options, this.style);
80
- const ink = style.color?.isDefault === false ? style.color : ColorSpec.fromRgba((style.color ?? PULSE_BACK).getTruecolor(theme, true));
80
+ const ink = style.color?.isDefault === false ? style.color : ColorSpec.fromRgba((style.color ?? PULSE_BACK).getTruecolor(terminal, true));
81
81
  const back = style.add(Style.fromColor(ink));
82
82
  // [LAW:no-ambient-temporal-coupling] The moment is the frame's; the bar
83
83
  // only says what it looks like then. An empty track, lit by the shimmer.
84
84
  const track = { render: (o) => this.track(o, width, 0, back) };
85
- yield* render(new Effected(track, this.pulseEffect(options, back, depth, theme), { key: "progress-bar", theme }), options);
85
+ yield* render(new Effected(track, this.pulseEffect(options, back), { key: "progress-bar" }), options);
86
86
  }
87
87
  /**
88
- * The shimmer over this bar's back colour as `Effected` sees it drawn at
89
- * `depth`, toward `bar.pulse`. A bar has no words to keep legible, so its
90
- * back takes the whole of the light.
88
+ * The shimmer over this bar's back color as `Effected` sees it drawn,
89
+ * toward `bar.pulse`. A bar has no words to keep legible, so its back takes
90
+ * the whole of the light.
91
91
  */
92
- pulseEffect(options, back, depth, theme) {
93
- const ink = back.drawnColors(depth).color ?? ColorSpec.default();
94
- const glow = getStyle(options, "bar.pulse").drawnColors(depth).color ?? ColorSpec.default();
95
- const loop = shimmer(PULSE_CURVE, PULSE_REACH, glow.getTruecolor(theme, true), 0);
96
- const whole = new Map([[ink.getTruecolor(theme, true).hex, 1]]);
97
- return atShares(loop, () => whole);
92
+ pulseEffect(options, back) {
93
+ return (drawn) => {
94
+ const { depth, terminal } = drawn;
95
+ const ink = back.drawnColors(depth).color ?? ColorSpec.default();
96
+ const glow = getStyle(options, "bar.pulse").drawnColors(depth).color ?? ColorSpec.default();
97
+ const loop = shimmer(PULSE_CURVE, PULSE_REACH, glow.getTruecolor(terminal, true), 0);
98
+ const whole = new Map([[ink.getTruecolor(terminal, true).hex, 1]]);
99
+ return atShares(loop, () => whole)(drawn);
100
+ };
98
101
  }
99
102
  /** Rich 9d8f9a3 `__rich_console__`: the fill is counted in half cells, and a total of zero is a bar already full. */
100
103
  completeHalves(width) {
@@ -114,7 +117,7 @@ export class ProgressBar {
114
117
  yield new Segment(bar.repeat(barCount), fillStyle);
115
118
  if (halfBarCount)
116
119
  yield new Segment(drawable(options, HALF_BAR_RIGHT, " "), fillStyle);
117
- // The back is drawn in colour alone, so an output with none leaves it blank.
120
+ // The back is drawn in color alone, so an output with none leaves it blank.
118
121
  if (options.colorSystem === null)
119
122
  return;
120
123
  let remaining = width - barCount - halfBarCount;
@@ -29,7 +29,7 @@ import { RichText } from "../core/text.js";
29
29
  * on, and resolves with the raw user response. Implementations decide where
30
30
  * the input comes from (stdin readline, network, in-memory queue, etc.); the
31
31
  * console is what the prompt is drawn with, as Rich's `Console.input` draws
32
- * it — its theme, its colours, the target it writes to.
32
+ * it — its theme, its colors, the target it writes to.
33
33
  *
34
34
  * [LAW:effects-at-boundaries] The prompt arrives as a `RichText`, not bytes:
35
35
  * encoding it is the console's job, and the console arrives with it.
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * [LAW:one-type-per-behavior] A marquee, a scroll and a slide are one window
7
7
  * and differ only in the motion handed in: the offset along each axis is an
8
- * `Animation<number>`, so a marquee is a looping `Phase` scaled to the span
8
+ * `Animation<number>`, so a marquee is a looping `WrapMode` scaled to the span
9
9
  * it crosses and a slide-in is a `tween` to 0, and when the window next
10
10
  * changes is `Animation.all`'s answer over the two. Nothing here knows which
11
11
  * one it is drawing.
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * [LAW:one-type-per-behavior] A marquee, a scroll and a slide are one window
7
7
  * and differ only in the motion handed in: the offset along each axis is an
8
- * `Animation<number>`, so a marquee is a looping `Phase` scaled to the span
8
+ * `Animation<number>`, so a marquee is a looping `WrapMode` scaled to the span
9
9
  * it crosses and a slide-in is a `tween` to 0, and when the window next
10
10
  * changes is `Animation.all`'s answer over the two. Nothing here knows which
11
11
  * one it is drawing.
@@ -91,8 +91,8 @@ class ScrollFrame {
91
91
  // [LAW:one-source-of-truth] What the window shows is cut from the whole content, so it may change
92
92
  // when anything in the content does — a row above the slot entering pushes every row down, and run
93
93
  // backwards, leaving it pulls them up. The cut throws away the cells that hold rode on, so it is
94
- // told to the frame (`RenderOptions.onHold`).
95
- options.onHold?.({ lastChange: Segment.lastChange(drawn), nextChange: Segment.nextChange(drawn) });
94
+ // told to the frame (`RenderOptions.onValidity`).
95
+ options.onValidity?.({ validFrom: Segment.validFrom(drawn), validUntil: Segment.validUntil(drawn) });
96
96
  for (let r = 0; r < this.height; r++) {
97
97
  const line = lines[this.row + r] ?? [];
98
98
  const shown = Segment.divide(line, [start, start + width - blank])[1] ?? [];
@@ -75,8 +75,8 @@ export declare class Spinner implements Animation<Renderable & Measurable> {
75
75
  private readonly _ascii;
76
76
  /**
77
77
  * The spinner drawn at the frame's time, its cells saying the hold it is in
78
- * on the host's clock (`stampFrame`) — exactly, for the frames the output
79
- * draws, where `nextChange` must answer for both. Under reduced motion it
78
+ * on the host's clock (`stampValidityFrame`) — exactly, for the frames the output
79
+ * draws, where `validUntil` must answer for both. Under reduced motion it
80
80
  * is the spinner reduced, which never turns (`shownAt`).
81
81
  */
82
82
  private readonly _placed;
@@ -109,9 +109,9 @@ export declare class Spinner implements Animation<Renderable & Measurable> {
109
109
  * output keeps its own interval. An early answer, on the output that draws
110
110
  * the other one, which the contract allows; the drawn cells carry the exact one.
111
111
  */
112
- nextChange(t: number): number;
112
+ validUntil(t: number): number;
113
113
  /** When the spinner drawn at `t` began to be drawn: the later of its two sequences' answers, by the same argument. */
114
- lastChange(t: number): number;
114
+ validFrom(t: number): number;
115
115
  private _render;
116
116
  /**
117
117
  * The frame in progress and the label as one text, as Rich's
@@ -46,7 +46,7 @@
46
46
  * frame forever, which a paused timeline is and a rate is not, and the
47
47
  * timeline refuses it.
48
48
  */
49
- import { Animation, frameTimeOf, holdOf, shownAt, stampFrame } from "../core/animation.js";
49
+ import { Animation, frameTimeOf, validityOf, shownAt, stampValidityFrame } from "../core/animation.js";
50
50
  import { Segment } from "../core/segment.js";
51
51
  import { NULL_STYLE } from "../core/style.js";
52
52
  import { RichText } from "../core/text.js";
@@ -88,8 +88,8 @@ export class Spinner {
88
88
  _ascii;
89
89
  /**
90
90
  * The spinner drawn at the frame's time, its cells saying the hold it is in
91
- * on the host's clock (`stampFrame`) — exactly, for the frames the output
92
- * draws, where `nextChange` must answer for both. Under reduced motion it
91
+ * on the host's clock (`stampValidityFrame`) — exactly, for the frames the output
92
+ * draws, where `validUntil` must answer for both. Under reduced motion it
93
93
  * is the spinner reduced, which never turns (`shownAt`).
94
94
  */
95
95
  _placed;
@@ -113,7 +113,7 @@ export class Spinner {
113
113
  this._placed = this._frame((played, options) => {
114
114
  const t = frameTimeOf(options);
115
115
  const shown = shownAt(played, t, options);
116
- return { value: shown.value, hold: holdOf(shown, t) };
116
+ return { value: shown.value, validity: validityOf(shown, t) };
117
117
  });
118
118
  }
119
119
  get frames() {
@@ -150,9 +150,9 @@ export class Spinner {
150
150
  const drawn = (options) => drawable(options, this._named, this._ascii, sequenceGlyphs);
151
151
  return {
152
152
  render: (options) => {
153
- const { value, hold } = choose(drawn(options).played, options);
153
+ const { value, validity } = choose(drawn(options).played, options);
154
154
  const segments = this._render(value, options);
155
- return hold === undefined ? segments : stampFrame(segments, hold);
155
+ return validity === undefined ? segments : stampValidityFrame(segments, validity);
156
156
  },
157
157
  // Measured at the frames' one width, the widest frame's, so a column
158
158
  // holding it never narrows as it turns, and never offered less than
@@ -172,12 +172,12 @@ export class Spinner {
172
172
  * output keeps its own interval. An early answer, on the output that draws
173
173
  * the other one, which the contract allows; the drawn cells carry the exact one.
174
174
  */
175
- nextChange(t) {
176
- return Math.min(this._named.played.nextChange(t), this._ascii.played.nextChange(t));
175
+ validUntil(t) {
176
+ return Math.min(this._named.played.validUntil(t), this._ascii.played.validUntil(t));
177
177
  }
178
178
  /** When the spinner drawn at `t` began to be drawn: the later of its two sequences' answers, by the same argument. */
179
- lastChange(t) {
180
- return Math.max(this._named.played.lastChange(t), this._ascii.played.lastChange(t));
179
+ validFrom(t) {
180
+ return Math.max(this._named.played.validFrom(t), this._ascii.played.validFrom(t));
181
181
  }
182
182
  *_render(frame, options) {
183
183
  yield* render(this._line(frame, options), options);
@@ -2,10 +2,10 @@
2
2
  * Syntax — renders source code with basic syntax highlighting.
3
3
  * Uses built-in tokenization (no external dependency).
4
4
  *
5
- * Colours are names in the console's theme (`syntax.keyword`,
5
+ * Colors are names in the console's theme (`syntax.keyword`,
6
6
  * `syntax.line_number`, …), not a per-instance palette: a theme is how every
7
7
  * other renderable here is restyled, and a second styling vocabulary on this
8
- * one class would be a second answer to "what colour is a keyword".
8
+ * one class would be a second answer to "what color is a keyword".
9
9
  */
10
10
  import { Segment } from "../core/segment.js";
11
11
  import { type Measurable, type Renderable, type RenderOptions } from "../core/protocol.js";
@@ -2,12 +2,13 @@
2
2
  * Syntax — renders source code with basic syntax highlighting.
3
3
  * Uses built-in tokenization (no external dependency).
4
4
  *
5
- * Colours are names in the console's theme (`syntax.keyword`,
5
+ * Colors are names in the console's theme (`syntax.keyword`,
6
6
  * `syntax.line_number`, …), not a per-instance palette: a theme is how every
7
7
  * other renderable here is restyled, and a second styling vocabulary on this
8
- * one class would be a second answer to "what colour is a keyword".
8
+ * one class would be a second answer to "what color is a keyword".
9
9
  */
10
- import { cellLen, expandTabs, parseTabSize } from "../core/cells.js";
10
+ import { escaped } from "../core/bidi.js";
11
+ import { cellLen, expandTabs, parseTabSize, unicodeEscape } from "../core/cells.js";
11
12
  import { Segment } from "../core/segment.js";
12
13
  import { RichText } from "../core/text.js";
13
14
  import { drawable, groundedStyle, getStyle, withBoundedWidth, withCellWidth, } from "../core/protocol.js";
@@ -89,7 +90,11 @@ export class Syntax {
89
90
  this.wordWrap = options?.wordWrap ?? false;
90
91
  const tabSize = parseTabSize(options?.tabSize ?? 4);
91
92
  this.tabSize = tabSize;
92
- this._lines = expandTabs(code, tabSize).text.split("\n");
93
+ // Code on show: what the wire would drop is escaped, and an explicit bidi
94
+ // control with it. That is the Trojan Source attack: the order and the lines on
95
+ // screen differing from what the compiler reads. A joiner stays, since a Persian
96
+ // comment or an emoji string needs it. Only a CRLF's CR goes, with its line ending.
97
+ this._lines = expandTabs(escaped(code.replace(/\r\n/g, "\n"), unicodeEscape), tabSize).text.split("\n");
93
98
  }
94
99
  /**
95
100
  * The lines shown, as a 0-based half-open slice of `_lines`, and the number
@@ -6,7 +6,7 @@ import { Segment } from "../core/segment.js";
6
6
  import { NULL_STYLE } from "../core/style.js";
7
7
  import { HEAVY_HEAD } from "../core/box.js";
8
8
  import { RichText } from "../core/text.js";
9
- import { EmbeddedText, embed, present } from "./embed.js";
9
+ import { EmbeddedText, embed, mounted } from "./embed.js";
10
10
  import { Padding, normalizePadding } from "./padding.js";
11
11
  import { exactWeights, ratioDistribute, ratioReduce } from "./ratio.js";
12
12
  import { getStyle, render, stackedHeight, withBoundedWidth, withCellWidth } from "../core/protocol.js";
@@ -367,9 +367,9 @@ export class Table {
367
367
  // An empty title draws no row, as the reference's `if self.title` draws
368
368
  // none; markup that styles nothing is a title, and draws a blank one.
369
369
  const titleVal = options?.title;
370
- this._title = present(titleVal) ? new EmbeddedText(titleVal) : undefined;
370
+ this._title = mounted(titleVal) ? new EmbeddedText(titleVal) : undefined;
371
371
  const captionVal = options?.caption;
372
- this._caption = present(captionVal) ? new EmbeddedText(captionVal) : undefined;
372
+ this._caption = mounted(captionVal) ? new EmbeddedText(captionVal) : undefined;
373
373
  // A declared width is the table's size, not a ceiling on it: the
374
374
  // reference's `expand` is `self._expand or self.width is not None`, and a
375
375
  // port that sized to content inside it drew Rich code narrower than Rich.
@@ -454,11 +454,12 @@ export class Table {
454
454
  // that is all it styles: the reference reads it as `table_style` for the
455
455
  // frame and nowhere else, so a cell is untouched by it.
456
456
  const borderStyle = getStyle(options, this.style).add(getStyle(options, this.borderStyle));
457
- const border = borderStyle.isNull ? undefined : borderStyle;
458
457
  // The one division of the width every row below is measured against.
459
458
  const outerWidth = this._outerWidth(options);
460
459
  const geometry = this._geometry(options, outerWidth);
461
460
  const edge = geometry.edge === 1;
461
+ // Only an edge closes a frame: without one, a table's rules are not a border.
462
+ const border = box && edge ? box.frame(borderStyle) : borderStyle.isNull ? undefined : borderStyle;
462
463
  // Title
463
464
  if (this._title) {
464
465
  yield* this._renderTitle(options, this._title, geometry.totalWidth, this.titleStyle, this.titleJustify);
@@ -13,7 +13,7 @@
13
13
  * an inspector session pausing on exceptions — would have to pause on every
14
14
  * throw, caught ones included, to serve `new Traceback(caughtError)`, and it
15
15
  * cannot exist in a browser at all. Nor is there a `width` or `theme`: those
16
- * sized and coloured a source-code excerpt this renderer does not produce.
16
+ * sized and colored a source-code excerpt this renderer does not produce.
17
17
  */
18
18
  import { Segment } from "../core/segment.js";
19
19
  import { type Renderable, type RenderOptions } from "../core/protocol.js";
@@ -13,7 +13,7 @@
13
13
  * an inspector session pausing on exceptions — would have to pause on every
14
14
  * throw, caught ones included, to serve `new Traceback(caughtError)`, and it
15
15
  * cannot exist in a browser at all. Nor is there a `width` or `theme`: those
16
- * sized and coloured a source-code excerpt this renderer does not produce.
16
+ * sized and colored a source-code excerpt this renderer does not produce.
17
17
  */
18
18
  import { Segment } from "../core/segment.js";
19
19
  import { RichText } from "../core/text.js";
@@ -18,15 +18,15 @@ const ASCII_GUIDES = { fork: "+-- ", end: "`-- ", continue: "| ", space: "
18
18
  * The guide attributes a glyph set stands for, switched off on the guide itself
19
19
  * as Rich's `remove_guide_styles` does: a bold guide is drawn heavy, not bold.
20
20
  */
21
- const REMOVE_GUIDE_STYLES = new Style({ bold: false, underline2: false });
21
+ const REMOVE_GUIDE_STYLES = new Style({ bold: false, doubleUnderline: false });
22
22
  const guideGlyphs = (glyphs) => Object.values(glyphs).join("");
23
23
  /**
24
24
  * The glyphs a column of guide is drawn with, read off its resolved guide style
25
- * as Rich does: heavy when bold, double when underline2, light otherwise — and
25
+ * as Rich does: heavy when bold, double when doubleUnderline, light otherwise — and
26
26
  * ASCII whenever the terminal cannot draw the chosen set.
27
27
  */
28
28
  function glyphsFor(options, style) {
29
- const weight = style.bold === true ? HEAVY_GUIDES : style.underline2 === true ? DOUBLE_GUIDES : LIGHT_GUIDES;
29
+ const weight = style.bold === true ? HEAVY_GUIDES : style.doubleUnderline === true ? DOUBLE_GUIDES : LIGHT_GUIDES;
30
30
  return drawable(options, weight, ASCII_GUIDES, guideGlyphs);
31
31
  }
32
32
  /** A row a label is known to draw, unread. */
@@ -20,7 +20,7 @@
20
20
  * number the time is turned into first: a frame's stamp says when it changes
21
21
  * on the host's clock, which only `Timeline.run` can carry it back onto —
22
22
  * paused, never; backwards, at the hold it is about to drop under.
23
- * [LAW:single-enforcer] `drawing` is the one place the time, the timeline
23
+ * [LAW:single-enforcer] `drawn` is the one place the time, the timeline
24
24
  * and the reader's preference for motion are read, for every function.
25
25
  *
26
26
  * `frames <seconds> <frame> <seconds> <frame> …` is a `FrameSequence`
@@ -29,78 +29,109 @@
29
29
  * arguments on every evaluation, so the sequence is prepared on every one:
30
30
  * a template has nowhere to keep it, and keeping it here would be the memo
31
31
  * keyed on the arguments that `FrameSequence` is instead of. `stepped <seconds>
32
- * <color> <seconds> <color> …` is the colour in progress at the moment, a
33
- * colour value like every colour function's (`./color-funcs.ts`), so it
34
- * nests where a colour goes — `fg (stepped …)`, `readableOn (stepped …) $bg
32
+ * <color> <seconds> <color> …` is the color in progress at the moment, a
33
+ * color value like every color function's (`./color-funcs.ts`), so it
34
+ * nests where a color goes — `fg (stepped …)`, `ensureContrast (stepped …) $bg
35
35
  * 4.5` — and the ground it must read on is the template's to name, as it
36
- * is for any colour. Either argument list is one float/value cycle end to
36
+ * is for any color. Either argument list is one float/value cycle end to
37
37
  * end, so the engine's `alternating` gate types every slot and lifts each
38
38
  * string frame to a fragment; the pairing — a trailing hold with no frame or
39
- * colour — is parsed in `pairs`, once. [LAW:types-are-the-program]
39
+ * color — is parsed in `pairs`, once. [LAW:types-are-the-program]
40
40
  *
41
41
  * The rest start at a moment the template names, on the timeline's clock —
42
42
  * a field of the host's state, the moment a value changed — and each is the
43
43
  * animation `Timeline.play(start)` runs, so a start is a timeline like any
44
44
  * other and nothing here offsets a time by hand. `tween <seconds> <ease>
45
45
  * <start> <from> <to>` is a number eased from one value to another and held
46
- * (`core/tween.ts`), its ease spelled by name (`parseEase`); `loop <seconds>
46
+ * (`core/tween.ts`), its ease spelled by name (`parseEasing`); `loop <seconds>
47
47
  * <start>` is the way through a period, 0 to 1 and round again — two
48
48
  * cells started a step apart are a stagger. Each is a number, so it goes
49
- * wherever a number goes: a colour tween is `ramp (tween …) "linear" 0 $from
49
+ * wherever a number goes: a color tween is `ramp (tween …) "linear" 0 $from
50
50
  * 1 $to` and a ramp scrolling is `ramp (loop …) …`, because `ColorRamp` is
51
- * the one place two colours are blended and a colour tween is a two-stop
51
+ * the one place two colors are blended and a color tween is a two-stop
52
52
  * ramp read over time (`ColorRamp.over`). [LAW:one-source-of-truth] A
53
53
  * second blender here would be a second answer to what lies between two
54
- * colours. `glyphs <name> <start> <fragment>` is the fragment through a
54
+ * colors. `glyphs <name> <start> <fragment>` is the fragment through a
55
55
  * glyph effect from the catalogue (`GLYPH_EFFECT_CATALOGUE`), at its
56
56
  * defaults, before any render (`reglyphText`). Every argument list is an
57
57
  * exact arity of typed slots, so a slot of the wrong kind fails at the gate.
58
58
  *
59
59
  * Every function says when what it drew changes, so a host paints a
60
- * template while it moves and not after. `frames` and `glyphs` stamp the
61
- * fragment they draw (`textFrameAt`), covering its own glyphs. A number or a
62
- * colour carries no cells to stamp, so `tween`, `loop` and `stepped` tell
63
- * their hold to the frame instead (`TemplateFrame.onHold`), as a placed
64
- * animation of no cells does (`RenderOptions.onHold`). Nothing here claims
65
- * a moment it does not know.
60
+ * template while it moves and not after — and says it only for what the
61
+ * output depends on. A number or a color has no cells to carry a hold, and
62
+ * a fragment's cells lose theirs the moment it is flattened (`printf "%s"`),
63
+ * so no function stamps cells or tells the frame itself. Each marks its
64
+ * return with the hold its value is in (`marked`, the engine's evaluation
65
+ * provenance), and the engine carries the mark beside the value into every
66
+ * variable, dot and function the value passes through, reporting it where
67
+ * the output depends on it. `emitted` is where a template's cells take a
68
+ * hold: every fragment the template emits, stamped with the holds of the
69
+ * marked values its action read. `decided` is the route for a hold no cell
70
+ * can carry, told to the frame (`TemplateFrame.onValidity`): a mark an
71
+ * `if`/`with`/`range` consumed, whichever arm it took, because the frame may
72
+ * show cells for it later and must say when without a cell to say it on, as
73
+ * a placed animation of no cells does (`placed`) — and, by the same rule, a
74
+ * mark a writing function or a throwing action read; `EngineConfig.decided`
75
+ * is the authority on when it is called. A mark bound somewhere nobody reads
76
+ * is reported nowhere, so a value a template computes and never draws or
77
+ * decides on tells the frame nothing (rich-animation-27tq.0gd). Early, never
78
+ * late: an emission may carry a hold it did not strictly need, and a hold
79
+ * the output depends on is never dropped. [LAW:single-enforcer]
80
+ *
81
+ * The functions and the two hooks go into the host's engine config together,
82
+ * by `withAnimation`: the functions wired without the hooks would compile,
83
+ * draw and never say they move. [LAW:no-silent-failure]
66
84
  */
67
- import type { FuncMap } from "@promptctl/go-template-js";
85
+ import { type EngineConfig } from "@promptctl/go-template-js";
68
86
  import type { RenderOptions } from "../core/protocol.js";
87
+ import type { RichText } from "../core/text.js";
69
88
  import { Timeline } from "../core/timeline.js";
70
- /** What a registration is drawn on, beside the frame each evaluation is handed. */
71
- export interface AnimationFuncsOptions {
89
+ /** What the animation functions are drawn on, beside the frame each evaluation is handed. */
90
+ export interface AnimationOptions {
72
91
  /** The timeline to draw on, read once per call; the frame's own time when omitted. */
73
92
  readonly timeline?: () => Timeline;
74
93
  }
75
94
  /**
76
95
  * The frame a template is evaluated for, in the fields a render's options
77
- * carry under the same names: `time` in seconds, `onHold`, told when what a
78
- * function drew there changes, and the reader's preferences —
96
+ * carry under the same names: `time` in seconds, `onValidity`, told the hold of
97
+ * an animated value the output may depend on with no cell to carry it — one
98
+ * a decision read, whichever arm it took, a writing function's input, a
99
+ * throwing action's (the engine's `decided`) — and the reader's preferences,
79
100
  * `reducedMotion`, and `asciiOnly`, which the glyphs an effect picks for
80
101
  * itself go through. They arrive with every frame and nowhere else, so a
81
102
  * template drawn in an `App` is drawn as the app's preferences say.
82
103
  * [LAW:one-source-of-truth]
83
104
  *
84
- * `time` is on the clock `AnimationFuncsOptions.timeline` maps from, and
85
- * `onHold` hears on that clock. A host outside rich-js hands its own clock's
105
+ * `time` is on the clock `AnimationOptions.timeline` maps from, and
106
+ * `onValidity` hears on that clock. A host outside rich-js hands its own clock's
86
107
  * time. Inside a rich-js frame the time is the frame's, which its host's
87
108
  * timeline has already mapped, so a template drawn there registers no
88
109
  * `timeline` and is handed `templateFrame(options)`.
89
110
  */
90
- export interface TemplateFrame extends Pick<RenderOptions, "onHold" | "reducedMotion" | "asciiOnly"> {
111
+ export interface TemplateFrame extends Pick<RenderOptions, "onValidity" | "reducedMotion" | "asciiOnly"> {
91
112
  readonly time: number;
92
113
  }
93
114
  /** The frame a renderable is drawn in, as the frame a template it evaluates there is drawn for. */
94
115
  export declare function templateFrame(options: RenderOptions): TemplateFrame;
95
116
  /**
96
- * Register `frames`, `stepped`, `tween`, `loop` and `glyphs`, each drawn for
97
- * the frame `frame` reports when the template is evaluated — its time in
98
- * seconds, from whatever zero the host counts from — through
99
- * `options.timeline`, and each telling that frame when what it drew changes.
117
+ * `config` with `frames`, `stepped`, `tween`, `loop` and `glyphs` registered
118
+ * beside its own functions, each drawn for the frame `frame` reports when
119
+ * the template is evaluated — its time in seconds, from whatever zero the
120
+ * host counts from — through `options.timeline`, and each marking its
121
+ * return with the hold it is in there; and with the two hooks that carry
122
+ * those holds to the cells (`emitted`) and the frame (`decided`) installed
123
+ * over any `config` already has, which still hear everything they did. One
124
+ * call, so the functions and the hooks that report for them cannot be wired
125
+ * apart: the functions alone would compile, draw, and never say they move.
126
+ * [LAW:types-are-the-program] [LAW:no-silent-failure] [LAW:composability]
127
+ * A `config` that already has one of the five names is refused, which is
128
+ * also what a second `withAnimation` over its own result meets: its template
129
+ * calls would otherwise reach these with no word said.
130
+ *
100
131
  * The functions exist whatever `frame` returns, so a template compiles once
101
132
  * and is evaluated at every moment the host draws. In a frame that says
102
133
  * `reducedMotion` each draws its animation reduced (`Animation.reduced`): a
103
134
  * loop at its start, a one-shot cut from where it starts to where it arrives
104
135
  * when it starts.
105
136
  */
106
- export declare function animationFuncs(frame: () => TemplateFrame, options?: AnimationFuncsOptions): FuncMap;
137
+ export declare function withAnimation(config: EngineConfig<RichText>, frame: () => TemplateFrame, options?: AnimationOptions): EngineConfig<RichText>;