@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,61 +0,0 @@
1
- /**
2
- * Columns — arranges renderables in a multi-column layout.
3
- */
4
- import { Segment } from "../core/segment.js";
5
- import type { PaddingDimensions } from "./padding.js";
6
- import type { Renderable, Measurable, RenderOptions } from "../core/protocol.js";
7
- export interface ColumnsOptions {
8
- expand?: boolean;
9
- equal?: boolean;
10
- width?: number;
11
- padding?: PaddingDimensions;
12
- columnFirst?: boolean;
13
- }
14
- export declare class Columns implements Renderable, Measurable {
15
- renderables: (Renderable & Partial<Measurable>)[];
16
- readonly expand: boolean;
17
- readonly equal: boolean;
18
- readonly colWidth: number | undefined;
19
- readonly columnFirst: boolean;
20
- readonly gutterWidth: number;
21
- constructor(items?: Iterable<unknown>, options?: ColumnsOptions);
22
- render(rawOptions: RenderOptions): Iterable<Segment>;
23
- /**
24
- * The requested width divided into columns, once.
25
- *
26
- * [LAW:single-enforcer] The three modes disagree only about how many columns
27
- * there are and how wide each one is, so they disagree in one place. `render`
28
- * lays out against this division and `_naturalWidth` is the width at which it
29
- * comes out as one row of every item.
30
- */
31
- private _divide;
32
- /**
33
- * The widest any one item wants to be.
34
- *
35
- * An item that cannot measure itself wants the offer, which is what `Panel`,
36
- * `Padding`, `Layout` and `Tree` all answer for the same case — and under an
37
- * unbounded offer that is `Infinity`, so `withBoundedWidth` throws and says
38
- * the request was unanswerable. Counting it as one cell instead reported a
39
- * natural width of 1, which resolved an unbounded offer to a single column and
40
- * cropped forty cells of content down to `"x"` with no error at all.
41
- */
42
- private _itemWidth;
43
- /**
44
- * The declared column width, bounded by the width offered.
45
- *
46
- * [LAW:single-enforcer] A declared width is what a column asks for, not what
47
- * it takes — the contract `Table._outerWidth` keeps for a declared table
48
- * width. Read by `_divide` for render and by `_naturalWidth` for measure, so
49
- * the two answer from one number: laid out at the raw declared width, a
50
- * six-cell column offered three emitted six-cell lines while `measure`
51
- * reported three, and the terminal's soft wrap took the frame of everything
52
- * printed after it.
53
- */
54
- private _declaredWidth;
55
- /** The width at which every item sits on one row: n columns and the gutters between them. */
56
- private _naturalWidth;
57
- measure(rawOptions: RenderOptions): {
58
- minimum: number;
59
- maximum: number;
60
- };
61
- }
@@ -1,168 +0,0 @@
1
- /**
2
- * Columns — arranges renderables in a multi-column layout.
3
- */
4
- import { Segment } from "../core/segment.js";
5
- import { Measurement } from "../core/measure.js";
6
- import { RichText } from "../core/text.js";
7
- import { isMeasurable, withBoundedWidth, withCellWidth } from "../core/protocol.js";
8
- import { cellCount } from "../core/cells.js";
9
- function toRenderable(item) {
10
- // [LAW:single-enforcer] A `RichText` passed directly implements `render`
11
- // and would otherwise leave through the passthrough arm below untouched,
12
- // keeping its default `end: "\n"` (rich-text-5ai) — the same gap the code
13
- // review found in table.ts's `toRenderable`. A column item is a row
14
- // fragment, not a printed unit, same as every other embedding site.
15
- if (item instanceof RichText) {
16
- const copy = item.copy();
17
- copy.end = "";
18
- return copy;
19
- }
20
- if (typeof item === "object" && item !== null && "render" in item) {
21
- return item;
22
- }
23
- return new RichText(String(item ?? ""), { end: "" });
24
- }
25
- export class Columns {
26
- renderables;
27
- expand;
28
- equal;
29
- colWidth;
30
- columnFirst;
31
- gutterWidth;
32
- constructor(items, options) {
33
- this.renderables = items ? [...items].map(toRenderable) : [];
34
- this.expand = options?.expand ?? false;
35
- this.equal = options?.equal ?? false;
36
- // A declared column width is a cell count like any other, and it reaches
37
- // the layout without passing through `withCellWidth`, which parses only
38
- // what the *caller of render* supplied. Left raw, `width: NaN` made
39
- // `Math.max(1, NaN)` a NaN column count and `new Array` threw `Invalid
40
- // array length` before a single column was laid out. Absence is preserved
41
- // rather than parsed: `undefined` selects auto-fit, and `cellCount` would
42
- // read it as a declared zero.
43
- this.colWidth = options?.width === undefined ? undefined : cellCount(options.width);
44
- this.columnFirst = options?.columnFirst ?? false;
45
- this.gutterWidth = 2; // default gutter between columns
46
- }
47
- *render(rawOptions) {
48
- const items = this.renderables;
49
- if (items.length === 0)
50
- return;
51
- // The parsed options, not just a parsed local: `_divide` hands them to
52
- // `Measurement.get`, and a raw NaN reaching it comes back as a NaN item
53
- // width, then a NaN column count, then `Invalid array length` out of
54
- // `new Array(numCols)` before a single column is laid out. An unbounded
55
- // width reaches the same `new Array` by the same route, which is why the
56
- // parse here is the bounded one.
57
- const options = withBoundedWidth(rawOptions, this);
58
- const { numCols, colWidths } = this._divide(options);
59
- // Layout items into rows
60
- const numRows = Math.ceil(items.length / numCols);
61
- // [LAW:types-are-the-program] The element type is Renderable — any number
62
- // of lines. Each grid cell renders to its full set of lines; an out-of-range
63
- // slot (the last row is rarely full) is an empty cell. mergeHorizontal then
64
- // stacks every row of every cell line-by-line, so multi-line children
65
- // (Panels, Tables) compose instead of being truncated to their first row.
66
- for (let row = 0; row < numRows; row++) {
67
- const cells = colWidths.map((width, col) => {
68
- const idx = this.columnFirst ? col * numRows + row : row * numCols + col;
69
- const item = items[idx];
70
- const lines = item === undefined
71
- ? []
72
- : Segment.splitLines([
73
- ...item.render({ ...options, maxWidth: width }),
74
- ]);
75
- return { lines, width };
76
- });
77
- yield* Segment.mergeHorizontal(cells, this.gutterWidth);
78
- }
79
- }
80
- /**
81
- * The requested width divided into columns, once.
82
- *
83
- * [LAW:single-enforcer] The three modes disagree only about how many columns
84
- * there are and how wide each one is, so they disagree in one place. `render`
85
- * lays out against this division and `_naturalWidth` is the width at which it
86
- * comes out as one row of every item.
87
- */
88
- _divide(options) {
89
- const maxWidth = options.maxWidth;
90
- const gutter = this.gutterWidth;
91
- const declared = this._declaredWidth(options);
92
- if (declared !== undefined) {
93
- const numCols = Math.max(1, Math.floor((maxWidth + gutter) / (declared + gutter)));
94
- return { numCols, colWidths: new Array(numCols).fill(declared) };
95
- }
96
- if (this.equal) {
97
- const itemWidth = this._itemWidth(options);
98
- const numCols = Math.max(1, Math.floor((maxWidth + gutter) / (itemWidth + gutter)));
99
- const equalWidth = Math.floor((maxWidth - gutter * (numCols - 1)) / numCols);
100
- return { numCols, colWidths: new Array(numCols).fill(equalWidth) };
101
- }
102
- // Auto: as many columns as the widest item fits into, capped at the item
103
- // count. Sized from the content rather than from a flat four cells per
104
- // column, because `_naturalWidth` below is the inverse of *this* expression
105
- // and a constant has no inverse: six two-cell items reported a natural
106
- // width of 22 and then wrapped into two rows at 22, since `floor(22 / 4)`
107
- // is five columns no matter how narrow the items are.
108
- const itemWidth = this._itemWidth(options);
109
- const numCols = Math.min(this.renderables.length, Math.max(1, Math.floor((maxWidth + gutter) / (itemWidth + gutter))));
110
- const colW = Math.floor((maxWidth - gutter * (numCols - 1)) / numCols);
111
- return { numCols, colWidths: new Array(numCols).fill(colW) };
112
- }
113
- /**
114
- * The widest any one item wants to be.
115
- *
116
- * An item that cannot measure itself wants the offer, which is what `Panel`,
117
- * `Padding`, `Layout` and `Tree` all answer for the same case — and under an
118
- * unbounded offer that is `Infinity`, so `withBoundedWidth` throws and says
119
- * the request was unanswerable. Counting it as one cell instead reported a
120
- * natural width of 1, which resolved an unbounded offer to a single column and
121
- * cropped forty cells of content down to `"x"` with no error at all.
122
- */
123
- _itemWidth(options) {
124
- let widest = 1;
125
- for (const item of this.renderables) {
126
- widest = Math.max(widest, isMeasurable(item)
127
- ? Measurement.get(options, item).maximum
128
- : options.maxWidth);
129
- }
130
- return widest;
131
- }
132
- /**
133
- * The declared column width, bounded by the width offered.
134
- *
135
- * [LAW:single-enforcer] A declared width is what a column asks for, not what
136
- * it takes — the contract `Table._outerWidth` keeps for a declared table
137
- * width. Read by `_divide` for render and by `_naturalWidth` for measure, so
138
- * the two answer from one number: laid out at the raw declared width, a
139
- * six-cell column offered three emitted six-cell lines while `measure`
140
- * reported three, and the terminal's soft wrap took the frame of everything
141
- * printed after it.
142
- */
143
- _declaredWidth(options) {
144
- return this.colWidth === undefined ? undefined : Math.min(this.colWidth, options.maxWidth);
145
- }
146
- /** The width at which every item sits on one row: n columns and the gutters between them. */
147
- _naturalWidth(options) {
148
- const count = this.renderables.length;
149
- if (count === 0)
150
- return 0;
151
- const width = this._declaredWidth(options) ?? this._itemWidth(options);
152
- return count * width + this.gutterWidth * (count - 1);
153
- }
154
- measure(rawOptions) {
155
- // A floor of one cell is what Columns asks for; the ceiling is what it was
156
- // offered, and the ceiling wins. Stated as a bare `minimum: 1`, a Columns
157
- // measured into no width at all reported the range 1..0 — a floor above
158
- // its own ceiling, which the parent that asked cannot divide.
159
- //
160
- // The ceiling is not the whole answer, though: reported as the offer alone,
161
- // a Columns claimed every cell it was shown, so `Panel` in fit mode drew a
162
- // 40-cell frame around five cells of content and an unbounded offer came
163
- // back unbounded.
164
- const parsed = withCellWidth(rawOptions);
165
- const maximum = Math.min(this._naturalWidth(parsed), parsed.maxWidth);
166
- return { minimum: Math.min(1, maximum), maximum };
167
- }
168
- }
@@ -1,15 +0,0 @@
1
- /**
2
- * Constrain — wraps a renderable and constrains its maximum width.
3
- */
4
- import { Segment } from "../core/segment.js";
5
- import type { Renderable, Measurable, RenderOptions } from "../core/protocol.js";
6
- export declare class Constrain implements Renderable, Measurable {
7
- readonly renderable: Renderable;
8
- readonly width: number | undefined;
9
- constructor(renderable: Renderable, width?: number);
10
- render(options: RenderOptions): Iterable<Segment>;
11
- measure(options: RenderOptions): {
12
- minimum: number;
13
- maximum: number;
14
- };
15
- }
@@ -1,37 +0,0 @@
1
- /**
2
- * Constrain — wraps a renderable and constrains its maximum width.
3
- */
4
- import { Measurement } from "../core/measure.js";
5
- import { isMeasurable } from "../core/protocol.js";
6
- export class Constrain {
7
- renderable;
8
- width;
9
- constructor(renderable, width) {
10
- this.renderable = renderable;
11
- this.width = width;
12
- }
13
- *render(options) {
14
- // [LAW:dataflow-not-control-flow] Always compute constrainedWidth; undefined means no constraint
15
- const constrainedWidth = this.width !== undefined
16
- ? Math.min(this.width, options.maxWidth)
17
- : options.maxWidth;
18
- const innerOptions = {
19
- ...options,
20
- maxWidth: constrainedWidth,
21
- };
22
- yield* this.renderable.render(innerOptions);
23
- }
24
- measure(options) {
25
- if (isMeasurable(this.renderable)) {
26
- const measurement = Measurement.get(options, this.renderable);
27
- const maxWidth = this.width !== undefined
28
- ? Math.min(this.width, options.maxWidth)
29
- : options.maxWidth;
30
- return {
31
- minimum: measurement.minimum,
32
- maximum: Math.min(measurement.maximum, maxWidth),
33
- };
34
- }
35
- return { minimum: 1, maximum: options.maxWidth };
36
- }
37
- }
@@ -1,41 +0,0 @@
1
- /**
2
- * FlexStrip — wrap-to-width horizontal layout for `StyledRenderable` items.
3
- *
4
- * Pack as many items as fit on a line, then break and continue. Each line is
5
- * an independent sub-strip: optional `Joiner` end-caps fire at every line
6
- * boundary (not just the strip's first/last position), so a line break looks
7
- * the same as an endpoint to the joiner. Composes with the Strip + Joiner
8
- * primitive in `core/strip` — same `Joiner` protocol, no new join semantics.
9
- *
10
- * [LAW:dataflow-not-control-flow] The pack walk is the same shape every
11
- * render: measure each item, render every joiner form (start-cap, end-cap,
12
- * mid-join) up-front, then sweep items into lines. Line breaks are decided by
13
- * width data, not by control-flow special cases.
14
- *
15
- * [LAW:one-source-of-truth] The Strip primitive owns the join protocol;
16
- * FlexStrip reuses it verbatim. There is no second concept of "join."
17
- */
18
- import { Segment } from "../core/segment.js";
19
- import type { Renderable, Measurable, RenderOptions } from "../core/protocol.js";
20
- import type { Joiner, StyledRenderable } from "../core/strip.js";
21
- export type FlexAlign = "left" | "center" | "right" | "justify";
22
- export interface FlexStripOptions<T extends StyledRenderable> {
23
- /** Joiner used between items within a line, and at line endpoints. */
24
- joiner?: Joiner<T>;
25
- /** Cells of horizontal space inserted on each side of an inter-item joiner (default 0). */
26
- gap?: number;
27
- /** Line alignment when packed items don't fill the available width (default "left"). */
28
- align?: FlexAlign;
29
- }
30
- export declare class FlexStrip<T extends StyledRenderable = StyledRenderable> implements Renderable, Measurable {
31
- readonly items: readonly T[];
32
- readonly joiner: Joiner<T> | undefined;
33
- readonly gap: number;
34
- readonly align: FlexAlign;
35
- constructor(items: readonly T[], options?: FlexStripOptions<T>);
36
- render(options: RenderOptions): Iterable<Segment>;
37
- measure(options: RenderOptions): {
38
- minimum: number;
39
- maximum: number;
40
- };
41
- }
@@ -1,200 +0,0 @@
1
- /**
2
- * FlexStrip — wrap-to-width horizontal layout for `StyledRenderable` items.
3
- *
4
- * Pack as many items as fit on a line, then break and continue. Each line is
5
- * an independent sub-strip: optional `Joiner` end-caps fire at every line
6
- * boundary (not just the strip's first/last position), so a line break looks
7
- * the same as an endpoint to the joiner. Composes with the Strip + Joiner
8
- * primitive in `core/strip` — same `Joiner` protocol, no new join semantics.
9
- *
10
- * [LAW:dataflow-not-control-flow] The pack walk is the same shape every
11
- * render: measure each item, render every joiner form (start-cap, end-cap,
12
- * mid-join) up-front, then sweep items into lines. Line breaks are decided by
13
- * width data, not by control-flow special cases.
14
- *
15
- * [LAW:one-source-of-truth] The Strip primitive owns the join protocol;
16
- * FlexStrip reuses it verbatim. There is no second concept of "join."
17
- */
18
- import { Segment } from "../core/segment.js";
19
- function renderToBlock(r, options) {
20
- const segments = [...r.render(options)];
21
- let width = 0;
22
- for (const s of segments)
23
- width += s.cellLength;
24
- return { segments, width };
25
- }
26
- const EMPTY_BLOCK = { segments: [], width: 0 };
27
- const PAD_LEFT_BY_ALIGN = {
28
- left: () => 0,
29
- justify: () => 0,
30
- center: (spare) => Math.floor(spare / 2),
31
- right: (spare) => spare,
32
- };
33
- function computePadLeft(align, spare) {
34
- return PAD_LEFT_BY_ALIGN[align](spare);
35
- }
36
- // Return per-slot fill widths (length = itemCount - 1, possibly empty).
37
- // Non-justify cases produce all zeros — the emit loop runs the same way.
38
- function computeSlotFills(align, isFinalLine, spare, itemCount) {
39
- const slots = Math.max(0, itemCount - 1);
40
- const distribute = align === "justify" && !isFinalLine && slots > 0 && spare > 0;
41
- const totalFill = distribute ? spare : 0;
42
- const base = slots > 0 ? Math.floor(totalFill / slots) : 0;
43
- const extra = totalFill - base * slots;
44
- return Array.from({ length: slots }, (_, k) => base + (k < extra ? 1 : 0));
45
- }
46
- export class FlexStrip {
47
- items;
48
- joiner;
49
- gap;
50
- align;
51
- constructor(items, options) {
52
- this.items = items;
53
- this.joiner = options?.joiner;
54
- this.gap = options?.gap ?? 0;
55
- this.align = options?.align ?? "left";
56
- }
57
- *render(options) {
58
- const items = this.items;
59
- if (items.length === 0)
60
- return;
61
- const maxWidth = Math.max(1, options.maxWidth);
62
- const gap = this.gap;
63
- const joiner = this.joiner;
64
- // Pre-render every item and every joiner form we might need. Doing this
65
- // up-front turns packing into a pure data computation over widths.
66
- const itemBlocks = items.map((it) => renderToBlock(it, options));
67
- const startCap = (i) => joiner ? renderToBlock(joiner.join(null, items[i]), options) : EMPTY_BLOCK;
68
- const endCap = (i) => joiner ? renderToBlock(joiner.join(items[i], null), options) : EMPTY_BLOCK;
69
- const midJoin = (i, j) => joiner ? renderToBlock(joiner.join(items[i], items[j]), options) : EMPTY_BLOCK;
70
- const lines = [];
71
- let line = null;
72
- const gapBlock = gap > 0
73
- ? { segments: [new Segment(" ".repeat(gap))], width: gap }
74
- : EMPTY_BLOCK;
75
- for (let i = 0; i < items.length; i++) {
76
- const item = itemBlocks[i];
77
- if (line === null) {
78
- const sc = startCap(i);
79
- const ec = endCap(i);
80
- const blocks = sc.width > 0 ? [sc, item] : [item];
81
- const itemBlockIndices = [blocks.length - 1];
82
- line = { blocks, width: sc.width + item.width, lastIndex: i, itemBlockIndices };
83
- // Tentatively reserve space for the line's eventual end-cap by
84
- // tracking it separately below — `line.width` is the open width
85
- // (without end-cap). We test fit using width + endCap on each step.
86
- // Always start a new line with at least the first item (overflow ok
87
- // per ticket: graceful fallback when an item is wider than terminal).
88
- // Stash end-cap for the close step.
89
- line.endCap = ec;
90
- continue;
91
- }
92
- const prev = line.lastIndex;
93
- const mid = midJoin(prev, i);
94
- const newEndCap = endCap(i);
95
- const addCost = (gap > 0 ? gap : 0) + mid.width + (gap > 0 ? gap : 0) + item.width;
96
- const closedWidthIfAdded = line.width + addCost + newEndCap.width;
97
- if (closedWidthIfAdded <= maxWidth) {
98
- if (gap > 0)
99
- line.blocks.push(gapBlock);
100
- if (mid.width > 0)
101
- line.blocks.push(mid);
102
- if (gap > 0)
103
- line.blocks.push(gapBlock);
104
- line.blocks.push(item);
105
- line.itemBlockIndices.push(line.blocks.length - 1);
106
- line.width += addCost;
107
- line.lastIndex = i;
108
- line.endCap = newEndCap;
109
- }
110
- else {
111
- // Close current line and start a new one with this item.
112
- const closing = line.endCap;
113
- if (closing.width > 0)
114
- line.blocks.push(closing);
115
- line.width += closing.width;
116
- lines.push(line);
117
- const sc = startCap(i);
118
- const ec = endCap(i);
119
- const blocks = sc.width > 0 ? [sc, item] : [item];
120
- const itemBlockIndices = [blocks.length - 1];
121
- line = { blocks, width: sc.width + item.width, lastIndex: i, itemBlockIndices };
122
- line.endCap = ec;
123
- }
124
- }
125
- // Close final line. `line` is non-null here: the loop runs at least once
126
- // (items.length > 0 was handled above).
127
- const finalLine = line;
128
- {
129
- const closing = finalLine.endCap;
130
- if (closing.width > 0)
131
- finalLine.blocks.push(closing);
132
- finalLine.width += closing.width;
133
- lines.push(finalLine);
134
- }
135
- // [LAW:dataflow-not-control-flow] Single emission path for every line:
136
- // emit pad, walk every block, optionally emit a per-slot fill before each
137
- // non-first item. Variability lives in two precomputed value sequences
138
- // (`padLeft` and `slotFills`); the emit loop has no per-mode branch.
139
- //
140
- // - `padLeft` pushes the line right by 0 (left/justify), spare/2 (center),
141
- // or full spare (right). Justify-on-final-line follows the left rule.
142
- // - `slotFills[k]` is the cells of fill emitted before the (k+1)-th item.
143
- // Always 0 unless align=justify on a non-final line with ≥2 items.
144
- //
145
- // Putting the variability in data means the emit loop is the same shape
146
- // every render, and "what happens between two items" can never accidentally
147
- // become "what happens between joiner halves" — the data decides.
148
- const align = this.align;
149
- for (let li = 0; li < lines.length; li++) {
150
- const ln = lines[li];
151
- const isLast = li === lines.length - 1;
152
- const spare = Math.max(0, maxWidth - ln.width);
153
- const padLeft = computePadLeft(align, spare);
154
- const slotFills = computeSlotFills(align, isLast, spare, ln.itemBlockIndices.length);
155
- const itemRankOf = new Map();
156
- ln.itemBlockIndices.forEach((blockIdx, rank) => itemRankOf.set(blockIdx, rank));
157
- if (padLeft > 0)
158
- yield new Segment(" ".repeat(padLeft));
159
- for (let bi = 0; bi < ln.blocks.length; bi++) {
160
- const rank = itemRankOf.get(bi);
161
- if (rank !== undefined && rank > 0) {
162
- const fill = slotFills[rank - 1];
163
- if (fill > 0)
164
- yield new Segment(" ".repeat(fill));
165
- }
166
- yield* ln.blocks[bi].segments;
167
- }
168
- yield Segment.line();
169
- }
170
- }
171
- measure(options) {
172
- const items = this.items;
173
- if (items.length === 0)
174
- return { minimum: 0, maximum: 0 };
175
- let minimum = 0;
176
- let maximum = 0;
177
- for (let i = 0; i < items.length; i++) {
178
- const itemWidth = renderToBlock(items[i], options).width;
179
- const sc = this.joiner
180
- ? renderToBlock(this.joiner.join(null, items[i]), options).width
181
- : 0;
182
- const ec = this.joiner
183
- ? renderToBlock(this.joiner.join(items[i], null), options).width
184
- : 0;
185
- minimum = Math.max(minimum, itemWidth + sc + ec);
186
- maximum += itemWidth;
187
- if (i > 0) {
188
- maximum += this.gap * 2;
189
- if (this.joiner) {
190
- maximum += renderToBlock(this.joiner.join(items[i - 1], items[i]), options).width;
191
- }
192
- }
193
- }
194
- if (this.joiner) {
195
- maximum += renderToBlock(this.joiner.join(null, items[0]), options).width;
196
- maximum += renderToBlock(this.joiner.join(items[items.length - 1], null), options).width;
197
- }
198
- return { minimum, maximum };
199
- }
200
- }
@@ -1,11 +0,0 @@
1
- /**
2
- * Group — renders multiple renderables in sequence.
3
- * No visual chrome — purely a container.
4
- */
5
- import { Segment } from "../core/segment.js";
6
- import type { Renderable, RenderOptions } from "../core/protocol.js";
7
- export declare class Group implements Renderable {
8
- readonly renderables: Renderable[];
9
- constructor(...renderables: Renderable[]);
10
- render(options: RenderOptions): Iterable<Segment>;
11
- }
@@ -1,15 +0,0 @@
1
- /**
2
- * Group — renders multiple renderables in sequence.
3
- * No visual chrome — purely a container.
4
- */
5
- export class Group {
6
- renderables;
7
- constructor(...renderables) {
8
- this.renderables = renderables;
9
- }
10
- *render(options) {
11
- for (const renderable of this.renderables) {
12
- yield* renderable.render(options);
13
- }
14
- }
15
- }
@@ -1,92 +0,0 @@
1
- /**
2
- * Layout — divides the screen into rectangular regions.
3
- */
4
- import { Segment } from "../core/segment.js";
5
- import type { Renderable, Measurable, RenderOptions } from "../core/protocol.js";
6
- export interface LayoutOptions {
7
- name?: string;
8
- ratio?: number;
9
- size?: number;
10
- minimumSize?: number;
11
- visible?: boolean;
12
- }
13
- export declare class Layout implements Renderable, Measurable {
14
- name: string | undefined;
15
- visible: boolean;
16
- private _ratio;
17
- private _size;
18
- private _minimumSize;
19
- private _renderable;
20
- private _children;
21
- private _splitDirection;
22
- constructor(renderable?: Renderable | string, options?: LayoutOptions);
23
- /**
24
- * The three declared numbers, parsed on assignment rather than at the
25
- * constructor. All three are public and a caller reaches them long after
26
- * construction — `layout.getByName("pane")!.ratio = -1` walked straight past a
27
- * constructor-only parse and put a negative weight back into the division that
28
- * `_rowBudgetFor` and `_distributeSpace` are written to trust.
29
- *
30
- * [LAW:parse-dont-validate] The setter is the border, so the guarantee holds
31
- * for the object's whole lifetime and nothing downstream re-checks. `size` and
32
- * `minimumSize` are cell counts; `ratio` is a share weight and keeps its
33
- * fractions. Absence is preserved rather than parsed: an undefined `size`
34
- * selects a flex pane, and `cellCount` would read it as a declared zero.
35
- */
36
- get ratio(): number;
37
- set ratio(value: number);
38
- get size(): number | undefined;
39
- set size(value: number | undefined);
40
- get minimumSize(): number;
41
- set minimumSize(value: number);
42
- get children(): Layout[];
43
- /**
44
- * Whether this layout draws `_renderable` or its children.
45
- *
46
- * [LAW:one-source-of-truth] `render` and `_naturalWidth` must answer this the
47
- * same way. Asked separately they did not: `_naturalWidth` read it off the
48
- * *visible* children, so a layout holding content and a single hidden child
49
- * reported that content's width while `render` emitted nothing at all, and a
50
- * fit-mode `Panel` framed twelve cells of air.
51
- */
52
- private get _isLeaf();
53
- splitColumn(...layouts: Layout[]): void;
54
- splitRow(...layouts: Layout[]): void;
55
- update(renderable: Renderable | string): void;
56
- getByName(name: string): Layout | undefined;
57
- render(rawOptions: RenderOptions): Iterable<Segment>;
58
- private _renderColumn;
59
- private _renderRow;
60
- private _distributeSpace;
61
- /**
62
- * The width this layout would take if nothing constrained it: its content's
63
- * for a leaf, its children's laid out the way the split lays them out.
64
- *
65
- * A `size` counts only across a row, which is the one direction in which it
66
- * is a width — down a column the same field is a height, and reading it as a
67
- * width there would report a two-line pane as two cells wide.
68
- */
69
- private _naturalWidth;
70
- /**
71
- * The budget at which `_distributeSpace` gives every child of a row at least
72
- * the width it wants — the inverse of the ratio rule above, and the reason
73
- * this is not the sum of those widths.
74
- *
75
- * [LAW:one-source-of-truth] A growing child receives `floor(budget * ratio /
76
- * totalRatio)`, so to receive `want` cells it needs the budget to reach
77
- * `want * totalRatio / ratio`, and one budget serves them all: the row needs
78
- * the largest demand, never their total. Summed instead, a 1:1 split of
79
- * "left" and "right" reported 9 and then rendered "right" into the 4 cells
80
- * that `floor(9/2)` actually hands it.
81
- *
82
- * The panes that do not grow are paid first here for the same reason they
83
- * are paid first there — and it is the reason this divides by no zero: a
84
- * `ratio` of 0 never reaches the division, because a pane that does not grow
85
- * demands nothing of a budget for growing.
86
- */
87
- private _rowBudgetFor;
88
- measure(rawOptions: RenderOptions): {
89
- minimum: number;
90
- maximum: number;
91
- };
92
- }