@promptctl/rich-js 0.23.0 → 0.24.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/THIRD-PARTY-NOTICES +29 -1
  2. package/dist/core/animation.d.ts +78 -0
  3. package/dist/core/animation.js +107 -0
  4. package/dist/core/cell-widths.d.ts +9 -0
  5. package/dist/core/cell-widths.js +140 -0
  6. package/dist/core/cells.d.ts +17 -11
  7. package/dist/core/cells.js +100 -25
  8. package/dist/core/clock.d.ts +9 -1
  9. package/dist/core/clock.js +22 -2
  10. package/dist/core/color.d.ts +10 -0
  11. package/dist/core/color.js +36 -1
  12. package/dist/core/console.js +5 -5
  13. package/dist/core/easing.d.ts +138 -7
  14. package/dist/core/easing.js +355 -17
  15. package/dist/core/json.js +2 -1
  16. package/dist/core/measure.js +2 -2
  17. package/dist/core/place.js +2 -1
  18. package/dist/core/pretty.js +2 -2
  19. package/dist/core/protocol.d.ts +73 -19
  20. package/dist/core/protocol.js +56 -15
  21. package/dist/core/render.js +2 -1
  22. package/dist/core/segment.d.ts +28 -1
  23. package/dist/core/segment.js +60 -44
  24. package/dist/core/strip.d.ts +107 -29
  25. package/dist/core/strip.js +242 -131
  26. package/dist/core/style.d.ts +18 -1
  27. package/dist/core/style.js +49 -9
  28. package/dist/core/text.js +26 -15
  29. package/dist/core/timeline.d.ts +84 -0
  30. package/dist/core/timeline.js +144 -0
  31. package/dist/core/tween.d.ts +49 -0
  32. package/dist/core/tween.js +69 -0
  33. package/dist/host/app.js +4 -6
  34. package/dist/host/index.d.ts +1 -1
  35. package/dist/host/input.d.ts +37 -13
  36. package/dist/host/input.js +146 -56
  37. package/dist/host/terminal-host.d.ts +16 -0
  38. package/dist/host/terminal-host.js +1 -0
  39. package/dist/index.d.ts +16 -11
  40. package/dist/index.js +15 -5
  41. package/dist/node/terminal-host.d.ts +8 -1
  42. package/dist/node/terminal-host.js +2 -0
  43. package/dist/renderables/align.d.ts +3 -2
  44. package/dist/renderables/align.js +3 -0
  45. package/dist/renderables/columns.d.ts +3 -2
  46. package/dist/renderables/columns.js +7 -2
  47. package/dist/renderables/constrain.d.ts +3 -2
  48. package/dist/renderables/constrain.js +5 -1
  49. package/dist/renderables/effect.d.ts +83 -30
  50. package/dist/renderables/effect.js +155 -57
  51. package/dist/renderables/effects.d.ts +88 -39
  52. package/dist/renderables/effects.js +444 -105
  53. package/dist/renderables/embed.d.ts +10 -0
  54. package/dist/renderables/embed.js +18 -9
  55. package/dist/renderables/flexStrip.d.ts +3 -2
  56. package/dist/renderables/flexStrip.js +6 -1
  57. package/dist/renderables/frames.d.ts +116 -0
  58. package/dist/renderables/frames.js +133 -0
  59. package/dist/renderables/group.d.ts +3 -2
  60. package/dist/renderables/group.js +5 -2
  61. package/dist/renderables/html-entities.d.ts +8 -0
  62. package/dist/renderables/html-entities.js +386 -0
  63. package/dist/renderables/layout.d.ts +3 -2
  64. package/dist/renderables/layout.js +15 -2
  65. package/dist/renderables/list-view.d.ts +3 -2
  66. package/dist/renderables/list-view.js +12 -5
  67. package/dist/renderables/live.d.ts +23 -7
  68. package/dist/renderables/live.js +69 -35
  69. package/dist/renderables/markdown-parse.d.ts +109 -0
  70. package/dist/renderables/markdown-parse.js +1421 -0
  71. package/dist/renderables/markdown-url.d.ts +19 -0
  72. package/dist/renderables/markdown-url.js +314 -0
  73. package/dist/renderables/markdown.d.ts +7 -6
  74. package/dist/renderables/markdown.js +363 -499
  75. package/dist/renderables/padding.d.ts +8 -20
  76. package/dist/renderables/padding.js +32 -50
  77. package/dist/renderables/panel.d.ts +26 -26
  78. package/dist/renderables/panel.js +67 -103
  79. package/dist/renderables/progress.d.ts +5 -3
  80. package/dist/renderables/progress.js +14 -3
  81. package/dist/renderables/progressBar.js +4 -4
  82. package/dist/renderables/python-space.d.ts +13 -0
  83. package/dist/renderables/python-space.js +24 -0
  84. package/dist/renderables/spinner.d.ts +73 -27
  85. package/dist/renderables/spinner.js +110 -51
  86. package/dist/renderables/status.d.ts +8 -3
  87. package/dist/renderables/status.js +8 -5
  88. package/dist/renderables/syntax.js +2 -1
  89. package/dist/renderables/table.d.ts +19 -11
  90. package/dist/renderables/table.js +140 -156
  91. package/dist/renderables/traceback.d.ts +1 -1
  92. package/dist/renderables/traceback.js +6 -5
  93. package/dist/renderables/tree.d.ts +6 -5
  94. package/dist/renderables/tree.js +11 -7
  95. package/dist/renderables/viewport.d.ts +13 -5
  96. package/dist/renderables/viewport.js +41 -20
  97. package/dist/template-bindings/animation-funcs.d.ts +37 -0
  98. package/dist/template-bindings/animation-funcs.js +83 -0
  99. package/dist/template-bindings/color-funcs.d.ts +13 -0
  100. package/dist/template-bindings/color-funcs.js +1 -1
  101. package/dist/template-bindings/helpers.d.ts +6 -0
  102. package/dist/template-bindings/helpers.js +12 -3
  103. package/dist/template-bindings/index.d.ts +6 -2
  104. package/dist/template-bindings/index.js +8 -3
  105. package/dist/themes/ramp.d.ts +18 -3
  106. package/dist/themes/ramp.js +23 -2
  107. package/dist/widgets/focus-manager.d.ts +21 -14
  108. package/dist/widgets/focus-manager.js +28 -29
  109. package/dist/widgets/index.d.ts +1 -1
  110. package/dist/widgets/static-item.d.ts +4 -3
  111. package/dist/widgets/static-item.js +9 -7
  112. package/dist/widgets/text-input.d.ts +28 -18
  113. package/dist/widgets/text-input.js +144 -101
  114. package/dist/widgets/widget-app.d.ts +19 -3
  115. package/dist/widgets/widget-app.js +39 -21
  116. package/dist/widgets/widget-base.js +0 -3
  117. package/package.json +2 -5
@@ -27,10 +27,26 @@
27
27
  import { type Clock } from "../core/clock.js";
28
28
  import { Console } from "../core/console.js";
29
29
  import { type Renderable } from "../core/protocol.js";
30
+ /**
31
+ * What a Live draws: a renderable, or a view asked for one each frame with
32
+ * the frame's time in seconds on the Live's clock, as an `App`'s view is
33
+ * (`host/app.ts`). A renderable is the view that is the same at every `t` —
34
+ * a table mutated in place between frames — and a function is what a frame
35
+ * drawn for a time needs: `(t) => spinner.at(t)`.
36
+ * [LAW:no-ambient-temporal-coupling] The Live reads its clock once a frame
37
+ * and hands the time down as data, so nothing it draws reads one.
38
+ */
39
+ export type LiveView = Renderable | ((t: number) => Renderable);
30
40
  export interface LiveOptions {
31
- /** Frames a second while auto-refreshing: positive and finite, fractional included. */
41
+ /**
42
+ * How often the auto-refresh draws on its own, in frames a second:
43
+ * positive and finite, fractional included. The next frame comes a frame
44
+ * interval after the last one drawn, a `refresh()` of your own included,
45
+ * or sooner when that frame's content says it changes sooner
46
+ * (`Segment.nextChange`).
47
+ */
32
48
  refreshPerSecond?: number;
33
- /** What the auto-refresh ticks on. The platform's, unless given. */
49
+ /** What the auto-refresh waits on. The platform's, unless given. */
34
50
  clock?: Clock;
35
51
  autoRefresh?: boolean;
36
52
  transient?: boolean;
@@ -42,7 +58,7 @@ export interface LiveOptions {
42
58
  altScreen?: boolean;
43
59
  }
44
60
  export declare class Live {
45
- private _renderable;
61
+ private _view;
46
62
  private _console;
47
63
  private readonly _rate;
48
64
  private readonly _clock;
@@ -50,21 +66,21 @@ export declare class Live {
50
66
  private _transient;
51
67
  private _verticalOverflow;
52
68
  private readonly _painter;
53
- private _stopTicking;
69
+ private _waking;
54
70
  private _held;
55
- constructor(renderable?: Renderable, options?: LiveOptions);
71
+ constructor(view?: LiveView, options?: LiveOptions);
56
72
  /**
57
73
  * The console to print through while this Live runs: what it prints lands
58
74
  * above an inline frame. The alternate screen is all frame, and paints over it.
59
75
  */
60
76
  get console(): Console;
61
- get renderable(): Renderable | undefined;
62
77
  start(): void;
63
78
  stop(): void;
64
- update(renderable?: Renderable, options?: {
79
+ update(view?: LiveView, options?: {
65
80
  refresh?: boolean;
66
81
  }): void;
67
82
  refresh(): void;
83
+ private _wake;
68
84
  private _frame;
69
85
  private _stepAround;
70
86
  private _paint;
@@ -28,9 +28,13 @@ import { frameRate, systemClock } from "../core/clock.js";
28
28
  import { Console } from "../core/console.js";
29
29
  import { Segment } from "../core/segment.js";
30
30
  import { FinalFramePainter, SurfacePainter } from "../core/paint.js";
31
- import { fitHeight } from "../core/protocol.js";
31
+ import { fitHeight, render } from "../core/protocol.js";
32
+ // [LAW:parse-dont-validate] The one shape Live holds: a renderable becomes
33
+ // the view that answers it at every t, where it is given, so no frame asks
34
+ // which it was.
35
+ const viewOf = (view) => view === undefined || typeof view === "function" ? view : () => view;
32
36
  export class Live {
33
- _renderable;
37
+ _view;
34
38
  _console;
35
39
  _rate;
36
40
  _clock;
@@ -38,13 +42,14 @@ export class Live {
38
42
  _transient;
39
43
  _verticalOverflow;
40
44
  _painter;
41
- _stopTicking;
45
+ // The one wait for the next auto-refresh frame, armed after each frame.
46
+ _waking;
42
47
  // [LAW:one-source-of-truth] The console's live region, held from `start()`
43
48
  // to the hand-back — which is what "started" means, so there is no separate
44
49
  // flag to disagree with it.
45
50
  _held;
46
- constructor(renderable, options) {
47
- this._renderable = renderable;
51
+ constructor(view, options) {
52
+ this._view = viewOf(view);
48
53
  this._console = options?.console ?? new Console();
49
54
  // [LAW:parse-dont-validate] Parsed whether or not it auto-refreshes, so a
50
55
  // rate that could never tick is refused where it is given.
@@ -69,9 +74,6 @@ export class Live {
69
74
  get console() {
70
75
  return this._console;
71
76
  }
72
- get renderable() {
73
- return this._renderable;
74
- }
75
77
  start() {
76
78
  if (this._held !== null)
77
79
  return;
@@ -80,21 +82,9 @@ export class Live {
80
82
  const held = this._console.claimLiveRegion({ around: (text) => this._stepAround(text) });
81
83
  this._held = held;
82
84
  this._painter.take();
83
- if (this._autoRefresh) {
84
- // No caller is on the stack to run `stop()` for a frame that throws
85
- // here, and in Node the throw ends the process — so the terminal is
86
- // handed back first, and the error then goes on to wherever an uncaught
87
- // one goes.
88
- this._stopTicking = this._clock.every(this._rate, () => {
89
- try {
90
- this.refresh();
91
- }
92
- catch (error) {
93
- this._release(held);
94
- throw error;
95
- }
96
- });
97
- }
85
+ // Nothing is drawn yet, so nothing has said when it changes: the first
86
+ // auto-refresh frame comes at the rate, from now.
87
+ this._wake(held, { t: this._clock.now(), nextChange: Number.POSITIVE_INFINITY });
98
88
  }
99
89
  stop() {
100
90
  const held = this._held;
@@ -114,9 +104,9 @@ export class Live {
114
104
  this._release(held);
115
105
  }
116
106
  }
117
- update(renderable, options) {
118
- if (renderable !== undefined) {
119
- this._renderable = renderable;
107
+ update(view, options) {
108
+ if (view !== undefined) {
109
+ this._view = viewOf(view);
120
110
  }
121
111
  if (options?.refresh) {
122
112
  this.refresh();
@@ -126,13 +116,57 @@ export class Live {
126
116
  // A frame is drawn only while this Live holds the terminal, as in Rich:
127
117
  // before `start()` or after the hand-back it would land on a screen that
128
118
  // belongs to someone else — the main buffer, under the user's own output.
129
- if (this._held === null)
119
+ const held = this._held;
120
+ if (held === null)
121
+ return;
122
+ // [LAW:no-ambient-temporal-coupling] The frame says when what it shows
123
+ // next changes; the next auto-refresh frame is then, or at the rate,
124
+ // whichever is sooner. The painter decides whether this frame is drawn
125
+ // now — a final-frame painter draws nothing before the hand-back — and a
126
+ // frame not drawn has said nothing.
127
+ let said;
128
+ this._paint(() => {
129
+ const { t, lines } = this._frame();
130
+ said = { t, nextChange: lines.reduce((soonest, row) => Math.min(soonest, Segment.nextChange(row)), Number.POSITIVE_INFINITY) };
131
+ return lines;
132
+ });
133
+ this._wake(held, said ?? { t: this._clock.now(), nextChange: Number.POSITIVE_INFINITY });
134
+ }
135
+ // Arm the one wait for the next auto-refresh frame while `held`: at the
136
+ // frame's `nextChange` when that is before the rate's next frame, else at
137
+ // the rate — both measured from the frame's own `t`, never from a second
138
+ // reading of the clock, so the rate does not stretch by what a frame cost
139
+ // to draw, and a change that fell due while it was drawn is drawn at once
140
+ // (`Clock.at` fires now for a time already past). Content that changes
141
+ // every frame answers `t` itself, and is drawn at the rate.
142
+ _wake(held, { t, nextChange }) {
143
+ this._waking?.();
144
+ this._waking = undefined;
145
+ if (!this._autoRefresh)
130
146
  return;
131
- this._paint(() => this._frame());
147
+ const at = Math.min(t + this._rate.interval, nextChange > t ? nextChange : Number.POSITIVE_INFINITY);
148
+ // No caller is on the stack to run `stop()` for a frame that throws
149
+ // here, and in Node the throw ends the process — so the terminal is
150
+ // handed back first, and the error then goes on to wherever an uncaught
151
+ // one goes.
152
+ this._waking = this._clock.at(at, () => {
153
+ try {
154
+ this.refresh();
155
+ }
156
+ catch (error) {
157
+ this._release(held);
158
+ throw error;
159
+ }
160
+ });
132
161
  }
162
+ // The frame at the clock's now, with that time: the one the view was handed, and the one the next wake is measured from.
133
163
  _frame() {
134
- if (!this._renderable)
135
- return [];
164
+ // [LAW:effects-at-boundaries] One read of the clock a frame, handed to
165
+ // the view as the frame's time.
166
+ const t = this._clock.now();
167
+ if (!this._view)
168
+ return { t, lines: [] };
169
+ const renderable = this._view(t);
136
170
  // [LAW:dataflow-not-control-flow] Both modes render under the terminal's
137
171
  // rows and differ only in what those rows are: the alternate screen is a
138
172
  // region the frame stands in, and an inline frame keeps its natural height
@@ -140,14 +174,14 @@ export class Live {
140
174
  // back — its overflow policy, then `fitHeight`, which pads only a region.
141
175
  const options = this._console.options;
142
176
  const height = this._painter.height(options.height.rows);
143
- const lines = Segment.splitLines(this._renderable.render({ ...options, height }));
144
- return fitHeight(this._overflow(lines, height.rows), height);
177
+ const lines = Segment.splitLines(render(renderable, { ...options, height }));
178
+ return { t, lines: fitHeight(this._overflow(lines, height.rows), height) };
145
179
  }
146
180
  // What the console writes goes where the frame was, and the frame is painted
147
181
  // again under it, as Rich's render hook does — in the bytes the console
148
182
  // writes, so a render that throws writes nothing.
149
183
  _stepAround(text) {
150
- return this._painter.around(text, () => this._frame(), this._screen(), this._console.destination);
184
+ return this._painter.around(text, () => this._frame().lines, this._screen(), this._console.destination);
151
185
  }
152
186
  // [LAW:effects-at-boundaries] The frame is fully rendered before a byte
153
187
  // reaches the terminal, and whatever takes the last frame away goes out in
@@ -177,8 +211,8 @@ export class Live {
177
211
  _release(held) {
178
212
  held();
179
213
  this._held = null;
180
- this._stopTicking?.();
181
- this._stopTicking = undefined;
214
+ this._waking?.();
215
+ this._waking = undefined;
182
216
  this._painter.handBack();
183
217
  }
184
218
  }
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Markdown's parse, with no dependency: a source read into blocks, then the
3
+ * text of each block read into inlines — the two passes markdown-it, Rich's
4
+ * parser, makes. Nothing here reads a `RenderOptions`, so a `Markdown` parses
5
+ * its source once, when it is made, as Rich's does, and every render after
6
+ * only walks the tokens.
7
+ *
8
+ * Block structure is read as CommonMark reads it: a blockquote and a list
9
+ * item are both containers, their lines tokenized as Markdown of their own
10
+ * once the container's marker or indentation is cut off. A table, which
11
+ * CommonMark has none of, and an HTML block are read as markdown-it reads
12
+ * them.
13
+ */
14
+ /** Where a table column's cells sit, from the colons on its delimiter cell. None is GFM's no alignment, which Rich leaves to its column: left. */
15
+ export type CellAlign = "left" | "center" | "right" | undefined;
16
+ interface TableColumn<Text> {
17
+ readonly header: Text;
18
+ readonly align: CellAlign;
19
+ }
20
+ /** A heading's level: an ATX heading's count of `#`s, or 1 and 2 for a setext heading's `=` and `-`. */
21
+ export type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
22
+ /**
23
+ * A block, its text as `Text`: the source's own text after the block pass,
24
+ * and that text read into inlines after the inline pass, which is what
25
+ * `parseMarkdown` hands back. `Mark` is what the block pass keeps besides:
26
+ * none, once the inline pass has dropped its marks.
27
+ */
28
+ export type MdToken<Text = readonly Inline[], Mark = never> = {
29
+ type: "heading";
30
+ level: HeadingLevel;
31
+ text: Text;
32
+ } | {
33
+ type: "paragraph";
34
+ text: Text;
35
+ } | {
36
+ type: "code_block";
37
+ code: string;
38
+ } | {
39
+ type: "hr";
40
+ }
41
+ /** `start` is an ordered list's first number, as its source writes it; a bullet list has none. Each item is its blocks. */
42
+ | {
43
+ type: "list";
44
+ start: number | undefined;
45
+ items: readonly (readonly MdToken<Text, Mark>[])[];
46
+ } | {
47
+ type: "blockquote";
48
+ children: readonly MdToken<Text, Mark>[];
49
+ }
50
+ /** Each row holds one cell per column: a short row filled out with empty cells, a long one cut. */
51
+ | {
52
+ type: "table";
53
+ columns: readonly TableColumn<Text>[];
54
+ rows: readonly (readonly Text[])[];
55
+ }
56
+ /** Kept, though nothing draws it, as Rich exits it like any other block. */
57
+ | {
58
+ type: "html_block";
59
+ } | Mark;
60
+ /** The element markdown-it makes of a pair of delimiters. */
61
+ type EmphasisTag = "em" | "strong" | "s";
62
+ /**
63
+ * CommonMark's inlines, as one block's text reads: a code span, emphasis or a
64
+ * link holding inlines of its own, an autolink, an image, inline HTML, a line
65
+ * ending, and text — a backslash escape and a character reference already the
66
+ * characters they stand for. Emphasis is named by the element markdown-it
67
+ * makes of it, GFM's strikethrough `s` among them.
68
+ */
69
+ export type Inline = {
70
+ kind: "text";
71
+ text: string;
72
+ } | {
73
+ kind: "code";
74
+ code: string;
75
+ } | {
76
+ kind: "emphasis";
77
+ tag: EmphasisTag;
78
+ children: readonly Inline[];
79
+ } | {
80
+ kind: "link";
81
+ children: readonly Inline[];
82
+ url: string;
83
+ }
84
+ /** `text` is the URL as written, normalized as markdown-it shows it, never read as Markdown. */
85
+ | {
86
+ kind: "autolink";
87
+ text: string;
88
+ url: string;
89
+ }
90
+ /** `alt` is as written, never read as Markdown. */
91
+ | {
92
+ kind: "image";
93
+ alt: string;
94
+ src: string;
95
+ }
96
+ /** `content` is the tag as written, markdown-it's `html_inline` content. */
97
+ | {
98
+ kind: "html";
99
+ content: string;
100
+ }
101
+ /** markdown-it's `softbreak` and `hardbreak`: tokens of their own, never joined to the text beside them. */
102
+ | {
103
+ kind: "softbreak";
104
+ } | {
105
+ kind: "hardbreak";
106
+ };
107
+ /** `source` read as Markdown: its blocks, and the text in each read into inlines. A line ends at `\r\n`, `\r` or `\n`, as markdown-it normalizes them. */
108
+ export declare function parseMarkdown(source: string): MdToken[];
109
+ export {};