@promptctl/rich-js 0.18.1 → 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 (150) hide show
  1. package/README.md +9 -7
  2. package/dist/core/box.d.ts +3 -1
  3. package/dist/core/box.js +6 -13
  4. package/dist/core/cells.d.ts +61 -28
  5. package/dist/core/cells.js +171 -124
  6. package/dist/core/clock.d.ts +48 -0
  7. package/dist/core/clock.js +42 -0
  8. package/dist/core/color.d.ts +98 -18
  9. package/dist/core/color.js +331 -144
  10. package/dist/core/console.d.ts +38 -7
  11. package/dist/core/console.js +159 -90
  12. package/dist/core/easing.d.ts +86 -0
  13. package/dist/core/easing.js +171 -0
  14. package/dist/core/env.d.ts +16 -0
  15. package/dist/core/env.js +16 -0
  16. package/dist/core/export-html.js +1 -4
  17. package/dist/core/export-lines.d.ts +22 -13
  18. package/dist/core/export-lines.js +37 -19
  19. package/dist/core/export-svg.d.ts +49 -0
  20. package/dist/core/export-svg.js +213 -0
  21. package/dist/core/fnv1a.d.ts +10 -0
  22. package/dist/core/fnv1a.js +17 -0
  23. package/dist/core/highlighter.d.ts +2 -2
  24. package/dist/core/highlighter.js +113 -47
  25. package/dist/core/json.js +1 -1
  26. package/dist/core/markup.d.ts +28 -7
  27. package/dist/core/markup.js +235 -312
  28. package/dist/core/measure.d.ts +3 -3
  29. package/dist/core/measure.js +12 -7
  30. package/dist/core/memo.d.ts +19 -0
  31. package/dist/core/memo.js +34 -0
  32. package/dist/core/osc8.d.ts +6 -6
  33. package/dist/core/osc8.js +9 -21
  34. package/dist/core/paint.d.ts +107 -0
  35. package/dist/core/paint.js +150 -0
  36. package/dist/core/place.d.ts +1 -1
  37. package/dist/core/place.js +4 -6
  38. package/dist/core/pretty.d.ts +105 -2
  39. package/dist/core/pretty.js +527 -97
  40. package/dist/core/protocol.d.ts +41 -3
  41. package/dist/core/protocol.js +20 -0
  42. package/dist/core/render.d.ts +33 -30
  43. package/dist/core/render.js +43 -83
  44. package/dist/core/segment.d.ts +6 -5
  45. package/dist/core/segment.js +11 -15
  46. package/dist/core/strip.d.ts +13 -2
  47. package/dist/core/strip.js +44 -23
  48. package/dist/core/style.d.ts +27 -7
  49. package/dist/core/style.js +97 -34
  50. package/dist/core/text.d.ts +43 -16
  51. package/dist/core/text.js +180 -59
  52. package/dist/core/wrap.d.ts +5 -0
  53. package/dist/core/wrap.js +16 -3
  54. package/dist/host/app.d.ts +31 -19
  55. package/dist/host/app.js +90 -60
  56. package/dist/host/index.d.ts +2 -1
  57. package/dist/host/terminal-host.d.ts +3 -2
  58. package/dist/index.d.ts +17 -10
  59. package/dist/index.js +11 -4
  60. package/dist/node/prompt.d.ts +13 -0
  61. package/dist/node/prompt.js +30 -9
  62. package/dist/node/save.d.ts +4 -3
  63. package/dist/node/save.js +6 -3
  64. package/dist/node/terminal-host.d.ts +3 -2
  65. package/dist/node/traceback.d.ts +2 -1
  66. package/dist/node/traceback.js +3 -39
  67. package/dist/renderables/align.js +2 -9
  68. package/dist/renderables/columns.d.ts +45 -29
  69. package/dist/renderables/columns.js +142 -103
  70. package/dist/renderables/constrain.js +6 -12
  71. package/dist/renderables/effect.d.ts +98 -0
  72. package/dist/renderables/effect.js +160 -0
  73. package/dist/renderables/embed.d.ts +87 -30
  74. package/dist/renderables/embed.js +134 -45
  75. package/dist/renderables/group.d.ts +6 -3
  76. package/dist/renderables/group.js +5 -0
  77. package/dist/renderables/layout.d.ts +6 -23
  78. package/dist/renderables/layout.js +24 -113
  79. package/dist/renderables/live.d.ts +40 -16
  80. package/dist/renderables/live.js +96 -83
  81. package/dist/renderables/markdown.d.ts +16 -2
  82. package/dist/renderables/markdown.js +521 -118
  83. package/dist/renderables/padding.d.ts +22 -1
  84. package/dist/renderables/padding.js +30 -24
  85. package/dist/renderables/panel.d.ts +31 -33
  86. package/dist/renderables/panel.js +137 -141
  87. package/dist/renderables/progress.d.ts +104 -16
  88. package/dist/renderables/progress.js +205 -81
  89. package/dist/renderables/progressBar.d.ts +2 -2
  90. package/dist/renderables/progressBar.js +30 -19
  91. package/dist/renderables/prompt.d.ts +37 -15
  92. package/dist/renderables/prompt.js +125 -85
  93. package/dist/renderables/ratio.d.ts +123 -0
  94. package/dist/renderables/ratio.js +213 -0
  95. package/dist/renderables/rule.d.ts +1 -0
  96. package/dist/renderables/rule.js +44 -48
  97. package/dist/renderables/spinner.d.ts +28 -2
  98. package/dist/renderables/spinner.js +52 -19
  99. package/dist/renderables/status.d.ts +13 -7
  100. package/dist/renderables/status.js +15 -29
  101. package/dist/renderables/syntax.d.ts +37 -7
  102. package/dist/renderables/syntax.js +134 -83
  103. package/dist/renderables/table.d.ts +58 -20
  104. package/dist/renderables/table.js +370 -192
  105. package/dist/renderables/traceback.d.ts +5 -4
  106. package/dist/renderables/traceback.js +239 -68
  107. package/dist/renderables/tree.d.ts +10 -2
  108. package/dist/renderables/tree.js +61 -34
  109. package/dist/renderables/viewport.js +2 -4
  110. package/dist/template-bindings/color-funcs.d.ts +31 -9
  111. package/dist/template-bindings/color-funcs.js +40 -14
  112. package/dist/template-bindings/helpers.d.ts +4 -2
  113. package/dist/template-bindings/helpers.js +5 -20
  114. package/dist/template-bindings/index.d.ts +29 -17
  115. package/dist/template-bindings/index.js +36 -20
  116. package/dist/template-bindings/palette-funcs.d.ts +3 -1
  117. package/dist/template-bindings/palette-funcs.js +11 -7
  118. package/dist/template-bindings/style-funcs.js +4 -2
  119. package/dist/themes/buildPalette.d.ts +24 -3
  120. package/dist/themes/buildPalette.js +52 -8
  121. package/dist/themes/colorMath.d.ts +29 -17
  122. package/dist/themes/colorMath.js +74 -48
  123. package/dist/themes/colorRef.d.ts +2 -17
  124. package/dist/themes/colorRef.js +13 -31
  125. package/dist/themes/palette.d.ts +24 -0
  126. package/dist/themes/palette.js +46 -3
  127. package/dist/themes/ramp.d.ts +21 -34
  128. package/dist/themes/ramp.js +23 -42
  129. package/dist/themes/registry.js +39 -28
  130. package/dist/widgets/button.d.ts +2 -6
  131. package/dist/widgets/button.js +9 -40
  132. package/dist/widgets/checkbox.d.ts +2 -5
  133. package/dist/widgets/checkbox.js +9 -31
  134. package/dist/widgets/dropdown.d.ts +2 -5
  135. package/dist/widgets/dropdown.js +14 -48
  136. package/dist/widgets/ink.d.ts +22 -0
  137. package/dist/widgets/ink.js +31 -0
  138. package/dist/widgets/slider.d.ts +2 -5
  139. package/dist/widgets/slider.js +11 -34
  140. package/dist/widgets/text-input.d.ts +19 -10
  141. package/dist/widgets/text-input.js +156 -147
  142. package/dist/widgets/themed-widget.d.ts +20 -0
  143. package/dist/widgets/themed-widget.js +89 -0
  144. package/dist/widgets/toggle.d.ts +2 -5
  145. package/dist/widgets/toggle.js +8 -31
  146. package/dist/widgets/widget-app.d.ts +2 -0
  147. package/dist/widgets/widget-app.js +13 -6
  148. package/dist/widgets/widget-base.d.ts +2 -0
  149. package/dist/widgets/widget-base.js +9 -2
  150. package/package.json +11 -1
@@ -0,0 +1,89 @@
1
+ /**
2
+ * ThemedWidget — a widget that draws every cell it owns from one theme's
3
+ * palette: the interactive widgets, as against `StaticItem`, which shows
4
+ * content it did not draw.
5
+ *
6
+ * [LAW:one-type-per-behavior] The theme, how it is swapped, what a disabled
7
+ * widget looks like, and the ground a padded row stands on are the same for
8
+ * every such widget, so they live here once.
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 { observableRef, action } from "mobx";
45
+ import { Style } from "../core/style.js";
46
+ import { DEFAULT_TERMINAL_THEME } from "../themes/terminalThemes.js";
47
+ import { WidgetBase } from "./widget-base.js";
48
+ import { ink } from "./ink.js";
49
+ let ThemedWidget = (() => {
50
+ let _classSuper = WidgetBase;
51
+ let _instanceExtraInitializers = [];
52
+ let _theme_decorators;
53
+ let _theme_initializers = [];
54
+ let _theme_extraInitializers = [];
55
+ let _setTheme_decorators;
56
+ return class ThemedWidget extends _classSuper {
57
+ static {
58
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
59
+ _theme_decorators = [observableRef];
60
+ _setTheme_decorators = [action];
61
+ __esDecorate(this, null, _theme_decorators, { kind: "accessor", name: "theme", static: false, private: false, access: { has: obj => "theme" in obj, get: obj => obj.theme, set: (obj, value) => { obj.theme = value; } }, metadata: _metadata }, _theme_initializers, _theme_extraInitializers);
62
+ __esDecorate(this, null, _setTheme_decorators, { kind: "method", name: "setTheme", static: false, private: false, access: { has: obj => "setTheme" in obj, get: obj => obj.setTheme }, metadata: _metadata }, null, _instanceExtraInitializers);
63
+ if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
64
+ }
65
+ #theme_accessor_storage = (__runInitializers(this, _instanceExtraInitializers), __runInitializers(this, _theme_initializers, void 0));
66
+ // [LAW:types-are-the-program] @observableRef so setTheme() triggers a
67
+ // re-render: every draw reads its colours through this reference.
68
+ get theme() { return this.#theme_accessor_storage; }
69
+ set theme(value) { this.#theme_accessor_storage = value; }
70
+ constructor(theme) {
71
+ super();
72
+ __runInitializers(this, _theme_extraInitializers);
73
+ this.theme = theme ?? DEFAULT_TERMINAL_THEME;
74
+ }
75
+ setTheme(theme) {
76
+ this.theme = theme;
77
+ }
78
+ get ground() {
79
+ return new Style(ink(this.theme, "foreground", "background"));
80
+ }
81
+ // [LAW:one-source-of-truth] How every widget looks while disabled: the
82
+ // palette's de-emphasized text on its ground. WCAG sets no contrast floor for
83
+ // an inactive control, and this sits under the one for text on purpose.
84
+ get disabledInk() {
85
+ return ink(this.theme, "foreground-muted", "background");
86
+ }
87
+ };
88
+ })();
89
+ export { ThemedWidget };
@@ -13,7 +13,7 @@
13
13
  import { Segment } from "../core/segment.js";
14
14
  import type { RenderOptions } from "../core/protocol.js";
15
15
  import type { TerminalTheme } from "../core/color.js";
16
- import { WidgetBase } from "./widget-base.js";
16
+ import { ThemedWidget } from "./themed-widget.js";
17
17
  import type { KeyEvent, WidgetMouseEvent } from "./types.js";
18
18
  export type ToggleVariant = "default" | "primary" | "success" | "warning" | "danger";
19
19
  export interface ToggleOptions {
@@ -24,15 +24,13 @@ export interface ToggleOptions {
24
24
  theme?: TerminalTheme;
25
25
  variant?: ToggleVariant;
26
26
  }
27
- export declare class Toggle extends WidgetBase {
27
+ export declare class Toggle extends ThemedWidget {
28
28
  readonly id: string;
29
29
  readonly focusable = true;
30
30
  accessor label: string;
31
31
  accessor on: boolean;
32
32
  accessor variant: ToggleVariant;
33
- private accessor _theme;
34
33
  constructor(options: ToggleOptions);
35
- setTheme(theme: TerminalTheme): void;
36
34
  handleKey(event: KeyEvent): void;
37
35
  handleMouse(event: WidgetMouseEvent): void;
38
36
  protected draw(_options: RenderOptions): Iterable<Segment>;
@@ -40,5 +38,4 @@ export declare class Toggle extends WidgetBase {
40
38
  minimum: number;
41
39
  maximum: number;
42
40
  };
43
- private resolvePalette;
44
41
  }
@@ -47,10 +47,9 @@ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn,
47
47
  import { observable, action, observableRef } from "mobx";
48
48
  import { Segment } from "../core/segment.js";
49
49
  import { Style } from "../core/style.js";
50
- import { ColorSpec } from "../core/color.js";
51
50
  import { cellLen } from "../core/cells.js";
52
- import { DEFAULT_TERMINAL_THEME } from "../themes/terminalThemes.js";
53
- import { WidgetBase } from "./widget-base.js";
51
+ import { ThemedWidget } from "./themed-widget.js";
52
+ import { ink } from "./ink.js";
54
53
  // onFg uses on-${accent} (WCAG-correct contrast, picked once at palette
55
54
  // build time) so the ON state stays readable on a full accent bg. Earlier
56
55
  // versions used text-${accent} here, which is mostly-accent and clashed
@@ -64,7 +63,7 @@ const VARIANT_KEYS = {
64
63
  danger: { onBg: "error", onFg: "on-error", offBg: "error-muted", offFg: "text-error" },
65
64
  };
66
65
  let Toggle = (() => {
67
- let _classSuper = WidgetBase;
66
+ let _classSuper = ThemedWidget;
68
67
  let _instanceExtraInitializers = [];
69
68
  let _label_decorators;
70
69
  let _label_initializers = [];
@@ -75,10 +74,6 @@ let Toggle = (() => {
75
74
  let _variant_decorators;
76
75
  let _variant_initializers = [];
77
76
  let _variant_extraInitializers = [];
78
- let __theme_decorators;
79
- let __theme_initializers = [];
80
- let __theme_extraInitializers = [];
81
- let _setTheme_decorators;
82
77
  let _handleKey_decorators;
83
78
  let _handleMouse_decorators;
84
79
  return class Toggle extends _classSuper {
@@ -87,15 +82,11 @@ let Toggle = (() => {
87
82
  _label_decorators = [observable];
88
83
  _on_decorators = [observable];
89
84
  _variant_decorators = [observableRef];
90
- __theme_decorators = [observableRef];
91
- _setTheme_decorators = [action];
92
85
  _handleKey_decorators = [action];
93
86
  _handleMouse_decorators = [action];
94
87
  __esDecorate(this, null, _label_decorators, { kind: "accessor", name: "label", static: false, private: false, access: { has: obj => "label" in obj, get: obj => obj.label, set: (obj, value) => { obj.label = value; } }, metadata: _metadata }, _label_initializers, _label_extraInitializers);
95
88
  __esDecorate(this, null, _on_decorators, { kind: "accessor", name: "on", static: false, private: false, access: { has: obj => "on" in obj, get: obj => obj.on, set: (obj, value) => { obj.on = value; } }, metadata: _metadata }, _on_initializers, _on_extraInitializers);
96
89
  __esDecorate(this, null, _variant_decorators, { kind: "accessor", name: "variant", static: false, private: false, access: { has: obj => "variant" in obj, get: obj => obj.variant, set: (obj, value) => { obj.variant = value; } }, metadata: _metadata }, _variant_initializers, _variant_extraInitializers);
97
- __esDecorate(this, null, __theme_decorators, { kind: "accessor", name: "_theme", static: false, private: false, access: { has: obj => "_theme" in obj, get: obj => obj._theme, set: (obj, value) => { obj._theme = value; } }, metadata: _metadata }, __theme_initializers, __theme_extraInitializers);
98
- __esDecorate(this, null, _setTheme_decorators, { kind: "method", name: "setTheme", static: false, private: false, access: { has: obj => "setTheme" in obj, get: obj => obj.setTheme }, metadata: _metadata }, null, _instanceExtraInitializers);
99
90
  __esDecorate(this, null, _handleKey_decorators, { kind: "method", name: "handleKey", static: false, private: false, access: { has: obj => "handleKey" in obj, get: obj => obj.handleKey }, metadata: _metadata }, null, _instanceExtraInitializers);
100
91
  __esDecorate(this, null, _handleMouse_decorators, { kind: "method", name: "handleMouse", static: false, private: false, access: { has: obj => "handleMouse" in obj, get: obj => obj.handleMouse }, metadata: _metadata }, null, _instanceExtraInitializers);
101
92
  if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
@@ -111,22 +102,15 @@ let Toggle = (() => {
111
102
  #variant_accessor_storage = (__runInitializers(this, _on_extraInitializers), __runInitializers(this, _variant_initializers, void 0));
112
103
  get variant() { return this.#variant_accessor_storage; }
113
104
  set variant(value) { this.#variant_accessor_storage = value; }
114
- #_theme_accessor_storage = (__runInitializers(this, _variant_extraInitializers), __runInitializers(this, __theme_initializers, void 0));
115
- // [LAW:types-are-the-program] @observableRef so setTheme() triggers a
116
- // re-render — see slider.ts.
117
- get _theme() { return this.#_theme_accessor_storage; }
118
- set _theme(value) { this.#_theme_accessor_storage = value; }
119
105
  constructor(options) {
120
- super();
121
- __runInitializers(this, __theme_extraInitializers);
106
+ super(options.theme);
107
+ __runInitializers(this, _variant_extraInitializers);
122
108
  this.id = options.id ?? `toggle-${options.label.toLowerCase().replace(/\s+/g, "-")}`;
123
109
  this.label = options.label;
124
110
  this.on = options.on ?? false;
125
111
  this.variant = options.variant ?? "default";
126
112
  this.disabled = options.disabled ?? false;
127
- this._theme = options.theme ?? DEFAULT_TERMINAL_THEME;
128
113
  }
129
- setTheme(theme) { this._theme = theme; }
130
114
  // --- Event handlers ---
131
115
  handleKey(event) {
132
116
  if (this.disabled)
@@ -157,24 +141,17 @@ let Toggle = (() => {
157
141
  const indicator = this.on ? "[ON] " : "[OFF]";
158
142
  const text = `${indicator} ${this.label}`;
159
143
  if (this.disabled) {
160
- return [new Segment(text, new Style({ color: "#666666", bgcolor: "#333333", dim: true }))];
144
+ return [new Segment(text, new Style(this.disabledInk))];
161
145
  }
162
146
  const keys = VARIANT_KEYS[this.variant];
163
- const fg = this.resolvePalette(this.on ? keys.onFg : keys.offFg);
164
- const bg = this.resolvePalette(this.on ? keys.onBg : keys.offBg);
165
- return [new Segment(text, new Style({ color: fg, bgcolor: bg, underline: this.focused }))];
147
+ const colors = this.on ? ink(this.theme, keys.onFg, keys.onBg) : ink(this.theme, keys.offFg, keys.offBg);
148
+ return [new Segment(text, new Style({ ...colors, underline: this.focused }))];
166
149
  }
167
150
  measure(_options) {
168
151
  // [LAW:one-source-of-truth] cellLen — see button.ts.
169
152
  const width = 5 + 1 + cellLen(this.label);
170
153
  return { minimum: width, maximum: width };
171
154
  }
172
- // --- Palette resolution ---
173
- resolvePalette(key) {
174
- const rgba = this._theme.palette.get(key);
175
- // [LAW:no-defensive-null-guards] palette must contain all keys; missing is a construction bug.
176
- return ColorSpec.fromRgba(rgba);
177
- }
178
155
  };
179
156
  })();
180
157
  export { Toggle };
@@ -51,6 +51,8 @@ export declare class WidgetApp implements FrameSource {
51
51
  */
52
52
  refresh(): void;
53
53
  suspend(): Promise<void>;
54
+ /** Paint at the app's rate until the returned function is called, as `App.animate` does. */
55
+ animate(): Unsubscribe;
54
56
  /**
55
57
  * Hear every key. A `high` handler hears it before the focused widget, a
56
58
  * `normal` one after it — and after Tab has moved focus — and a handler
@@ -42,8 +42,10 @@ export class WidgetApp {
42
42
  constructor(options) {
43
43
  const { view } = options;
44
44
  this.reaction = new Reaction("WidgetApp", () => this.app.refresh());
45
- const root = { render: (renderOptions) => this.compose(view, renderOptions) };
46
- this.app = new App({ ...options, view: () => root });
45
+ this.app = new App({
46
+ ...options,
47
+ view: (t) => ({ render: (renderOptions) => this.compose(() => view(t), renderOptions) }),
48
+ });
47
49
  this.focus = new DefaultFocusManager(() => {
48
50
  const owners = ownersOn(this.app.frame);
49
51
  return this.drawn.filter((widget) => owners.has(widget));
@@ -102,6 +104,10 @@ export class WidgetApp {
102
104
  suspend() {
103
105
  return this.app.suspend();
104
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
+ }
105
111
  /**
106
112
  * Hear every key. A `high` handler hears it before the focused widget, a
107
113
  * `normal` one after it — and after Tab has moved focus — and a handler
@@ -176,12 +182,13 @@ function paintOverlay(lines, widget, options) {
176
182
  const at = footprintOf(lines, widget);
177
183
  if (!at)
178
184
  return;
179
- // An overlay floats over the frame; no region holds it.
180
- const overlay = widget.renderOverlay({ ...options, height: undefined });
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 });
181
189
  if (overlay === null)
182
190
  return;
183
- const width = Math.max(0, options.maxWidth - at.x);
184
- const rows = Segment.anchorLines(Segment.splitLines(overlay), widget, at.rows).map((line) => Segment.adjustLineLength(line, width, undefined, false));
191
+ const rows = Segment.anchorLines(Segment.splitLines(Segment.cropLines(overlay, width)), widget, at.rows);
185
192
  paintLines(lines, rows, at.x, at.y + at.rows);
186
193
  }
187
194
  // Paint `rows` into `lines` with its top-left cell at (x, y), growing `lines`
@@ -8,6 +8,7 @@
8
8
  * makeObservable call and subclasses add observables the same way.
9
9
  */
10
10
  import { Segment } from "../core/segment.js";
11
+ import { type Style } from "../core/style.js";
11
12
  import type { RenderOptions } from "../core/protocol.js";
12
13
  import type { InteractiveWidget, KeyEvent, WidgetMouseEvent, WidgetFocusEvent } from "./types.js";
13
14
  import type { Unsubscribe } from "../core/subscription.js";
@@ -32,6 +33,7 @@ export declare abstract class WidgetBase implements InteractiveWidget {
32
33
  protected emitChange(): void;
33
34
  protected emitSubmit(): void;
34
35
  render(options: RenderOptions): Iterable<Segment>;
36
+ protected get ground(): Style;
35
37
  protected abstract draw(options: RenderOptions): Iterable<Segment>;
36
38
  abstract measure(options: RenderOptions): {
37
39
  minimum: number;
@@ -43,6 +43,7 @@ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn,
43
43
  };
44
44
  import { observable, action } from "mobx";
45
45
  import { Segment } from "../core/segment.js";
46
+ import { NULL_STYLE } from "../core/style.js";
46
47
  let WidgetBase = (() => {
47
48
  let _instanceExtraInitializers = [];
48
49
  let _focused_decorators;
@@ -150,16 +151,22 @@ let WidgetBase = (() => {
150
151
  // anchor, so stamping has to follow every text layout `draw` does.
151
152
  // Every row is padded to the widest, so the widget owns its whole rectangle
152
153
  // and a click past the end of a short row still reaches it; every row ends
153
- // in a newline, so a blank last row survives the next `splitLines`.
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.
154
156
  // Reporting itself before it draws is what puts a widget ahead of the
155
157
  // widgets nested in it in document order — the order focus traverses.
156
158
  render(options) {
157
159
  options.onDraw?.(this);
158
160
  const drawn = Segment.splitLines(this.draw(options));
159
161
  const [width] = Segment.getShape(drawn);
160
- const lines = Segment.anchorLines(drawn.map((line) => Segment.adjustLineLength(line, width)), this);
162
+ const lines = Segment.anchorLines(drawn.map((line) => Segment.adjustLineLength(line, width, this.ground)), this);
161
163
  return lines.flatMap((line) => [...line, Segment.line()]);
162
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
+ }
163
170
  };
164
171
  })();
165
172
  export { WidgetBase };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@promptctl/rich-js",
3
- "version": "0.18.1",
3
+ "version": "0.19.0",
4
4
  "description": "Rich text and beautiful formatting in the terminal — a TypeScript port of Python's Rich",
5
5
  "repository": {
6
6
  "type": "git",
@@ -70,6 +70,7 @@
70
70
  "demo:build": "tsc -p tsconfig.demo.json",
71
71
  "sessions": "tsc -p tsconfig.demo.json && node dist-demo/examples/claude-sessions/index.js",
72
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",
73
74
  "viewport": "tsc -p tsconfig.demo.json && node dist-demo/examples/rich-viewport/index.js",
74
75
  "markup-plugins": "tsc -p tsconfig.demo.json && node dist-demo/examples/rich-markup-plugins/index.js",
75
76
  "demo-inputs": "tsc -p tsconfig.demo.json && node dist-demo/examples/rich-config/index.js",
@@ -100,16 +101,25 @@
100
101
  "node": ">=20"
101
102
  },
102
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",
103
110
  "@playwright/test": "^1.63.0",
104
111
  "@promptctl/go-template-js": "^0.10.0",
105
112
  "@types/markdown-it": "^14.2.0",
106
113
  "@types/node": "^26.5.1",
107
114
  "@types/semver": "^7.8.0",
115
+ "@xterm/headless": "^6.0.0",
108
116
  "markdown-it": "^14.3.2",
109
117
  "markdown-it-container": "^4.0.0",
110
118
  "mobx": "^7.0.3",
119
+ "node-inspect-extracted": "^3.3.3",
111
120
  "semver": "^7.8.5",
112
121
  "strip-ansi": "^7.2.0",
122
+ "sucrase": "^3.35.1",
113
123
  "typescript": "^6.0.3",
114
124
  "vite": "^8.3.0",
115
125
  "vitepress": "^1.6.4",