@flyingrobots/bijou-tui 2.1.0 → 3.0.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 (117) hide show
  1. package/README.md +64 -21
  2. package/dist/animate.d.ts +0 -2
  3. package/dist/animate.d.ts.map +1 -1
  4. package/dist/animate.js +17 -26
  5. package/dist/animate.js.map +1 -1
  6. package/dist/app-frame-actions.d.ts.map +1 -1
  7. package/dist/app-frame-actions.js +2 -2
  8. package/dist/app-frame-actions.js.map +1 -1
  9. package/dist/app-frame-render.d.ts +2 -0
  10. package/dist/app-frame-render.d.ts.map +1 -1
  11. package/dist/app-frame-render.js +49 -7
  12. package/dist/app-frame-render.js.map +1 -1
  13. package/dist/app-frame-types.js +4 -4
  14. package/dist/app-frame-types.js.map +1 -1
  15. package/dist/app-frame.d.ts +3 -1
  16. package/dist/app-frame.d.ts.map +1 -1
  17. package/dist/app-frame.js +61 -13
  18. package/dist/app-frame.js.map +1 -1
  19. package/dist/canvas.d.ts +37 -25
  20. package/dist/canvas.d.ts.map +1 -1
  21. package/dist/canvas.js +116 -30
  22. package/dist/canvas.js.map +1 -1
  23. package/dist/commands.js +2 -2
  24. package/dist/commands.js.map +1 -1
  25. package/dist/css/install.d.ts +4 -0
  26. package/dist/css/install.d.ts.map +1 -0
  27. package/dist/css/install.js +24 -0
  28. package/dist/css/install.js.map +1 -0
  29. package/dist/css/parser.d.ts +14 -0
  30. package/dist/css/parser.d.ts.map +1 -0
  31. package/dist/css/parser.js +92 -0
  32. package/dist/css/parser.js.map +1 -0
  33. package/dist/css/resolver.d.ts +36 -0
  34. package/dist/css/resolver.d.ts.map +1 -0
  35. package/dist/css/resolver.js +130 -0
  36. package/dist/css/resolver.js.map +1 -0
  37. package/dist/css/text-style.d.ts +17 -0
  38. package/dist/css/text-style.d.ts.map +1 -0
  39. package/dist/css/text-style.js +59 -0
  40. package/dist/css/text-style.js.map +1 -0
  41. package/dist/css/types.d.ts +27 -0
  42. package/dist/css/types.d.ts.map +1 -0
  43. package/dist/css/types.js +5 -0
  44. package/dist/css/types.js.map +1 -0
  45. package/dist/driver.d.ts +10 -2
  46. package/dist/driver.d.ts.map +1 -1
  47. package/dist/driver.js +25 -2
  48. package/dist/driver.js.map +1 -1
  49. package/dist/eventbus.d.ts +37 -3
  50. package/dist/eventbus.d.ts.map +1 -1
  51. package/dist/eventbus.js +91 -4
  52. package/dist/eventbus.js.map +1 -1
  53. package/dist/focus-area.d.ts +4 -0
  54. package/dist/focus-area.d.ts.map +1 -1
  55. package/dist/focus-area.js +11 -1
  56. package/dist/focus-area.js.map +1 -1
  57. package/dist/index.d.ts +10 -0
  58. package/dist/index.d.ts.map +1 -1
  59. package/dist/index.js +7 -0
  60. package/dist/index.js.map +1 -1
  61. package/dist/layout-v3.d.ts +10 -0
  62. package/dist/layout-v3.d.ts.map +1 -0
  63. package/dist/layout-v3.js +35 -0
  64. package/dist/layout-v3.js.map +1 -0
  65. package/dist/motion/motion.d.ts +14 -0
  66. package/dist/motion/motion.d.ts.map +1 -0
  67. package/dist/motion/motion.js +31 -0
  68. package/dist/motion/motion.js.map +1 -0
  69. package/dist/motion/reconciler.d.ts +15 -0
  70. package/dist/motion/reconciler.d.ts.map +1 -0
  71. package/dist/motion/reconciler.js +109 -0
  72. package/dist/motion/reconciler.js.map +1 -0
  73. package/dist/motion/types.d.ts +52 -0
  74. package/dist/motion/types.d.ts.map +1 -0
  75. package/dist/motion/types.js +2 -0
  76. package/dist/motion/types.js.map +1 -0
  77. package/dist/pipeline/middleware/css.d.ts +20 -0
  78. package/dist/pipeline/middleware/css.d.ts.map +1 -0
  79. package/dist/pipeline/middleware/css.js +41 -0
  80. package/dist/pipeline/middleware/css.js.map +1 -0
  81. package/dist/pipeline/middleware/grayscale.d.ts +9 -0
  82. package/dist/pipeline/middleware/grayscale.d.ts.map +1 -0
  83. package/dist/pipeline/middleware/grayscale.js +39 -0
  84. package/dist/pipeline/middleware/grayscale.js.map +1 -0
  85. package/dist/pipeline/middleware/motion.d.ts +6 -0
  86. package/dist/pipeline/middleware/motion.d.ts.map +1 -0
  87. package/dist/pipeline/middleware/motion.js +19 -0
  88. package/dist/pipeline/middleware/motion.js.map +1 -0
  89. package/dist/pipeline/middleware/paint.d.ts +6 -0
  90. package/dist/pipeline/middleware/paint.d.ts.map +1 -0
  91. package/dist/pipeline/middleware/paint.js +21 -0
  92. package/dist/pipeline/middleware/paint.js.map +1 -0
  93. package/dist/pipeline/pipeline.d.ts +56 -0
  94. package/dist/pipeline/pipeline.d.ts.map +1 -0
  95. package/dist/pipeline/pipeline.js +45 -0
  96. package/dist/pipeline/pipeline.js.map +1 -0
  97. package/dist/runtime.d.ts +1 -1
  98. package/dist/runtime.d.ts.map +1 -1
  99. package/dist/runtime.js +153 -8
  100. package/dist/runtime.js.map +1 -1
  101. package/dist/screen.d.ts +12 -1
  102. package/dist/screen.d.ts.map +1 -1
  103. package/dist/screen.js +14 -1
  104. package/dist/screen.js.map +1 -1
  105. package/dist/subapp/mount.d.ts +67 -0
  106. package/dist/subapp/mount.d.ts.map +1 -0
  107. package/dist/subapp/mount.js +60 -0
  108. package/dist/subapp/mount.js.map +1 -0
  109. package/dist/types.d.ts +45 -8
  110. package/dist/types.d.ts.map +1 -1
  111. package/dist/types.js +9 -0
  112. package/dist/types.js.map +1 -1
  113. package/dist/view-output.d.ts +15 -0
  114. package/dist/view-output.d.ts.map +1 -0
  115. package/dist/view-output.js +53 -0
  116. package/dist/view-output.js.map +1 -0
  117. package/package.json +2 -2
@@ -0,0 +1,52 @@
1
+ import type { LayoutRect } from '@flyingrobots/bijou';
2
+ import type { SpringConfig, SpringPreset } from '../spring.js';
3
+ /**
4
+ * Options for the motion() declarative wrapper.
5
+ */
6
+ export interface MotionOptions {
7
+ /**
8
+ * A unique key to track this component across frames.
9
+ * Essential for stable transitions during list re-ordering.
10
+ */
11
+ key: string;
12
+ /**
13
+ * Transition configuration.
14
+ * Defaults to a 'gentle' spring.
15
+ */
16
+ transition?: {
17
+ type?: 'spring' | 'tween';
18
+ spring?: SpringPreset | SpringConfig;
19
+ duration?: number;
20
+ };
21
+ /**
22
+ * Initial layout offsets or size overrides when the component first appears.
23
+ * e.g. { x: -10, y: 2, width: 0 }
24
+ */
25
+ initial?: Partial<LayoutRect>;
26
+ }
27
+ /**
28
+ * Internal state for a tracked motion component.
29
+ */
30
+ export interface TrackedMotion {
31
+ key: string;
32
+ /** The target rect we are moving towards. */
33
+ targetRect: LayoutRect;
34
+ /** The current interpolated rect being rendered. */
35
+ currentRect: LayoutRect;
36
+ /** Velocity for spring physics. */
37
+ velocity: {
38
+ x: number;
39
+ y: number;
40
+ w: number;
41
+ h: number;
42
+ };
43
+ /** Resolved motion mode for the current transition. */
44
+ mode: 'spring' | 'tween';
45
+ /** Elapsed time for tween transitions. */
46
+ tweenElapsedMs: number;
47
+ /** Starting rect for the active tween. */
48
+ tweenFromRect: LayoutRect;
49
+ /** Whether the motion has settled. */
50
+ done: boolean;
51
+ }
52
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/motion/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE/D;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ;;;OAGG;IACH,UAAU,CAAC,EAAE;QACX,IAAI,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC;QAC1B,MAAM,CAAC,EAAE,YAAY,GAAG,YAAY,CAAC;QACrC,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;IACF;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;CAC/B;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,6CAA6C;IAC7C,UAAU,EAAE,UAAU,CAAC;IACvB,oDAAoD;IACpD,WAAW,EAAE,UAAU,CAAC;IACxB,mCAAmC;IACnC,QAAQ,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACzD,uDAAuD;IACvD,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC;IACzB,0CAA0C;IAC1C,cAAc,EAAE,MAAM,CAAC;IACvB,0CAA0C;IAC1C,aAAa,EAAE,UAAU,CAAC;IAC1B,sCAAsC;IACtC,IAAI,EAAE,OAAO,CAAC;CACf"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/motion/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,20 @@
1
+ import type { RenderMiddleware } from '../pipeline.js';
2
+ /**
3
+ * Creates a BCSS styling middleware for the rendering pipeline.
4
+ *
5
+ * This middleware resolves CSS rules against component identities
6
+ * and applies them to the render state.
7
+ *
8
+ * @param css - The BCSS stylesheet string.
9
+ * @returns A RenderMiddleware.
10
+ */
11
+ export declare function bcssMiddleware(css: string): RenderMiddleware;
12
+ /**
13
+ * Helper for components to resolve their own BCSS styles.
14
+ */
15
+ export declare function useBCSS(state: any, identity: {
16
+ type: string;
17
+ id?: string;
18
+ classes?: string[];
19
+ }): import("../../css/resolver.js").ResolvedStyles;
20
+ //# sourceMappingURL=css.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"css.d.ts","sourceRoot":"","sources":["../../../src/pipeline/middleware/css.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAKvD;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB,CAoB5D;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,kDAU9F"}
@@ -0,0 +1,41 @@
1
+ import { parseBCSS } from '../../css/parser.js';
2
+ import { resolveStyles } from '../../css/resolver.js';
3
+ /**
4
+ * Creates a BCSS styling middleware for the rendering pipeline.
5
+ *
6
+ * This middleware resolves CSS rules against component identities
7
+ * and applies them to the render state.
8
+ *
9
+ * @param css - The BCSS stylesheet string.
10
+ * @returns A RenderMiddleware.
11
+ */
12
+ export function bcssMiddleware(css) {
13
+ let cachedSheet = null;
14
+ return (state, next) => {
15
+ if (!cachedSheet) {
16
+ cachedSheet = parseBCSS(css);
17
+ }
18
+ // Pass the sheet through the data bag so stages can use it
19
+ state.data['bcss_sheet'] = cachedSheet;
20
+ // The actual application of styles happens within the components
21
+ // by calling a helper that uses this sheet, OR we can do a global
22
+ // post-layout pass here.
23
+ // For now, we'll just ensure the sheet is available.
24
+ // Future: implement global layout override pass here.
25
+ next();
26
+ };
27
+ }
28
+ /**
29
+ * Helper for components to resolve their own BCSS styles.
30
+ */
31
+ export function useBCSS(state, identity) {
32
+ const sheet = state.data['bcss_sheet'];
33
+ if (!sheet)
34
+ return {};
35
+ const terminal = {
36
+ width: state.ctx.runtime.columns,
37
+ height: state.ctx.runtime.rows
38
+ };
39
+ return resolveStyles(identity, sheet, terminal);
40
+ }
41
+ //# sourceMappingURL=css.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"css.js","sourceRoot":"","sources":["../../../src/pipeline/middleware/css.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAEhD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEtD;;;;;;;;GAQG;AACH,MAAM,UAAU,cAAc,CAAC,GAAW;IACxC,IAAI,WAAW,GAAqB,IAAI,CAAC;IAEzC,OAAO,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QACrB,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,WAAW,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;QAC/B,CAAC;QAED,2DAA2D;QAC3D,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG,WAAW,CAAC;QAEvC,kEAAkE;QAClE,mEAAmE;QACnE,yBAAyB;QAEzB,qDAAqD;QACrD,sDAAsD;QAEtD,IAAI,EAAE,CAAC;IACT,CAAC,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,OAAO,CAAC,KAAU,EAAE,QAA2D;IAC7F,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,YAAY,CAA0B,CAAC;IAChE,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAC;IAEtB,MAAM,QAAQ,GAAG;QACf,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO;QAChC,MAAM,EAAE,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI;KAC/B,CAAC;IAEF,OAAO,aAAa,CAAC,QAAQ,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;AAClD,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { RenderMiddleware } from '../pipeline.js';
2
+ /**
3
+ * Creates a post-processing middleware that converts all colors in the
4
+ * target surface to grayscale luminance values.
5
+ *
6
+ * @returns A RenderMiddleware for the 'PostProcess' stage.
7
+ */
8
+ export declare function grayscaleFilter(): RenderMiddleware;
9
+ //# sourceMappingURL=grayscale.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grayscale.d.ts","sourceRoot":"","sources":["../../../src/pipeline/middleware/grayscale.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEvD;;;;;GAKG;AACH,wBAAgB,eAAe,IAAI,gBAAgB,CAiBlD"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Creates a post-processing middleware that converts all colors in the
3
+ * target surface to grayscale luminance values.
4
+ *
5
+ * @returns A RenderMiddleware for the 'PostProcess' stage.
6
+ */
7
+ export function grayscaleFilter() {
8
+ return (state, next) => {
9
+ const { targetSurface } = state;
10
+ for (let i = 0; i < targetSurface.cells.length; i++) {
11
+ const cell = targetSurface.cells[i];
12
+ if (cell.fg) {
13
+ cell.fg = hexToGrayscale(cell.fg);
14
+ }
15
+ if (cell.bg) {
16
+ cell.bg = hexToGrayscale(cell.bg);
17
+ }
18
+ }
19
+ next();
20
+ };
21
+ }
22
+ /** Convert a #RRGGBB hex string to a grayscale hex string based on luminance. */
23
+ function hexToGrayscale(hex) {
24
+ if (hex.length !== 7 || hex[0] !== '#')
25
+ return hex;
26
+ const digits = hex.slice(1);
27
+ if (!/^[0-9A-Fa-f]{6}$/.test(digits))
28
+ return hex;
29
+ const r = parseInt(digits.slice(0, 2), 16);
30
+ const g = parseInt(digits.slice(2, 4), 16);
31
+ const b = parseInt(digits.slice(4, 6), 16);
32
+ if (isNaN(r) || isNaN(g) || isNaN(b))
33
+ return hex;
34
+ // Standard luminance formula
35
+ const lum = Math.round(0.299 * r + 0.587 * g + 0.114 * b);
36
+ const hexLum = lum.toString(16).padStart(2, '0');
37
+ return `#${hexLum}${hexLum}${hexLum}`;
38
+ }
39
+ //# sourceMappingURL=grayscale.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grayscale.js","sourceRoot":"","sources":["../../../src/pipeline/middleware/grayscale.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,MAAM,UAAU,eAAe;IAC7B,OAAO,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QACrB,MAAM,EAAE,aAAa,EAAE,GAAG,KAAK,CAAC;QAEhC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,aAAa,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACpD,MAAM,IAAI,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC;YAErC,IAAI,IAAI,CAAC,EAAE,EAAE,CAAC;gBACZ,IAAI,CAAC,EAAE,GAAG,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACpC,CAAC;YACD,IAAI,IAAI,CAAC,EAAE,EAAE,CAAC;gBACZ,IAAI,CAAC,EAAE,GAAG,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACpC,CAAC;QACH,CAAC;QAED,IAAI,EAAE,CAAC;IACT,CAAC,CAAC;AACJ,CAAC;AAED,iFAAiF;AACjF,SAAS,cAAc,CAAC,GAAW;IACjC,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG;QAAE,OAAO,GAAG,CAAC;IAEnD,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC5B,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,MAAM,CAAC;QAAE,OAAO,GAAG,CAAC;IAEjD,MAAM,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC3C,MAAM,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC3C,MAAM,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAE3C,IAAI,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC;QAAE,OAAO,GAAG,CAAC;IAEjD,6BAA6B;IAC7B,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,GAAG,KAAK,GAAG,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC;IAE1D,MAAM,MAAM,GAAG,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACjD,OAAO,IAAI,MAAM,GAAG,MAAM,GAAG,MAAM,EAAE,CAAC;AACxC,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { RenderMiddleware } from '../pipeline.js';
2
+ /**
3
+ * Pipeline middleware that performs layout interpolation for motion components.
4
+ */
5
+ export declare function motionMiddleware(): RenderMiddleware;
6
+ //# sourceMappingURL=motion.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"motion.d.ts","sourceRoot":"","sources":["../../../src/pipeline/middleware/motion.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAMvD;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,gBAAgB,CAenD"}
@@ -0,0 +1,19 @@
1
+ import { MotionReconciler } from '../../motion/reconciler.js';
2
+ const reconciler = new MotionReconciler();
3
+ /**
4
+ * Pipeline middleware that performs layout interpolation for motion components.
5
+ */
6
+ export function motionMiddleware() {
7
+ return (state, next) => {
8
+ const { dt } = state;
9
+ // We assume the Layout pass has already populated targetSurface
10
+ // with a LayoutNode tree if it's not a raw Surface.
11
+ // In our simplified V3, App.view might return a LayoutNode.
12
+ const root = state.layoutRoot;
13
+ if (root) {
14
+ reconciler.reconcile(root, dt);
15
+ }
16
+ next();
17
+ };
18
+ }
19
+ //# sourceMappingURL=motion.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"motion.js","sourceRoot":"","sources":["../../../src/pipeline/middleware/motion.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAG9D,MAAM,UAAU,GAAG,IAAI,gBAAgB,EAAE,CAAC;AAE1C;;GAEG;AACH,MAAM,UAAU,gBAAgB;IAC9B,OAAO,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QACrB,MAAM,EAAE,EAAE,EAAE,GAAG,KAAK,CAAC;QAErB,iEAAiE;QACjE,oDAAoD;QACpD,4DAA4D;QAE5D,MAAM,IAAI,GAAI,KAAa,CAAC,UAAoC,CAAC;QACjE,IAAI,IAAI,EAAE,CAAC;YACT,UAAU,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACjC,CAAC;QAED,IAAI,EAAE,CAAC;IACT,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { RenderMiddleware } from '../pipeline.js';
2
+ /**
3
+ * Pipeline middleware that paints a LayoutNode tree onto the target surface.
4
+ */
5
+ export declare function paintMiddleware(): RenderMiddleware;
6
+ //# sourceMappingURL=paint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paint.d.ts","sourceRoot":"","sources":["../../../src/pipeline/middleware/paint.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAGvD;;GAEG;AACH,wBAAgB,eAAe,IAAI,gBAAgB,CAQlD"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Pipeline middleware that paints a LayoutNode tree onto the target surface.
3
+ */
4
+ export function paintMiddleware() {
5
+ return (state, next) => {
6
+ const root = state.layoutRoot;
7
+ if (root) {
8
+ paintNode(state.targetSurface, root);
9
+ }
10
+ next();
11
+ };
12
+ }
13
+ function paintNode(target, node) {
14
+ if (node.surface) {
15
+ target.blit(node.surface, node.rect.x, node.rect.y);
16
+ }
17
+ for (const child of node.children) {
18
+ paintNode(target, child);
19
+ }
20
+ }
21
+ //# sourceMappingURL=paint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paint.js","sourceRoot":"","sources":["../../../src/pipeline/middleware/paint.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,MAAM,UAAU,eAAe;IAC7B,OAAO,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QACrB,MAAM,IAAI,GAAI,KAAa,CAAC,UAAoC,CAAC;QACjE,IAAI,IAAI,EAAE,CAAC;YACT,SAAS,CAAC,KAAK,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;QACvC,CAAC;QACD,IAAI,EAAE,CAAC;IACT,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,SAAS,CAAC,MAAe,EAAE,IAAgB;IAClD,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;QACjB,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACtD,CAAC;IAED,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAClC,SAAS,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC3B,CAAC;AACH,CAAC"}
@@ -0,0 +1,56 @@
1
+ import type { Surface, BijouContext } from '@flyingrobots/bijou';
2
+ /**
3
+ * The state passed through the rendering pipeline.
4
+ */
5
+ export interface RenderState {
6
+ /** The model being rendered. */
7
+ model: any;
8
+ /** The Bijou context (ports, theme, mode). */
9
+ ctx: BijouContext;
10
+ /** Time delta in seconds since the last frame. */
11
+ dt: number;
12
+ /**
13
+ * The surface currently visible on the terminal.
14
+ * This should NOT be modified by pipeline stages.
15
+ */
16
+ readonly currentSurface: Surface;
17
+ /**
18
+ * The target surface being painted.
19
+ * Middleware in the `Paint` and `PostProcess` stages modify this surface.
20
+ */
21
+ targetSurface: Surface;
22
+ /**
23
+ * Layout geometry calculated during the `Layout` stage.
24
+ * Keyed by component ID.
25
+ */
26
+ layoutMap: Map<string, any>;
27
+ /**
28
+ * Custom data bag for middleware to pass state between stages.
29
+ */
30
+ data: Record<string, any>;
31
+ }
32
+ /**
33
+ * A middleware function in the rendering pipeline.
34
+ *
35
+ * @param state - The current render state.
36
+ * @param next - Function to yield control to the next middleware.
37
+ */
38
+ export type RenderMiddleware = (state: RenderState, next: () => void) => void | Promise<void>;
39
+ /**
40
+ * A stage in the rendering pipeline.
41
+ */
42
+ export type RenderStage = 'Layout' | 'Paint' | 'PostProcess' | 'Diff' | 'Output';
43
+ /**
44
+ * The rendering pipeline registry.
45
+ */
46
+ export interface RenderPipeline {
47
+ /** Register middleware for a specific stage. */
48
+ use(stage: RenderStage, middleware: RenderMiddleware): void;
49
+ /** Execute the entire pipeline for a given state. */
50
+ execute(state: RenderState): void;
51
+ }
52
+ /**
53
+ * Create a new programmable rendering pipeline.
54
+ */
55
+ export declare function createPipeline(): RenderPipeline;
56
+ //# sourceMappingURL=pipeline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../src/pipeline/pipeline.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEjE;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,gCAAgC;IAChC,KAAK,EAAE,GAAG,CAAC;IACX,8CAA8C;IAC9C,GAAG,EAAE,YAAY,CAAC;IAClB,kDAAkD;IAClD,EAAE,EAAE,MAAM,CAAC;IACX;;;OAGG;IACH,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC;;;OAGG;IACH,aAAa,EAAE,OAAO,CAAC;IACvB;;;OAGG;IACH,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC5B;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC3B;AAED;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,IAAI,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE9F;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,OAAO,GAAG,aAAa,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEjF;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,gDAAgD;IAChD,GAAG,CAAC,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC5D,qDAAqD;IACrD,OAAO,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CAAC;CACnC;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,cAAc,CA8C/C"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Create a new programmable rendering pipeline.
3
+ */
4
+ export function createPipeline() {
5
+ const stages = {
6
+ Layout: [],
7
+ Paint: [],
8
+ PostProcess: [],
9
+ Diff: [],
10
+ Output: [],
11
+ };
12
+ const STAGE_ORDER = ['Layout', 'Paint', 'PostProcess', 'Diff', 'Output'];
13
+ function use(stage, middleware) {
14
+ stages[stage].push(middleware);
15
+ }
16
+ function execute(state) {
17
+ // Flatten all middleware into a single execution chain based on stage order
18
+ const chain = [];
19
+ for (const stage of STAGE_ORDER) {
20
+ chain.push(...stages[stage]);
21
+ }
22
+ let index = 0;
23
+ const next = () => {
24
+ if (index < chain.length) {
25
+ const mw = chain[index++];
26
+ try {
27
+ // Note: The pipeline is currently synchronous. If a middleware returns a Promise,
28
+ // it won't block the TEA update loop, but it might resolve out-of-order.
29
+ // For now, we expect render middleware to be entirely synchronous.
30
+ void mw(state, next);
31
+ }
32
+ catch (err) {
33
+ // Log and continue if a specific middleware fails, preventing full crash
34
+ if (state.ctx.io.writeError) {
35
+ state.ctx.io.writeError(`[Pipeline Error] ${err instanceof Error ? err.stack : err}\n`);
36
+ }
37
+ next();
38
+ }
39
+ }
40
+ };
41
+ next();
42
+ }
43
+ return { use, execute };
44
+ }
45
+ //# sourceMappingURL=pipeline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipeline.js","sourceRoot":"","sources":["../../src/pipeline/pipeline.ts"],"names":[],"mappings":"AAwDA;;GAEG;AACH,MAAM,UAAU,cAAc;IAC5B,MAAM,MAAM,GAA4C;QACtD,MAAM,EAAE,EAAE;QACV,KAAK,EAAE,EAAE;QACT,WAAW,EAAE,EAAE;QACf,IAAI,EAAE,EAAE;QACR,MAAM,EAAE,EAAE;KACX,CAAC;IAEF,MAAM,WAAW,GAAkB,CAAC,QAAQ,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;IAExF,SAAS,GAAG,CAAC,KAAkB,EAAE,UAA4B;QAC3D,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACjC,CAAC;IAED,SAAS,OAAO,CAAC,KAAkB;QACjC,4EAA4E;QAC5E,MAAM,KAAK,GAAuB,EAAE,CAAC;QACrC,KAAK,MAAM,KAAK,IAAI,WAAW,EAAE,CAAC;YAChC,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QAC/B,CAAC;QAED,IAAI,KAAK,GAAG,CAAC,CAAC;QAEd,MAAM,IAAI,GAAG,GAAG,EAAE;YAChB,IAAI,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;gBACzB,MAAM,EAAE,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;gBAC1B,IAAI,CAAC;oBACH,kFAAkF;oBAClF,yEAAyE;oBACzE,mEAAmE;oBACnE,KAAK,EAAG,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;gBACxB,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,yEAAyE;oBACzE,IAAI,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,UAAU,EAAE,CAAC;wBAC5B,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,UAAU,CAAC,oBAAoB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;oBAC1F,CAAC;oBACD,IAAI,EAAE,CAAC;gBACT,CAAC;YACH,CAAC;QACH,CAAC,CAAC;QAEF,IAAI,EAAE,CAAC;IACT,CAAC;IAED,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC;AAC1B,CAAC"}
package/dist/runtime.d.ts CHANGED
@@ -15,5 +15,5 @@ import type { App, RunOptions } from './types.js';
15
15
  * @param options - Optional runtime configuration (context, alt screen, cursor, mouse).
16
16
  * @returns A promise that resolves when the application exits.
17
17
  */
18
- export declare function run<Model, M>(app: App<Model, M>, options?: RunOptions): Promise<void>;
18
+ export declare function run<Model, M>(app: App<Model, M>, options?: RunOptions<M>): Promise<void>;
19
19
  //# sourceMappingURL=runtime.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,GAAG,EAAO,UAAU,EAAa,MAAM,YAAY,CAAC;AAkBlE;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,GAAG,CAAC,KAAK,EAAE,CAAC,EAChC,GAAG,EAAE,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,EAClB,OAAO,CAAC,EAAE,UAAU,GACnB,OAAO,CAAC,IAAI,CAAC,CAoHf"}
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,GAAG,EAAO,UAAU,EAAa,MAAM,YAAY,CAAC;AAwBlE;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,GAAG,CAAC,KAAK,EAAE,CAAC,EAChC,GAAG,EAAE,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,EAClB,OAAO,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,GACtB,OAAO,CAAC,IAAI,CAAC,CAgQf"}
package/dist/runtime.js CHANGED
@@ -1,12 +1,18 @@
1
- import { getDefaultContext } from '@flyingrobots/bijou';
2
- import { isKeyMsg } from './types.js';
3
- import { enterScreen, exitScreen, renderFrame } from './screen.js';
1
+ import { getDefaultContext, createSurface, surfaceToString } from '@flyingrobots/bijou';
2
+ import { isKeyMsg, isPulseMsg, isResizeMsg } from './types.js';
3
+ import { clearAndHome, enterScreen, exitScreen, renderSurfaceFrame } from './screen.js';
4
4
  import { createEventBus } from './eventbus.js';
5
+ import { createPipeline } from './pipeline/pipeline.js';
6
+ import { bcssMiddleware } from './pipeline/middleware/css.js';
7
+ import { motionMiddleware } from './pipeline/middleware/motion.js';
8
+ import { paintMiddleware } from './pipeline/middleware/paint.js';
9
+ import { installBCSSResolver } from './css/install.js';
10
+ import { normalizeViewOutput, wrapViewOutputAsLayoutRoot } from './view-output.js';
5
11
  /**
6
12
  * Disable mouse reporting sequences that terminals may send.
7
13
  * Some terminals auto-enable mouse tracking in alt screen mode.
8
14
  */
9
- const DISABLE_MOUSE = '\x1b[?1000l\x1b[?1002l\x1b[?1003l\x1b[?1006l';
15
+ const DISABLE_MOUSE = '\x1b[?1000l\x1b[?1002l\x1b[?1006l';
10
16
  /**
11
17
  * Enable SGR mouse reporting.
12
18
  * 1000 = basic press/release, 1002 = button-event tracking (drag),
@@ -31,13 +37,27 @@ const ENABLE_MOUSE = '\x1b[?1000h\x1b[?1002h\x1b[?1006h';
31
37
  */
32
38
  export async function run(app, options) {
33
39
  const ctx = options?.ctx ?? getDefaultContext();
40
+ installRuntimeOverlay(ctx);
34
41
  const useAltScreen = options?.altScreen ?? true;
35
42
  const useHideCursor = options?.hideCursor ?? true;
36
43
  const useMouse = options?.mouse ?? false;
44
+ installBCSSResolver(ctx, options?.css);
37
45
  const [initModel, initCmds] = app.init();
38
46
  // Non-interactive: render once and return
39
47
  if (ctx.mode !== 'interactive') {
40
- ctx.io.write(app.view(initModel));
48
+ const viewOutput = app.view(initModel);
49
+ let output;
50
+ if (typeof viewOutput === 'string') {
51
+ output = viewOutput;
52
+ }
53
+ else {
54
+ const normalized = normalizeViewOutput(viewOutput, {
55
+ width: sanitizeDimension(ctx.runtime.columns),
56
+ height: sanitizeDimension(ctx.runtime.rows),
57
+ });
58
+ output = surfaceToString(normalized.surface, ctx.style);
59
+ }
60
+ ctx.io.write(output);
41
61
  return;
42
62
  }
43
63
  // Interactive mode
@@ -45,6 +65,10 @@ export async function run(app, options) {
45
65
  let running = true;
46
66
  let lastCtrlC = 0;
47
67
  let resolveQuit = null;
68
+ let currentDt = 0.016; // Default to 60fps for first frame
69
+ let fatalError = null;
70
+ // Double Buffering: track what is currently on screen
71
+ let currentSurface = createSurface(sanitizeDimension(ctx.runtime.columns), sanitizeDimension(ctx.runtime.rows));
48
72
  const bus = createEventBus({
49
73
  onCommandRejected(error) {
50
74
  const message = error instanceof Error
@@ -53,8 +77,46 @@ export async function run(app, options) {
53
77
  writeErrorLine(ctx.io, `[EventBus] Command rejected: ${message}\n`);
54
78
  },
55
79
  });
80
+ // Setup Programmable Rendering Pipeline
81
+ const pipeline = createPipeline();
82
+ // 1. Layout Logic Stage
83
+ pipeline.use('Layout', (state, next) => {
84
+ const viewOutput = app.view(state.model);
85
+ state.layoutRoot = wrapViewOutputAsLayoutRoot(viewOutput, {
86
+ width: sanitizeDimension(ctx.runtime.columns),
87
+ height: sanitizeDimension(ctx.runtime.rows),
88
+ });
89
+ next();
90
+ });
91
+ // 2. Motion Interpolation Stage
92
+ pipeline.use('Layout', motionMiddleware());
93
+ if (options?.css) {
94
+ pipeline.use('Layout', bcssMiddleware(options.css));
95
+ }
96
+ // 3. Paint Stage
97
+ pipeline.use('Paint', paintMiddleware());
98
+ // Add default Diff stage (double-buffering)
99
+ pipeline.use('Diff', (state, next) => {
100
+ renderSurfaceFrame(state.ctx.io, state.currentSurface, state.targetSurface, state.ctx.style);
101
+ next();
102
+ });
103
+ // Add default Output stage (sync current surface)
104
+ pipeline.use('Output', (state, next) => {
105
+ currentSurface = state.targetSurface.clone();
106
+ next();
107
+ });
108
+ options?.configurePipeline?.(pipeline);
109
+ // Register user middleware
110
+ if (options?.middlewares) {
111
+ for (const mw of options.middlewares) {
112
+ bus.use(mw);
113
+ }
114
+ }
56
115
  /** Tear down the run loop and signal the quit promise. */
57
- function shutdown() {
116
+ function shutdown(error) {
117
+ if (error !== undefined && fatalError === null) {
118
+ fatalError = error;
119
+ }
58
120
  if (!running)
59
121
  return;
60
122
  running = false;
@@ -72,11 +134,34 @@ export async function run(app, options) {
72
134
  ctx.io.write(DISABLE_MOUSE);
73
135
  }
74
136
  // Render helper
137
+ let renderRequested = false;
75
138
  /** Render the current model's view to the terminal. */
76
139
  function render() {
77
- if (!running)
140
+ if (!running || renderRequested)
78
141
  return;
79
- renderFrame(ctx.io, app.view(model));
142
+ renderRequested = true;
143
+ setTimeout(() => {
144
+ try {
145
+ const targetSurface = createSurface(sanitizeDimension(ctx.runtime.columns), sanitizeDimension(ctx.runtime.rows));
146
+ const renderState = {
147
+ model,
148
+ ctx,
149
+ dt: currentDt,
150
+ currentSurface,
151
+ targetSurface,
152
+ layoutMap: new Map(),
153
+ data: {},
154
+ };
155
+ pipeline.execute(renderState);
156
+ }
157
+ catch (error) {
158
+ writeErrorLine(ctx.io, `[Runtime Error] ${error instanceof Error ? (error.stack ?? error.message) : String(error)}\n`);
159
+ shutdown(error);
160
+ }
161
+ finally {
162
+ renderRequested = false;
163
+ }
164
+ }, 0);
80
165
  }
81
166
  // Execute commands through the bus
82
167
  /** Submit TEA commands to the event bus for async execution. */
@@ -89,10 +174,22 @@ export async function run(app, options) {
89
174
  bus.connectIO(ctx.io, { mouse: useMouse });
90
175
  // Handle quit signals from commands
91
176
  bus.onQuit(shutdown);
177
+ // Start heartbeat for animations
178
+ bus.startPulse(ctx.runtime.refreshRate);
92
179
  // Single subscription drives the entire update cycle
93
180
  bus.on((msg) => {
94
181
  if (!running)
95
182
  return;
183
+ // Track time delta from pulse
184
+ if (isPulseMsg(msg)) {
185
+ currentDt = msg.dt;
186
+ }
187
+ if (isResizeMsg(msg)) {
188
+ ctx.runtime.columns = sanitizeDimension(msg.columns);
189
+ ctx.runtime.rows = sanitizeDimension(msg.rows);
190
+ currentSurface = createSurface(ctx.runtime.columns, ctx.runtime.rows);
191
+ clearAndHome(ctx.io);
192
+ }
96
193
  // Double Ctrl+C force-quit
97
194
  if (isKeyMsg(msg) && msg.key === 'c' && msg.ctrl) {
98
195
  const now = Date.now();
@@ -116,6 +213,9 @@ export async function run(app, options) {
116
213
  };
117
214
  const [resizedModel, resizeCmds] = app.update(initialResize, model);
118
215
  model = resizedModel;
216
+ // After a potential resize in update, ensure currentSurface matches size
217
+ // to avoid diffing mismatched grids.
218
+ currentSurface = createSurface(sanitizeDimension(ctx.runtime.columns), sanitizeDimension(ctx.runtime.rows));
119
219
  // Initial render + startup commands
120
220
  render();
121
221
  executeCommands(initCmds);
@@ -126,7 +226,14 @@ export async function run(app, options) {
126
226
  if (!running)
127
227
  resolve();
128
228
  });
229
+ // Ensure any pending render is flushed before exiting
230
+ if (renderRequested) {
231
+ await new Promise((resolve) => {
232
+ setTimeout(resolve, 0);
233
+ });
234
+ }
129
235
  // Cleanup — bus disposes all I/O connections
236
+ bus.stopPulse();
130
237
  bus.dispose();
131
238
  if (useMouse) {
132
239
  ctx.io.write(DISABLE_MOUSE);
@@ -134,6 +241,9 @@ export async function run(app, options) {
134
241
  if (useAltScreen || useHideCursor) {
135
242
  exitScreen(ctx.io);
136
243
  }
244
+ if (fatalError != null) {
245
+ throw fatalError instanceof Error ? fatalError : new Error(String(fatalError));
246
+ }
137
247
  }
138
248
  /** Clamp a terminal dimension to a non-negative integer. */
139
249
  function sanitizeDimension(value) {
@@ -141,6 +251,41 @@ function sanitizeDimension(value) {
141
251
  return 0;
142
252
  return Math.max(0, Math.floor(value));
143
253
  }
254
+ function installRuntimeOverlay(ctx) {
255
+ const baseRuntime = ctx.runtime;
256
+ const state = {
257
+ columns: sanitizeDimension(baseRuntime.columns),
258
+ rows: sanitizeDimension(baseRuntime.rows),
259
+ };
260
+ const runtime = {
261
+ env(key) {
262
+ return baseRuntime.env(key);
263
+ },
264
+ get stdoutIsTTY() {
265
+ return baseRuntime.stdoutIsTTY;
266
+ },
267
+ get stdinIsTTY() {
268
+ return baseRuntime.stdinIsTTY;
269
+ },
270
+ get columns() {
271
+ return state.columns;
272
+ },
273
+ set columns(value) {
274
+ state.columns = sanitizeDimension(value);
275
+ },
276
+ get rows() {
277
+ return state.rows;
278
+ },
279
+ set rows(value) {
280
+ state.rows = sanitizeDimension(value);
281
+ },
282
+ get refreshRate() {
283
+ return baseRuntime.refreshRate;
284
+ },
285
+ };
286
+ ctx.runtime = runtime;
287
+ return runtime;
288
+ }
144
289
  /** Write an error message to stderr if available, otherwise stdout. */
145
290
  function writeErrorLine(io, data) {
146
291
  if (io.writeError != null) {