@promptctl/rich-js 0.7.0 → 0.9.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 (201) hide show
  1. package/README.md +99 -31
  2. package/dist/core/box.d.ts +96 -42
  3. package/dist/core/box.d.ts.map +1 -1
  4. package/dist/core/box.js +348 -490
  5. package/dist/core/box.js.map +1 -1
  6. package/dist/core/cells.d.ts +46 -1
  7. package/dist/core/cells.d.ts.map +1 -1
  8. package/dist/core/cells.js +55 -14
  9. package/dist/core/cells.js.map +1 -1
  10. package/dist/core/color.d.ts.map +1 -1
  11. package/dist/core/color.js +22 -8
  12. package/dist/core/color.js.map +1 -1
  13. package/dist/core/console.d.ts +30 -14
  14. package/dist/core/console.d.ts.map +1 -1
  15. package/dist/core/console.js +176 -104
  16. package/dist/core/console.js.map +1 -1
  17. package/dist/core/highlighter.d.ts +2 -1
  18. package/dist/core/highlighter.d.ts.map +1 -1
  19. package/dist/core/highlighter.js +28 -6
  20. package/dist/core/highlighter.js.map +1 -1
  21. package/dist/{renderables → core}/json.d.ts +8 -3
  22. package/dist/core/json.d.ts.map +1 -0
  23. package/dist/{renderables → core}/json.js +7 -2
  24. package/dist/core/json.js.map +1 -0
  25. package/dist/core/markup.d.ts +6 -9
  26. package/dist/core/markup.d.ts.map +1 -1
  27. package/dist/core/markup.js +153 -47
  28. package/dist/core/markup.js.map +1 -1
  29. package/dist/core/measure.d.ts +11 -0
  30. package/dist/core/measure.d.ts.map +1 -1
  31. package/dist/core/measure.js +15 -3
  32. package/dist/core/measure.js.map +1 -1
  33. package/dist/core/oklch.d.ts +22 -0
  34. package/dist/core/oklch.d.ts.map +1 -1
  35. package/dist/core/oklch.js +62 -18
  36. package/dist/core/oklch.js.map +1 -1
  37. package/dist/core/pretty.d.ts +122 -0
  38. package/dist/core/pretty.d.ts.map +1 -0
  39. package/dist/core/pretty.js +398 -0
  40. package/dist/core/pretty.js.map +1 -0
  41. package/dist/core/protocol.d.ts +80 -0
  42. package/dist/core/protocol.d.ts.map +1 -1
  43. package/dist/core/protocol.js +85 -0
  44. package/dist/core/protocol.js.map +1 -1
  45. package/dist/core/segment.d.ts +26 -0
  46. package/dist/core/segment.d.ts.map +1 -1
  47. package/dist/core/segment.js +45 -0
  48. package/dist/core/segment.js.map +1 -1
  49. package/dist/core/strip.d.ts +9 -5
  50. package/dist/core/strip.d.ts.map +1 -1
  51. package/dist/core/strip.js +39 -41
  52. package/dist/core/strip.js.map +1 -1
  53. package/dist/core/style.d.ts +19 -0
  54. package/dist/core/style.d.ts.map +1 -1
  55. package/dist/core/style.js +46 -38
  56. package/dist/core/style.js.map +1 -1
  57. package/dist/core/subscription.d.ts +13 -0
  58. package/dist/core/subscription.d.ts.map +1 -0
  59. package/dist/core/subscription.js +13 -0
  60. package/dist/core/subscription.js.map +1 -0
  61. package/dist/core/text.d.ts +102 -4
  62. package/dist/core/text.d.ts.map +1 -1
  63. package/dist/core/text.js +332 -162
  64. package/dist/core/text.js.map +1 -1
  65. package/dist/core/wrap.d.ts +42 -0
  66. package/dist/core/wrap.d.ts.map +1 -0
  67. package/dist/core/wrap.js +128 -0
  68. package/dist/core/wrap.js.map +1 -0
  69. package/dist/host/host-stream.d.ts.map +1 -0
  70. package/dist/host/host-stream.js.map +1 -0
  71. package/dist/host/index.d.ts +4 -0
  72. package/dist/host/index.d.ts.map +1 -0
  73. package/dist/host/index.js +3 -0
  74. package/dist/host/index.js.map +1 -0
  75. package/dist/{widgets → host}/terminal-host.d.ts +20 -64
  76. package/dist/host/terminal-host.d.ts.map +1 -0
  77. package/dist/host/terminal-host.js +133 -0
  78. package/dist/host/terminal-host.js.map +1 -0
  79. package/dist/index.d.ts +13 -36
  80. package/dist/index.d.ts.map +1 -1
  81. package/dist/index.js +35 -22
  82. package/dist/index.js.map +1 -1
  83. package/dist/node/terminal-host.d.ts +85 -0
  84. package/dist/node/terminal-host.d.ts.map +1 -0
  85. package/dist/node/terminal-host.js +161 -0
  86. package/dist/node/terminal-host.js.map +1 -0
  87. package/dist/node/traceback.d.ts +28 -0
  88. package/dist/node/traceback.d.ts.map +1 -0
  89. package/dist/node/traceback.js +113 -0
  90. package/dist/node/traceback.js.map +1 -0
  91. package/dist/renderables/columns.d.ts +24 -2
  92. package/dist/renderables/columns.d.ts.map +1 -1
  93. package/dist/renderables/columns.js +91 -32
  94. package/dist/renderables/columns.js.map +1 -1
  95. package/dist/renderables/layout.d.ts +61 -5
  96. package/dist/renderables/layout.d.ts.map +1 -1
  97. package/dist/renderables/layout.js +191 -19
  98. package/dist/renderables/layout.js.map +1 -1
  99. package/dist/renderables/markdown.d.ts +1 -1
  100. package/dist/renderables/markdown.d.ts.map +1 -1
  101. package/dist/renderables/markdown.js +7 -14
  102. package/dist/renderables/markdown.js.map +1 -1
  103. package/dist/renderables/padding.d.ts +16 -3
  104. package/dist/renderables/padding.d.ts.map +1 -1
  105. package/dist/renderables/padding.js +96 -47
  106. package/dist/renderables/padding.js.map +1 -1
  107. package/dist/renderables/panel.d.ts +48 -7
  108. package/dist/renderables/panel.d.ts.map +1 -1
  109. package/dist/renderables/panel.js +175 -106
  110. package/dist/renderables/panel.js.map +1 -1
  111. package/dist/renderables/progress.d.ts +3 -3
  112. package/dist/renderables/progress.d.ts.map +1 -1
  113. package/dist/renderables/progress.js +11 -9
  114. package/dist/renderables/progress.js.map +1 -1
  115. package/dist/renderables/progressBar.d.ts +3 -3
  116. package/dist/renderables/progressBar.d.ts.map +1 -1
  117. package/dist/renderables/progressBar.js +9 -15
  118. package/dist/renderables/progressBar.js.map +1 -1
  119. package/dist/renderables/prompt.js +1 -1
  120. package/dist/renderables/prompt.js.map +1 -1
  121. package/dist/renderables/rule.d.ts +1 -1
  122. package/dist/renderables/rule.d.ts.map +1 -1
  123. package/dist/renderables/rule.js +5 -10
  124. package/dist/renderables/rule.js.map +1 -1
  125. package/dist/renderables/spinner.d.ts +2 -2
  126. package/dist/renderables/spinner.d.ts.map +1 -1
  127. package/dist/renderables/spinner.js +6 -11
  128. package/dist/renderables/spinner.js.map +1 -1
  129. package/dist/renderables/status.d.ts.map +1 -1
  130. package/dist/renderables/status.js +5 -11
  131. package/dist/renderables/status.js.map +1 -1
  132. package/dist/renderables/table.d.ts +90 -16
  133. package/dist/renderables/table.d.ts.map +1 -1
  134. package/dist/renderables/table.js +481 -223
  135. package/dist/renderables/table.js.map +1 -1
  136. package/dist/renderables/traceback.d.ts +7 -3
  137. package/dist/renderables/traceback.d.ts.map +1 -1
  138. package/dist/renderables/traceback.js +15 -18
  139. package/dist/renderables/traceback.js.map +1 -1
  140. package/dist/renderables/tree.d.ts +29 -4
  141. package/dist/renderables/tree.d.ts.map +1 -1
  142. package/dist/renderables/tree.js +86 -33
  143. package/dist/renderables/tree.js.map +1 -1
  144. package/dist/template-bindings/color-funcs.d.ts +3 -3
  145. package/dist/template-bindings/color-funcs.js +3 -3
  146. package/dist/template-bindings/helpers.d.ts.map +1 -1
  147. package/dist/template-bindings/helpers.js +19 -1
  148. package/dist/template-bindings/helpers.js.map +1 -1
  149. package/dist/template-bindings/index.d.ts +6 -7
  150. package/dist/template-bindings/index.d.ts.map +1 -1
  151. package/dist/template-bindings/index.js +6 -7
  152. package/dist/template-bindings/index.js.map +1 -1
  153. package/dist/template-bindings/palette-funcs.d.ts +17 -5
  154. package/dist/template-bindings/palette-funcs.d.ts.map +1 -1
  155. package/dist/template-bindings/palette-funcs.js +69 -6
  156. package/dist/template-bindings/palette-funcs.js.map +1 -1
  157. package/dist/template-bindings/style-funcs.d.ts +1 -1
  158. package/dist/template-bindings/style-funcs.js +1 -1
  159. package/dist/themes/ramp.d.ts +84 -0
  160. package/dist/themes/ramp.d.ts.map +1 -0
  161. package/dist/themes/ramp.js +123 -0
  162. package/dist/themes/ramp.js.map +1 -0
  163. package/dist/widgets/dropdown.d.ts +1 -1
  164. package/dist/widgets/dropdown.js +1 -1
  165. package/dist/widgets/event-router.d.ts +3 -2
  166. package/dist/widgets/event-router.d.ts.map +1 -1
  167. package/dist/widgets/focus-manager.d.ts +2 -1
  168. package/dist/widgets/focus-manager.d.ts.map +1 -1
  169. package/dist/widgets/index.d.ts +3 -6
  170. package/dist/widgets/index.d.ts.map +1 -1
  171. package/dist/widgets/index.js +4 -3
  172. package/dist/widgets/index.js.map +1 -1
  173. package/dist/widgets/screen.d.ts +1 -1
  174. package/dist/widgets/screen.d.ts.map +1 -1
  175. package/dist/widgets/text-input.d.ts +2 -3
  176. package/dist/widgets/text-input.d.ts.map +1 -1
  177. package/dist/widgets/text-input.js +4 -12
  178. package/dist/widgets/text-input.js.map +1 -1
  179. package/dist/widgets/types.d.ts +1 -1
  180. package/dist/widgets/types.d.ts.map +1 -1
  181. package/dist/widgets/types.js.map +1 -1
  182. package/dist/widgets/widget-base.d.ts +2 -1
  183. package/dist/widgets/widget-base.d.ts.map +1 -1
  184. package/package.json +38 -6
  185. package/dist/renderables/json.d.ts.map +0 -1
  186. package/dist/renderables/json.js.map +0 -1
  187. package/dist/renderables/pretty.d.ts +0 -29
  188. package/dist/renderables/pretty.d.ts.map +0 -1
  189. package/dist/renderables/pretty.js +0 -141
  190. package/dist/renderables/pretty.js.map +0 -1
  191. package/dist/themes/paletteResolver.d.ts +0 -35
  192. package/dist/themes/paletteResolver.d.ts.map +0 -1
  193. package/dist/themes/paletteResolver.js +0 -88
  194. package/dist/themes/paletteResolver.js.map +0 -1
  195. package/dist/widgets/host-stream.d.ts.map +0 -1
  196. package/dist/widgets/host-stream.js.map +0 -1
  197. package/dist/widgets/terminal-host.d.ts.map +0 -1
  198. package/dist/widgets/terminal-host.js +0 -258
  199. package/dist/widgets/terminal-host.js.map +0 -1
  200. /package/dist/{widgets → host}/host-stream.d.ts +0 -0
  201. /package/dist/{widgets → host}/host-stream.js +0 -0
@@ -0,0 +1,113 @@
1
+ /**
2
+ * node:traceback — install `Traceback` as the process-wide crash renderer.
3
+ *
4
+ * [LAW:locality-or-seam] `process.on` / `process.exit` live only here, on the
5
+ * node side of the API boundary. The `Traceback` renderable itself is pure
6
+ * rendering and stays in the browser-safe barrel; installing it as a global
7
+ * handler is a node capability, so it is a node subpath:
8
+ *
9
+ * import { installTraceback } from "@promptctl/rich-js/node/traceback";
10
+ * installTraceback({ suppress: ["node_modules"] });
11
+ *
12
+ * [LAW:dataflow-not-control-flow] Both crash channels register the same
13
+ * `report` function, unadapted. Neither payload is trusted to be an `Error`:
14
+ * `@types/node` annotates `uncaughtException` with `Error`, but `throw "boom"`
15
+ * delivers the raw string, so the annotation is a claim about the common case
16
+ * rather than a guarantee. `report` takes `unknown` and both channels are
17
+ * literally one code path.
18
+ */
19
+ import { inspect, types } from "node:util";
20
+ import { Console } from "../core/console.js";
21
+ import { Traceback } from "../renderables/traceback.js";
22
+ /** A payload's own stack, when it carries one, or `""` when it does not. */
23
+ function stackOf(reason) {
24
+ if (typeof reason !== "object" || reason === null)
25
+ return "";
26
+ if (!("stack" in reason))
27
+ return "";
28
+ return typeof reason.stack === "string" ? reason.stack : "";
29
+ }
30
+ /**
31
+ * [LAW:parse-dont-validate] The checkpoint between a crash payload, which node
32
+ * allows to be any value at all, and `Traceback`, which renders an `Error`.
33
+ * The return type is the proof: nothing downstream re-asks whether the payload
34
+ * was throwable, and no raw value can reach the renderable to have `.name` and
35
+ * `.message` read off it as `undefined`.
36
+ *
37
+ * The gate is `isNativeError`, not `instanceof Error`. An error thrown inside a
38
+ * `node:vm` context is a real error with real frames, but its prototype comes
39
+ * from that realm, so `instanceof` answers false and the frames would be
40
+ * discarded by the one component that exists to show them. `isNativeError`
41
+ * reads V8's internal error slot and is realm-independent.
42
+ *
43
+ * Whatever survives that gate is not an error, so it has no call site of its
44
+ * own; `stackOf` salvages a stack from a duck-typed thrower and otherwise
45
+ * leaves it empty rather than reporting the frames of this function, which
46
+ * point into rich-js and describe nothing about the fault. `inspect` renders
47
+ * every payload shape, including circular objects, where `String(reason)`
48
+ * would collapse them to `[object Object]`.
49
+ */
50
+ function toError(reason) {
51
+ if (types.isNativeError(reason))
52
+ return reason;
53
+ const error = new Error(inspect(reason));
54
+ error.name = "NonError";
55
+ error.stack = stackOf(reason);
56
+ return error;
57
+ }
58
+ /**
59
+ * The handler currently registered, or a no-op standing in for "none yet".
60
+ *
61
+ * [LAW:no-shared-mutable-globals] The process listener registry is a shared
62
+ * global, so the handler installed into it gets a single owner here rather
63
+ * than a caller appending to it once per call. Holding the reference is what
64
+ * lets `installTraceback` replace rather than accumulate.
65
+ *
66
+ * [LAW:types-are-the-program] A no-op rather than `null`: `process.off` on an
67
+ * unregistered listener does nothing, so the first call needs no null check
68
+ * and no "have we installed yet" branch. There is no absent case to handle.
69
+ */
70
+ let installed = () => { };
71
+ /**
72
+ * Register rich tracebacks for uncaught exceptions and unhandled rejections.
73
+ *
74
+ * Calling this more than once replaces the previous handler — the last call
75
+ * wins, options included — so a process always has exactly one rich crash
76
+ * renderer no matter how many entry points reach for it.
77
+ */
78
+ export function installTraceback(options) {
79
+ // [LAW:no-ambient-temporal-coupling] "This process is already crashing" is
80
+ // lifecycle state with one owner, not a fact left to the interleaving of two
81
+ // writes. A cascade — cleanup code rejecting a promise while the first
82
+ // report is still draining — would otherwise start a second write and a
83
+ // second `process.exit(1)`, and whichever drained first would cut the other
84
+ // off mid-frame. The first crash is the fault and the rest is its wake, so
85
+ // one complete report of the fault beats two corrupt ones.
86
+ let crashing = false;
87
+ const report = (reason) => {
88
+ if (crashing)
89
+ return;
90
+ crashing = true;
91
+ // [LAW:effects-at-boundaries] Render to a string first, then perform one
92
+ // write. `beginCapture` redirects the console's sink without setting
93
+ // `file:`, so stderr's TTY status still drives colour depth — a `file:`
94
+ // sink would report `isTTY === false` and silently strip colour.
95
+ const out = new Console({ stderr: true });
96
+ out.beginCapture();
97
+ out.print(new Traceback(toError(reason), options));
98
+ // [LAW:no-silent-failure] Exit only once the bytes have drained. When
99
+ // stderr is a pipe (CI logs, `2>&1 | tee`), writes are asynchronous and
100
+ // an immediate `process.exit(1)` truncates the traceback at the pipe
101
+ // buffer — the longer the traceback, the more of it is lost, which is
102
+ // the reverse of what a crash report should do.
103
+ process.stderr.write(out.endCapture(), () => {
104
+ process.exit(1);
105
+ });
106
+ };
107
+ process.off("uncaughtException", installed);
108
+ process.off("unhandledRejection", installed);
109
+ process.on("uncaughtException", report);
110
+ process.on("unhandledRejection", report);
111
+ installed = report;
112
+ }
113
+ //# sourceMappingURL=traceback.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"traceback.js","sourceRoot":"","sources":["../../src/node/traceback.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAE3C,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAGxD,4EAA4E;AAC5E,SAAS,OAAO,CAAC,MAAe;IAC9B,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,EAAE,CAAC;IAC7D,IAAI,CAAC,CAAC,OAAO,IAAI,MAAM,CAAC;QAAE,OAAO,EAAE,CAAC;IACpC,OAAO,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;AAC9D,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,SAAS,OAAO,CAAC,MAAe;IAC9B,IAAI,KAAK,CAAC,aAAa,CAAC,MAAM,CAAC;QAAE,OAAO,MAAM,CAAC;IAC/C,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IACzC,KAAK,CAAC,IAAI,GAAG,UAAU,CAAC;IACxB,KAAK,CAAC,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9B,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;;;GAWG;AACH,IAAI,SAAS,GAA8B,GAAG,EAAE,GAAE,CAAC,CAAC;AAEpD;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAA0B;IACzD,2EAA2E;IAC3E,6EAA6E;IAC7E,uEAAuE;IACvE,wEAAwE;IACxE,4EAA4E;IAC5E,2EAA2E;IAC3E,2DAA2D;IAC3D,IAAI,QAAQ,GAAG,KAAK,CAAC;IAErB,MAAM,MAAM,GAAG,CAAC,MAAe,EAAQ,EAAE;QACvC,IAAI,QAAQ;YAAE,OAAO;QACrB,QAAQ,GAAG,IAAI,CAAC;QAEhB,yEAAyE;QACzE,qEAAqE;QACrE,wEAAwE;QACxE,iEAAiE;QACjE,MAAM,GAAG,GAAG,IAAI,OAAO,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1C,GAAG,CAAC,YAAY,EAAE,CAAC;QACnB,GAAG,CAAC,KAAK,CAAC,IAAI,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;QAEnD,sEAAsE;QACtE,wEAAwE;QACxE,qEAAqE;QACrE,sEAAsE;QACtE,gDAAgD;QAChD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,EAAE,EAAE,GAAG,EAAE;YAC1C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,OAAO,CAAC,GAAG,CAAC,mBAAmB,EAAE,SAAS,CAAC,CAAC;IAC5C,OAAO,CAAC,GAAG,CAAC,oBAAoB,EAAE,SAAS,CAAC,CAAC;IAE7C,OAAO,CAAC,EAAE,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAC;IACxC,OAAO,CAAC,EAAE,CAAC,oBAAoB,EAAE,MAAM,CAAC,CAAC;IACzC,SAAS,GAAG,MAAM,CAAC;AACrB,CAAC"}
@@ -19,8 +19,30 @@ export declare class Columns implements Renderable, Measurable {
19
19
  readonly columnFirst: boolean;
20
20
  readonly gutterWidth: number;
21
21
  constructor(items?: Iterable<unknown>, options?: ColumnsOptions);
22
- render(options: RenderOptions): Iterable<Segment>;
23
- measure(options: RenderOptions): {
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
+ /** The width at which every item sits on one row: n columns and the gutters between them. */
44
+ private _naturalWidth;
45
+ measure(rawOptions: RenderOptions): {
24
46
  minimum: number;
25
47
  maximum: number;
26
48
  };
@@ -1 +1 @@
1
- {"version":3,"file":"columns.d.ts","sourceRoot":"","sources":["../../src/renderables/columns.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAG7C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,KAAK,EACV,UAAU,EACV,UAAU,EACV,aAAa,EACd,MAAM,qBAAqB,CAAC;AAG7B,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAC5B,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AASD,qBAAa,OAAQ,YAAW,UAAU,EAAE,UAAU;IACpD,WAAW,EAAE,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC;IAClD,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;gBAEjB,KAAK,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,EAAE,cAAc;IAS9D,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,QAAQ,CAAC,OAAO,CAAC;IAwDlD,OAAO,CAAC,OAAO,EAAE,aAAa,GAAG;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE;CAGtE"}
1
+ {"version":3,"file":"columns.d.ts","sourceRoot":"","sources":["../../src/renderables/columns.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAG7C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,KAAK,EACV,UAAU,EACV,UAAU,EACV,aAAa,EACd,MAAM,qBAAqB,CAAC;AAI7B,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAC5B,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AASD,qBAAa,OAAQ,YAAW,UAAU,EAAE,UAAU;IACpD,WAAW,EAAE,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC;IAClD,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;gBAEjB,KAAK,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,EAAE,cAAc;IAgB9D,MAAM,CAAC,UAAU,EAAE,aAAa,GAAG,QAAQ,CAAC,OAAO,CAAC;IAqCrD;;;;;;;OAOG;IACH,OAAO,CAAC,OAAO;IA+Bf;;;;;;;;;OASG;IACH,OAAO,CAAC,UAAU;IAalB,6FAA6F;IAC7F,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,UAAU,EAAE,aAAa,GAAG;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE;CAczE"}
@@ -4,7 +4,8 @@
4
4
  import { Segment } from "../core/segment.js";
5
5
  import { Measurement } from "../core/measure.js";
6
6
  import { RichText } from "../core/text.js";
7
- import { isMeasurable } from "../core/protocol.js";
7
+ import { isMeasurable, withBoundedWidth, withCellWidth } from "../core/protocol.js";
8
+ import { cellCount } from "../core/cells.js";
8
9
  function toRenderable(item) {
9
10
  if (typeof item === "object" && item !== null && "render" in item) {
10
11
  return item;
@@ -22,41 +23,29 @@ export class Columns {
22
23
  this.renderables = items ? [...items].map(toRenderable) : [];
23
24
  this.expand = options?.expand ?? false;
24
25
  this.equal = options?.equal ?? false;
25
- this.colWidth = options?.width;
26
+ // A declared column width is a cell count like any other, and it reaches
27
+ // the layout without passing through `withCellWidth`, which parses only
28
+ // what the *caller of render* supplied. Left raw, `width: NaN` made
29
+ // `Math.max(1, NaN)` a NaN column count and `new Array` threw `Invalid
30
+ // array length` before a single column was laid out. Absence is preserved
31
+ // rather than parsed: `undefined` selects auto-fit, and `cellCount` would
32
+ // read it as a declared zero.
33
+ this.colWidth = options?.width === undefined ? undefined : cellCount(options.width);
26
34
  this.columnFirst = options?.columnFirst ?? false;
27
35
  this.gutterWidth = 2; // default gutter between columns
28
36
  }
29
- *render(options) {
37
+ *render(rawOptions) {
30
38
  const items = this.renderables;
31
39
  if (items.length === 0)
32
40
  return;
33
- const maxWidth = options.maxWidth;
34
- // Determine column widths
35
- let numCols;
36
- let colWidths;
37
- if (this.colWidth !== undefined) {
38
- numCols = Math.max(1, Math.floor((maxWidth + this.gutterWidth) / (this.colWidth + this.gutterWidth)));
39
- colWidths = new Array(numCols).fill(this.colWidth);
40
- }
41
- else if (this.equal) {
42
- // Measure all items to find the widest
43
- let maxItemWidth = 1;
44
- for (const item of items) {
45
- if (isMeasurable(item)) {
46
- const m = Measurement.get(options, item);
47
- maxItemWidth = Math.max(maxItemWidth, m.maximum);
48
- }
49
- }
50
- numCols = Math.max(1, Math.floor((maxWidth + this.gutterWidth) / (maxItemWidth + this.gutterWidth)));
51
- const equalWidth = Math.floor((maxWidth - this.gutterWidth * (numCols - 1)) / numCols);
52
- colWidths = new Array(numCols).fill(equalWidth);
53
- }
54
- else {
55
- // Auto: fit as many columns as possible
56
- numCols = Math.min(items.length, Math.max(1, Math.floor(maxWidth / 4)));
57
- const colW = Math.floor((maxWidth - this.gutterWidth * (numCols - 1)) / numCols);
58
- colWidths = new Array(numCols).fill(colW);
59
- }
41
+ // The parsed options, not just a parsed local: `_divide` hands them to
42
+ // `Measurement.get`, and a raw NaN reaching it comes back as a NaN item
43
+ // width, then a NaN column count, then `Invalid array length` out of
44
+ // `new Array(numCols)` before a single column is laid out. An unbounded
45
+ // width reaches the same `new Array` by the same route, which is why the
46
+ // parse here is the bounded one.
47
+ const options = withBoundedWidth(rawOptions, this);
48
+ const { numCols, colWidths } = this._divide(options);
60
49
  // Layout items into rows
61
50
  const numRows = Math.ceil(items.length / numCols);
62
51
  // [LAW:types-are-the-program] The element type is Renderable — any number
@@ -78,8 +67,78 @@ export class Columns {
78
67
  yield* Segment.mergeHorizontal(cells, this.gutterWidth);
79
68
  }
80
69
  }
81
- measure(options) {
82
- return { minimum: 1, maximum: options.maxWidth };
70
+ /**
71
+ * The requested width divided into columns, once.
72
+ *
73
+ * [LAW:single-enforcer] The three modes disagree only about how many columns
74
+ * there are and how wide each one is, so they disagree in one place. `render`
75
+ * lays out against this division and `_naturalWidth` is the width at which it
76
+ * comes out as one row of every item.
77
+ */
78
+ _divide(options) {
79
+ const maxWidth = options.maxWidth;
80
+ const gutter = this.gutterWidth;
81
+ if (this.colWidth !== undefined) {
82
+ const numCols = Math.max(1, Math.floor((maxWidth + gutter) / (this.colWidth + gutter)));
83
+ return { numCols, colWidths: new Array(numCols).fill(this.colWidth) };
84
+ }
85
+ if (this.equal) {
86
+ const itemWidth = this._itemWidth(options);
87
+ const numCols = Math.max(1, Math.floor((maxWidth + gutter) / (itemWidth + gutter)));
88
+ const equalWidth = Math.floor((maxWidth - gutter * (numCols - 1)) / numCols);
89
+ return { numCols, colWidths: new Array(numCols).fill(equalWidth) };
90
+ }
91
+ // Auto: as many columns as the widest item fits into, capped at the item
92
+ // count. Sized from the content rather than from a flat four cells per
93
+ // column, because `_naturalWidth` below is the inverse of *this* expression
94
+ // and a constant has no inverse: six two-cell items reported a natural
95
+ // width of 22 and then wrapped into two rows at 22, since `floor(22 / 4)`
96
+ // is five columns no matter how narrow the items are.
97
+ const itemWidth = this._itemWidth(options);
98
+ const numCols = Math.min(this.renderables.length, Math.max(1, Math.floor((maxWidth + gutter) / (itemWidth + gutter))));
99
+ const colW = Math.floor((maxWidth - gutter * (numCols - 1)) / numCols);
100
+ return { numCols, colWidths: new Array(numCols).fill(colW) };
101
+ }
102
+ /**
103
+ * The widest any one item wants to be.
104
+ *
105
+ * An item that cannot measure itself wants the offer, which is what `Panel`,
106
+ * `Padding`, `Layout` and `Tree` all answer for the same case — and under an
107
+ * unbounded offer that is `Infinity`, so `withBoundedWidth` throws and says
108
+ * the request was unanswerable. Counting it as one cell instead reported a
109
+ * natural width of 1, which resolved an unbounded offer to a single column and
110
+ * cropped forty cells of content down to `"x"` with no error at all.
111
+ */
112
+ _itemWidth(options) {
113
+ let widest = 1;
114
+ for (const item of this.renderables) {
115
+ widest = Math.max(widest, isMeasurable(item)
116
+ ? Measurement.get(options, item).maximum
117
+ : options.maxWidth);
118
+ }
119
+ return widest;
120
+ }
121
+ /** The width at which every item sits on one row: n columns and the gutters between them. */
122
+ _naturalWidth(options) {
123
+ const count = this.renderables.length;
124
+ if (count === 0)
125
+ return 0;
126
+ const width = this.colWidth ?? this._itemWidth(options);
127
+ return count * width + this.gutterWidth * (count - 1);
128
+ }
129
+ measure(rawOptions) {
130
+ // A floor of one cell is what Columns asks for; the ceiling is what it was
131
+ // offered, and the ceiling wins. Stated as a bare `minimum: 1`, a Columns
132
+ // measured into no width at all reported the range 1..0 — a floor above
133
+ // its own ceiling, which the parent that asked cannot divide.
134
+ //
135
+ // The ceiling is not the whole answer, though: reported as the offer alone,
136
+ // a Columns claimed every cell it was shown, so `Panel` in fit mode drew a
137
+ // 40-cell frame around five cells of content and an unbounded offer came
138
+ // back unbounded.
139
+ const parsed = withCellWidth(rawOptions);
140
+ const maximum = Math.min(this._naturalWidth(parsed), parsed.maxWidth);
141
+ return { minimum: Math.min(1, maximum), maximum };
83
142
  }
84
143
  }
85
144
  //# sourceMappingURL=columns.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"columns.js","sourceRoot":"","sources":["../../src/renderables/columns.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"columns.js","sourceRoot":"","sources":["../../src/renderables/columns.ts"],"names":[],"mappings":"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"}
@@ -12,24 +12,80 @@ export interface LayoutOptions {
12
12
  }
13
13
  export declare class Layout implements Renderable, Measurable {
14
14
  name: string | undefined;
15
- ratio: number;
16
- size: number | undefined;
17
- minimumSize: number;
18
15
  visible: boolean;
16
+ private _ratio;
17
+ private _size;
18
+ private _minimumSize;
19
19
  private _renderable;
20
20
  private _children;
21
21
  private _splitDirection;
22
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);
23
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();
24
53
  splitColumn(...layouts: Layout[]): void;
25
54
  splitRow(...layouts: Layout[]): void;
26
55
  update(renderable: Renderable | string): void;
27
56
  getByName(name: string): Layout | undefined;
28
- render(options: RenderOptions): Iterable<Segment>;
57
+ render(rawOptions: RenderOptions): Iterable<Segment>;
29
58
  private _renderColumn;
30
59
  private _renderRow;
31
60
  private _distributeSpace;
32
- measure(options: RenderOptions): {
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): {
33
89
  minimum: number;
34
90
  maximum: number;
35
91
  };
@@ -1 +1 @@
1
- {"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../src/renderables/layout.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAE7C,OAAO,KAAK,EACV,UAAU,EACV,UAAU,EACV,aAAa,EACd,MAAM,qBAAqB,CAAC;AAE7B,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,qBAAa,MAAO,YAAW,UAAU,EAAE,UAAU;IACnD,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,WAAW,CAAyB;IAC5C,OAAO,CAAC,SAAS,CAAW;IAC5B,OAAO,CAAC,eAAe,CAA+B;gBAE1C,UAAU,CAAC,EAAE,UAAU,GAAG,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa;IAerE,IAAI,QAAQ,IAAI,MAAM,EAAE,CAEvB;IAED,WAAW,CAAC,GAAG,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI;IAKvC,QAAQ,CAAC,GAAG,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI;IAKpC,MAAM,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI;IAM7C,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAS1C,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,QAAQ,CAAC,OAAO,CAAC;IAqBlD,OAAO,CAAE,aAAa;IAmBtB,OAAO,CAAE,UAAU;IAenB,OAAO,CAAC,gBAAgB;IAoCxB,OAAO,CAAC,OAAO,EAAE,aAAa,GAAG;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE;CAGtE"}
1
+ {"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../src/renderables/layout.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAE7C,OAAO,KAAK,EACV,UAAU,EACV,UAAU,EACV,aAAa,EACd,MAAM,qBAAqB,CAAC;AAK7B,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAaD,qBAAa,MAAO,YAAW,UAAU,EAAE,UAAU;IACnD,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,MAAM,CAAU;IACxB,OAAO,CAAC,KAAK,CAAqB;IAClC,OAAO,CAAC,YAAY,CAAU;IAC9B,OAAO,CAAC,WAAW,CAAyB;IAC5C,OAAO,CAAC,SAAS,CAAW;IAC5B,OAAO,CAAC,eAAe,CAA+B;gBAE1C,UAAU,CAAC,EAAE,UAAU,GAAG,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa;IAerE;;;;;;;;;;;;OAYG;IACH,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,IAAI,KAAK,CAAC,KAAK,EAAE,MAAM,EAEtB;IAED,IAAI,IAAI,IAAI,MAAM,GAAG,SAAS,CAE7B;IAED,IAAI,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAEjC;IAED,IAAI,WAAW,IAAI,MAAM,CAExB;IAED,IAAI,WAAW,CAAC,KAAK,EAAE,MAAM,EAE5B;IAED,IAAI,QAAQ,IAAI,MAAM,EAAE,CAEvB;IAED;;;;;;;;OAQG;IACH,OAAO,KAAK,OAAO,GAElB;IAED,WAAW,CAAC,GAAG,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI;IAKvC,QAAQ,CAAC,GAAG,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI;IAKpC,MAAM,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI;IAM7C,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAS1C,MAAM,CAAC,UAAU,EAAE,aAAa,GAAG,QAAQ,CAAC,OAAO,CAAC;IA8BrD,OAAO,CAAE,aAAa;IAmBtB,OAAO,CAAE,UAAU;IAiBnB,OAAO,CAAC,gBAAgB;IAuDxB;;;;;;;OAOG;IACH,OAAO,CAAC,aAAa;IA4BrB;;;;;;;;;;;;;;;;OAgBG;IACH,OAAO,CAAC,aAAa;IAqBrB,OAAO,CAAC,UAAU,EAAE,aAAa,GAAG;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE;CAczE"}