@promptctl/rich-js 0.3.0 → 0.4.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 (61) hide show
  1. package/README.md +19 -15
  2. package/dist/core/console.d.ts +29 -11
  3. package/dist/core/console.d.ts.map +1 -1
  4. package/dist/core/console.js +86 -30
  5. package/dist/core/console.js.map +1 -1
  6. package/dist/core/render.d.ts.map +1 -1
  7. package/dist/core/render.js +8 -1
  8. package/dist/core/render.js.map +1 -1
  9. package/dist/core/sanitize.d.ts +16 -0
  10. package/dist/core/sanitize.d.ts.map +1 -0
  11. package/dist/core/sanitize.js +19 -0
  12. package/dist/core/sanitize.js.map +1 -0
  13. package/dist/core/strip.d.ts +16 -74
  14. package/dist/core/strip.d.ts.map +1 -1
  15. package/dist/core/strip.js +39 -75
  16. package/dist/core/strip.js.map +1 -1
  17. package/dist/core/style.d.ts +1 -0
  18. package/dist/core/style.d.ts.map +1 -1
  19. package/dist/core/style.js +38 -5
  20. package/dist/core/style.js.map +1 -1
  21. package/dist/core/text.d.ts +54 -1
  22. package/dist/core/text.d.ts.map +1 -1
  23. package/dist/core/text.js +194 -12
  24. package/dist/core/text.js.map +1 -1
  25. package/dist/index.d.ts +7 -3
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +3 -1
  28. package/dist/index.js.map +1 -1
  29. package/dist/node/prompt.d.ts +22 -0
  30. package/dist/node/prompt.d.ts.map +1 -0
  31. package/dist/node/prompt.js +31 -0
  32. package/dist/node/prompt.js.map +1 -0
  33. package/dist/node/save.d.ts +22 -0
  34. package/dist/node/save.d.ts.map +1 -0
  35. package/dist/node/save.js +22 -0
  36. package/dist/node/save.js.map +1 -0
  37. package/dist/renderables/prompt.d.ts +31 -6
  38. package/dist/renderables/prompt.d.ts.map +1 -1
  39. package/dist/renderables/prompt.js +39 -22
  40. package/dist/renderables/prompt.js.map +1 -1
  41. package/dist/widgets/event-router.d.ts +16 -14
  42. package/dist/widgets/event-router.d.ts.map +1 -1
  43. package/dist/widgets/event-router.js +71 -38
  44. package/dist/widgets/event-router.js.map +1 -1
  45. package/dist/widgets/host-stream.d.ts +31 -0
  46. package/dist/widgets/host-stream.d.ts.map +1 -0
  47. package/dist/widgets/host-stream.js +36 -0
  48. package/dist/widgets/host-stream.js.map +1 -0
  49. package/dist/widgets/index.d.ts +3 -0
  50. package/dist/widgets/index.d.ts.map +1 -1
  51. package/dist/widgets/index.js +2 -0
  52. package/dist/widgets/index.js.map +1 -1
  53. package/dist/widgets/screen.d.ts +12 -5
  54. package/dist/widgets/screen.d.ts.map +1 -1
  55. package/dist/widgets/screen.js +9 -11
  56. package/dist/widgets/screen.js.map +1 -1
  57. package/dist/widgets/terminal-host.d.ts +188 -0
  58. package/dist/widgets/terminal-host.d.ts.map +1 -0
  59. package/dist/widgets/terminal-host.js +258 -0
  60. package/dist/widgets/terminal-host.js.map +1 -0
  61. package/package.json +20 -6
@@ -0,0 +1,258 @@
1
+ /**
2
+ * TerminalHost — the capability seam between the interactive runtime and its
3
+ * I/O environment.
4
+ *
5
+ * [LAW:locality-or-seam] The missing seam *was* the missing type. Before this
6
+ * type existed, Screen and EventRouter reached for ambient `process.stdin`/
7
+ * `process.stdout`, which crystallised them to node. With a `TerminalHost`
8
+ * value flowing across one boundary, the same runtime code runs against any
9
+ * implementation — node's TTY (`NodeTerminalHost`, this file) or a browser
10
+ * xterm.js terminal (a downstream ticket's adapter).
11
+ *
12
+ * [LAW:dataflow-not-control-flow] The runtime never branches on "am I in
13
+ * node or browser." The host is the value that differs; the code path is
14
+ * the same. Variability lives in which host is constructed at the program
15
+ * entry, not in any `if` inside the runtime.
16
+ *
17
+ * [LAW:no-shared-mutable-globals] This file is the *single owner* of node
18
+ * TTY access in the codebase. Every other module receives a `TerminalHost`
19
+ * from outside. If you find yourself reaching for `process.stdin`,
20
+ * `process.stdout`, or `setRawMode` anywhere else in the runtime or in a
21
+ * demo, fix the call site to take a `TerminalHost` parameter instead —
22
+ * the seam is already here; use it.
23
+ *
24
+ * [boundaries: capabilities over context] A `TerminalHost` grants exactly
25
+ * the I/O capabilities the runtime needs: write bytes, read input, query
26
+ * size, observe resize, switch raw mode, lifecycle. It is not an
27
+ * omniscient handle to "the process."
28
+ */
29
+ const DEFAULT_COLS = 80;
30
+ const DEFAULT_ROWS = 24;
31
+ export class NodeTerminalHost {
32
+ stdin;
33
+ stdout;
34
+ dataHandlers = new Set();
35
+ resizeHandlers = new Set();
36
+ dataListener;
37
+ resizeListener;
38
+ rawModeRequested = false;
39
+ constructor(options = {}) {
40
+ // [LAW:no-shared-mutable-globals] The ONLY place in the codebase that
41
+ // reaches for `process.stdin`/`process.stdout` directly. All other
42
+ // callers receive a `TerminalHost` and stay environment-agnostic.
43
+ this.stdin = options.stdin ?? process.stdin;
44
+ this.stdout = options.stdout ?? process.stdout;
45
+ }
46
+ get isTTY() {
47
+ return !!this.stdin.isTTY && !!this.stdout.isTTY;
48
+ }
49
+ write(data) {
50
+ this.stdout.write(data);
51
+ }
52
+ size() {
53
+ return {
54
+ cols: this.stdout.columns ?? DEFAULT_COLS,
55
+ rows: this.stdout.rows ?? DEFAULT_ROWS,
56
+ };
57
+ }
58
+ setRawMode(raw) {
59
+ this.rawModeRequested = raw;
60
+ this.applyRawMode(raw);
61
+ }
62
+ // [LAW:single-enforcer] One site decides what to do when toggling raw
63
+ // mode on the underlying transport fails. Both `setRawMode` (call from
64
+ // outside) and `stop` (cleanup) go through this helper so neither
65
+ // caller has to branch on capability or exception-handling.
66
+ //
67
+ // [LAW:dataflow-not-control-flow] Two shapes of "the transport can't do
68
+ // this" are absorbed identically: the optional chain handles "method
69
+ // missing" (non-TTY streams, PassThrough in tests); the try/catch
70
+ // handles "method exists but threw" (node's `setRawMode` throws on
71
+ // closed or non-TTY streams). Callers see neither shape — they always
72
+ // call `setRawMode(bool)`, and the host decides what's possible.
73
+ //
74
+ // The catch is silent because the only call sites are lifecycle
75
+ // transitions: if the stream is already gone, there is nothing
76
+ // semantically meaningful for the host to do, and surfacing the
77
+ // exception would only make teardown brittle without giving the caller
78
+ // a recovery path. The demos this replaces did `try { ... } catch {}`
79
+ // at every call site for exactly this reason — absorbing it once here
80
+ // means the pattern stops spreading.
81
+ applyRawMode(raw) {
82
+ try {
83
+ this.stdin.setRawMode?.(raw);
84
+ }
85
+ catch {
86
+ // Underlying stream couldn't honor the request (closed, no longer
87
+ // a TTY, etc.). Lifecycle continues.
88
+ }
89
+ }
90
+ onData(handler) {
91
+ this.dataHandlers.add(handler);
92
+ // [LAW:single-enforcer] One underlying `data` listener fans out to all
93
+ // subscribers — keeps stdin's flowing/paused state derivable from
94
+ // "any subscribers?" instead of from per-call ref-counts scattered
95
+ // through the runtime.
96
+ if (!this.dataListener) {
97
+ this.dataListener = (chunk) => {
98
+ for (const h of this.dataHandlers)
99
+ h(chunk);
100
+ };
101
+ this.stdin.on("data", this.dataListener);
102
+ this.stdin.resume?.();
103
+ }
104
+ return () => {
105
+ this.dataHandlers.delete(handler);
106
+ if (this.dataHandlers.size === 0 && this.dataListener) {
107
+ this.stdin.off("data", this.dataListener);
108
+ this.dataListener = undefined;
109
+ // Returning stdin to paused state mirrors the symmetric "no
110
+ // subscribers → no flow" invariant. Without this, removing the
111
+ // last subscriber leaves stdin in flowing mode and the process
112
+ // hangs forever waiting for an event nobody is listening for.
113
+ this.stdin.pause?.();
114
+ }
115
+ };
116
+ }
117
+ onResize(handler) {
118
+ this.resizeHandlers.add(handler);
119
+ if (!this.resizeListener) {
120
+ this.resizeListener = () => {
121
+ const size = this.size();
122
+ for (const h of this.resizeHandlers)
123
+ h(size);
124
+ };
125
+ this.stdout.on("resize", this.resizeListener);
126
+ }
127
+ return () => {
128
+ this.resizeHandlers.delete(handler);
129
+ if (this.resizeHandlers.size === 0 && this.resizeListener) {
130
+ this.stdout.off("resize", this.resizeListener);
131
+ this.resizeListener = undefined;
132
+ }
133
+ };
134
+ }
135
+ start() {
136
+ // No-op on node: stdin/stdout are always available. Lifecycle exists
137
+ // so the browser host (downstream ticket) can attach to the DOM here
138
+ // without the runtime branching on "do I need to start?"
139
+ }
140
+ stop() {
141
+ // [LAW:one-source-of-truth] Symmetric teardown: drop every subscriber
142
+ // and detach the underlying stream listeners. After stop(), the host
143
+ // is in the same shape a freshly-constructed one would be in. Calling
144
+ // stop() repeatedly is safe — the dataListener/resizeListener guards
145
+ // make later calls no-ops.
146
+ if (this.dataListener) {
147
+ this.stdin.off("data", this.dataListener);
148
+ this.dataListener = undefined;
149
+ this.stdin.pause?.();
150
+ }
151
+ if (this.resizeListener) {
152
+ this.stdout.off("resize", this.resizeListener);
153
+ this.resizeListener = undefined;
154
+ }
155
+ this.dataHandlers.clear();
156
+ this.resizeHandlers.clear();
157
+ if (this.rawModeRequested) {
158
+ this.applyRawMode(false);
159
+ this.rawModeRequested = false;
160
+ }
161
+ }
162
+ }
163
+ export class BrowserTerminalHost {
164
+ terminal;
165
+ dataHandlers = new Set();
166
+ resizeHandlers = new Set();
167
+ dataSubscription;
168
+ resizeSubscription;
169
+ constructor(options) {
170
+ this.terminal = options.terminal;
171
+ }
172
+ // [LAW:dataflow-not-control-flow] xterm.js is always an interactive
173
+ // terminal — there is no "non-TTY" code path in the browser. The
174
+ // runtime never branches on the value; this is the only sensible
175
+ // constant for this host.
176
+ get isTTY() {
177
+ return true;
178
+ }
179
+ write(data) {
180
+ this.terminal.write(data);
181
+ }
182
+ size() {
183
+ return { cols: this.terminal.cols, rows: this.terminal.rows };
184
+ }
185
+ // [LAW:dataflow-not-control-flow] xterm.js delivers keystrokes raw
186
+ // unconditionally — there is no cooked mode to toggle. The no-op
187
+ // matches the TerminalHost contract's documented "silently no-op when
188
+ // the transport has no cooked mode," so callers stay uniform across
189
+ // hosts. (Mirror: NodeTerminalHost on a non-TTY stream takes the same
190
+ // no-op path via the optional-chain in `applyRawMode`.)
191
+ setRawMode(_raw) { }
192
+ // [LAW:single-enforcer] One underlying `terminal.onData` subscription
193
+ // fans out to every host-level handler — the same lazy-attach,
194
+ // last-unsubscribe-detaches shape as NodeTerminalHost.onData. The
195
+ // runtime never sees the underlying subscription; it sees a single
196
+ // uniform `(handler) => Unsubscribe` API across hosts.
197
+ onData(handler) {
198
+ this.dataHandlers.add(handler);
199
+ if (!this.dataSubscription) {
200
+ this.dataSubscription = this.terminal.onData((chunk) => {
201
+ for (const h of this.dataHandlers)
202
+ h(chunk);
203
+ });
204
+ }
205
+ return () => {
206
+ this.dataHandlers.delete(handler);
207
+ if (this.dataHandlers.size === 0 && this.dataSubscription) {
208
+ this.dataSubscription.dispose();
209
+ this.dataSubscription = undefined;
210
+ }
211
+ };
212
+ }
213
+ onResize(handler) {
214
+ this.resizeHandlers.add(handler);
215
+ if (!this.resizeSubscription) {
216
+ this.resizeSubscription = this.terminal.onResize((size) => {
217
+ const snapshot = { cols: size.cols, rows: size.rows };
218
+ for (const h of this.resizeHandlers)
219
+ h(snapshot);
220
+ });
221
+ }
222
+ return () => {
223
+ this.resizeHandlers.delete(handler);
224
+ if (this.resizeHandlers.size === 0 && this.resizeSubscription) {
225
+ this.resizeSubscription.dispose();
226
+ this.resizeSubscription = undefined;
227
+ }
228
+ };
229
+ }
230
+ start() {
231
+ // [LAW:one-source-of-truth] The host does NOT own the terminal's DOM
232
+ // lifecycle (`terminal.open` / `terminal.dispose`). That is the
233
+ // caller's responsibility, exactly as NodeTerminalHost does not own
234
+ // `process.stdin`/`process.stdout`. start() is here so the runtime
235
+ // can call it uniformly across hosts; for this host there is
236
+ // nothing to do at this seam.
237
+ }
238
+ stop() {
239
+ // [LAW:one-source-of-truth] Symmetric teardown mirroring
240
+ // NodeTerminalHost.stop: drop every host-level subscriber and
241
+ // dispose the underlying terminal subscriptions. The terminal
242
+ // itself stays alive — re-subscribing via onData/onResize after
243
+ // stop() works, since the lazy-attach path re-creates the
244
+ // subscription on next subscribe. Calling stop() repeatedly is
245
+ // safe — the subscription guards make later calls no-ops.
246
+ if (this.dataSubscription) {
247
+ this.dataSubscription.dispose();
248
+ this.dataSubscription = undefined;
249
+ }
250
+ if (this.resizeSubscription) {
251
+ this.resizeSubscription.dispose();
252
+ this.resizeSubscription = undefined;
253
+ }
254
+ this.dataHandlers.clear();
255
+ this.resizeHandlers.clear();
256
+ }
257
+ }
258
+ //# sourceMappingURL=terminal-host.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminal-host.js","sourceRoot":"","sources":["../../src/widgets/terminal-host.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAqHH,MAAM,YAAY,GAAG,EAAE,CAAC;AACxB,MAAM,YAAY,GAAG,EAAE,CAAC;AAExB,MAAM,OAAO,gBAAgB;IACV,KAAK,CAAe;IACpB,MAAM,CAAe;IACrB,YAAY,GAAG,IAAI,GAAG,EAAe,CAAC;IACtC,cAAc,GAAG,IAAI,GAAG,EAAiB,CAAC;IACnD,YAAY,CAAiD;IAC7D,cAAc,CAA2B;IACzC,gBAAgB,GAAG,KAAK,CAAC;IAEjC,YAAY,UAAmC,EAAE;QAC/C,sEAAsE;QACtE,mEAAmE;QACnE,kEAAkE;QAClE,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,IAAK,OAAO,CAAC,KAAiC,CAAC;QACzE,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,IAAK,OAAO,CAAC,MAAkC,CAAC;IAC9E,CAAC;IAED,IAAI,KAAK;QACP,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;IACnD,CAAC;IAED,KAAK,CAAC,IAAyB;QAC7B,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAED,IAAI;QACF,OAAO;YACL,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,YAAY;YACzC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,YAAY;SACvC,CAAC;IACJ,CAAC;IAED,UAAU,CAAC,GAAY;QACrB,IAAI,CAAC,gBAAgB,GAAG,GAAG,CAAC;QAC5B,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;IACzB,CAAC;IAED,sEAAsE;IACtE,uEAAuE;IACvE,kEAAkE;IAClE,4DAA4D;IAC5D,EAAE;IACF,wEAAwE;IACxE,qEAAqE;IACrE,kEAAkE;IAClE,mEAAmE;IACnE,sEAAsE;IACtE,iEAAiE;IACjE,EAAE;IACF,gEAAgE;IAChE,+DAA+D;IAC/D,gEAAgE;IAChE,uEAAuE;IACvE,sEAAsE;IACtE,sEAAsE;IACtE,qCAAqC;IAC7B,YAAY,CAAC,GAAY;QAC/B,IAAI,CAAC;YACH,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC,GAAG,CAAC,CAAC;QAC/B,CAAC;QAAC,MAAM,CAAC;YACP,kEAAkE;YAClE,qCAAqC;QACvC,CAAC;IACH,CAAC;IAED,MAAM,CAAC,OAAoB;QACzB,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC/B,uEAAuE;QACvE,kEAAkE;QAClE,mEAAmE;QACnE,uBAAuB;QACvB,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;YACvB,IAAI,CAAC,YAAY,GAAG,CAAC,KAAsB,EAAE,EAAE;gBAC7C,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,YAAY;oBAAE,CAAC,CAAC,KAAK,CAAC,CAAC;YAC9C,CAAC,CAAC;YACF,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;YACzC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;QACxB,CAAC;QACD,OAAO,GAAG,EAAE;YACV,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAClC,IAAI,IAAI,CAAC,YAAY,CAAC,IAAI,KAAK,CAAC,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;gBACtD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;gBAC1C,IAAI,CAAC,YAAY,GAAG,SAAS,CAAC;gBAC9B,4DAA4D;gBAC5D,+DAA+D;gBAC/D,+DAA+D;gBAC/D,8DAA8D;gBAC9D,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC;YACvB,CAAC;QACH,CAAC,CAAC;IACJ,CAAC;IAED,QAAQ,CAAC,OAAsB;QAC7B,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACjC,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;YACzB,IAAI,CAAC,cAAc,GAAG,GAAG,EAAE;gBACzB,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;gBACzB,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,cAAc;oBAAE,CAAC,CAAC,IAAI,CAAC,CAAC;YAC/C,CAAC,CAAC;YACF,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;QAChD,CAAC;QACD,OAAO,GAAG,EAAE;YACV,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YACpC,IAAI,IAAI,CAAC,cAAc,CAAC,IAAI,KAAK,CAAC,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;gBAC1D,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;gBAC/C,IAAI,CAAC,cAAc,GAAG,SAAS,CAAC;YAClC,CAAC;QACH,CAAC,CAAC;IACJ,CAAC;IAED,KAAK;QACH,qEAAqE;QACrE,qEAAqE;QACrE,yDAAyD;IAC3D,CAAC;IAED,IAAI;QACF,sEAAsE;QACtE,qEAAqE;QACrE,sEAAsE;QACtE,qEAAqE;QACrE,2BAA2B;QAC3B,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;YAC1C,IAAI,CAAC,YAAY,GAAG,SAAS,CAAC;YAC9B,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC;QACvB,CAAC;QACD,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACxB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;YAC/C,IAAI,CAAC,cAAc,GAAG,SAAS,CAAC;QAClC,CAAC;QACD,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC;QAC1B,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,CAAC;QAC5B,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC1B,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;YACzB,IAAI,CAAC,gBAAgB,GAAG,KAAK,CAAC;QAChC,CAAC;IACH,CAAC;CACF;AA2CD,MAAM,OAAO,mBAAmB;IACb,QAAQ,CAAgB;IACxB,YAAY,GAAG,IAAI,GAAG,EAAe,CAAC;IACtC,cAAc,GAAG,IAAI,GAAG,EAAiB,CAAC;IACnD,gBAAgB,CAA8B;IAC9C,kBAAkB,CAA8B;IAExD,YAAY,OAAmC;QAC7C,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;IACnC,CAAC;IAED,oEAAoE;IACpE,iEAAiE;IACjE,iEAAiE;IACjE,0BAA0B;IAC1B,IAAI,KAAK;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,IAAyB;QAC7B,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IAED,IAAI;QACF,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;IAChE,CAAC;IAED,mEAAmE;IACnE,iEAAiE;IACjE,sEAAsE;IACtE,oEAAoE;IACpE,sEAAsE;IACtE,wDAAwD;IACxD,UAAU,CAAC,IAAa,IAAS,CAAC;IAElC,sEAAsE;IACtE,+DAA+D;IAC/D,kEAAkE;IAClE,mEAAmE;IACnE,uDAAuD;IACvD,MAAM,CAAC,OAAoB;QACzB,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC/B,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC3B,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,KAAa,EAAE,EAAE;gBAC7D,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,YAAY;oBAAE,CAAC,CAAC,KAAK,CAAC,CAAC;YAC9C,CAAC,CAAC,CAAC;QACL,CAAC;QACD,OAAO,GAAG,EAAE;YACV,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAClC,IAAI,IAAI,CAAC,YAAY,CAAC,IAAI,KAAK,CAAC,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;gBAC1D,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,CAAC;gBAChC,IAAI,CAAC,gBAAgB,GAAG,SAAS,CAAC;YACpC,CAAC;QACH,CAAC,CAAC;IACJ,CAAC;IAED,QAAQ,CAAC,OAAsB;QAC7B,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACjC,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC7B,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,IAAsB,EAAE,EAAE;gBAC1E,MAAM,QAAQ,GAAiB,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC;gBACpE,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,cAAc;oBAAE,CAAC,CAAC,QAAQ,CAAC,CAAC;YACnD,CAAC,CAAC,CAAC;QACL,CAAC;QACD,OAAO,GAAG,EAAE;YACV,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YACpC,IAAI,IAAI,CAAC,cAAc,CAAC,IAAI,KAAK,CAAC,IAAI,IAAI,CAAC,kBAAkB,EAAE,CAAC;gBAC9D,IAAI,CAAC,kBAAkB,CAAC,OAAO,EAAE,CAAC;gBAClC,IAAI,CAAC,kBAAkB,GAAG,SAAS,CAAC;YACtC,CAAC;QACH,CAAC,CAAC;IACJ,CAAC;IAED,KAAK;QACH,qEAAqE;QACrE,gEAAgE;QAChE,oEAAoE;QACpE,mEAAmE;QACnE,6DAA6D;QAC7D,8BAA8B;IAChC,CAAC;IAED,IAAI;QACF,yDAAyD;QACzD,8DAA8D;QAC9D,8DAA8D;QAC9D,gEAAgE;QAChE,0DAA0D;QAC1D,+DAA+D;QAC/D,0DAA0D;QAC1D,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC1B,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,CAAC;YAChC,IAAI,CAAC,gBAAgB,GAAG,SAAS,CAAC;QACpC,CAAC;QACD,IAAI,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC5B,IAAI,CAAC,kBAAkB,CAAC,OAAO,EAAE,CAAC;YAClC,IAAI,CAAC,kBAAkB,GAAG,SAAS,CAAC;QACtC,CAAC;QACD,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC;QAC1B,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,CAAC;IAC9B,CAAC;CACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@promptctl/rich-js",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Rich text and beautiful formatting in the terminal — a TypeScript port of Python's Rich",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -21,6 +21,14 @@
21
21
  "./template-bindings": {
22
22
  "types": "./dist/template-bindings/index.d.ts",
23
23
  "import": "./dist/template-bindings/index.js"
24
+ },
25
+ "./node/save": {
26
+ "types": "./dist/node/save.d.ts",
27
+ "import": "./dist/node/save.js"
28
+ },
29
+ "./node/prompt": {
30
+ "types": "./dist/node/prompt.d.ts",
31
+ "import": "./dist/node/prompt.js"
24
32
  }
25
33
  },
26
34
  "files": [
@@ -30,10 +38,10 @@
30
38
  "build": "tsc",
31
39
  "test": "vitest run",
32
40
  "test:watch": "vitest",
33
- "lint": "tsc --noEmit",
41
+ "lint": "tsc --noEmit && tsc -p tsconfig.scripts.json",
34
42
  "clean": "rm -rf dist",
35
- "docs:dev": "vitepress dev docs",
36
- "docs:build": "vitepress build docs",
43
+ "docs:dev": "npm run demos:build && vitepress dev docs",
44
+ "docs:build": "npm run demos:build && vitepress build docs",
37
45
  "docs:preview": "vitepress preview docs",
38
46
  "demo": "tsc -p tsconfig.demo.json && node dist-demo/examples/rich-explore/index.js",
39
47
  "demo:build": "tsc -p tsconfig.demo.json",
@@ -44,7 +52,11 @@
44
52
  "demo:dropdown": "tsc -p tsconfig.demo.json && node dist-demo/examples/dropdown-demo/index.js",
45
53
  "dash": "tsc -p tsconfig.demo.json && node dist-demo/examples/rich-dash/index.js",
46
54
  "template-bindings": "tsc -p tsconfig.demo.json && node dist-demo/examples/rich-template-bindings/index.js",
47
- "themes-and-color-studio": "tsc -p tsconfig.demo.json && node dist-demo/examples/themes-and-color-studio/index.js"
55
+ "themes-and-color-studio": "tsc -p tsconfig.demo.json && node dist-demo/examples/themes-and-color-studio/index.js",
56
+ "harness:browser-host": "tsc -p tsconfig.demo.json && echo 'Serving harness at http://localhost:8080/examples/browser-terminal-host-harness/' && npx --yes http-server@14.1.1 -p 8080 -c-1 -s .",
57
+ "demos:build": "tsc -p tsconfig.demo.json && vite build --config vite.config.demos.ts",
58
+ "demos:preview": "vite preview --config vite.config.demos.ts",
59
+ "test:demos": "playwright test"
48
60
  },
49
61
  "keywords": [
50
62
  "terminal",
@@ -61,11 +73,13 @@
61
73
  ],
62
74
  "license": "MIT",
63
75
  "engines": {
64
- "node": ">=20"
76
+ "node": "^20.19.0 || >=22.12.0"
65
77
  },
66
78
  "devDependencies": {
79
+ "@playwright/test": "^1.60.0",
67
80
  "@types/node": "^25.5.0",
68
81
  "typescript": "^5.7.0",
82
+ "vite": "^7.3.3",
69
83
  "vitepress": "^1.6.4",
70
84
  "vitest": "^3.0.0"
71
85
  },