@promptctl/rich-js 0.18.0 → 0.19.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 (244) hide show
  1. package/README.md +9 -7
  2. package/dist/core/anchor.d.ts +45 -0
  3. package/dist/core/anchor.js +39 -0
  4. package/dist/core/ansi.d.ts +65 -0
  5. package/dist/core/ansi.js +231 -0
  6. package/dist/core/box.d.ts +145 -0
  7. package/dist/core/box.js +372 -0
  8. package/dist/core/cells.d.ts +192 -0
  9. package/dist/core/cells.js +334 -0
  10. package/dist/core/clock.d.ts +48 -0
  11. package/dist/core/clock.js +42 -0
  12. package/dist/core/color.d.ts +326 -0
  13. package/dist/core/color.js +1225 -0
  14. package/dist/core/console.d.ts +196 -0
  15. package/dist/core/console.js +596 -0
  16. package/dist/core/easing.d.ts +86 -0
  17. package/dist/core/easing.js +171 -0
  18. package/dist/core/emoji.d.ts +24 -0
  19. package/dist/core/emoji.js +391 -0
  20. package/dist/core/env.d.ts +16 -0
  21. package/dist/core/env.js +16 -0
  22. package/dist/core/export-html.d.ts +59 -0
  23. package/dist/core/export-html.js +197 -0
  24. package/dist/core/export-lines.d.ts +130 -0
  25. package/dist/core/export-lines.js +159 -0
  26. package/dist/core/export-svg.d.ts +49 -0
  27. package/dist/core/export-svg.js +213 -0
  28. package/dist/core/fnv1a.d.ts +10 -0
  29. package/dist/core/fnv1a.js +17 -0
  30. package/dist/core/highlighter.d.ts +33 -0
  31. package/dist/core/highlighter.js +190 -0
  32. package/dist/core/json.d.ts +27 -0
  33. package/dist/core/json.js +53 -0
  34. package/dist/core/markup.d.ts +140 -0
  35. package/dist/core/markup.js +654 -0
  36. package/dist/core/measure.d.ts +27 -0
  37. package/dist/core/measure.js +67 -0
  38. package/dist/core/memo.d.ts +19 -0
  39. package/dist/core/memo.js +34 -0
  40. package/dist/core/oklch.d.ts +161 -0
  41. package/dist/core/oklch.js +312 -0
  42. package/dist/core/osc8.d.ts +68 -0
  43. package/dist/core/osc8.js +78 -0
  44. package/dist/core/paint.d.ts +107 -0
  45. package/dist/core/paint.js +150 -0
  46. package/dist/core/place.d.ts +30 -0
  47. package/dist/core/place.js +49 -0
  48. package/dist/core/pretty.d.ts +247 -0
  49. package/dist/core/pretty.js +906 -0
  50. package/dist/core/protocol.d.ts +305 -0
  51. package/dist/core/protocol.js +183 -0
  52. package/dist/core/render.d.ts +87 -0
  53. package/dist/core/render.js +105 -0
  54. package/dist/core/segment.d.ts +162 -0
  55. package/dist/core/segment.js +488 -0
  56. package/dist/core/spinnerData.d.ts +12 -0
  57. package/dist/core/spinnerData.js +117 -0
  58. package/dist/core/strip.d.ts +152 -0
  59. package/dist/core/strip.js +326 -0
  60. package/dist/core/style.d.ts +189 -0
  61. package/dist/core/style.js +758 -0
  62. package/dist/core/subscription.d.ts +12 -0
  63. package/dist/core/subscription.js +12 -0
  64. package/dist/core/text.d.ts +283 -0
  65. package/dist/core/text.js +1097 -0
  66. package/dist/core/wrap.d.ts +46 -0
  67. package/dist/core/wrap.js +140 -0
  68. package/dist/host/app.d.ts +133 -0
  69. package/dist/host/app.js +285 -0
  70. package/dist/host/host-environment.d.ts +26 -0
  71. package/dist/host/host-environment.js +44 -0
  72. package/dist/host/index.d.ts +6 -0
  73. package/dist/host/index.js +3 -0
  74. package/dist/host/terminal-host.d.ts +180 -0
  75. package/dist/host/terminal-host.js +186 -0
  76. package/dist/index.d.ts +98 -0
  77. package/dist/index.js +114 -0
  78. package/dist/node/prompt.d.ts +34 -0
  79. package/dist/node/prompt.js +51 -0
  80. package/dist/node/save.d.ts +18 -0
  81. package/dist/node/save.js +24 -0
  82. package/dist/node/terminal-host.d.ts +114 -0
  83. package/dist/node/terminal-host.js +227 -0
  84. package/dist/node/traceback.d.ts +38 -0
  85. package/dist/node/traceback.js +98 -0
  86. package/dist/renderables/align.d.ts +16 -0
  87. package/dist/renderables/align.js +26 -0
  88. package/dist/renderables/columns.d.ts +77 -0
  89. package/dist/renderables/columns.js +191 -0
  90. package/dist/renderables/constrain.d.ts +15 -0
  91. package/dist/renderables/constrain.js +31 -0
  92. package/dist/renderables/effect.d.ts +98 -0
  93. package/dist/renderables/effect.js +160 -0
  94. package/dist/renderables/embed.d.ts +107 -0
  95. package/dist/renderables/embed.js +160 -0
  96. package/dist/renderables/flexStrip.d.ts +41 -0
  97. package/dist/renderables/flexStrip.js +200 -0
  98. package/dist/renderables/group.d.ts +14 -0
  99. package/dist/renderables/group.js +22 -0
  100. package/dist/renderables/layout.d.ts +75 -0
  101. package/dist/renderables/layout.js +223 -0
  102. package/dist/renderables/live.d.ts +74 -0
  103. package/dist/renderables/live.js +188 -0
  104. package/dist/renderables/markdown.d.ts +38 -0
  105. package/dist/renderables/markdown.js +587 -0
  106. package/dist/renderables/padding.d.ts +59 -0
  107. package/dist/renderables/padding.js +147 -0
  108. package/dist/renderables/panel.d.ts +114 -0
  109. package/dist/renderables/panel.js +303 -0
  110. package/dist/renderables/progress.d.ts +178 -0
  111. package/dist/renderables/progress.js +362 -0
  112. package/dist/renderables/progressBar.d.ts +31 -0
  113. package/dist/renderables/progressBar.js +64 -0
  114. package/dist/renderables/prompt.d.ts +71 -0
  115. package/dist/renderables/prompt.js +165 -0
  116. package/dist/renderables/ratio.d.ts +123 -0
  117. package/dist/renderables/ratio.js +213 -0
  118. package/dist/renderables/rule.d.ts +26 -0
  119. package/dist/renderables/rule.js +86 -0
  120. package/dist/renderables/spinner.d.ts +57 -0
  121. package/dist/renderables/spinner.js +103 -0
  122. package/dist/renderables/status.d.ts +29 -0
  123. package/dist/renderables/status.js +46 -0
  124. package/dist/renderables/syntax.d.ts +65 -0
  125. package/dist/renderables/syntax.js +166 -0
  126. package/dist/renderables/table.d.ts +232 -0
  127. package/dist/renderables/table.js +973 -0
  128. package/dist/renderables/traceback.d.ts +32 -0
  129. package/dist/renderables/traceback.js +279 -0
  130. package/dist/renderables/tree.d.ts +76 -0
  131. package/dist/renderables/tree.js +177 -0
  132. package/dist/renderables/viewport.d.ts +114 -0
  133. package/dist/renderables/viewport.js +196 -0
  134. package/dist/template-bindings/color-funcs.d.ts +109 -0
  135. package/dist/template-bindings/color-funcs.js +243 -0
  136. package/dist/template-bindings/helpers.d.ts +26 -0
  137. package/dist/template-bindings/helpers.js +33 -0
  138. package/dist/template-bindings/index.d.ts +119 -0
  139. package/dist/template-bindings/index.js +162 -0
  140. package/dist/template-bindings/palette-funcs.d.ts +96 -0
  141. package/dist/template-bindings/palette-funcs.js +145 -0
  142. package/dist/template-bindings/style-funcs.d.ts +28 -0
  143. package/dist/template-bindings/style-funcs.js +157 -0
  144. package/dist/themes/buildPalette.d.ts +57 -0
  145. package/dist/themes/buildPalette.js +100 -0
  146. package/dist/themes/colorMath.d.ts +94 -0
  147. package/dist/themes/colorMath.js +280 -0
  148. package/dist/themes/colorRef.d.ts +36 -0
  149. package/dist/themes/colorRef.js +78 -0
  150. package/dist/themes/data/ansi-tables.d.ts +14 -0
  151. package/dist/themes/data/ansi-tables.js +49 -0
  152. package/dist/themes/data/atom-one-dark.d.ts +3 -0
  153. package/dist/themes/data/atom-one-dark.js +177 -0
  154. package/dist/themes/data/atom-one-light.d.ts +3 -0
  155. package/dist/themes/data/atom-one-light.js +177 -0
  156. package/dist/themes/data/catppuccin-frappe.d.ts +3 -0
  157. package/dist/themes/data/catppuccin-frappe.js +177 -0
  158. package/dist/themes/data/catppuccin-latte.d.ts +3 -0
  159. package/dist/themes/data/catppuccin-latte.js +177 -0
  160. package/dist/themes/data/catppuccin-macchiato.d.ts +3 -0
  161. package/dist/themes/data/catppuccin-macchiato.js +177 -0
  162. package/dist/themes/data/catppuccin-mocha.d.ts +3 -0
  163. package/dist/themes/data/catppuccin-mocha.js +177 -0
  164. package/dist/themes/data/cyberpunk.d.ts +17 -0
  165. package/dist/themes/data/cyberpunk.js +193 -0
  166. package/dist/themes/data/default.d.ts +3 -0
  167. package/dist/themes/data/default.js +160 -0
  168. package/dist/themes/data/dracula.d.ts +3 -0
  169. package/dist/themes/data/dracula.js +177 -0
  170. package/dist/themes/data/flexoki.d.ts +3 -0
  171. package/dist/themes/data/flexoki.js +177 -0
  172. package/dist/themes/data/gruvbox.d.ts +3 -0
  173. package/dist/themes/data/gruvbox.js +177 -0
  174. package/dist/themes/data/index.d.ts +35 -0
  175. package/dist/themes/data/index.js +64 -0
  176. package/dist/themes/data/monokai.d.ts +3 -0
  177. package/dist/themes/data/monokai.js +177 -0
  178. package/dist/themes/data/nord.d.ts +3 -0
  179. package/dist/themes/data/nord.js +177 -0
  180. package/dist/themes/data/rose-pine-dawn.d.ts +3 -0
  181. package/dist/themes/data/rose-pine-dawn.js +177 -0
  182. package/dist/themes/data/rose-pine-moon.d.ts +3 -0
  183. package/dist/themes/data/rose-pine-moon.js +177 -0
  184. package/dist/themes/data/rose-pine.d.ts +3 -0
  185. package/dist/themes/data/rose-pine.js +177 -0
  186. package/dist/themes/data/solarized-dark.d.ts +3 -0
  187. package/dist/themes/data/solarized-dark.js +177 -0
  188. package/dist/themes/data/solarized-light.d.ts +3 -0
  189. package/dist/themes/data/solarized-light.js +177 -0
  190. package/dist/themes/data/svg-export.d.ts +3 -0
  191. package/dist/themes/data/svg-export.js +177 -0
  192. package/dist/themes/data/textual-ansi.d.ts +3 -0
  193. package/dist/themes/data/textual-ansi.js +160 -0
  194. package/dist/themes/data/textual-dark.d.ts +3 -0
  195. package/dist/themes/data/textual-dark.js +160 -0
  196. package/dist/themes/data/textual-light.d.ts +3 -0
  197. package/dist/themes/data/textual-light.js +160 -0
  198. package/dist/themes/data/tokyo-night.d.ts +3 -0
  199. package/dist/themes/data/tokyo-night.js +177 -0
  200. package/dist/themes/data/types.d.ts +31 -0
  201. package/dist/themes/data/types.js +10 -0
  202. package/dist/themes/palette.d.ts +45 -0
  203. package/dist/themes/palette.js +71 -0
  204. package/dist/themes/ramp.d.ts +70 -0
  205. package/dist/themes/ramp.js +103 -0
  206. package/dist/themes/registry.d.ts +44 -0
  207. package/dist/themes/registry.js +105 -0
  208. package/dist/themes/terminalThemes.d.ts +49 -0
  209. package/dist/themes/terminalThemes.js +63 -0
  210. package/dist/themes/transpose.d.ts +87 -0
  211. package/dist/themes/transpose.js +138 -0
  212. package/dist/widgets/button.d.ts +45 -0
  213. package/dist/widgets/button.js +177 -0
  214. package/dist/widgets/checkbox.d.ts +38 -0
  215. package/dist/widgets/checkbox.js +135 -0
  216. package/dist/widgets/dropdown.d.ts +64 -0
  217. package/dist/widgets/dropdown.js +416 -0
  218. package/dist/widgets/event-router.d.ts +71 -0
  219. package/dist/widgets/event-router.js +629 -0
  220. package/dist/widgets/focus-manager.d.ts +45 -0
  221. package/dist/widgets/focus-manager.js +191 -0
  222. package/dist/widgets/hit.d.ts +64 -0
  223. package/dist/widgets/hit.js +113 -0
  224. package/dist/widgets/index.d.ts +20 -0
  225. package/dist/widgets/index.js +14 -0
  226. package/dist/widgets/ink.d.ts +22 -0
  227. package/dist/widgets/ink.js +31 -0
  228. package/dist/widgets/slider.d.ts +54 -0
  229. package/dist/widgets/slider.js +247 -0
  230. package/dist/widgets/static-item.d.ts +34 -0
  231. package/dist/widgets/static-item.js +54 -0
  232. package/dist/widgets/text-input.d.ts +283 -0
  233. package/dist/widgets/text-input.js +1174 -0
  234. package/dist/widgets/themed-widget.d.ts +20 -0
  235. package/dist/widgets/themed-widget.js +89 -0
  236. package/dist/widgets/toggle.d.ts +41 -0
  237. package/dist/widgets/toggle.js +157 -0
  238. package/dist/widgets/types.d.ts +87 -0
  239. package/dist/widgets/types.js +30 -0
  240. package/dist/widgets/widget-app.d.ts +65 -0
  241. package/dist/widgets/widget-app.js +206 -0
  242. package/dist/widgets/widget-base.d.ts +42 -0
  243. package/dist/widgets/widget-base.js +172 -0
  244. package/package.json +16 -2
@@ -0,0 +1,206 @@
1
+ /**
2
+ * WidgetApp — an `App` whose view has widgets in it: keys reach the focused
3
+ * widget, the pointer reaches the widget that drew the cell under it, and a
4
+ * change to anything a frame read paints the next one.
5
+ *
6
+ * A widget goes wherever a renderable goes — in a `Panel`, a `Layout` pane, a
7
+ * `Table` cell — and nothing lays it out a second time. Everything added here
8
+ * reads the frame the view drew:
9
+ *
10
+ * - Focus moves in document order: the order widgets start to render in
11
+ * (`RenderOptions.onDraw`), among those with a cell on the frame on screen.
12
+ * Not reading order — in a `Layout` split into columns, reading order
13
+ * zigzags between the columns row by row.
14
+ * - Overlays are painted over the view's frame, in document order, directly
15
+ * below their owner's footprint (`OverlayRenderable`). Paint order is the
16
+ * z-order: a later overlay covers an earlier one.
17
+ * - A pointer event goes to the innermost widget that drew the cell under it
18
+ * (`widgetAt`), in that widget's own coordinates; the wheel goes to the
19
+ * innermost `Viewport` that can move (`scrollTargetAt`).
20
+ *
21
+ * [LAW:locality-or-seam] `App` knows nothing of widgets — `host/` depends on
22
+ * `core/` alone — and this reaches it only through the seams it already has:
23
+ * the view is a renderable, and `onPaint` hears the frame.
24
+ */
25
+ import { Reaction } from "mobx";
26
+ import { Segment } from "../core/segment.js";
27
+ import { App } from "../host/app.js";
28
+ import { DefaultFocusManager } from "./focus-manager.js";
29
+ import { EventRouter } from "./event-router.js";
30
+ import { footprintOf, ownersOn } from "./hit.js";
31
+ import { hasOverlay } from "./types.js";
32
+ import { WidgetBase } from "./widget-base.js";
33
+ export class WidgetApp {
34
+ focusManager;
35
+ focus;
36
+ app;
37
+ router;
38
+ // Paints the next frame when anything the last one read changes.
39
+ reaction;
40
+ // The widgets the view drew for the frame on screen, in document order.
41
+ drawn = [];
42
+ constructor(options) {
43
+ const { view } = options;
44
+ this.reaction = new Reaction("WidgetApp", () => this.app.refresh());
45
+ this.app = new App({
46
+ ...options,
47
+ view: (t) => ({ render: (renderOptions) => this.compose(() => view(t), renderOptions) }),
48
+ });
49
+ this.focus = new DefaultFocusManager(() => {
50
+ const owners = ownersOn(this.app.frame);
51
+ return this.drawn.filter((widget) => owners.has(widget));
52
+ });
53
+ this.focusManager = this.focus;
54
+ this.router = new EventRouter({ source: this, host: options.host });
55
+ this.app.onPaint(() => {
56
+ this.focus.settle(this.drawn);
57
+ this.router.framePainted();
58
+ });
59
+ // [LAW:single-enforcer] `settle` judges every focus move, so every move
60
+ // paints — including one onto a widget no frame has read yet.
61
+ this.focus.onChange(() => this.app.refresh());
62
+ }
63
+ get phase() {
64
+ return this.app.phase;
65
+ }
66
+ /** The rows on screen now, as painted: what a pointer event lands on. */
67
+ get frame() {
68
+ return this.app.frame;
69
+ }
70
+ /**
71
+ * Take the terminal, paint the first frame, and route input to its widgets
72
+ * until the app stops — `App.run`, with the input.
73
+ */
74
+ async run() {
75
+ // App refuses a second run; this one must not end the input of the first.
76
+ if (this.app.phase !== "idle")
77
+ return this.app.run();
78
+ // Listening before the app starts, so a first frame that throws — and
79
+ // stops the host — is not followed by a subscription that restarts it.
80
+ this.router.start();
81
+ const running = this.app.run();
82
+ try {
83
+ await running;
84
+ }
85
+ finally {
86
+ this.router.stop();
87
+ this.reaction.dispose();
88
+ }
89
+ }
90
+ stop() {
91
+ this.app.stop();
92
+ }
93
+ fail(error) {
94
+ this.app.fail(error);
95
+ }
96
+ /**
97
+ * Paint the next frame, as `App.refresh` does. A change to an observable
98
+ * the view read needs none; one no observable carries — a `Viewport`
99
+ * scrolled from a key handler — does.
100
+ */
101
+ refresh() {
102
+ this.app.refresh();
103
+ }
104
+ suspend() {
105
+ return this.app.suspend();
106
+ }
107
+ /** Paint at the app's rate until the returned function is called, as `App.animate` does. */
108
+ animate() {
109
+ return this.app.animate();
110
+ }
111
+ /**
112
+ * Hear every key. A `high` handler hears it before the focused widget, a
113
+ * `normal` one after it — and after Tab has moved focus — and a handler
114
+ * that calls `event.stop()` ends the key there.
115
+ */
116
+ onKey(handler, options) {
117
+ return this.router.onKey(handler, options);
118
+ }
119
+ /** Hear every pointer event, in terminal cells, before any widget does. */
120
+ onMouse(handler) {
121
+ return this.router.onMouse(handler);
122
+ }
123
+ compose(view, options) {
124
+ const drawn = new Set();
125
+ // [LAW:parse-dont-validate] An owner is any object; only a `WidgetBase`
126
+ // reports itself as a widget, so this is where `object` becomes one.
127
+ const onDraw = (owner) => {
128
+ if (owner instanceof WidgetBase)
129
+ drawn.add(owner);
130
+ };
131
+ // The view, its render and the overlays are all tracked, so an observable
132
+ // any of them reads — a label, `focused`, the state that picks what the
133
+ // view shows — paints the next frame when it changes.
134
+ // An overlay reports what it draws too: a widget in it is on the frame,
135
+ // and visiting `drawn` in insertion order paints its overlay in turn.
136
+ const render = { ...options, onDraw };
137
+ const lines = tracked(this.reaction, () => {
138
+ const base = Segment.splitLines(view().render(render));
139
+ for (const widget of drawn)
140
+ paintOverlay(base, widget, render);
141
+ return base;
142
+ });
143
+ this.drawn = [...drawn];
144
+ return lines.flatMap((line) => [...line, Segment.line()]);
145
+ }
146
+ }
147
+ /**
148
+ * `fn`, with every observable it reads subscribed to by `reaction`.
149
+ *
150
+ * [LAW:no-silent-failure] mobx reports an error thrown inside `track` and
151
+ * carries on, which would paint an empty frame for a view that threw. The
152
+ * error is carried out instead, and fails the app as `App` fails on any frame
153
+ * that throws.
154
+ */
155
+ function tracked(reaction, fn) {
156
+ // Written inside `track`, which the compiler cannot see run.
157
+ let outcome = {
158
+ ok: false,
159
+ error: new Error("WidgetApp: a frame drawn after the app stopped"),
160
+ };
161
+ reaction.track(() => {
162
+ try {
163
+ outcome = { ok: true, value: fn() };
164
+ }
165
+ catch (error) {
166
+ outcome = { ok: false, error };
167
+ }
168
+ });
169
+ if (!outcome.ok)
170
+ throw outcome.error;
171
+ return outcome.value;
172
+ }
173
+ /**
174
+ * Paint `widget`'s overlay over `lines`, below its footprint and at the column
175
+ * of its row 0, each overlay row stamped as the widget's row below its last —
176
+ * so a click there reaches the widget, in its own coordinates. A widget whose
177
+ * row 0 is not on the frame has nowhere to hang an overlay, and paints none.
178
+ */
179
+ function paintOverlay(lines, widget, options) {
180
+ if (!hasOverlay(widget))
181
+ return;
182
+ const at = footprintOf(lines, widget);
183
+ if (!at)
184
+ return;
185
+ // An overlay floats over the frame; no region holds it, and the cells right
186
+ // of its column are all it can reach.
187
+ const width = Math.max(0, options.maxWidth - at.x);
188
+ const overlay = widget.renderOverlay({ ...options, maxWidth: width, height: undefined });
189
+ if (overlay === null)
190
+ return;
191
+ const rows = Segment.anchorLines(Segment.splitLines(Segment.cropLines(overlay, width)), widget, at.rows);
192
+ paintLines(lines, rows, at.x, at.y + at.rows);
193
+ }
194
+ // Paint `rows` into `lines` with its top-left cell at (x, y), growing `lines`
195
+ // as needed; each painted cell replaces the one beneath it.
196
+ function paintLines(lines, rows, x, y) {
197
+ while (lines.length < y + rows.length)
198
+ lines.push([]);
199
+ rows.forEach((source, i) => {
200
+ const row = lines[y + i];
201
+ // Padded to `x`, so a row that ends short of the overlay still reaches it.
202
+ const gap = Math.max(0, x - Segment.getLineLength(row));
203
+ const [before = [], , after = []] = Segment.divide([...row, new Segment(" ".repeat(gap))], [x, x + Segment.getLineLength(source)]);
204
+ lines[y + i] = [...before, ...source, ...after];
205
+ });
206
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * WidgetBase — shared MobX observable foundation for interactive widgets.
3
+ * [LAW:one-type-per-behavior] All widgets share the same base; differences
4
+ * are in configuration and state, not in infrastructure.
5
+ *
6
+ * Reactive state is declared with TC39 decorators, which MobX 7 requires on
7
+ * `accessor` members. Each decorator registers its own member, so there is no
8
+ * makeObservable call and subclasses add observables the same way.
9
+ */
10
+ import { Segment } from "../core/segment.js";
11
+ import { type Style } from "../core/style.js";
12
+ import type { RenderOptions } from "../core/protocol.js";
13
+ import type { InteractiveWidget, KeyEvent, WidgetMouseEvent, WidgetFocusEvent } from "./types.js";
14
+ import type { Unsubscribe } from "../core/subscription.js";
15
+ export declare abstract class WidgetBase implements InteractiveWidget {
16
+ abstract readonly id: string;
17
+ abstract readonly focusable: boolean;
18
+ accessor focused: boolean;
19
+ accessor hovered: boolean;
20
+ accessor active: boolean;
21
+ accessor disabled: boolean;
22
+ private readonly changeHandlers;
23
+ private readonly submitHandlers;
24
+ abstract handleKey(event: KeyEvent): void;
25
+ handleMouse(_event: WidgetMouseEvent): void;
26
+ handleFocus(event: WidgetFocusEvent): void;
27
+ focus(): void;
28
+ blur(): void;
29
+ setDisabled(value: boolean): void;
30
+ setHovered(value: boolean): void;
31
+ onChange(handler: (widget: InteractiveWidget) => void): Unsubscribe;
32
+ onSubmit(handler: (widget: InteractiveWidget) => void): Unsubscribe;
33
+ protected emitChange(): void;
34
+ protected emitSubmit(): void;
35
+ render(options: RenderOptions): Iterable<Segment>;
36
+ protected get ground(): Style;
37
+ protected abstract draw(options: RenderOptions): Iterable<Segment>;
38
+ abstract measure(options: RenderOptions): {
39
+ minimum: number;
40
+ maximum: number;
41
+ };
42
+ }
@@ -0,0 +1,172 @@
1
+ /**
2
+ * WidgetBase — shared MobX observable foundation for interactive widgets.
3
+ * [LAW:one-type-per-behavior] All widgets share the same base; differences
4
+ * are in configuration and state, not in infrastructure.
5
+ *
6
+ * Reactive state is declared with TC39 decorators, which MobX 7 requires on
7
+ * `accessor` members. Each decorator registers its own member, so there is no
8
+ * makeObservable call and subclasses add observables the same way.
9
+ */
10
+ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
11
+ var useValue = arguments.length > 2;
12
+ for (var i = 0; i < initializers.length; i++) {
13
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
14
+ }
15
+ return useValue ? value : void 0;
16
+ };
17
+ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
18
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
19
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
20
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
21
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
22
+ var _, done = false;
23
+ for (var i = decorators.length - 1; i >= 0; i--) {
24
+ var context = {};
25
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
26
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
27
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
28
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
29
+ if (kind === "accessor") {
30
+ if (result === void 0) continue;
31
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
32
+ if (_ = accept(result.get)) descriptor.get = _;
33
+ if (_ = accept(result.set)) descriptor.set = _;
34
+ if (_ = accept(result.init)) initializers.unshift(_);
35
+ }
36
+ else if (_ = accept(result)) {
37
+ if (kind === "field") initializers.unshift(_);
38
+ else descriptor[key] = _;
39
+ }
40
+ }
41
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
42
+ done = true;
43
+ };
44
+ import { observable, action } from "mobx";
45
+ import { Segment } from "../core/segment.js";
46
+ import { NULL_STYLE } from "../core/style.js";
47
+ let WidgetBase = (() => {
48
+ let _instanceExtraInitializers = [];
49
+ let _focused_decorators;
50
+ let _focused_initializers = [];
51
+ let _focused_extraInitializers = [];
52
+ let _hovered_decorators;
53
+ let _hovered_initializers = [];
54
+ let _hovered_extraInitializers = [];
55
+ let _active_decorators;
56
+ let _active_initializers = [];
57
+ let _active_extraInitializers = [];
58
+ let _disabled_decorators;
59
+ let _disabled_initializers = [];
60
+ let _disabled_extraInitializers = [];
61
+ let _handleFocus_decorators;
62
+ let _focus_decorators;
63
+ let _blur_decorators;
64
+ let _setDisabled_decorators;
65
+ let _setHovered_decorators;
66
+ return class WidgetBase {
67
+ static {
68
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
69
+ _focused_decorators = [observable];
70
+ _hovered_decorators = [observable];
71
+ _active_decorators = [observable];
72
+ _disabled_decorators = [observable];
73
+ _handleFocus_decorators = [action];
74
+ _focus_decorators = [action];
75
+ _blur_decorators = [action];
76
+ _setDisabled_decorators = [action];
77
+ _setHovered_decorators = [action];
78
+ __esDecorate(this, null, _focused_decorators, { kind: "accessor", name: "focused", static: false, private: false, access: { has: obj => "focused" in obj, get: obj => obj.focused, set: (obj, value) => { obj.focused = value; } }, metadata: _metadata }, _focused_initializers, _focused_extraInitializers);
79
+ __esDecorate(this, null, _hovered_decorators, { kind: "accessor", name: "hovered", static: false, private: false, access: { has: obj => "hovered" in obj, get: obj => obj.hovered, set: (obj, value) => { obj.hovered = value; } }, metadata: _metadata }, _hovered_initializers, _hovered_extraInitializers);
80
+ __esDecorate(this, null, _active_decorators, { kind: "accessor", name: "active", static: false, private: false, access: { has: obj => "active" in obj, get: obj => obj.active, set: (obj, value) => { obj.active = value; } }, metadata: _metadata }, _active_initializers, _active_extraInitializers);
81
+ __esDecorate(this, null, _disabled_decorators, { kind: "accessor", name: "disabled", static: false, private: false, access: { has: obj => "disabled" in obj, get: obj => obj.disabled, set: (obj, value) => { obj.disabled = value; } }, metadata: _metadata }, _disabled_initializers, _disabled_extraInitializers);
82
+ __esDecorate(this, null, _handleFocus_decorators, { kind: "method", name: "handleFocus", static: false, private: false, access: { has: obj => "handleFocus" in obj, get: obj => obj.handleFocus }, metadata: _metadata }, null, _instanceExtraInitializers);
83
+ __esDecorate(this, null, _focus_decorators, { kind: "method", name: "focus", static: false, private: false, access: { has: obj => "focus" in obj, get: obj => obj.focus }, metadata: _metadata }, null, _instanceExtraInitializers);
84
+ __esDecorate(this, null, _blur_decorators, { kind: "method", name: "blur", static: false, private: false, access: { has: obj => "blur" in obj, get: obj => obj.blur }, metadata: _metadata }, null, _instanceExtraInitializers);
85
+ __esDecorate(this, null, _setDisabled_decorators, { kind: "method", name: "setDisabled", static: false, private: false, access: { has: obj => "setDisabled" in obj, get: obj => obj.setDisabled }, metadata: _metadata }, null, _instanceExtraInitializers);
86
+ __esDecorate(this, null, _setHovered_decorators, { kind: "method", name: "setHovered", static: false, private: false, access: { has: obj => "setHovered" in obj, get: obj => obj.setHovered }, metadata: _metadata }, null, _instanceExtraInitializers);
87
+ if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
88
+ }
89
+ #focused_accessor_storage = (__runInitializers(this, _instanceExtraInitializers), __runInitializers(this, _focused_initializers, false));
90
+ get focused() { return this.#focused_accessor_storage; }
91
+ set focused(value) { this.#focused_accessor_storage = value; }
92
+ #hovered_accessor_storage = (__runInitializers(this, _focused_extraInitializers), __runInitializers(this, _hovered_initializers, false));
93
+ get hovered() { return this.#hovered_accessor_storage; }
94
+ set hovered(value) { this.#hovered_accessor_storage = value; }
95
+ #active_accessor_storage = (__runInitializers(this, _hovered_extraInitializers), __runInitializers(this, _active_initializers, false));
96
+ get active() { return this.#active_accessor_storage; }
97
+ set active(value) { this.#active_accessor_storage = value; }
98
+ #disabled_accessor_storage = (__runInitializers(this, _active_extraInitializers), __runInitializers(this, _disabled_initializers, false));
99
+ get disabled() { return this.#disabled_accessor_storage; }
100
+ set disabled(value) { this.#disabled_accessor_storage = value; }
101
+ changeHandlers = (__runInitializers(this, _disabled_extraInitializers), new Set());
102
+ submitHandlers = new Set();
103
+ handleMouse(_event) { }
104
+ handleFocus(event) {
105
+ this.focused = event.type === "focus";
106
+ }
107
+ // --- Programmatic control ---
108
+ // [LAW:single-enforcer] focus()/blur() route through handleFocus so any
109
+ // subclass override (e.g. Dropdown collapsing its overlay on blur)
110
+ // participates in the same transition path the event router uses. There
111
+ // is no second code path that flips `focused` directly from the outside.
112
+ focus() {
113
+ this.handleFocus({ type: "focus" });
114
+ }
115
+ blur() {
116
+ this.handleFocus({ type: "blur" });
117
+ }
118
+ setDisabled(value) {
119
+ this.disabled = value;
120
+ }
121
+ // [LAW:single-enforcer] One canonical setter for hover state lives on
122
+ // the base. EventRouter calls it when the pointer moves onto or off this
123
+ // widget; widgets that need to react to hover transitions override
124
+ // handleMouse and read the synthesized `mouse_move`.
125
+ setHovered(value) {
126
+ this.hovered = value;
127
+ }
128
+ // --- Subscriptions ---
129
+ onChange(handler) {
130
+ this.changeHandlers.add(handler);
131
+ return () => this.changeHandlers.delete(handler);
132
+ }
133
+ onSubmit(handler) {
134
+ this.submitHandlers.add(handler);
135
+ return () => this.submitHandlers.delete(handler);
136
+ }
137
+ emitChange() {
138
+ for (const handler of this.changeHandlers) {
139
+ handler(this);
140
+ }
141
+ }
142
+ emitSubmit() {
143
+ for (const handler of this.submitHandlers) {
144
+ handler(this);
145
+ }
146
+ }
147
+ // --- Renderable + Measurable ---
148
+ // [LAW:single-enforcer] The one place a widget's cells get their anchor, so
149
+ // a hit on any cell names this widget and the cell's place in its output
150
+ // (`widgetAt`). Subclasses draw; none stamps itself. A `RichText` admits no
151
+ // anchor, so stamping has to follow every text layout `draw` does.
152
+ // Every row is padded to the widest, so the widget owns its whole rectangle
153
+ // and a click past the end of a short row still reaches it; every row ends
154
+ // in a newline, so a blank last row survives the next `splitLines`. The
155
+ // padding stands on `ground`, whatever the widget draws its own cells on.
156
+ // Reporting itself before it draws is what puts a widget ahead of the
157
+ // widgets nested in it in document order — the order focus traverses.
158
+ render(options) {
159
+ options.onDraw?.(this);
160
+ const drawn = Segment.splitLines(this.draw(options));
161
+ const [width] = Segment.getShape(drawn);
162
+ const lines = Segment.anchorLines(drawn.map((line) => Segment.adjustLineLength(line, width, this.ground)), this);
163
+ return lines.flatMap((line) => [...line, Segment.line()]);
164
+ }
165
+ // What a padded row stands on. None by default: the padding shows whatever
166
+ // the widget is drawn over, as the cells of content it did not draw do.
167
+ get ground() {
168
+ return NULL_STYLE;
169
+ }
170
+ };
171
+ })();
172
+ export { WidgetBase };
package/package.json CHANGED
@@ -1,7 +1,11 @@
1
1
  {
2
2
  "name": "@promptctl/rich-js",
3
- "version": "0.18.0",
3
+ "version": "0.19.0",
4
4
  "description": "Rich text and beautiful formatting in the terminal — a TypeScript port of Python's Rich",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/brandon-fryslie/rich-js"
8
+ },
5
9
  "type": "module",
6
10
  "sideEffects": false,
7
11
  "main": "dist/index.js",
@@ -53,7 +57,7 @@
53
57
  "THIRD-PARTY-NOTICES"
54
58
  ],
55
59
  "scripts": {
56
- "prepublishOnly": "node scripts/verify-release-tag.mjs",
60
+ "prepublishOnly": "node scripts/verify-release-tag.mjs && node scripts/verify-entry-points.mjs",
57
61
  "build": "npm run clean && tsc",
58
62
  "test": "vitest run",
59
63
  "test:watch": "vitest",
@@ -66,6 +70,7 @@
66
70
  "demo:build": "tsc -p tsconfig.demo.json",
67
71
  "sessions": "tsc -p tsconfig.demo.json && node dist-demo/examples/claude-sessions/index.js",
68
72
  "strip": "tsc -p tsconfig.demo.json && node dist-demo/examples/rich-strip/index.js",
73
+ "effects-feel": "tsc -p tsconfig.demo.json && node dist-demo/examples/effects-feel/index.js",
69
74
  "viewport": "tsc -p tsconfig.demo.json && node dist-demo/examples/rich-viewport/index.js",
70
75
  "markup-plugins": "tsc -p tsconfig.demo.json && node dist-demo/examples/rich-markup-plugins/index.js",
71
76
  "demo-inputs": "tsc -p tsconfig.demo.json && node dist-demo/examples/rich-config/index.js",
@@ -96,16 +101,25 @@
96
101
  "node": ">=20"
97
102
  },
98
103
  "devDependencies": {
104
+ "@codemirror/commands": "^6.11.1",
105
+ "@codemirror/lang-javascript": "^6.2.5",
106
+ "@codemirror/language": "^6.12.4",
107
+ "@codemirror/state": "^6.7.6",
108
+ "@codemirror/view": "^6.43.13",
109
+ "@lezer/highlight": "^1.2.5",
99
110
  "@playwright/test": "^1.63.0",
100
111
  "@promptctl/go-template-js": "^0.10.0",
101
112
  "@types/markdown-it": "^14.2.0",
102
113
  "@types/node": "^26.5.1",
103
114
  "@types/semver": "^7.8.0",
115
+ "@xterm/headless": "^6.0.0",
104
116
  "markdown-it": "^14.3.2",
105
117
  "markdown-it-container": "^4.0.0",
106
118
  "mobx": "^7.0.3",
119
+ "node-inspect-extracted": "^3.3.3",
107
120
  "semver": "^7.8.5",
108
121
  "strip-ansi": "^7.2.0",
122
+ "sucrase": "^3.35.1",
109
123
  "typescript": "^6.0.3",
110
124
  "vite": "^8.3.0",
111
125
  "vitepress": "^1.6.4",