@promptctl/rich-js 0.16.0 → 0.18.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 (206) hide show
  1. package/README.md +14 -27
  2. package/package.json +7 -3
  3. package/dist/core/box.d.ts +0 -144
  4. package/dist/core/box.js +0 -391
  5. package/dist/core/cells.d.ts +0 -148
  6. package/dist/core/cells.js +0 -266
  7. package/dist/core/color.d.ts +0 -229
  8. package/dist/core/color.js +0 -992
  9. package/dist/core/console.d.ts +0 -142
  10. package/dist/core/console.js +0 -477
  11. package/dist/core/emoji.d.ts +0 -24
  12. package/dist/core/emoji.js +0 -391
  13. package/dist/core/export-html.d.ts +0 -24
  14. package/dist/core/export-html.js +0 -106
  15. package/dist/core/export-lines.d.ts +0 -121
  16. package/dist/core/export-lines.js +0 -141
  17. package/dist/core/highlighter.d.ts +0 -33
  18. package/dist/core/highlighter.js +0 -124
  19. package/dist/core/json.d.ts +0 -27
  20. package/dist/core/json.js +0 -53
  21. package/dist/core/markup.d.ts +0 -97
  22. package/dist/core/markup.js +0 -619
  23. package/dist/core/measure.d.ts +0 -27
  24. package/dist/core/measure.js +0 -61
  25. package/dist/core/oklch.d.ts +0 -161
  26. package/dist/core/oklch.js +0 -312
  27. package/dist/core/osc8.d.ts +0 -68
  28. package/dist/core/osc8.js +0 -90
  29. package/dist/core/pretty.d.ts +0 -141
  30. package/dist/core/pretty.js +0 -472
  31. package/dist/core/protocol.d.ts +0 -137
  32. package/dist/core/protocol.js +0 -101
  33. package/dist/core/render.d.ts +0 -69
  34. package/dist/core/render.js +0 -140
  35. package/dist/core/segment.d.ts +0 -142
  36. package/dist/core/segment.js +0 -433
  37. package/dist/core/spinnerData.d.ts +0 -10
  38. package/dist/core/spinnerData.js +0 -115
  39. package/dist/core/strip.d.ts +0 -139
  40. package/dist/core/strip.js +0 -302
  41. package/dist/core/style.d.ts +0 -149
  42. package/dist/core/style.js +0 -741
  43. package/dist/core/subscription.d.ts +0 -12
  44. package/dist/core/subscription.js +0 -12
  45. package/dist/core/text.d.ts +0 -255
  46. package/dist/core/text.js +0 -990
  47. package/dist/core/wrap.d.ts +0 -41
  48. package/dist/core/wrap.js +0 -127
  49. package/dist/host/host-stream.d.ts +0 -30
  50. package/dist/host/host-stream.js +0 -35
  51. package/dist/host/index.d.ts +0 -3
  52. package/dist/host/index.js +0 -2
  53. package/dist/host/terminal-host.d.ts +0 -143
  54. package/dist/host/terminal-host.js +0 -132
  55. package/dist/index.d.ts +0 -87
  56. package/dist/index.js +0 -104
  57. package/dist/node/prompt.d.ts +0 -21
  58. package/dist/node/prompt.js +0 -30
  59. package/dist/node/save.d.ts +0 -17
  60. package/dist/node/save.js +0 -21
  61. package/dist/node/terminal-host.d.ts +0 -84
  62. package/dist/node/terminal-host.js +0 -160
  63. package/dist/node/traceback.d.ts +0 -27
  64. package/dist/node/traceback.js +0 -112
  65. package/dist/renderables/align.d.ts +0 -16
  66. package/dist/renderables/align.js +0 -46
  67. package/dist/renderables/columns.d.ts +0 -61
  68. package/dist/renderables/columns.js +0 -168
  69. package/dist/renderables/constrain.d.ts +0 -15
  70. package/dist/renderables/constrain.js +0 -37
  71. package/dist/renderables/flexStrip.d.ts +0 -41
  72. package/dist/renderables/flexStrip.js +0 -200
  73. package/dist/renderables/group.d.ts +0 -11
  74. package/dist/renderables/group.js +0 -15
  75. package/dist/renderables/layout.d.ts +0 -92
  76. package/dist/renderables/layout.js +0 -315
  77. package/dist/renderables/live.d.ts +0 -47
  78. package/dist/renderables/live.js +0 -131
  79. package/dist/renderables/markdown.d.ts +0 -24
  80. package/dist/renderables/markdown.js +0 -182
  81. package/dist/renderables/padding.d.ts +0 -38
  82. package/dist/renderables/padding.js +0 -140
  83. package/dist/renderables/panel.d.ts +0 -116
  84. package/dist/renderables/panel.js +0 -322
  85. package/dist/renderables/progress.d.ts +0 -90
  86. package/dist/renderables/progress.js +0 -241
  87. package/dist/renderables/progressBar.d.ts +0 -31
  88. package/dist/renderables/progressBar.js +0 -52
  89. package/dist/renderables/prompt.d.ts +0 -49
  90. package/dist/renderables/prompt.js +0 -125
  91. package/dist/renderables/rule.d.ts +0 -24
  92. package/dist/renderables/rule.js +0 -90
  93. package/dist/renderables/spinner.d.ts +0 -29
  94. package/dist/renderables/spinner.js +0 -64
  95. package/dist/renderables/status.d.ts +0 -23
  96. package/dist/renderables/status.js +0 -60
  97. package/dist/renderables/syntax.d.ts +0 -35
  98. package/dist/renderables/syntax.js +0 -114
  99. package/dist/renderables/table.d.ts +0 -182
  100. package/dist/renderables/table.js +0 -697
  101. package/dist/renderables/traceback.d.ts +0 -31
  102. package/dist/renderables/traceback.js +0 -109
  103. package/dist/renderables/tree.d.ts +0 -54
  104. package/dist/renderables/tree.js +0 -159
  105. package/dist/template-bindings/color-funcs.d.ts +0 -87
  106. package/dist/template-bindings/color-funcs.js +0 -219
  107. package/dist/template-bindings/helpers.d.ts +0 -24
  108. package/dist/template-bindings/helpers.js +0 -48
  109. package/dist/template-bindings/index.d.ts +0 -107
  110. package/dist/template-bindings/index.js +0 -144
  111. package/dist/template-bindings/palette-funcs.d.ts +0 -94
  112. package/dist/template-bindings/palette-funcs.js +0 -151
  113. package/dist/template-bindings/style-funcs.d.ts +0 -28
  114. package/dist/template-bindings/style-funcs.js +0 -151
  115. package/dist/themes/buildPalette.d.ts +0 -36
  116. package/dist/themes/buildPalette.js +0 -56
  117. package/dist/themes/colorMath.d.ts +0 -82
  118. package/dist/themes/colorMath.js +0 -254
  119. package/dist/themes/colorRef.d.ts +0 -51
  120. package/dist/themes/colorRef.js +0 -96
  121. package/dist/themes/data/atom-one-dark.d.ts +0 -3
  122. package/dist/themes/data/atom-one-dark.js +0 -158
  123. package/dist/themes/data/atom-one-light.d.ts +0 -3
  124. package/dist/themes/data/atom-one-light.js +0 -158
  125. package/dist/themes/data/catppuccin-frappe.d.ts +0 -3
  126. package/dist/themes/data/catppuccin-frappe.js +0 -158
  127. package/dist/themes/data/catppuccin-latte.d.ts +0 -3
  128. package/dist/themes/data/catppuccin-latte.js +0 -158
  129. package/dist/themes/data/catppuccin-macchiato.d.ts +0 -3
  130. package/dist/themes/data/catppuccin-macchiato.js +0 -158
  131. package/dist/themes/data/catppuccin-mocha.d.ts +0 -3
  132. package/dist/themes/data/catppuccin-mocha.js +0 -158
  133. package/dist/themes/data/cyberpunk.d.ts +0 -17
  134. package/dist/themes/data/cyberpunk.js +0 -191
  135. package/dist/themes/data/default.d.ts +0 -3
  136. package/dist/themes/data/default.js +0 -158
  137. package/dist/themes/data/dracula.d.ts +0 -3
  138. package/dist/themes/data/dracula.js +0 -158
  139. package/dist/themes/data/flexoki.d.ts +0 -3
  140. package/dist/themes/data/flexoki.js +0 -158
  141. package/dist/themes/data/gruvbox.d.ts +0 -3
  142. package/dist/themes/data/gruvbox.js +0 -158
  143. package/dist/themes/data/index.d.ts +0 -35
  144. package/dist/themes/data/index.js +0 -64
  145. package/dist/themes/data/monokai.d.ts +0 -3
  146. package/dist/themes/data/monokai.js +0 -158
  147. package/dist/themes/data/nord.d.ts +0 -3
  148. package/dist/themes/data/nord.js +0 -158
  149. package/dist/themes/data/rose-pine-dawn.d.ts +0 -3
  150. package/dist/themes/data/rose-pine-dawn.js +0 -158
  151. package/dist/themes/data/rose-pine-moon.d.ts +0 -3
  152. package/dist/themes/data/rose-pine-moon.js +0 -158
  153. package/dist/themes/data/rose-pine.d.ts +0 -3
  154. package/dist/themes/data/rose-pine.js +0 -158
  155. package/dist/themes/data/solarized-dark.d.ts +0 -3
  156. package/dist/themes/data/solarized-dark.js +0 -158
  157. package/dist/themes/data/solarized-light.d.ts +0 -3
  158. package/dist/themes/data/solarized-light.js +0 -158
  159. package/dist/themes/data/svg-export.d.ts +0 -3
  160. package/dist/themes/data/svg-export.js +0 -158
  161. package/dist/themes/data/textual-ansi.d.ts +0 -3
  162. package/dist/themes/data/textual-ansi.js +0 -158
  163. package/dist/themes/data/textual-dark.d.ts +0 -3
  164. package/dist/themes/data/textual-dark.js +0 -158
  165. package/dist/themes/data/textual-light.d.ts +0 -3
  166. package/dist/themes/data/textual-light.js +0 -158
  167. package/dist/themes/data/tokyo-night.d.ts +0 -3
  168. package/dist/themes/data/tokyo-night.js +0 -158
  169. package/dist/themes/data/types.d.ts +0 -15
  170. package/dist/themes/data/types.js +0 -1
  171. package/dist/themes/palette.d.ts +0 -21
  172. package/dist/themes/palette.js +0 -28
  173. package/dist/themes/ramp.d.ts +0 -83
  174. package/dist/themes/ramp.js +0 -122
  175. package/dist/themes/registry.d.ts +0 -43
  176. package/dist/themes/registry.js +0 -82
  177. package/dist/themes/terminalThemes.d.ts +0 -54
  178. package/dist/themes/terminalThemes.js +0 -68
  179. package/dist/themes/transpose.d.ts +0 -87
  180. package/dist/themes/transpose.js +0 -138
  181. package/dist/widgets/button.d.ts +0 -49
  182. package/dist/widgets/button.js +0 -206
  183. package/dist/widgets/checkbox.d.ts +0 -41
  184. package/dist/widgets/checkbox.js +0 -155
  185. package/dist/widgets/dropdown.d.ts +0 -67
  186. package/dist/widgets/dropdown.js +0 -453
  187. package/dist/widgets/event-router.d.ts +0 -78
  188. package/dist/widgets/event-router.js +0 -622
  189. package/dist/widgets/focus-manager.d.ts +0 -26
  190. package/dist/widgets/focus-manager.js +0 -193
  191. package/dist/widgets/index.d.ts +0 -22
  192. package/dist/widgets/index.js +0 -15
  193. package/dist/widgets/screen.d.ts +0 -98
  194. package/dist/widgets/screen.js +0 -542
  195. package/dist/widgets/slider.d.ts +0 -57
  196. package/dist/widgets/slider.js +0 -270
  197. package/dist/widgets/static-item.d.ts +0 -40
  198. package/dist/widgets/static-item.js +0 -60
  199. package/dist/widgets/text-input.d.ts +0 -274
  200. package/dist/widgets/text-input.js +0 -1162
  201. package/dist/widgets/toggle.d.ts +0 -44
  202. package/dist/widgets/toggle.js +0 -179
  203. package/dist/widgets/types.d.ts +0 -108
  204. package/dist/widgets/types.js +0 -31
  205. package/dist/widgets/widget-base.d.ts +0 -42
  206. package/dist/widgets/widget-base.js +0 -173
@@ -1,315 +0,0 @@
1
- /**
2
- * Layout — divides the screen into rectangular regions.
3
- */
4
- import { Segment } from "../core/segment.js";
5
- import { RichText } from "../core/text.js";
6
- import { isMeasurable, withBoundedWidth, withCellWidth } from "../core/protocol.js";
7
- import { Measurement } from "../core/measure.js";
8
- import { cellCount } from "../core/cells.js";
9
- /**
10
- * A share weight, not a cell count: fractions divide space meaningfully, so
11
- * this parses where `cellCount` would floor. A weight that cannot name a share
12
- * — negative, NaN, infinite — reads as zero, which already means "this pane
13
- * does not grow" and is filtered out before any division. That is what makes
14
- * every ratio reaching `_distributeSpace` and `_rowBudgetFor` positive.
15
- */
16
- function growthRatio(ratio) {
17
- return Number.isFinite(ratio) && ratio > 0 ? ratio : 0;
18
- }
19
- // [LAW:single-enforcer] A leaf's content is a pane, not a printed unit — the
20
- // same reason panel.ts and table.ts clear `end` on a `RichText` they embed.
21
- // The string branch already built one with `end: ""`; a `RichText` passed
22
- // directly needs the same treatment, or it keeps its default `end: "\n"`
23
- // (rich-text-5ai code review) and draws a trailing blank row through
24
- // `Segment.cropLines`, which — unlike table.ts's `splitLines`-based cell
25
- // rendering — does not normalize a trailing empty line away.
26
- function toRenderable(renderable) {
27
- if (typeof renderable === "string") {
28
- return new RichText(renderable, { end: "" });
29
- }
30
- if (renderable instanceof RichText) {
31
- const copy = renderable.copy();
32
- copy.end = "";
33
- return copy;
34
- }
35
- return renderable;
36
- }
37
- export class Layout {
38
- name;
39
- visible;
40
- _ratio;
41
- _size;
42
- _minimumSize;
43
- _renderable;
44
- _children;
45
- _splitDirection;
46
- constructor(renderable, options) {
47
- if (renderable !== undefined) {
48
- this._renderable = toRenderable(renderable);
49
- }
50
- this.name = options?.name;
51
- this.ratio = options?.ratio ?? 1;
52
- this.size = options?.size;
53
- this.minimumSize = options?.minimumSize ?? 1;
54
- this.visible = options?.visible !== false;
55
- this._children = [];
56
- this._splitDirection = undefined;
57
- }
58
- /**
59
- * The three declared numbers, parsed on assignment rather than at the
60
- * constructor. All three are public and a caller reaches them long after
61
- * construction — `layout.getByName("pane")!.ratio = -1` walked straight past a
62
- * constructor-only parse and put a negative weight back into the division that
63
- * `_rowBudgetFor` and `_distributeSpace` are written to trust.
64
- *
65
- * [LAW:parse-dont-validate] The setter is the border, so the guarantee holds
66
- * for the object's whole lifetime and nothing downstream re-checks. `size` and
67
- * `minimumSize` are cell counts; `ratio` is a share weight and keeps its
68
- * fractions. Absence is preserved rather than parsed: an undefined `size`
69
- * selects a flex pane, and `cellCount` would read it as a declared zero.
70
- */
71
- get ratio() {
72
- return this._ratio;
73
- }
74
- set ratio(value) {
75
- this._ratio = growthRatio(value);
76
- }
77
- get size() {
78
- return this._size;
79
- }
80
- set size(value) {
81
- this._size = value === undefined ? undefined : cellCount(value);
82
- }
83
- get minimumSize() {
84
- return this._minimumSize;
85
- }
86
- set minimumSize(value) {
87
- this._minimumSize = cellCount(value);
88
- }
89
- get children() {
90
- return this._children;
91
- }
92
- /**
93
- * Whether this layout draws `_renderable` or its children.
94
- *
95
- * [LAW:one-source-of-truth] `render` and `_naturalWidth` must answer this the
96
- * same way. Asked separately they did not: `_naturalWidth` read it off the
97
- * *visible* children, so a layout holding content and a single hidden child
98
- * reported that content's width while `render` emitted nothing at all, and a
99
- * fit-mode `Panel` framed twelve cells of air.
100
- */
101
- get _isLeaf() {
102
- return this._children.length === 0;
103
- }
104
- splitColumn(...layouts) {
105
- this._children = layouts;
106
- this._splitDirection = "column";
107
- }
108
- splitRow(...layouts) {
109
- this._children = layouts;
110
- this._splitDirection = "row";
111
- }
112
- update(renderable) {
113
- this._renderable = toRenderable(renderable);
114
- }
115
- getByName(name) {
116
- if (this.name === name)
117
- return this;
118
- for (const child of this._children) {
119
- const found = child.getByName(name);
120
- if (found)
121
- return found;
122
- }
123
- return undefined;
124
- }
125
- *render(rawOptions) {
126
- if (!this.visible)
127
- return;
128
- // Parsed once at the top of the layout rather than at the row split below,
129
- // because a column split forwards the width to its children untouched and
130
- // would otherwise hand each of them the caller's raw number.
131
- const options = withBoundedWidth(rawOptions, this);
132
- if (this._isLeaf) {
133
- // Cropped rather than forwarded: a leaf hands its content the offer and
134
- // content is free to ignore it, and a pane wider than the region it was
135
- // given is the one thing a layout may never emit — in a row split it
136
- // overwrites the pane beside it. The row path already crops each share,
137
- // so this is the same rule at the one place that skipped it.
138
- if (this._renderable) {
139
- yield* Segment.cropLines(this._renderable.render(options), options.maxWidth);
140
- }
141
- return;
142
- }
143
- const visibleChildren = this._children.filter((c) => c.visible);
144
- if (visibleChildren.length === 0)
145
- return;
146
- if (this._splitDirection === "row") {
147
- yield* this._renderRow(visibleChildren, options);
148
- }
149
- else {
150
- yield* this._renderColumn(visibleChildren, options);
151
- }
152
- }
153
- *_renderColumn(children, options) {
154
- // Vertical stacking: each child gets full width, proportional height
155
- const totalHeight = options.maxHeight ?? options.height ?? 24;
156
- const heights = this._distributeSpace(children, totalHeight);
157
- for (let i = 0; i < children.length; i++) {
158
- const child = children[i];
159
- const childOptions = {
160
- ...options,
161
- maxHeight: heights[i],
162
- height: heights[i],
163
- };
164
- yield* child.render(childOptions);
165
- }
166
- }
167
- *_renderRow(children, options) {
168
- // Horizontal side-by-side: divide the requested width once, then merge
169
- // child lines. The budget arrives parsed from `render` — unparsed, a NaN
170
- // width made every share NaN and the merge threw `Invalid array length`.
171
- const widths = this._distributeSpace(children, options.maxWidth);
172
- const cells = children.map((child, i) => ({
173
- width: widths[i],
174
- lines: Segment.splitLines([
175
- ...child.render({ ...options, maxWidth: widths[i] }),
176
- ]),
177
- }));
178
- yield* Segment.mergeHorizontal(cells);
179
- }
180
- _distributeSpace(children, totalSpace) {
181
- const sizes = new Array(children.length).fill(0);
182
- let remaining = totalSpace;
183
- // Children that do not grow are paid first: a declared `size`, and equally
184
- // a `ratio` of 0, which is the caller saying this pane stays at its
185
- // `minimumSize`. Left in the ratio pass below, a zero-ratio child divided
186
- // by its own ratio and drew its minimum out of a budget the shares had
187
- // already spent — three panes of `zzz`/`aaaaa`/`bbbbb` consumed
188
- // `3 + 2*floor(T/2)` cells out of `T`, which no `T` satisfies, and the row
189
- // only came out right at 16 because the `Math.min(remaining, ...)` below
190
- // happened to clip the last share to the right number.
191
- const growing = [];
192
- for (let i = 0; i < children.length; i++) {
193
- const child = children[i];
194
- if (child.size !== undefined) {
195
- sizes[i] = Math.min(child.size, remaining);
196
- }
197
- else if (child.ratio === 0) {
198
- sizes[i] = Math.min(child.minimumSize, remaining);
199
- }
200
- else {
201
- growing.push(i);
202
- continue;
203
- }
204
- remaining -= sizes[i];
205
- }
206
- // Distribute what is left by ratio. Each child's share comes off one
207
- // budget and is bounded by what that budget still holds: `minimumSize` is
208
- // what a child asks for, `remaining` is what there is to give, and the
209
- // second beats the first. Applied without that bound — as
210
- // `Math.max(minimumSize, share)` alone — every child claimed its whole
211
- // minimum out of a budget that could not cover one of them, so two
212
- // children of a Layout asked to fit one cell merged into a two-cell row.
213
- if (growing.length > 0 && remaining > 0) {
214
- const budget = remaining;
215
- // Every ratio in here is positive — `growthRatio` parses the rest to zero
216
- // at the constructor and zero is filtered out above — which is what makes
217
- // the division safe and `_rowBudgetFor`'s inverse of it finite.
218
- const totalRatio = growing.reduce((s, idx) => s + children[idx].ratio, 0);
219
- for (const idx of growing) {
220
- const child = children[idx];
221
- const share = Math.floor(budget * child.ratio / totalRatio);
222
- const allocated = Math.min(remaining, Math.max(child.minimumSize, share));
223
- sizes[idx] = allocated;
224
- remaining -= allocated;
225
- }
226
- }
227
- return sizes;
228
- }
229
- /**
230
- * The width this layout would take if nothing constrained it: its content's
231
- * for a leaf, its children's laid out the way the split lays them out.
232
- *
233
- * A `size` counts only across a row, which is the one direction in which it
234
- * is a width — down a column the same field is a height, and reading it as a
235
- * width there would report a two-line pane as two cells wide.
236
- */
237
- _naturalWidth(options) {
238
- if (!this.visible)
239
- return 0;
240
- if (this._isLeaf) {
241
- if (this._renderable === undefined)
242
- return 0;
243
- // A leaf whose content cannot measure itself has no width of its own to
244
- // report, so it reports the offer — unbounded included, which is where
245
- // `withBoundedWidth` says so rather than inventing a number.
246
- return isMeasurable(this._renderable)
247
- ? Measurement.get(options, this._renderable).maximum
248
- : options.maxWidth;
249
- }
250
- const visible = this._children.filter((c) => c.visible);
251
- if (visible.length === 0)
252
- return 0;
253
- const widths = visible.map((c) => c._naturalWidth(options));
254
- if (this._splitDirection === "row")
255
- return this._rowBudgetFor(visible, widths);
256
- // Accumulated, not spread: a column split holds as many children as a caller
257
- // made, and `Math.max(...widths)` passes one argument per child, so a
258
- // generated dashboard deep enough overruns the engine's argument limit and
259
- // throws out of `measure()`.
260
- let widest = 0;
261
- for (const width of widths)
262
- widest = Math.max(widest, width);
263
- return widest;
264
- }
265
- /**
266
- * The budget at which `_distributeSpace` gives every child of a row at least
267
- * the width it wants — the inverse of the ratio rule above, and the reason
268
- * this is not the sum of those widths.
269
- *
270
- * [LAW:one-source-of-truth] A growing child receives `floor(budget * ratio /
271
- * totalRatio)`, so to receive `want` cells it needs the budget to reach
272
- * `want * totalRatio / ratio`, and one budget serves them all: the row needs
273
- * the largest demand, never their total. Summed instead, a 1:1 split of
274
- * "left" and "right" reported 9 and then rendered "right" into the 4 cells
275
- * that `floor(9/2)` actually hands it.
276
- *
277
- * The panes that do not grow are paid first here for the same reason they
278
- * are paid first there — and it is the reason this divides by no zero: a
279
- * `ratio` of 0 never reaches the division, because a pane that does not grow
280
- * demands nothing of a budget for growing.
281
- */
282
- _rowBudgetFor(children, widths) {
283
- const growing = children.filter((c) => c.size === undefined && c.ratio !== 0);
284
- const totalRatio = growing.reduce((sum, c) => sum + c.ratio, 0);
285
- let pinned = 0;
286
- let budget = 0;
287
- for (const [i, child] of children.entries()) {
288
- if (child.size !== undefined) {
289
- pinned += child.size;
290
- }
291
- else if (child.ratio === 0) {
292
- pinned += child.minimumSize;
293
- }
294
- else {
295
- const want = Math.max(child.minimumSize, widths[i]);
296
- budget = Math.max(budget, Math.ceil(want * totalRatio / child.ratio));
297
- }
298
- }
299
- return pinned + budget;
300
- }
301
- measure(rawOptions) {
302
- // `minimumSize` is what this layout asks for and the ceiling is what it was
303
- // offered; the ceiling wins. Unclamped, a layout measured into no width
304
- // reported the range 1..0 — a floor above its own ceiling, which the parent
305
- // that asked cannot divide.
306
- //
307
- // The ceiling alone was not the answer either: a layout that reported the
308
- // whole offer as its maximum told every parent it wanted all of it, so
309
- // `Panel` in fit mode drew its frame at the full console width around a
310
- // layout of two short panes, and an unbounded offer came back unbounded.
311
- const parsed = withCellWidth(rawOptions);
312
- const maximum = Math.min(this._naturalWidth(parsed), parsed.maxWidth);
313
- return { minimum: Math.min(this.minimumSize, maximum), maximum };
314
- }
315
- }
@@ -1,47 +0,0 @@
1
- /**
2
- * Live — animates a portion of the terminal by continuously re-rendering.
3
- *
4
- * Two modes:
5
- * - **Inline** (default): clears and redraws N lines in the current scroll
6
- * region. Good for spinners/progress bars below other output.
7
- * - **Alt-screen** (`altScreen: true`): enters the alternate screen buffer
8
- * on start, cursor-homes on each refresh (no clear flicker), and restores
9
- * the original buffer on stop. Good for full-screen TUI apps.
10
- */
11
- import { Console } from "../core/console.js";
12
- import type { Renderable } from "../core/protocol.js";
13
- export interface LiveOptions {
14
- refreshPerSecond?: number;
15
- autoRefresh?: boolean;
16
- transient?: boolean;
17
- console?: Console;
18
- verticalOverflow?: "crop" | "ellipsis" | "visible";
19
- /** When true, Live enters the alternate screen buffer on start() and
20
- * restores the original on stop(). Refresh uses cursor-home instead of
21
- * cursor-up-and-clear, eliminating flicker for full-screen layouts. */
22
- altScreen?: boolean;
23
- }
24
- export declare class Live {
25
- private _renderable;
26
- private _console;
27
- private _refreshPerSecond;
28
- private _autoRefresh;
29
- private _transient;
30
- private _verticalOverflow;
31
- private _altScreen;
32
- private _timer;
33
- private _lastLineCount;
34
- private _started;
35
- private _firstRefresh;
36
- constructor(renderable?: Renderable, options?: LiveOptions);
37
- get console(): Console;
38
- get renderable(): Renderable | undefined;
39
- start(): void;
40
- stop(): void;
41
- update(renderable?: Renderable, options?: {
42
- refresh?: boolean;
43
- }): void;
44
- refresh(): void;
45
- private _clearLast;
46
- private _writeCursorControl;
47
- }
@@ -1,131 +0,0 @@
1
- /**
2
- * Live — animates a portion of the terminal by continuously re-rendering.
3
- *
4
- * Two modes:
5
- * - **Inline** (default): clears and redraws N lines in the current scroll
6
- * region. Good for spinners/progress bars below other output.
7
- * - **Alt-screen** (`altScreen: true`): enters the alternate screen buffer
8
- * on start, cursor-homes on each refresh (no clear flicker), and restores
9
- * the original buffer on stop. Good for full-screen TUI apps.
10
- */
11
- import { Console } from "../core/console.js";
12
- import { Segment } from "../core/segment.js";
13
- import { segmentsToString } from "../core/render.js";
14
- export class Live {
15
- _renderable;
16
- _console;
17
- _refreshPerSecond;
18
- _autoRefresh;
19
- _transient;
20
- _verticalOverflow;
21
- _altScreen;
22
- _timer;
23
- _lastLineCount;
24
- _started;
25
- _firstRefresh;
26
- constructor(renderable, options) {
27
- this._renderable = renderable;
28
- this._console = options?.console ?? new Console({ forceTerminal: true });
29
- this._refreshPerSecond = options?.refreshPerSecond ?? 4;
30
- this._autoRefresh = options?.autoRefresh !== false;
31
- this._transient = options?.transient ?? false;
32
- this._verticalOverflow = options?.verticalOverflow ?? "ellipsis";
33
- this._altScreen = options?.altScreen ?? false;
34
- this._lastLineCount = 0;
35
- this._started = false;
36
- this._firstRefresh = true;
37
- }
38
- get console() {
39
- return this._console;
40
- }
41
- get renderable() {
42
- return this._renderable;
43
- }
44
- start() {
45
- if (this._started)
46
- return;
47
- this._started = true;
48
- this._firstRefresh = true;
49
- const stream = this._console.file;
50
- if (this._altScreen) {
51
- stream.write("\x1b[?1049h"); // enter alt screen
52
- }
53
- this._writeCursorControl(false);
54
- if (this._autoRefresh) {
55
- const interval = Math.floor(1000 / this._refreshPerSecond);
56
- this._timer = setInterval(() => this.refresh(), interval);
57
- }
58
- }
59
- stop() {
60
- if (!this._started)
61
- return;
62
- this._started = false;
63
- if (this._timer) {
64
- clearInterval(this._timer);
65
- this._timer = undefined;
66
- }
67
- if (!this._transient) {
68
- this.refresh();
69
- }
70
- else {
71
- this._clearLast();
72
- }
73
- this._writeCursorControl(true);
74
- if (this._altScreen) {
75
- this._console.file.write("\x1b[0m\x1b[?1049l"); // reset attrs + exit alt screen
76
- }
77
- }
78
- update(renderable, options) {
79
- if (renderable !== undefined) {
80
- this._renderable = renderable;
81
- }
82
- if (options?.refresh) {
83
- this.refresh();
84
- }
85
- }
86
- refresh() {
87
- if (!this._renderable)
88
- return;
89
- if (this._altScreen) {
90
- // Alt-screen mode: cursor home (first refresh clears the buffer)
91
- this._console.file.write(this._firstRefresh ? "\x1b[2J\x1b[H" : "\x1b[H");
92
- this._firstRefresh = false;
93
- }
94
- else {
95
- // Inline mode: erase previous output
96
- this._clearLast();
97
- }
98
- const segments = [...this._renderable.render(this._console.options)];
99
- const lines = Segment.splitLines(segments);
100
- const maxHeight = this._console.height;
101
- let displayLines = lines;
102
- if (this._verticalOverflow !== "visible" && lines.length > maxHeight) {
103
- displayLines = lines.slice(0, maxHeight);
104
- if (this._verticalOverflow === "ellipsis" && displayLines.length > 0) {
105
- displayLines[displayLines.length - 1] = [new Segment("...")];
106
- }
107
- }
108
- // [LAW:single-enforcer] Per-line encoding routes through the same
109
- // tree-coalescer `Console._writeSegments` uses, so Live frames coalesce
110
- // adjacent same-style segments into shared SGR pairs on the wire and
111
- // honor `colorSystem === null` (NO_COLOR, dumb terminal) the same way.
112
- const colorSystem = this._console.colorSystem;
113
- const output = displayLines
114
- .map((line) => segmentsToString(line, colorSystem))
115
- .join("\n");
116
- this._console.file.write(output + "\n");
117
- this._lastLineCount = displayLines.length;
118
- }
119
- _clearLast() {
120
- if (this._lastLineCount > 0) {
121
- const stream = this._console.file;
122
- for (let i = 0; i < this._lastLineCount; i++) {
123
- stream.write("\x1b[1A\x1b[2K");
124
- }
125
- this._lastLineCount = 0;
126
- }
127
- }
128
- _writeCursorControl(show) {
129
- this._console.file.write(show ? "\x1b[?25h" : "\x1b[?25l");
130
- }
131
- }
@@ -1,24 +0,0 @@
1
- /**
2
- * Markdown — renders Markdown content to the terminal.
3
- * Uses built-in parsing (no external dependency).
4
- */
5
- import { Segment } from "../core/segment.js";
6
- import { Style } from "../core/style.js";
7
- import type { Renderable, Measurable, RenderOptions } from "../core/protocol.js";
8
- export interface MarkdownOptions {
9
- codeTheme?: string;
10
- inlineCodeStyle?: string | Style;
11
- hyperlinks?: boolean;
12
- justify?: "left" | "center" | "right" | "full";
13
- }
14
- export declare class Markdown implements Renderable, Measurable {
15
- readonly markdown: string;
16
- readonly inlineCodeStyle: string | Style;
17
- readonly hyperlinks: boolean;
18
- constructor(markdown: string, options?: MarkdownOptions);
19
- render(options: RenderOptions): Iterable<Segment>;
20
- measure(options: RenderOptions): {
21
- minimum: number;
22
- maximum: number;
23
- };
24
- }