@flyingrobots/bijou-tui 3.1.0 → 4.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 (112) hide show
  1. package/LICENSE +159 -21
  2. package/README.md +181 -32
  3. package/dist/app-frame-actions.d.ts.map +1 -1
  4. package/dist/app-frame-actions.js +4 -5
  5. package/dist/app-frame-actions.js.map +1 -1
  6. package/dist/app-frame-render.d.ts +17 -11
  7. package/dist/app-frame-render.d.ts.map +1 -1
  8. package/dist/app-frame-render.js +100 -99
  9. package/dist/app-frame-render.js.map +1 -1
  10. package/dist/app-frame-types.d.ts +1 -1
  11. package/dist/app-frame-types.d.ts.map +1 -1
  12. package/dist/app-frame.d.ts +3 -1
  13. package/dist/app-frame.d.ts.map +1 -1
  14. package/dist/app-frame.js +89 -61
  15. package/dist/app-frame.js.map +1 -1
  16. package/dist/browsable-list.d.ts +20 -1
  17. package/dist/browsable-list.d.ts.map +1 -1
  18. package/dist/browsable-list.js +38 -10
  19. package/dist/browsable-list.js.map +1 -1
  20. package/dist/command-palette.d.ts +17 -1
  21. package/dist/command-palette.d.ts.map +1 -1
  22. package/dist/command-palette.js +45 -20
  23. package/dist/command-palette.js.map +1 -1
  24. package/dist/css/text-style.d.ts +2 -1
  25. package/dist/css/text-style.d.ts.map +1 -1
  26. package/dist/css/text-style.js +33 -0
  27. package/dist/css/text-style.js.map +1 -1
  28. package/dist/design-language.d.ts +49 -0
  29. package/dist/design-language.d.ts.map +1 -0
  30. package/dist/design-language.js +70 -0
  31. package/dist/design-language.js.map +1 -0
  32. package/dist/driver.d.ts +6 -1
  33. package/dist/driver.d.ts.map +1 -1
  34. package/dist/driver.js +3 -0
  35. package/dist/driver.js.map +1 -1
  36. package/dist/file-picker.d.ts +19 -1
  37. package/dist/file-picker.d.ts.map +1 -1
  38. package/dist/file-picker.js +47 -20
  39. package/dist/file-picker.js.map +1 -1
  40. package/dist/flex.d.ts +35 -1
  41. package/dist/flex.d.ts.map +1 -1
  42. package/dist/flex.js +127 -1
  43. package/dist/flex.js.map +1 -1
  44. package/dist/focus-area.d.ts +13 -1
  45. package/dist/focus-area.d.ts.map +1 -1
  46. package/dist/focus-area.js +89 -12
  47. package/dist/focus-area.js.map +1 -1
  48. package/dist/grid.d.ts +10 -0
  49. package/dist/grid.d.ts.map +1 -1
  50. package/dist/grid.js +25 -0
  51. package/dist/grid.js.map +1 -1
  52. package/dist/help.d.ts +41 -0
  53. package/dist/help.d.ts.map +1 -1
  54. package/dist/help.js +50 -0
  55. package/dist/help.js.map +1 -1
  56. package/dist/index.d.ts +17 -16
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.js +17 -15
  59. package/dist/index.js.map +1 -1
  60. package/dist/layout-node-surface.d.ts +9 -0
  61. package/dist/layout-node-surface.d.ts.map +1 -0
  62. package/dist/layout-node-surface.js +42 -0
  63. package/dist/layout-node-surface.js.map +1 -0
  64. package/dist/navigable-table.d.ts +16 -1
  65. package/dist/navigable-table.d.ts.map +1 -1
  66. package/dist/navigable-table.js +32 -1
  67. package/dist/navigable-table.js.map +1 -1
  68. package/dist/notification.d.ts +16 -0
  69. package/dist/notification.d.ts.map +1 -1
  70. package/dist/notification.js +163 -35
  71. package/dist/notification.js.map +1 -1
  72. package/dist/overlay.d.ts +21 -8
  73. package/dist/overlay.d.ts.map +1 -1
  74. package/dist/overlay.js +236 -138
  75. package/dist/overlay.js.map +1 -1
  76. package/dist/pager.d.ts +16 -0
  77. package/dist/pager.d.ts.map +1 -1
  78. package/dist/pager.js +61 -1
  79. package/dist/pager.js.map +1 -1
  80. package/dist/runtime.d.ts.map +1 -1
  81. package/dist/runtime.js +6 -12
  82. package/dist/runtime.js.map +1 -1
  83. package/dist/split-pane.d.ts +12 -1
  84. package/dist/split-pane.d.ts.map +1 -1
  85. package/dist/split-pane.js +31 -1
  86. package/dist/split-pane.js.map +1 -1
  87. package/dist/status-bar.d.ts +12 -0
  88. package/dist/status-bar.d.ts.map +1 -1
  89. package/dist/status-bar.js +45 -16
  90. package/dist/status-bar.js.map +1 -1
  91. package/dist/surface-layout.d.ts +19 -0
  92. package/dist/surface-layout.d.ts.map +1 -0
  93. package/dist/surface-layout.js +87 -0
  94. package/dist/surface-layout.js.map +1 -0
  95. package/dist/transition-shaders.d.ts +10 -8
  96. package/dist/transition-shaders.d.ts.map +1 -1
  97. package/dist/transition-shaders.js +65 -19
  98. package/dist/transition-shaders.js.map +1 -1
  99. package/dist/types.d.ts +2 -2
  100. package/dist/view-output.d.ts +4 -4
  101. package/dist/view-output.d.ts.map +1 -1
  102. package/dist/view-output.js +24 -26
  103. package/dist/view-output.js.map +1 -1
  104. package/dist/viewport.d.ts +30 -1
  105. package/dist/viewport.d.ts.map +1 -1
  106. package/dist/viewport.js +77 -1
  107. package/dist/viewport.js.map +1 -1
  108. package/package.json +3 -3
  109. package/dist/layout-v3.d.ts +0 -10
  110. package/dist/layout-v3.d.ts.map +0 -1
  111. package/dist/layout-v3.js +0 -35
  112. package/dist/layout-v3.js.map +0 -1
package/LICENSE CHANGED
@@ -1,21 +1,159 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 Flying Robots
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction, and
10
+ distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by the copyright
13
+ owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all other entities
16
+ that control, are controlled by, or are under common control with that entity.
17
+ For the purposes of this definition, "control" means (i) the power, direct or
18
+ indirect, to cause the direction or management of such entity, whether by
19
+ contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
20
+ outstanding shares, or (iii) beneficial ownership of such entity.
21
+
22
+ "You" (or "Your") shall mean an individual or Legal Entity exercising
23
+ permissions granted by this License.
24
+
25
+ "Source" form shall mean the preferred form for making modifications, including
26
+ but not limited to software source code, documentation source, and configuration
27
+ files.
28
+
29
+ "Object" form shall mean any form resulting from mechanical transformation or
30
+ translation of a Source form, including but not limited to compiled object code,
31
+ generated documentation, and conversions to other media types.
32
+
33
+ "Work" shall mean the work of authorship, whether in Source or Object form, made
34
+ available under the License, as indicated by a copyright notice that is included
35
+ in or attached to the work (an example is provided in the Appendix below).
36
+
37
+ "Derivative Works" shall mean any work, whether in Source or Object form, that
38
+ is based on (or derived from) the Work and for which the editorial revisions,
39
+ annotations, elaborations, or other modifications represent, as a whole, an
40
+ original work of authorship. For the purposes of this License, Derivative Works
41
+ shall not include works that remain separable from, or merely link (or bind by
42
+ name) to the interfaces of, the Work and Derivative Works thereof.
43
+
44
+ "Contribution" shall mean any work of authorship, including the original version
45
+ of the Work and any modifications or additions to that Work or Derivative Works
46
+ thereof, that is intentionally submitted to Licensor for inclusion in the Work
47
+ by the copyright owner or by an individual or Legal Entity authorized to submit
48
+ on behalf of the copyright owner. For the purposes of this definition,
49
+ "submitted" means any form of electronic, verbal, or written communication sent
50
+ to the Licensor or its representatives, including but not limited to
51
+ communication on electronic mailing lists, source code control systems, and
52
+ issue tracking systems that are managed by, or on behalf of, the Licensor for
53
+ the purpose of discussing and improving the Work, but excluding communication
54
+ that is conspicuously marked or otherwise designated in writing by the copyright
55
+ owner as "Not a Contribution."
56
+
57
+ "Contributor" shall mean Licensor and any individual or Legal Entity on behalf
58
+ of whom a Contribution has been received by Licensor and subsequently
59
+ incorporated within the Work.
60
+
61
+ 2. Grant of Copyright License. Subject to the terms and conditions of this
62
+ License, each Contributor hereby grants to You a perpetual, worldwide,
63
+ non-exclusive, no-charge, royalty-free, irrevocable copyright license to
64
+ reproduce, prepare Derivative Works of, publicly display, publicly perform,
65
+ sublicense, and distribute the Work and such Derivative Works in Source or
66
+ Object form.
67
+
68
+ 3. Grant of Patent License. Subject to the terms and conditions of this License,
69
+ each Contributor hereby grants to You a perpetual, worldwide, non-exclusive,
70
+ no-charge, royalty-free, irrevocable (except as stated in this section) patent
71
+ license to make, have made, use, offer to sell, sell, import, and otherwise
72
+ transfer the Work, where such license applies only to those patent claims
73
+ licensable by such Contributor that are necessarily infringed by their
74
+ Contribution(s) alone or by combination of their Contribution(s) with the Work
75
+ to which such Contribution(s) was submitted. If You institute patent litigation
76
+ against any entity (including a cross-claim or counterclaim in a lawsuit)
77
+ alleging that the Work or a Contribution incorporated within the Work
78
+ constitutes direct or contributory patent infringement, then any patent licenses
79
+ granted to You under this License for that Work shall terminate as of the date
80
+ such litigation is filed.
81
+
82
+ 4. Redistribution. You may reproduce and distribute copies of the Work or
83
+ Derivative Works thereof in any medium, with or without modifications, and in
84
+ Source or Object form, provided that You meet the following conditions:
85
+
86
+ (a) You must give any other recipients of the Work or Derivative Works a copy of
87
+ this License; and
88
+
89
+ (b) You must cause any modified files to carry prominent notices stating that You
90
+ changed the files; and
91
+
92
+ (c) You must retain, in the Source form of any Derivative Works that You
93
+ distribute, all copyright, patent, trademark, and attribution notices from the
94
+ Source form of the Work, excluding those notices that do not pertain to any part
95
+ of the Derivative Works; and
96
+
97
+ (d) If the Work includes a "NOTICE" text file as part of its distribution, then
98
+ any Derivative Works that You distribute must include a readable copy of the
99
+ attribution notices contained within such NOTICE file, excluding those notices
100
+ that do not pertain to any part of the Derivative Works, in at least one of the
101
+ following places: within a NOTICE text file distributed as part of the
102
+ Derivative Works; within the Source form or documentation, if provided along
103
+ with the Derivative Works; or, within a display generated by the Derivative
104
+ Works, if and wherever such third-party notices normally appear. The contents of
105
+ the NOTICE file are for informational purposes only and do not modify the
106
+ License. You may add Your own attribution notices within Derivative Works that
107
+ You distribute, alongside or as an addendum to the NOTICE text from the Work,
108
+ provided that such additional attribution notices cannot be construed as
109
+ modifying the License.
110
+
111
+ You may add Your own copyright statement to Your modifications and may provide
112
+ additional or different license terms and conditions for use, reproduction, or
113
+ distribution of Your modifications, or for any such Derivative Works as a whole,
114
+ provided Your use, reproduction, and distribution of the Work otherwise complies
115
+ with the conditions stated in this License.
116
+
117
+ 5. Submission of Contributions. Unless You explicitly state otherwise, any
118
+ Contribution intentionally submitted for inclusion in the Work by You to the
119
+ Licensor shall be under the terms and conditions of this License, without any
120
+ additional terms or conditions. Notwithstanding the above, nothing herein shall
121
+ supersede or modify the terms of any separate license agreement you may have
122
+ executed with Licensor regarding such Contributions.
123
+
124
+ 6. Trademarks. This License does not grant permission to use the trade names,
125
+ trademarks, service marks, or product names of the Licensor, except as required
126
+ for reasonable and customary use in describing the origin of the Work and
127
+ reproducing the content of the NOTICE file.
128
+
129
+ 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in
130
+ writing, Licensor provides the Work (and each Contributor provides its
131
+ Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
132
+ KIND, either express or implied, including, without limitation, any warranties
133
+ or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
134
+ PARTICULAR PURPOSE. You are solely responsible for determining the
135
+ appropriateness of using or redistributing the Work and assume any risks
136
+ associated with Your exercise of permissions under this License.
137
+
138
+ 8. Limitation of Liability. In no event and under no legal theory, whether in
139
+ tort (including negligence), contract, or otherwise, unless required by
140
+ applicable law (such as deliberate and grossly negligent acts) or agreed to in
141
+ writing, shall any Contributor be liable to You for damages, including any
142
+ direct, indirect, special, incidental, or consequential damages of any
143
+ character arising as a result of this License or out of the use or inability to
144
+ use the Work (including but not limited to damages for loss of goodwill, work
145
+ stoppage, computer failure or malfunction, or any and all other commercial
146
+ damages or losses), even if such Contributor has been advised of the
147
+ possibility of such damages.
148
+
149
+ 9. Accepting Warranty or Additional Liability. While redistributing the Work or
150
+ Derivative Works thereof, You may choose to offer, and charge a fee for,
151
+ acceptance of support, warranty, indemnity, or other liability obligations
152
+ and/or rights consistent with this License. However, in accepting such
153
+ obligations, You may act only on Your own behalf and on Your sole
154
+ responsibility, not on behalf of any other Contributor, and only if You agree to
155
+ indemnify, defend, and hold each Contributor harmless for any liability incurred
156
+ by, or claims asserted against, such Contributor by reason of your accepting any
157
+ such warranty or additional liability.
158
+
159
+ END OF TERMS AND CONDITIONS
package/README.md CHANGED
@@ -9,7 +9,7 @@ The high-fidelity TEA runtime for Bijou.
9
9
  The TUI package has been completely overhauled in v3.0.0 to operate as a true graphics engine.
10
10
 
11
11
  ### 🌟 What's New
12
- - **Honest view contract:** `App.view` and framed pane renderers now speak `ViewOutput` (`string | Surface | LayoutNode`). Strings still work, but they are the legacy compatibility path.
12
+ - **Pure view contract:** `App.view` and framed pane renderers now speak `ViewOutput` (`Surface | LayoutNode`).
13
13
  - **Programmable Rendering Pipeline:** The TEA `view` output is now processed through a 5-stage middleware pipeline (`Layout -> Paint -> PostProcess -> Diff -> Output`). Add custom fragment shaders or logging middleware effortlessly.
14
14
  - **Fractal TEA (Sub-Apps):** Compose nested apps with `initSubApp()`, `updateSubApp()`, `mount()`, and `mapCmds()` instead of flattening everything into one update loop.
15
15
  - **Bijou CSS (BCSS):** Style supported V3 surface components and frame shell regions with type/class/id selectors, `var()` token lookups, and terminal-aware media queries (`@media (width < 80)`). This is not yet a global cascade across arbitrary layout nodes.
@@ -22,7 +22,7 @@ The TUI package has been completely overhauled in v3.0.0 to operate as a true gr
22
22
  npm install @flyingrobots/bijou@3.0.0 @flyingrobots/bijou-node@3.0.0 @flyingrobots/bijou-tui@3.0.0
23
23
  ```
24
24
 
25
- If you are upgrading an existing app, see [`../../docs/MIGRATING_TO_V3.md`](../../docs/MIGRATING_TO_V3.md).
25
+ If you are upgrading an existing app, see [`../../docs/MIGRATING_TO_V4.md`](../../docs/MIGRATING_TO_V4.md).
26
26
 
27
27
  ## Quick Start (V3 Sub-App Composition)
28
28
 
@@ -73,6 +73,7 @@ run(app);
73
73
 
74
74
  ```typescript
75
75
  import { initDefaultContext } from '@flyingrobots/bijou-node';
76
+ import { stringToSurface } from '@flyingrobots/bijou';
76
77
  import { run, quit, type App, isKeyMsg } from '@flyingrobots/bijou-tui';
77
78
 
78
79
  initDefaultContext();
@@ -91,7 +92,11 @@ const app: App<Model> = {
91
92
  return [model, []];
92
93
  },
93
94
 
94
- view: (model) => `Count: ${model.count}\n\nPress +/- to change, q to quit`,
95
+ view: (model) => {
96
+ const text = `Count: ${model.count}\n\nPress +/- to change, q to quit`;
97
+ const lines = text.split('\n');
98
+ return stringToSurface(text, Math.max(1, ...lines.map((line) => line.length)), lines.length);
99
+ },
95
100
  };
96
101
 
97
102
  run(app);
@@ -116,6 +121,37 @@ In non-interactive modes, there is no normal interactive event loop.
116
121
  - **App shell**: `createFramedApp()` for tabs/help/chrome/pane-focus boilerplate with optional command palette.
117
122
  - **Stateful building blocks**: navigable table, browsable list, file picker, focus area, and DAG pane with vim-friendly keymaps.
118
123
 
124
+ ## Choosing Component Families
125
+
126
+ ### Overlays and interruption
127
+
128
+ - Use `toast()` when you are composing a single transient overlay directly.
129
+ - Use the notification system when the app needs stacking, placement, actions, routing, or history.
130
+ - Use `drawer()` when the user should keep the main surface visible while working in supplemental detail.
131
+ - Use `modal()` when background shortcuts and pointer actions should be blocked.
132
+ - Use `tooltip()` only for tiny local explanation, not for decisions or scrollable content.
133
+ - If the overlay needs embedded component surfaces or multiple real rows, keep it on the structured `Surface` path with `compositeSurface()`.
134
+
135
+ ### Collection interaction
136
+
137
+ - Use core `table()` or `tableSurface()` for passive comparison.
138
+ - Use `navigableTable()` when row/cell focus and keyboard traversal are the real job.
139
+ - Use `browsableList()` when the content is one-dimensional and description-led rather than grid-oriented.
140
+ - Use `commandPaletteSurface()` when the outcome is an action or navigation target, not a stored form value.
141
+ - If users are really choosing persisted values, keep that work in core `select()` / `filter()` / `multiselect()` instead of turning the palette into a value picker.
142
+
143
+ ### Shell and workspace layout
144
+
145
+ - Use `createFramedApp()` when the app has multiple destinations, overlays, and workspace state that should be standardized.
146
+ - Use `splitPane()` when the user benefits from primary-versus-secondary context or side-by-side comparison.
147
+ - Use `grid()` when multiple stable regions deserve simultaneous visibility.
148
+ - Use `statusBarSurface()` when shell chrome already lives on the structured `Surface` path; keep `statusBar()` for explicit text output.
149
+ - Use `helpShortSurface()` or `helpViewSurface()` when shortcut guidance stays inside the rich shell; keep `helpShort()` / `helpView()` for explicit text output.
150
+ - Use `commandPaletteSurface()` for action discovery and navigation inside the shell, not as a substitute value picker.
151
+ - Use notifications for events and follow-up, not as a replacement for the status rail.
152
+ - Keep status rails concise and global; explanatory text belongs in the page, not in shell chrome.
153
+ - Mouse is enhancement, not baseline. Overlay layers should consume pointer input before shell chrome or page content, and every click target should mirror an existing keyboard path.
154
+
119
155
  ## Animation
120
156
 
121
157
  ### Spring Physics
@@ -174,6 +210,25 @@ const fired = tl.firedCallbacks(prev, tlState); // ['onReady']
174
210
 
175
211
  Position syntax: `'<'` (parallel), `'+=N'` (gap), `'-=N'` (overlap), `'<+=N'` (offset from previous start), absolute ms, `'label'`, `'label+=N'`.
176
212
 
213
+ ## Transition Shaders
214
+
215
+ Custom page transitions are surface-native in v4. Shader functions decide whether each cell shows the previous page or next page, and may optionally provide override data for that cell.
216
+
217
+ ```typescript
218
+ import { type TransitionShaderFn } from '@flyingrobots/bijou-tui';
219
+
220
+ const shimmer: TransitionShaderFn = ({ progress, x, width }) => {
221
+ const edge = Math.floor(progress * width);
222
+ if (x < edge) return { showNext: true };
223
+ if (x === edge) return { showNext: false, overrideChar: '░', overrideRole: 'marker' };
224
+ return { showNext: false };
225
+ };
226
+ ```
227
+
228
+ Use `overrideChar` when the base cell styling should stay intact, `overrideCell` when the shader needs full fg/bg/modifier control, and `overrideRole` to tell combinators whether an override is ambient (`'decoration'`) or positional (`'marker'`).
229
+
230
+ Use transition shaders to reinforce workspace change, not as default spectacle. If the effect makes the new page harder to read or hides state meaning that should remain explicit in static or accessible modes, it is the wrong transition.
231
+
177
232
  ## Layout
178
233
 
179
234
  ### Flexbox
@@ -200,32 +255,55 @@ Children can be **render functions** `(width, height) => string` — they receiv
200
255
  ### Viewport
201
256
 
202
257
  ```typescript
203
- import { viewport, createScrollState, scrollBy, pageDown } from '@flyingrobots/bijou-tui';
258
+ import { viewportSurface, createScrollStateForContent, scrollBy, pageDown } from '@flyingrobots/bijou-tui';
259
+ import { boxSurface } from '@flyingrobots/bijou';
204
260
 
205
- let scroll = createScrollState(content, viewportHeight);
261
+ const content = boxSurface(longText, { width: 72 });
262
+ let scroll = createScrollStateForContent(content, viewportHeight);
206
263
 
207
- // Render visible window with scrollbar
208
- const view = viewport({ width: 60, height: 20, content, scrollY: scroll.y });
264
+ // Mask the content to a visible window with scrollbar
265
+ const view = viewportSurface({ width: 60, height: 20, content, scrollY: scroll.y });
209
266
 
210
267
  // Handle scroll keys
211
268
  scroll = scrollBy(scroll, 1); // down one line
212
269
  scroll = pageDown(scroll); // down one page
213
270
  ```
214
271
 
272
+ Treat `viewportSurface()` as the canonical scroll mask for rich TUI composition. Keep `viewport()` for explicit text-lowering paths.
273
+
215
274
  ### Basic Layout
216
275
 
217
276
  ```typescript
218
- import { vstack, hstack } from '@flyingrobots/bijou-tui';
277
+ import {
278
+ hstack,
279
+ hstackSurface,
280
+ place,
281
+ placeSurface,
282
+ vstack,
283
+ vstackSurface,
284
+ } from '@flyingrobots/bijou-tui';
219
285
 
220
- vstack(header, content, footer); // vertical stack
221
- hstack(2, leftPanel, rightPanel); // side-by-side with gap
286
+ vstack(header, content, footer); // explicit text-lowering path
287
+ hstack(2, leftPanel, rightPanel); // explicit text-lowering path
288
+ place('Title', { width: 20, height: 3 }); // text placement
289
+
290
+ vstackSurface(headerSurface, bodySurface); // structured surface stack
291
+ hstackSurface(2, navSurface, mainSurface); // structured horizontal stack
292
+ placeSurface(dialogSurface, { // structured placement/alignment
293
+ width: cols,
294
+ height: rows,
295
+ hAlign: 'center',
296
+ vAlign: 'middle',
297
+ });
222
298
  ```
223
299
 
300
+ Prefer `vstackSurface()` / `hstackSurface()` / `placeSurface()` when the view is already composed from `Surface` values. Keep `vstack()` / `hstack()` / `place()` for explicit text composition or lowering paths.
301
+
224
302
  ### Split Pane
225
303
 
226
304
  ```typescript
227
305
  import {
228
- createSplitPaneState, splitPane, splitPaneResizeBy, splitPaneFocusNext,
306
+ createSplitPaneState, splitPaneSurface, splitPaneResizeBy, splitPaneFocusNext,
229
307
  } from '@flyingrobots/bijou-tui';
230
308
 
231
309
  let state = createSplitPaneState({ ratio: 0.35 });
@@ -235,7 +313,7 @@ state = splitPaneResizeBy(state, 2, { total: cols, minA: 16, minB: 16 });
235
313
  state = splitPaneFocusNext(state);
236
314
 
237
315
  // in view:
238
- const output = splitPane(state, {
316
+ const output = splitPaneSurface(state, {
239
317
  direction: 'row',
240
318
  width: cols,
241
319
  height: rows,
@@ -246,12 +324,14 @@ const output = splitPane(state, {
246
324
  });
247
325
  ```
248
326
 
327
+ Prefer `splitPaneSurface()` when the panes are already structured `Surface` views. Keep `splitPane()` for explicit text composition or lowering paths.
328
+
249
329
  ### Grid
250
330
 
251
331
  ```typescript
252
- import { grid } from '@flyingrobots/bijou-tui';
332
+ import { gridSurface } from '@flyingrobots/bijou-tui';
253
333
 
254
- const output = grid({
334
+ const output = gridSurface({
255
335
  width: cols,
256
336
  height: rows,
257
337
  columns: [24, '1fr'],
@@ -271,6 +351,8 @@ const output = grid({
271
351
  });
272
352
  ```
273
353
 
354
+ Prefer `gridSurface()` when the regions are already structured `Surface` views. Keep `grid()` for explicit text composition or lowering paths.
355
+
274
356
  ## Resize Handling
275
357
 
276
358
  Terminal resize events are dispatched automatically as `ResizeMsg`:
@@ -341,11 +423,21 @@ kb.enable('Quit');
341
423
  Auto-generate help text from registered bindings:
342
424
 
343
425
  ```typescript
344
- import { helpView, helpShort, helpFor } from '@flyingrobots/bijou-tui';
426
+ import {
427
+ helpView,
428
+ helpViewSurface,
429
+ helpShort,
430
+ helpShortSurface,
431
+ helpFor,
432
+ helpForSurface,
433
+ } from '@flyingrobots/bijou-tui';
345
434
 
346
- helpView(kb); // full grouped multi-line help
347
- helpShort(kb); // "q Quit • ? Help • Ctrl+c Force quit • j Down • k Up"
348
- helpFor(kb, 'Nav'); // only Navigation group
435
+ helpView(kb); // full grouped multi-line help
436
+ helpShort(kb); // "q Quit • ? Help • Ctrl+c Force quit • j Down • k Up"
437
+ helpFor(kb, 'Nav'); // only Navigation group
438
+ helpShortSurface(kb, { width: 48 }); // shell hint that stays on the Surface path
439
+ helpViewSurface(kb, { width: 48 }); // grouped help as a Surface
440
+ helpForSurface(kb, 'Nav', { width: 48 });
349
441
  ```
350
442
 
351
443
  ### Input Stack
@@ -377,7 +469,7 @@ stack.remove(modalId);
377
469
  Paint overlays (modals, toasts) on top of existing content:
378
470
 
379
471
  ```typescript
380
- import { composite, modal, toast } from '@flyingrobots/bijou-tui';
472
+ import { compositeSurface, modal, toast } from '@flyingrobots/bijou-tui';
381
473
 
382
474
  // Create a centered dialog
383
475
  const dialog = modal({
@@ -398,12 +490,17 @@ const notification = toast({
398
490
  });
399
491
 
400
492
  // Paint overlays onto background content
401
- const output = composite(backgroundView, [dialog, notification], { dim: true });
493
+ const output = compositeSurface(backgroundSurface, [dialog, notification], { dim: true });
402
494
  ```
403
495
 
404
- Each overlay is a `{ content, row, col }` object. `composite()` splices them onto the background using painter's algorithm (last overlay wins on overlap). The `dim` option fades the background with ANSI dim.
496
+ Each overlay now exposes both `surface` and `content` forms. Prefer `compositeSurface()` when your app is already on the surface-native path. Keep the string-oriented `composite()` path for explicit lowering boundaries, not as the default mental model. The `dim` option fades the background with ANSI dim.
497
+
498
+ `modal().body`, `modal().hint`, `drawer().content`, and `tooltip().content` accept either plain strings or structured `Surface` content. Use surfaces when the overlay needs real rows, embedded component surfaces, or richer composition inside the interrupting layer.
499
+
500
+ Reach for `toast()` when the app is composing a one-off overlay directly. Reach for the notification system when stacking, actions, routing, or history matter. The notification lab in `examples/notifications` is the canonical higher-level example.
405
501
 
406
502
  `drawer()` now supports `left`/`right`/`top`/`bottom` anchors and optional `region` mounting for panel-scoped overlays.
503
+ Use `drawer()` when the user should keep the main task visible while consulting or editing supplemental context. Use `modal()` when the user must stop and decide. Use `tooltip()` only for tiny local explanation, not for commands or scrollable content.
407
504
 
408
505
  ## App Frame
409
506
 
@@ -414,10 +511,17 @@ Each overlay is a `{ content, row, col }` object. `composite()` splices them ont
414
511
  - frame help (`?`) and optional command palette (`ctrl+p` / `:`)
415
512
  - overlay factory with pane rects for panel-scoped drawers/modals
416
513
 
417
- Pane renderers may return a legacy string, a `Surface`, or a `LayoutNode`. The shell normalizes those outputs into the framed scroll/focus path for you.
514
+ Pane renderers return a `Surface` or a `LayoutNode`. The shell normalizes those outputs into the framed scroll/focus path for you.
418
515
 
419
516
  See `examples/release-workbench/main.ts` for the canonical shell demo and `examples/app-frame/main.ts` for a compact focused example.
420
517
 
518
+ Shell role split matters:
519
+
520
+ - `statusBarSurface()` communicates concise global state
521
+ - `helpShortSurface()` / `helpViewSurface()` teach shortcuts and scope
522
+ - `commandPaletteSurface()` handles action discovery and navigation
523
+ - notifications surface events and follow-up
524
+
421
525
  ## Building Blocks
422
526
 
423
527
  Reusable stateful components that follow the TEA state + pure transformers + sync render + convenience keymap pattern:
@@ -426,55 +530,100 @@ Reusable stateful components that follow the TEA state + pure transformers + syn
426
530
 
427
531
  ```typescript
428
532
  import {
429
- createNavigableTableState, navigableTable, navTableFocusNext,
533
+ createNavigableTableState, navigableTable, navigableTableSurface, navTableFocusNext,
430
534
  navTableKeyMap, helpShort,
431
535
  } from '@flyingrobots/bijou-tui';
432
536
 
433
537
  const state = createNavigableTableState({ columns, rows, height: 10 });
434
- const output = navigableTable(state, { ctx });
538
+ const textOutput = navigableTable(state, { ctx });
539
+ const surfaceOutput = navigableTableSurface(state, { ctx });
435
540
  const next = navTableFocusNext(state);
436
541
  ```
437
542
 
543
+ Use `navigableTableSurface()` when the user should actively traverse a table inside a rich TUI surface. Keep `navigableTable()` for explicit text lowering. If the job is still passive comparison, prefer core `table()` or `tableSurface()` and keep the interaction layer simpler.
544
+
438
545
  ### Browsable List
439
546
 
440
547
  ```typescript
441
548
  import {
442
- createBrowsableListState, browsableList, listFocusNext,
549
+ createBrowsableListState, browsableList, browsableListSurface, listFocusNext,
443
550
  browsableListKeyMap,
444
551
  } from '@flyingrobots/bijou-tui';
445
552
 
446
553
  const state = createBrowsableListState({ items, height: 10 });
447
- const output = browsableList(state);
554
+ const textOutput = browsableList(state);
555
+ const surfaceOutput = browsableListSurface(state, { width: 40 });
448
556
  ```
449
557
 
558
+ Use `browsableListSurface()` when the list belongs inside a rich TUI region and should share viewport masking semantics with pagers and focus areas. Keep `browsableList()` for explicit text lowering.
559
+
450
560
  ### File Picker
451
561
 
452
562
  ```typescript
453
563
  import {
454
- createFilePickerState, filePicker, fpFocusNext, fpEnter, fpBack,
564
+ createFilePickerState, filePicker, filePickerSurface, fpFocusNext, fpEnter, fpBack,
455
565
  filePickerKeyMap,
456
566
  } from '@flyingrobots/bijou-tui';
457
567
  import { nodeIO } from '@flyingrobots/bijou-node';
458
568
 
459
569
  const io = nodeIO();
460
570
  const state = createFilePickerState({ cwd: process.cwd(), io, height: 15 });
461
- const output = filePicker(state);
571
+ const textOutput = filePicker(state);
572
+ const surfaceOutput = filePickerSurface(state, { width: 60 });
573
+ ```
574
+
575
+ Use `filePickerSurface()` when the browser lives inside a rich TUI pane and should inherit shared viewport masking semantics. Keep `filePicker()` for explicit text lowering.
576
+
577
+ ### Command Palette
578
+
579
+ ```typescript
580
+ import {
581
+ createCommandPaletteState, commandPalette, commandPaletteSurface,
582
+ cpFilter, commandPaletteKeyMap,
583
+ } from '@flyingrobots/bijou-tui';
584
+
585
+ const state = createCommandPaletteState(items, 8);
586
+ const textOutput = commandPalette(state, { width: 60 });
587
+ const surfaceOutput = commandPaletteSurface(state, { width: 60, ctx });
588
+ ```
589
+
590
+ Use `commandPaletteSurface()` when the palette is part of a structured shell or overlay and should share viewport masking semantics. Keep `commandPalette()` for explicit text lowering.
591
+
592
+ ### Pager
593
+
594
+ ```typescript
595
+ import {
596
+ createPagerStateForSurface,
597
+ pagerSurface,
598
+ pagerScrollBy,
599
+ } from '@flyingrobots/bijou-tui';
600
+
601
+ const state = createPagerStateForSurface(contentSurface, { width: 60, height: 20 });
602
+ const output = pagerSurface(contentSurface, state);
462
603
  ```
463
604
 
464
605
  ### Focus Area
465
606
 
466
607
  ```typescript
467
608
  import {
468
- createFocusAreaState, focusArea, focusAreaScrollBy,
609
+ createFocusAreaStateForSurface, focusAreaScrollBy, focusAreaSurface,
469
610
  focusAreaKeyMap,
470
611
  } from '@flyingrobots/bijou-tui';
471
612
 
472
- const state = createFocusAreaState({ content, width: 60, height: 20, overflowX: 'scroll' });
473
- const output = focusArea(state, { focused: true, ctx });
613
+ const state = createFocusAreaStateForSurface(contentSurface, {
614
+ width: 60,
615
+ height: 20,
616
+ overflowX: 'scroll',
617
+ });
618
+ const output = focusAreaSurface(contentSurface, state, { focused: true, ctx });
474
619
  ```
475
620
 
621
+ If the pane is still intentionally text-composed, `createFocusAreaState()` + `focusArea()` remain the explicit lowering path.
622
+
476
623
  ### DAG Pane
477
624
 
625
+ Use `dagPane()` when graph inspection is an active task and the user needs keyboard-owned selection, path highlighting, and scroll control. Keep plain `dag()` in `@flyingrobots/bijou` for passive graph explanation, and move to `dagSlice()` or `dagStats()` when a focused fragment or structural summary would be more honest than a full interactive graph.
626
+
478
627
  ```typescript
479
628
  import {
480
629
  createDagPaneState, dagPane, dagPaneSelectChild,
@@ -495,7 +644,7 @@ All building blocks include `*KeyMap()` factories for preconfigured vim-style ke
495
644
 
496
645
  ## License
497
646
 
498
- MIT
647
+ Apache-2.0
499
648
 
500
649
  ---
501
650
 
@@ -1 +1 @@
1
- {"version":3,"file":"app-frame-actions.d.ts","sourceRoot":"","sources":["../src/app-frame-actions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"app-frame-actions.d.ts","sourceRoot":"","sources":["../src/app-frame-actions.ts"],"names":[],"mappings":"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"}
@@ -7,11 +7,11 @@
7
7
  import { wrapFrameMsg } from './app-frame-types.js';
8
8
  import { createPanelVisibilityState, createPanelMaximizeState, toggleMinimized, restorePane, isMinimized, toggleMaximize, } from './panel-state.js';
9
9
  import { createPanelDockState, movePaneInContainer, resolveChildOrder, findPaneContainer, } from './panel-dock.js';
10
- import { createFocusAreaState, focusAreaScrollBy, focusAreaScrollByX, focusAreaScrollToBottom, focusAreaScrollToTop, focusAreaPageDown, focusAreaPageUp, focusAreaScrollTo, focusAreaScrollToX, } from './focus-area.js';
10
+ import { createFocusAreaStateForSurface, focusAreaScrollBy, focusAreaScrollByX, focusAreaScrollToBottom, focusAreaScrollToTop, focusAreaPageDown, focusAreaPageUp, focusAreaScrollTo, focusAreaScrollToX, } from './focus-area.js';
11
11
  import { timeline } from './timeline.js';
12
12
  import { EASINGS } from './spring.js';
13
13
  import { collectPaneIds, assertUniquePaneIds, findPaneNode, frameBodyRect, } from './app-frame-utils.js';
14
- import { framePaneOutputToString, renderFrameNode } from './app-frame-render.js';
14
+ import { framePaneOutputToSurface, renderFrameNode } from './app-frame-render.js';
15
15
  /** Dispatch a frame-level action (tab switch, pane cycle, scroll, palette, help toggle, transitions). */
16
16
  export function applyFrameAction(action, model, options, pagesById) {
17
17
  switch (action.type) {
@@ -151,9 +151,8 @@ export function scrollFocusedPane(model, action, pagesById) {
151
151
  const paneNode = findPaneNode(layoutTree, focusedPaneId);
152
152
  if (paneNode == null)
153
153
  return model;
154
- const content = framePaneOutputToString(paneNode.render(paneRect.width, paneRect.height), paneRect.width, paneRect.height);
155
- let state = createFocusAreaState({
156
- content,
154
+ const contentSurface = framePaneOutputToSurface(paneNode.render(paneRect.width, paneRect.height), paneRect.width, paneRect.height);
155
+ let state = createFocusAreaStateForSurface(contentSurface, {
157
156
  width: paneRect.width,
158
157
  height: paneRect.height,
159
158
  overflowX: paneNode.overflowX ?? 'hidden',