@flyingrobots/bijou-tui 4.4.1 → 7.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.
- package/README.md +244 -21
- package/dist/animate.d.ts +4 -0
- package/dist/animate.d.ts.map +1 -1
- package/dist/animate.js +47 -20
- package/dist/animate.js.map +1 -1
- package/dist/app-frame-actions.d.ts.map +1 -1
- package/dist/app-frame-actions.js +55 -0
- package/dist/app-frame-actions.js.map +1 -1
- package/dist/app-frame-i18n.d.ts.map +1 -1
- package/dist/app-frame-i18n.js +51 -0
- package/dist/app-frame-i18n.js.map +1 -1
- package/dist/app-frame-layers.d.ts +11 -1
- package/dist/app-frame-layers.d.ts.map +1 -1
- package/dist/app-frame-layers.js +19 -0
- package/dist/app-frame-layers.js.map +1 -1
- package/dist/app-frame-overlays.d.ts +82 -0
- package/dist/app-frame-overlays.d.ts.map +1 -0
- package/dist/app-frame-overlays.js +480 -0
- package/dist/app-frame-overlays.js.map +1 -0
- package/dist/app-frame-palette.d.ts.map +1 -1
- package/dist/app-frame-palette.js +36 -16
- package/dist/app-frame-palette.js.map +1 -1
- package/dist/app-frame-render.d.ts.map +1 -1
- package/dist/app-frame-render.js +16 -7
- package/dist/app-frame-render.js.map +1 -1
- package/dist/app-frame-types.d.ts +141 -5
- package/dist/app-frame-types.d.ts.map +1 -1
- package/dist/app-frame-types.js +17 -1
- package/dist/app-frame-types.js.map +1 -1
- package/dist/app-frame-utils.d.ts +3 -1
- package/dist/app-frame-utils.d.ts.map +1 -1
- package/dist/app-frame-utils.js +11 -5
- package/dist/app-frame-utils.js.map +1 -1
- package/dist/app-frame.d.ts +22 -86
- package/dist/app-frame.d.ts.map +1 -1
- package/dist/app-frame.js +475 -629
- package/dist/app-frame.js.map +1 -1
- package/dist/browsable-list.d.ts +12 -0
- package/dist/browsable-list.d.ts.map +1 -1
- package/dist/browsable-list.js +17 -6
- package/dist/browsable-list.js.map +1 -1
- package/dist/canvas.d.ts +11 -3
- package/dist/canvas.d.ts.map +1 -1
- package/dist/canvas.js +146 -13
- package/dist/canvas.js.map +1 -1
- package/dist/cell-glyph-fit.d.ts +20 -0
- package/dist/cell-glyph-fit.d.ts.map +1 -0
- package/dist/cell-glyph-fit.js +82 -0
- package/dist/cell-glyph-fit.js.map +1 -0
- package/dist/collection-surface.d.ts +8 -0
- package/dist/collection-surface.d.ts.map +1 -1
- package/dist/collection-surface.js +72 -8
- package/dist/collection-surface.js.map +1 -1
- package/dist/command-palette.d.ts +2 -0
- package/dist/command-palette.d.ts.map +1 -1
- package/dist/command-palette.js +36 -19
- package/dist/command-palette.js.map +1 -1
- package/dist/css/text-style.d.ts +2 -0
- package/dist/css/text-style.d.ts.map +1 -1
- package/dist/css/text-style.js +18 -8
- package/dist/css/text-style.js.map +1 -1
- package/dist/debug-overlay.d.ts +19 -0
- package/dist/debug-overlay.d.ts.map +1 -0
- package/dist/debug-overlay.js +25 -0
- package/dist/debug-overlay.js.map +1 -0
- package/dist/design-language.d.ts.map +1 -1
- package/dist/design-language.js +4 -5
- package/dist/design-language.js.map +1 -1
- package/dist/driver.d.ts +102 -0
- package/dist/driver.d.ts.map +1 -1
- package/dist/driver.js +259 -19
- package/dist/driver.js.map +1 -1
- package/dist/eventbus.d.ts +20 -0
- package/dist/eventbus.d.ts.map +1 -1
- package/dist/eventbus.js +47 -0
- package/dist/eventbus.js.map +1 -1
- package/dist/file-picker.d.ts.map +1 -1
- package/dist/file-picker.js +2 -2
- package/dist/file-picker.js.map +1 -1
- package/dist/flex.d.ts.map +1 -1
- package/dist/flex.js +6 -6
- package/dist/flex.js.map +1 -1
- package/dist/focus-area.d.ts +13 -4
- package/dist/focus-area.d.ts.map +1 -1
- package/dist/focus-area.js +56 -14
- package/dist/focus-area.js.map +1 -1
- package/dist/focus-map.d.ts +40 -0
- package/dist/focus-map.d.ts.map +1 -0
- package/dist/focus-map.js +133 -0
- package/dist/focus-map.js.map +1 -0
- package/dist/grid.d.ts +2 -2
- package/dist/grid.d.ts.map +1 -1
- package/dist/grid.js +11 -148
- package/dist/grid.js.map +1 -1
- package/dist/help.d.ts +2 -0
- package/dist/help.d.ts.map +1 -1
- package/dist/help.js +6 -4
- package/dist/help.js.map +1 -1
- package/dist/icon-presentation.d.ts +10 -0
- package/dist/icon-presentation.d.ts.map +1 -0
- package/dist/icon-presentation.js +12 -0
- package/dist/icon-presentation.js.map +1 -0
- package/dist/index.d.ts +32 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -5
- package/dist/index.js.map +1 -1
- package/dist/input-map.d.ts +68 -0
- package/dist/input-map.d.ts.map +1 -0
- package/dist/input-map.js +162 -0
- package/dist/input-map.js.map +1 -0
- package/dist/input-routing-inspector.d.ts +24 -0
- package/dist/input-routing-inspector.d.ts.map +1 -0
- package/dist/input-routing-inspector.js +76 -0
- package/dist/input-routing-inspector.js.map +1 -0
- package/dist/keybindings.d.ts.map +1 -1
- package/dist/keybindings.js +3 -0
- package/dist/keybindings.js.map +1 -1
- package/dist/layout-inspector.d.ts +31 -0
- package/dist/layout-inspector.d.ts.map +1 -0
- package/dist/layout-inspector.js +133 -0
- package/dist/layout-inspector.js.map +1 -0
- package/dist/layout-preset.d.ts +1 -1
- package/dist/layout-preset.d.ts.map +1 -1
- package/dist/motion/reconciler.d.ts +6 -2
- package/dist/motion/reconciler.d.ts.map +1 -1
- package/dist/motion/reconciler.js +40 -10
- package/dist/motion/reconciler.js.map +1 -1
- package/dist/motion/types.d.ts +3 -1
- package/dist/motion/types.d.ts.map +1 -1
- package/dist/navigable-table.d.ts +9 -10
- package/dist/navigable-table.d.ts.map +1 -1
- package/dist/navigable-table.js +33 -14
- package/dist/navigable-table.js.map +1 -1
- package/dist/notification.d.ts +15 -0
- package/dist/notification.d.ts.map +1 -1
- package/dist/notification.js +136 -34
- package/dist/notification.js.map +1 -1
- package/dist/overlay.d.ts.map +1 -1
- package/dist/overlay.js +40 -14
- package/dist/overlay.js.map +1 -1
- package/dist/pager.d.ts +3 -1
- package/dist/pager.d.ts.map +1 -1
- package/dist/pager.js +4 -0
- package/dist/pager.js.map +1 -1
- package/dist/pipeline/middleware/grayscale.d.ts.map +1 -1
- package/dist/pipeline/middleware/grayscale.js +5 -5
- package/dist/pipeline/middleware/grayscale.js.map +1 -1
- package/dist/pipeline/middleware/motion.d.ts.map +1 -1
- package/dist/pipeline/middleware/motion.js +4 -3
- package/dist/pipeline/middleware/motion.js.map +1 -1
- package/dist/pipeline/middleware/paint.d.ts.map +1 -1
- package/dist/pipeline/middleware/paint.js +2 -1
- package/dist/pipeline/middleware/paint.js.map +1 -1
- package/dist/pipeline/middleware/surface-shaders.d.ts +37 -0
- package/dist/pipeline/middleware/surface-shaders.d.ts.map +1 -0
- package/dist/pipeline/middleware/surface-shaders.js +164 -0
- package/dist/pipeline/middleware/surface-shaders.js.map +1 -0
- package/dist/pipeline/pipeline.d.ts +37 -2
- package/dist/pipeline/pipeline.d.ts.map +1 -1
- package/dist/pipeline/pipeline.js +121 -22
- package/dist/pipeline/pipeline.js.map +1 -1
- package/dist/raytrace.d.ts +59 -0
- package/dist/raytrace.d.ts.map +1 -0
- package/dist/raytrace.js +121 -0
- package/dist/raytrace.js.map +1 -0
- package/dist/runtime-binding.d.ts +59 -0
- package/dist/runtime-binding.d.ts.map +1 -0
- package/dist/runtime-binding.js +271 -0
- package/dist/runtime-binding.js.map +1 -0
- package/dist/runtime.d.ts +19 -0
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +222 -31
- package/dist/runtime.js.map +1 -1
- package/dist/split-pane.d.ts +2 -2
- package/dist/split-pane.d.ts.map +1 -1
- package/dist/split-pane.js +28 -49
- package/dist/split-pane.js.map +1 -1
- package/dist/subapp/mount.d.ts +17 -0
- package/dist/subapp/mount.d.ts.map +1 -1
- package/dist/subapp/mount.js +13 -0
- package/dist/subapp/mount.js.map +1 -1
- package/dist/surface-budget.d.ts +27 -0
- package/dist/surface-budget.d.ts.map +1 -0
- package/dist/surface-budget.js +61 -0
- package/dist/surface-budget.js.map +1 -0
- package/dist/surface-diff.d.ts +36 -0
- package/dist/surface-diff.d.ts.map +1 -0
- package/dist/surface-diff.js +202 -0
- package/dist/surface-diff.js.map +1 -0
- package/dist/surface-layout.d.ts +19 -3
- package/dist/surface-layout.d.ts.map +1 -1
- package/dist/surface-layout.js +42 -12
- package/dist/surface-layout.js.map +1 -1
- package/dist/types.d.ts +8 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/view-output.js +2 -2
- package/dist/view-output.js.map +1 -1
- package/dist/viewport.d.ts +13 -1
- package/dist/viewport.d.ts.map +1 -1
- package/dist/viewport.js +33 -11
- package/dist/viewport.js.map +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ The high-fidelity TEA runtime for Bijou.
|
|
|
7
7
|
## Role
|
|
8
8
|
|
|
9
9
|
- **The Elm Architecture (TEA)**: A deterministic state-update-view loop for industrial-strength terminal software.
|
|
10
|
-
- **Fractal TEA**: Compose nested sub-apps with `initSubApp()`, `updateSubApp()`, and `mount()`.
|
|
10
|
+
- **Fractal TEA**: Compose nested sub-apps with `createSubAppAdapter()`, `initSubApp()`, `updateSubApp()`, and `mount()`.
|
|
11
11
|
- **Declarative Motion**: Interpolate layout changes smoothly with physics-based springs and tween animations.
|
|
12
12
|
- **Surface-First Pipeline**: Programmable rendering middleware for fragments, diffing, and shader-based transitions.
|
|
13
13
|
|
|
@@ -20,18 +20,18 @@ npm install @flyingrobots/bijou @flyingrobots/bijou-node @flyingrobots/bijou-tui
|
|
|
20
20
|
## Quick Start (Sub-App Composition)
|
|
21
21
|
|
|
22
22
|
```typescript
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
23
|
+
import { startApp } from '@flyingrobots/bijou-node';
|
|
24
|
+
import { createSubAppAdapter, mount, type App } from '@flyingrobots/bijou-tui';
|
|
25
25
|
import { createSurface } from '@flyingrobots/bijou';
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
type
|
|
27
|
+
type ChildMsg = { type: 'tick' };
|
|
28
|
+
type ParentModel = {
|
|
29
|
+
left: { count: number };
|
|
30
|
+
right: { count: number };
|
|
31
|
+
};
|
|
32
|
+
type ParentMsg = { type: 'left'; msg: ChildMsg } | { type: 'right'; msg: ChildMsg };
|
|
33
33
|
|
|
34
|
-
const childApp: App<
|
|
34
|
+
const childApp: App<{ count: number }, ChildMsg> = {
|
|
35
35
|
init: () => [{ count: 0 }, []],
|
|
36
36
|
update: (msg, model) => [model, []],
|
|
37
37
|
view: (model) => {
|
|
@@ -41,18 +41,20 @@ const childApp: App<ChildModel, ChildMsg> = {
|
|
|
41
41
|
}
|
|
42
42
|
};
|
|
43
43
|
|
|
44
|
-
const
|
|
44
|
+
const mapLeft = createSubAppAdapter<ParentMsg, ChildMsg>({
|
|
45
|
+
tick: (msg) => ({ type: 'left', msg }),
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const mapRight = createSubAppAdapter<ParentMsg, ChildMsg>({
|
|
49
|
+
tick: (msg) => ({ type: 'right', msg }),
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const app: App<ParentModel, ParentMsg> = {
|
|
45
53
|
init: () => [{ left: { count: 0 }, right: { count: 0 } }, []],
|
|
46
54
|
update: (msg, model) => [model, []],
|
|
47
55
|
view: (model) => {
|
|
48
|
-
const [left] = mount(childApp, {
|
|
49
|
-
|
|
50
|
-
onMsg: (msg) => ({ pane: 'left', msg }),
|
|
51
|
-
});
|
|
52
|
-
const [right] = mount(childApp, {
|
|
53
|
-
model: model.right,
|
|
54
|
-
onMsg: (msg) => ({ pane: 'right', msg }),
|
|
55
|
-
});
|
|
56
|
+
const [left] = mount(childApp, { model: model.left, onMsg: mapLeft });
|
|
57
|
+
const [right] = mount(childApp, { model: model.right, onMsg: mapRight });
|
|
56
58
|
|
|
57
59
|
const screen = createSurface(80, 24);
|
|
58
60
|
screen.blit(left, 0, 0);
|
|
@@ -61,9 +63,21 @@ const app: App<Model, Msg> = {
|
|
|
61
63
|
}
|
|
62
64
|
};
|
|
63
65
|
|
|
64
|
-
|
|
66
|
+
await startApp(app);
|
|
65
67
|
```
|
|
66
68
|
|
|
69
|
+
For Node hosts, prefer `startApp()` for the first-app path. Reach for
|
|
70
|
+
`run(app, { ctx })` when the host owns context creation explicitly.
|
|
71
|
+
|
|
72
|
+
When you need to mix small string fragments with surface-returning primitives,
|
|
73
|
+
keep composition on the surface side: use `contentSurface()` directly or pass
|
|
74
|
+
strings into `vstackSurface()` / `hstackSurface()`. Raw strings are still not a
|
|
75
|
+
valid `view()` return type.
|
|
76
|
+
|
|
77
|
+
Use `proseSurface(text, { width })` when paragraph-like text should wrap at
|
|
78
|
+
word boundaries before becoming a surface. Keep `contentSurface()` for
|
|
79
|
+
preformatted output, tables, boxes, and caller-managed line breaks.
|
|
80
|
+
|
|
67
81
|
## Strategy: Choosing Component Families
|
|
68
82
|
|
|
69
83
|
Select the family based on the interaction semantic.
|
|
@@ -73,6 +87,15 @@ Select the family based on the interaction semantic.
|
|
|
73
87
|
- **`modal()`**: Required decision that blocks background activity.
|
|
74
88
|
- **`toast()`**: Transient notification for a single event.
|
|
75
89
|
- **`tooltip()`**: Micro-explanation for a local target.
|
|
90
|
+
- **`debugOverlay()`**: Development-only perf HUD composited onto any app surface.
|
|
91
|
+
- **`layoutInspectorOverlay()`**: Development-only geometry HUD for region
|
|
92
|
+
bounds, focus, and layers.
|
|
93
|
+
- **`inputRoutingInspectorText()`**: Development-only route trace for recent
|
|
94
|
+
key and pointer events.
|
|
95
|
+
- **`focusMapText()`**: Development-only focus ownership and tab-order report.
|
|
96
|
+
- **`evaluateSurfaceBudget()`**: Deterministic warnings for surface and timing
|
|
97
|
+
thresholds.
|
|
98
|
+
- **`surfaceDiffSurface()`**: Side-by-side or overlay inspection for two rendered surfaces.
|
|
76
99
|
|
|
77
100
|
### Collection Interaction
|
|
78
101
|
- **`navigableTable()`**: Keyboard-driven traversal and cell inspection.
|
|
@@ -80,11 +103,43 @@ Select the family based on the interaction semantic.
|
|
|
80
103
|
- **`commandPalette()`**: Action discovery and navigation.
|
|
81
104
|
|
|
82
105
|
### Shell and Workspace Layout
|
|
83
|
-
- **`createFramedApp()`**: Batteries-included workspace with tabs, panes,
|
|
106
|
+
- **`createFramedApp()`**: Batteries-included workspace with tabs, panes,
|
|
107
|
+
help, and a shell-owned perf HUD toggled with the backtick key.
|
|
84
108
|
- **`splitPane()`**: Dynamic primary/secondary context comparison.
|
|
85
109
|
- **`grid()`**: Stable regions with simultaneous visibility.
|
|
86
110
|
- **`viewport()`**: The canonical scroll mask for rich composition.
|
|
87
111
|
|
|
112
|
+
For framed shells, Node hosts can still prefer `startApp(app)`: the hosted
|
|
113
|
+
Node bootstrap delegates to self-running framed apps automatically. Use the
|
|
114
|
+
explicit runner path when you want to stay inside `@flyingrobots/bijou-tui`
|
|
115
|
+
or when the host owns `ctx` directly:
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
import { createFramedApp, runFramedApp } from '@flyingrobots/bijou-tui';
|
|
119
|
+
|
|
120
|
+
const app = createFramedApp({ pages: [page] });
|
|
121
|
+
|
|
122
|
+
await app.run({ ctx });
|
|
123
|
+
// or: await runFramedApp({ pages: [page] }, { ctx });
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
This path keeps the shell batteries included: mouse input defaults to `true`,
|
|
127
|
+
the shared runtime loop still does the heavy lifting, and frame timing/budget
|
|
128
|
+
telemetry stays attached to the frame model for shell-owned UI. For Node-hosted
|
|
129
|
+
apps, `startApp(app)` remains the default bootstrap.
|
|
130
|
+
|
|
131
|
+
## Runtime Diagnostics
|
|
132
|
+
|
|
133
|
+
The EventBus exposes command queue facts through `getCommandDiagnostics()`.
|
|
134
|
+
`run(app, { commandBackpressureThreshold })` routes a warning when pending
|
|
135
|
+
commands cross the threshold, without hard-capping legitimate bursts. The
|
|
136
|
+
default threshold is 1000; use `0` to disable the warning.
|
|
137
|
+
|
|
138
|
+
Render pipeline middleware is synchronous by contract. If middleware returns a
|
|
139
|
+
Promise, Bijou reports a pipeline diagnostic, keeps the current frame moving,
|
|
140
|
+
and ignores any late `next()` continuation so stage ordering stays
|
|
141
|
+
deterministic.
|
|
142
|
+
|
|
88
143
|
## Animation
|
|
89
144
|
|
|
90
145
|
### Spring Physics
|
|
@@ -99,6 +154,11 @@ const cmd = animate({
|
|
|
99
154
|
});
|
|
100
155
|
```
|
|
101
156
|
|
|
157
|
+
Spring commands integrate physics with bounded fixed timesteps by default, so a
|
|
158
|
+
slow terminal frame cannot feed a large raw pulse delta into the spring. Tune
|
|
159
|
+
`fixedStepSeconds` and `maxPulseSeconds` only when an app needs a custom
|
|
160
|
+
simulation cadence.
|
|
161
|
+
|
|
102
162
|
### Timeline Orchestration
|
|
103
163
|
```typescript
|
|
104
164
|
import { timeline } from '@flyingrobots/bijou-tui';
|
|
@@ -110,10 +170,173 @@ const tl = timeline()
|
|
|
110
170
|
.build();
|
|
111
171
|
```
|
|
112
172
|
|
|
173
|
+
## Post-Process Shaders
|
|
174
|
+
|
|
175
|
+
`canvas()` can render procedural surfaces at cell, Quad, Braille, or glyph-fit
|
|
176
|
+
resolution. Use `resolution: 'glyph'` when a shader should sample a 2x4 grid but
|
|
177
|
+
collapse into geometry-aware Unicode or ASCII characters instead of Braille.
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
import { canvas } from '@flyingrobots/bijou-tui';
|
|
181
|
+
|
|
182
|
+
const logo = canvas(24, 6, ({ u, v }) => {
|
|
183
|
+
const lit = Math.abs(u - v) < 0.08;
|
|
184
|
+
return lit ? { char: 'X', fg: '#ffffff' } : ' ';
|
|
185
|
+
}, { resolution: 'glyph' });
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
import { run, surfaceShaderFilter, scanlines, vignette } from '@flyingrobots/bijou-tui';
|
|
190
|
+
|
|
191
|
+
await run(app, {
|
|
192
|
+
configurePipeline(pipeline) {
|
|
193
|
+
pipeline.use('PostProcess', surfaceShaderFilter(
|
|
194
|
+
scanlines({ dimFactor: 0.82 }),
|
|
195
|
+
vignette({ edgeFactor: 0.78 }),
|
|
196
|
+
));
|
|
197
|
+
},
|
|
198
|
+
});
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Use `surfaceShaderFilter(...)` to compose built-in post-process passes like
|
|
202
|
+
`scanlines()`, `flicker()`, `noise()`, and `vignette()` over the packed target
|
|
203
|
+
surface before diff/output.
|
|
204
|
+
|
|
205
|
+
## Testing
|
|
206
|
+
|
|
207
|
+
Use `testRuntime()` when you want an inspectable harness instead of a
|
|
208
|
+
one-shot script result:
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
import { testRuntime } from '@flyingrobots/bijou-tui';
|
|
212
|
+
|
|
213
|
+
const harness = await testRuntime(app, { ctx });
|
|
214
|
+
await harness.press('q');
|
|
215
|
+
|
|
216
|
+
expect(harness.frame).toBeDefined();
|
|
217
|
+
expect(harness.messages).toHaveLength(1);
|
|
218
|
+
expect(harness.commands.every((record) => record.settled)).toBe(true);
|
|
219
|
+
|
|
220
|
+
await harness.teardown();
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Keep `runScript()` for fixture-style interaction playback and GIF/demo
|
|
224
|
+
capture, and use `testRuntime()` when you need direct assertions on
|
|
225
|
+
snapshots, emitted messages, command outcomes, or cleanup disposal.
|
|
226
|
+
|
|
227
|
+
Use `surfaceDiffText()` or `surfaceDiffSurface()` when a frame assertion fails
|
|
228
|
+
and you need cell-level truth instead of a raw string dump:
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
import { surfaceDiffText } from '@flyingrobots/bijou-tui';
|
|
232
|
+
|
|
233
|
+
expect(surfaceDiffText(before, after)).toContain('surface diff: 2 changed');
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Use `layoutInspectorOverlay()` when a layout assertion needs visual geometry
|
|
237
|
+
truth on top of the rendered frame:
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
import {
|
|
241
|
+
layoutInspectorOverlay,
|
|
242
|
+
layoutInspectorText,
|
|
243
|
+
} from '@flyingrobots/bijou-tui';
|
|
244
|
+
|
|
245
|
+
const inspected = layoutInspectorOverlay(frame, [
|
|
246
|
+
{
|
|
247
|
+
id: 'editor',
|
|
248
|
+
rect: { col: 2, row: 1, width: 40, height: 12 },
|
|
249
|
+
focused: true,
|
|
250
|
+
},
|
|
251
|
+
]);
|
|
252
|
+
|
|
253
|
+
expect(layoutInspectorText([
|
|
254
|
+
{ id: 'editor', rect: { x: 2, y: 1, width: 40, height: 12 } },
|
|
255
|
+
]))
|
|
256
|
+
.toContain('focused=false');
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Use `evaluateSurfaceBudget()` directly in tests or pass `surfaceBudget` to
|
|
260
|
+
`run()` when an interactive app should route non-fatal runtime warnings:
|
|
261
|
+
|
|
262
|
+
```typescript
|
|
263
|
+
import { evaluateSurfaceBudget, run } from '@flyingrobots/bijou-tui';
|
|
264
|
+
|
|
265
|
+
expect(evaluateSurfaceBudget({
|
|
266
|
+
surface: frame,
|
|
267
|
+
thresholds: { maxArea: 4000 },
|
|
268
|
+
})).toEqual([]);
|
|
269
|
+
|
|
270
|
+
await run(app, {
|
|
271
|
+
ctx,
|
|
272
|
+
surfaceBudget: { maxArea: 4000, maxStageDurationMs: { Paint: 8 } },
|
|
273
|
+
});
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
Use `appendInputRoutingRecord()` and `inputRoutingInspectorText()` when a shell
|
|
277
|
+
or app wants to keep the last few routing decisions visible:
|
|
278
|
+
|
|
279
|
+
```typescript
|
|
280
|
+
import {
|
|
281
|
+
appendInputRoutingRecord,
|
|
282
|
+
inputRoutingInspectorText,
|
|
283
|
+
} from '@flyingrobots/bijou-tui';
|
|
284
|
+
|
|
285
|
+
const history = appendInputRoutingRecord({ records: [] }, {
|
|
286
|
+
event,
|
|
287
|
+
result,
|
|
288
|
+
commandLabels: ['save-file'],
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
console.log(inputRoutingInspectorText(history));
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
Use `createInputGestureRecognizer()` and `createInputActionMap()` when raw input
|
|
295
|
+
needs to become semantic gestures before routing. Bijou ships standard feature
|
|
296
|
+
event names such as `press`, `held`, `release`, `tap`, `double-tap`,
|
|
297
|
+
`long-press`, and lets apps provide their own event type strings.
|
|
298
|
+
|
|
299
|
+
```typescript
|
|
300
|
+
import {
|
|
301
|
+
createInputActionMap,
|
|
302
|
+
createInputGestureRecognizer,
|
|
303
|
+
} from '@flyingrobots/bijou-tui';
|
|
304
|
+
|
|
305
|
+
const recognizer = createInputGestureRecognizer({ doubleTapMs: 300 });
|
|
306
|
+
const actions = createInputActionMap<{ type: 'toggle-footer' }>()
|
|
307
|
+
.bind('footer.doubleTab', 'Toggle footer', [
|
|
308
|
+
{ deviceId: 'keyboard', featureId: 'key.tab', type: 'double-tap' },
|
|
309
|
+
], { type: 'toggle-footer' });
|
|
310
|
+
|
|
311
|
+
const action = actions.handle(recognizer.observeKey(keyMsg, ctx.runtime.clock.now()));
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
Use `focusMapText()` or `focusMapSurface()` when focus ownership and tab order
|
|
315
|
+
need the same deterministic treatment:
|
|
316
|
+
|
|
317
|
+
```typescript
|
|
318
|
+
import { focusMapText } from '@flyingrobots/bijou-tui';
|
|
319
|
+
|
|
320
|
+
const report = focusMapText([
|
|
321
|
+
{
|
|
322
|
+
id: 'editor',
|
|
323
|
+
owner: 'workspace',
|
|
324
|
+
role: 'textbox',
|
|
325
|
+
rect: { x: 2, y: 1, width: 60, height: 20 },
|
|
326
|
+
tabIndex: 1,
|
|
327
|
+
focusable: true,
|
|
328
|
+
focused: true,
|
|
329
|
+
},
|
|
330
|
+
]);
|
|
331
|
+
|
|
332
|
+
expect(report).toContain('[1] *editor');
|
|
333
|
+
```
|
|
334
|
+
|
|
113
335
|
## Documentation
|
|
114
336
|
|
|
115
337
|
- **[GUIDE.md](./GUIDE.md)**: Productive-fast path for building apps.
|
|
116
338
|
- **[ADVANCED_GUIDE.md](./ADVANCED_GUIDE.md)**: Shell doctrine, shaders, and motion internals.
|
|
339
|
+
- **[Render Pipeline Guide](../../docs/guides/render-pipeline.md)**: Stage order, `RenderState`, and `configurePipeline()` truth.
|
|
117
340
|
- **[Design System](../../docs/design-system/README.md)**: Semantic guidance and patterns.
|
|
118
341
|
|
|
119
342
|
---
|
package/dist/animate.d.ts
CHANGED
|
@@ -43,6 +43,10 @@ export interface SpringAnimateOptions<M> extends AnimateBase<M> {
|
|
|
43
43
|
readonly type?: 'spring';
|
|
44
44
|
/** Spring config — preset name or custom values. */
|
|
45
45
|
readonly spring?: Partial<SpringConfig> | SpringPreset;
|
|
46
|
+
/** Integration step in seconds. Defaults to 1/120 for stable spring physics. */
|
|
47
|
+
readonly fixedStepSeconds?: number;
|
|
48
|
+
/** Maximum pulse time accepted in one runtime pulse. Defaults to 1/20. */
|
|
49
|
+
readonly maxPulseSeconds?: number;
|
|
46
50
|
}
|
|
47
51
|
/**
|
|
48
52
|
* Options for a duration-based tween animation.
|
package/dist/animate.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"animate.d.ts","sourceRoot":"","sources":["../src/animate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,QAAQ,EAQd,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"animate.d.ts","sourceRoot":"","sources":["../src/animate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,QAAQ,EAQd,MAAM,aAAa,CAAC;AASrB;;;;GAIG;AACH,UAAU,WAAW,CAAC,CAAC;IACrB,sBAAsB;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,oBAAoB;IACpB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,oEAAoE;IACpE,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;IAC7B,+EAA+E;IAC/E,QAAQ,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,CAAC,CAAC;IACvC,qEAAqE;IACrE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC,CAAC;CAC/B;AAED;;;;GAIG;AACH,MAAM,WAAW,oBAAoB,CAAC,CAAC,CAAE,SAAQ,WAAW,CAAC,CAAC,CAAC;IAC7D,kFAAkF;IAClF,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC;IACzB,oDAAoD;IACpD,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG,YAAY,CAAC;IACvD,gFAAgF;IAChF,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,0EAA0E;IAC1E,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;CACnC;AAED;;;;GAIG;AACH,MAAM,WAAW,mBAAmB,CAAC,CAAC,CAAE,SAAQ,WAAW,CAAC,CAAC,CAAC;IAC5D,sEAAsE;IACtE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,gCAAgC;IAChC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,8CAA8C;IAC9C,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC;CAC1B;AAED;;;;GAIG;AACH,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI,oBAAoB,CAAC,CAAC,CAAC,GAAG,mBAAmB,CAAC,CAAC,CAAC,CAAC;AAMjF;;;;;;;;;;;GAWG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,OAAO,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CA2B7D;AAyID;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,CAMrD"}
|
package/dist/animate.js
CHANGED
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
* the target value in a single frame.
|
|
16
16
|
*/
|
|
17
17
|
import { springStep, createSpringState, resolveSpringConfig, tweenStep, createTweenState, resolveTweenConfig, EASINGS, } from './spring.js';
|
|
18
|
+
const DEFAULT_SPRING_FIXED_STEP_SECONDS = 1 / 120;
|
|
19
|
+
const DEFAULT_SPRING_MAX_PULSE_SECONDS = 1 / 20;
|
|
18
20
|
// ---------------------------------------------------------------------------
|
|
19
21
|
// animate() — the main API
|
|
20
22
|
// ---------------------------------------------------------------------------
|
|
@@ -44,28 +46,43 @@ export function animate(options) {
|
|
|
44
46
|
return createTweenCmd(from, to, options.duration, options.ease ?? EASINGS.easeOutCubic, onFrame, onComplete);
|
|
45
47
|
}
|
|
46
48
|
const config = resolveSpringConfig(options.spring);
|
|
47
|
-
return createSpringCmd(from, to, config,
|
|
49
|
+
return createSpringCmd(from, to, config, {
|
|
50
|
+
fixedStepSeconds: resolvePositiveSeconds(options.fixedStepSeconds, DEFAULT_SPRING_FIXED_STEP_SECONDS),
|
|
51
|
+
maxPulseSeconds: resolveMaxPulseSeconds(options.maxPulseSeconds, options.fixedStepSeconds),
|
|
52
|
+
}, onFrame, onComplete);
|
|
48
53
|
}
|
|
49
|
-
// ---------------------------------------------------------------------------
|
|
50
|
-
// Internal: spring command
|
|
51
|
-
// ---------------------------------------------------------------------------
|
|
52
54
|
/**
|
|
53
|
-
* Build a TEA command that
|
|
55
|
+
* Build a TEA command that advances a spring animation from runtime pulses.
|
|
56
|
+
*
|
|
57
|
+
* The command subscribes through `caps.onPulse`, caps each accepted pulse with
|
|
58
|
+
* `maxPulseSeconds`, and accumulates that time into fixed-step spring physics
|
|
59
|
+
* updates. Slow runtime pulses may delay progress, but they do not feed one
|
|
60
|
+
* large raw delta into the spring integrator.
|
|
54
61
|
*
|
|
55
62
|
* @template M - The message type emitted into the TEA update cycle.
|
|
56
|
-
* @param from
|
|
57
|
-
* @param to
|
|
58
|
-
* @param config
|
|
59
|
-
* @param
|
|
60
|
-
* @param onFrame
|
|
61
|
-
* @param onComplete
|
|
63
|
+
* @param from - Starting value.
|
|
64
|
+
* @param to - Target value.
|
|
65
|
+
* @param config - Resolved spring physics parameters.
|
|
66
|
+
* @param integration - `fixedStepSeconds` and `maxPulseSeconds` integration policy.
|
|
67
|
+
* @param onFrame - Callback invoked after spring physics advances.
|
|
68
|
+
* @param onComplete - Optional callback invoked when the spring settles.
|
|
62
69
|
* @returns A TEA command that resolves when the spring is done.
|
|
63
70
|
*/
|
|
64
|
-
function createSpringCmd(from, to, config, onFrame, onComplete) {
|
|
71
|
+
function createSpringCmd(from, to, config, integration, onFrame, onComplete) {
|
|
65
72
|
return (emit, caps) => new Promise((resolve) => {
|
|
66
73
|
let state = createSpringState(from);
|
|
74
|
+
let accumulatedSeconds = 0;
|
|
67
75
|
const handle = caps.onPulse((dt) => {
|
|
68
|
-
|
|
76
|
+
accumulatedSeconds = Math.min(integration.maxPulseSeconds, accumulatedSeconds + acceptedPulseSeconds(dt, integration.maxPulseSeconds));
|
|
77
|
+
let stepped = false;
|
|
78
|
+
while (accumulatedSeconds >= integration.fixedStepSeconds && !state.done) {
|
|
79
|
+
state = springStep(state, to, config, integration.fixedStepSeconds);
|
|
80
|
+
accumulatedSeconds -= integration.fixedStepSeconds;
|
|
81
|
+
stepped = true;
|
|
82
|
+
}
|
|
83
|
+
if (!stepped) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
69
86
|
emit(onFrame(state.value));
|
|
70
87
|
if (state.done) {
|
|
71
88
|
handle.dispose();
|
|
@@ -76,23 +93,33 @@ function createSpringCmd(from, to, config, onFrame, onComplete) {
|
|
|
76
93
|
});
|
|
77
94
|
});
|
|
78
95
|
}
|
|
96
|
+
function resolveMaxPulseSeconds(maxPulseSeconds, fixedStepSeconds) {
|
|
97
|
+
return Math.max(resolvePositiveSeconds(maxPulseSeconds, DEFAULT_SPRING_MAX_PULSE_SECONDS), resolvePositiveSeconds(fixedStepSeconds, DEFAULT_SPRING_FIXED_STEP_SECONDS));
|
|
98
|
+
}
|
|
99
|
+
function resolvePositiveSeconds(value, fallback) {
|
|
100
|
+
return value != null && Number.isFinite(value) && value > 0 ? value : fallback;
|
|
101
|
+
}
|
|
102
|
+
function acceptedPulseSeconds(dt, maxPulseSeconds) {
|
|
103
|
+
if (!Number.isFinite(dt) || dt <= 0) {
|
|
104
|
+
return 0;
|
|
105
|
+
}
|
|
106
|
+
return Math.min(dt, maxPulseSeconds);
|
|
107
|
+
}
|
|
79
108
|
// ---------------------------------------------------------------------------
|
|
80
109
|
// Internal: tween command
|
|
81
110
|
// ---------------------------------------------------------------------------
|
|
82
111
|
/**
|
|
83
|
-
* Build a TEA command that
|
|
112
|
+
* Build a TEA command that advances a tween animation from runtime pulse `dt`.
|
|
84
113
|
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
* timer jitter or system load.
|
|
114
|
+
* The command subscribes through `caps.onPulse`; each pulse supplies elapsed
|
|
115
|
+
* seconds, which are converted to milliseconds before calling `tweenStep()`.
|
|
88
116
|
*
|
|
89
117
|
* @template M - The message type emitted into the TEA update cycle.
|
|
90
118
|
* @param from - Starting value.
|
|
91
119
|
* @param to - Target value.
|
|
92
120
|
* @param duration - Total animation duration in milliseconds.
|
|
93
121
|
* @param ease - Easing function applied to normalized progress.
|
|
94
|
-
* @param
|
|
95
|
-
* @param onFrame - Callback invoked each frame with the interpolated value.
|
|
122
|
+
* @param onFrame - Callback invoked after tween progress advances.
|
|
96
123
|
* @param onComplete - Optional callback invoked when the tween completes.
|
|
97
124
|
* @returns A TEA command that resolves when the tween is done.
|
|
98
125
|
*/
|
|
@@ -101,7 +128,7 @@ function createTweenCmd(from, to, duration, ease, onFrame, onComplete) {
|
|
|
101
128
|
return (emit, caps) => new Promise((resolve) => {
|
|
102
129
|
let state = createTweenState(from);
|
|
103
130
|
const handle = caps.onPulse((dt) => {
|
|
104
|
-
//
|
|
131
|
+
// tweenStep expects the pulse delta in milliseconds.
|
|
105
132
|
state = tweenStep(state, config, dt * 1000);
|
|
106
133
|
emit(onFrame(state.value));
|
|
107
134
|
if (state.done) {
|
package/dist/animate.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"animate.js","sourceRoot":"","sources":["../src/animate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,EAIL,UAAU,EACV,iBAAiB,EACjB,mBAAmB,EACnB,SAAS,EACT,gBAAgB,EAChB,kBAAkB,EAClB,OAAO,GACR,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"animate.js","sourceRoot":"","sources":["../src/animate.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-frame-actions.d.ts","sourceRoot":"","sources":["../src/app-frame-actions.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,sBAAsB,
|
|
1
|
+
{"version":3,"file":"app-frame-actions.d.ts","sourceRoot":"","sources":["../src/app-frame-actions.ts"],"names":[],"mappings":"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"}
|
|
@@ -13,7 +13,9 @@ import { timeline } from './timeline.js';
|
|
|
13
13
|
import { EASINGS } from './spring.js';
|
|
14
14
|
import { collectPaneIds, assertUniquePaneIds, findPaneNode, frameBodyRect, } from './app-frame-utils.js';
|
|
15
15
|
import { framePaneOutputToSurface, renderFrameNode } from './app-frame-render.js';
|
|
16
|
+
import { animate } from './animate.js';
|
|
16
17
|
let focusedPaneMeasureScratch = null;
|
|
18
|
+
const FOOTER_TOGGLE_DURATION_MS = 200;
|
|
17
19
|
function renderPaneSurfaceForMeasurement(output, width, height) {
|
|
18
20
|
const scratch = focusedPaneMeasureScratch;
|
|
19
21
|
focusedPaneMeasureScratch = framePaneOutputToSurface(output, width, height, scratch != null && scratch.width === width && scratch.height === height
|
|
@@ -26,6 +28,10 @@ export function applyFrameAction(action, model, options, pagesById) {
|
|
|
26
28
|
switch (action.type) {
|
|
27
29
|
case 'toggle-help':
|
|
28
30
|
return [{ ...model, helpOpen: !model.helpOpen }, []];
|
|
31
|
+
case 'toggle-perf-hud':
|
|
32
|
+
return [{ ...model, perfHudOpen: !model.perfHudOpen }, []];
|
|
33
|
+
case 'toggle-footer':
|
|
34
|
+
return toggleFooter(model);
|
|
29
35
|
case 'toggle-settings': {
|
|
30
36
|
const activePage = pagesById.get(model.activePageId);
|
|
31
37
|
const settings = options.settings?.({
|
|
@@ -71,6 +77,8 @@ export function applyFrameAction(action, model, options, pagesById) {
|
|
|
71
77
|
quitConfirmOpen: false,
|
|
72
78
|
}, []];
|
|
73
79
|
}
|
|
80
|
+
case 'push-notification':
|
|
81
|
+
return [model, []];
|
|
74
82
|
case 'prev-tab':
|
|
75
83
|
return switchTab(model, -1, pagesById, options);
|
|
76
84
|
case 'next-tab':
|
|
@@ -109,11 +117,58 @@ export function applyFrameAction(action, model, options, pagesById) {
|
|
|
109
117
|
case 'runtime-issue':
|
|
110
118
|
case 'notification-tick':
|
|
111
119
|
return [model, []];
|
|
120
|
+
case 'footer-transition':
|
|
121
|
+
if (action.generation !== (model.footerAnimationGeneration ?? 0))
|
|
122
|
+
return [model, []];
|
|
123
|
+
return [{
|
|
124
|
+
...model,
|
|
125
|
+
footerTranslateY: Math.max(0, Math.min(1, action.translateY)),
|
|
126
|
+
}, []];
|
|
127
|
+
case 'footer-transition-complete':
|
|
128
|
+
if (action.generation !== (model.footerAnimationGeneration ?? 0))
|
|
129
|
+
return [model, []];
|
|
130
|
+
return [{
|
|
131
|
+
...model,
|
|
132
|
+
footerVisible: action.visible,
|
|
133
|
+
footerTranslateY: action.visible ? 0 : 1,
|
|
134
|
+
}, []];
|
|
112
135
|
case 'transition':
|
|
113
136
|
case 'transition-complete':
|
|
114
137
|
return [model, []];
|
|
115
138
|
}
|
|
116
139
|
}
|
|
140
|
+
function toggleFooter(model) {
|
|
141
|
+
const currentVisible = model.footerVisible ?? true;
|
|
142
|
+
const visible = !currentVisible;
|
|
143
|
+
const generation = (model.footerAnimationGeneration ?? 0) + 1;
|
|
144
|
+
const defaultTranslateY = currentVisible ? 0 : 1;
|
|
145
|
+
const from = Math.max(0, Math.min(1, model.footerTranslateY ?? defaultTranslateY));
|
|
146
|
+
const to = visible ? 0 : 1;
|
|
147
|
+
const ease = visible ? EASINGS.easeIn : EASINGS.easeOut;
|
|
148
|
+
return [{
|
|
149
|
+
...model,
|
|
150
|
+
footerVisible: visible,
|
|
151
|
+
footerAnimationGeneration: generation,
|
|
152
|
+
}, [
|
|
153
|
+
animate({
|
|
154
|
+
type: 'tween',
|
|
155
|
+
from,
|
|
156
|
+
to,
|
|
157
|
+
duration: FOOTER_TOGGLE_DURATION_MS,
|
|
158
|
+
ease,
|
|
159
|
+
onFrame: (translateY) => wrapFrameMsg({
|
|
160
|
+
type: 'footer-transition',
|
|
161
|
+
translateY,
|
|
162
|
+
generation,
|
|
163
|
+
}),
|
|
164
|
+
onComplete: () => wrapFrameMsg({
|
|
165
|
+
type: 'footer-transition-complete',
|
|
166
|
+
visible,
|
|
167
|
+
generation,
|
|
168
|
+
}),
|
|
169
|
+
}),
|
|
170
|
+
]];
|
|
171
|
+
}
|
|
117
172
|
function hasNotificationCenter(model, options, pagesById) {
|
|
118
173
|
const activePage = pagesById.get(model.activePageId);
|
|
119
174
|
if (activePage == null)
|