@flyingrobots/bijou-tui 4.0.0 → 4.1.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 +24 -7
- package/dist/app-frame-actions.d.ts +5 -5
- package/dist/app-frame-actions.d.ts.map +1 -1
- package/dist/app-frame-actions.js +71 -5
- package/dist/app-frame-actions.js.map +1 -1
- package/dist/app-frame-i18n.d.ts +12 -0
- package/dist/app-frame-i18n.d.ts.map +1 -0
- package/dist/app-frame-i18n.js +92 -0
- package/dist/app-frame-i18n.js.map +1 -0
- package/dist/app-frame-layers.d.ts +29 -0
- package/dist/app-frame-layers.d.ts.map +1 -0
- package/dist/app-frame-layers.js +104 -0
- package/dist/app-frame-layers.js.map +1 -0
- package/dist/app-frame-palette.d.ts +6 -2
- package/dist/app-frame-palette.d.ts.map +1 -1
- package/dist/app-frame-palette.js +42 -10
- package/dist/app-frame-palette.js.map +1 -1
- package/dist/app-frame-render.d.ts +13 -5
- package/dist/app-frame-render.d.ts.map +1 -1
- package/dist/app-frame-render.js +216 -70
- package/dist/app-frame-render.js.map +1 -1
- package/dist/app-frame-types.d.ts +40 -20
- package/dist/app-frame-types.d.ts.map +1 -1
- package/dist/app-frame-types.js +8 -6
- package/dist/app-frame-types.js.map +1 -1
- package/dist/app-frame-utils.d.ts +8 -3
- package/dist/app-frame-utils.d.ts.map +1 -1
- package/dist/app-frame-utils.js +42 -27
- package/dist/app-frame-utils.js.map +1 -1
- package/dist/app-frame.d.ts +125 -11
- package/dist/app-frame.d.ts.map +1 -1
- package/dist/app-frame.js +1184 -91
- package/dist/app-frame.js.map +1 -1
- package/dist/browsable-list.d.ts.map +1 -1
- package/dist/browsable-list.js +15 -5
- package/dist/browsable-list.js.map +1 -1
- package/dist/collection-surface.d.ts +8 -0
- package/dist/collection-surface.d.ts.map +1 -0
- package/dist/collection-surface.js +41 -0
- package/dist/collection-surface.js.map +1 -0
- package/dist/command-palette.d.ts.map +1 -1
- package/dist/command-palette.js +8 -3
- package/dist/command-palette.js.map +1 -1
- package/dist/commands.js +1 -1
- package/dist/commands.js.map +1 -1
- package/dist/eventbus.d.ts.map +1 -1
- package/dist/eventbus.js +21 -1
- package/dist/eventbus.js.map +1 -1
- package/dist/focus-area.d.ts +7 -0
- package/dist/focus-area.d.ts.map +1 -1
- package/dist/focus-area.js +33 -15
- package/dist/focus-area.js.map +1 -1
- package/dist/index.d.ts +7 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -2
- package/dist/index.js.map +1 -1
- package/dist/inspector-drawer.d.ts +36 -0
- package/dist/inspector-drawer.d.ts.map +1 -0
- package/dist/inspector-drawer.js +68 -0
- package/dist/inspector-drawer.js.map +1 -0
- package/dist/layout-node-surface.d.ts +8 -0
- package/dist/layout-node-surface.d.ts.map +1 -1
- package/dist/layout-node-surface.js +26 -7
- package/dist/layout-node-surface.js.map +1 -1
- package/dist/notification.d.ts +10 -1
- package/dist/notification.d.ts.map +1 -1
- package/dist/notification.js +142 -17
- package/dist/notification.js.map +1 -1
- package/dist/overlay.d.ts +8 -0
- package/dist/overlay.d.ts.map +1 -1
- package/dist/overlay.js +23 -10
- package/dist/overlay.js.map +1 -1
- package/dist/runtime-engine.d.ts +223 -0
- package/dist/runtime-engine.d.ts.map +1 -0
- package/dist/runtime-engine.js +457 -0
- package/dist/runtime-engine.js.map +1 -0
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +27 -7
- package/dist/runtime.js.map +1 -1
- package/dist/shell-quit.d.ts +12 -0
- package/dist/shell-quit.d.ts.map +1 -0
- package/dist/shell-quit.js +36 -0
- package/dist/shell-quit.js.map +1 -0
- package/dist/subapp/mount.d.ts.map +1 -1
- package/dist/subapp/mount.js +3 -0
- package/dist/subapp/mount.js.map +1 -1
- package/dist/types.d.ts +19 -5
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +11 -0
- package/dist/types.js.map +1 -1
- package/dist/view-output.d.ts +1 -0
- package/dist/view-output.d.ts.map +1 -1
- package/dist/view-output.js +25 -15
- package/dist/view-output.js.map +1 -1
- package/package.json +5 -2
package/dist/app-frame.js
CHANGED
|
@@ -4,23 +4,99 @@
|
|
|
4
4
|
* Provides tabs, pane focus/scroll isolation, shell key handling, help,
|
|
5
5
|
* panel-scoped overlay context, and optional frame-level command palette.
|
|
6
6
|
*/
|
|
7
|
-
import { createSurface, resolveClock, resolveSafeCtx, } from '@flyingrobots/bijou';
|
|
8
|
-
import {
|
|
7
|
+
import { createSurface, preparePreferenceSections, preferenceListSurface, resolvePreferenceRowLayout, resolveClock, resolveSafeCtx, } from '@flyingrobots/bijou';
|
|
8
|
+
import { helpViewSurface } from './help.js';
|
|
9
|
+
import { createKeyMap } from './keybindings.js';
|
|
9
10
|
import { isKeyMsg, isMouseMsg, isResizeMsg } from './types.js';
|
|
10
|
-
import {
|
|
11
|
+
import { quit } from './commands.js';
|
|
12
|
+
import { compositeSurfaceInto, drawer, modal } from './overlay.js';
|
|
13
|
+
import { isShellQuitConfirmAccept, isShellQuitConfirmDismiss, isShellQuitRequest, renderShellQuitOverlay, shouldUseShellQuitConfirm, } from './shell-quit.js';
|
|
11
14
|
import { commandPalette, commandPaletteKeyMap, } from './command-palette.js';
|
|
15
|
+
import { createPagerStateForSurface, pagerPageDown, pagerPageUp, pagerScrollBy, pagerScrollToBottom, pagerScrollToTop, pagerSurface, } from './pager.js';
|
|
12
16
|
import { restoreLayoutState } from './layout-preset.js';
|
|
13
|
-
import { createNotificationState, dismissNotification, hitTestNotificationStack, notificationsNeedTick, pushNotification, renderNotificationStack, tickNotifications, trimNotificationsToViewport, } from './notification.js';
|
|
17
|
+
import { countNotificationHistory, createNotificationState, dismissNotification, hitTestNotificationStack, notificationsNeedTick, pushNotification, renderNotificationHistorySurface, renderNotificationReviewEntrySurface, renderNotificationStack, tickNotifications, trimNotificationsToViewport, } from './notification.js';
|
|
18
|
+
import { insetLineSurface } from './collection-surface.js';
|
|
19
|
+
import { vstackSurface } from './surface-layout.js';
|
|
14
20
|
import { isFrameScopedMsg, isPageScopedMsg, wrapCmdForPage, emitMsg, emitMsgForPage, wrapFrameMsg, } from './app-frame-types.js';
|
|
15
21
|
import { createFrameKeyMap, frameBodyRect, mergeBindingSources, } from './app-frame-utils.js';
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
22
|
+
import { activeFrameLayer, describeFrameLayerStack, } from './app-frame-layers.js';
|
|
23
|
+
import { frameEndAnchor, frameMessage, frameNotificationCue, frameNotificationFilterLabel, frameStartAnchor, } from './app-frame-i18n.js';
|
|
24
|
+
import { resolveHeaderLine, renderHelpLine, renderPageContent, renderPageContentInto, renderMaximizedPane, renderMaximizedPaneInto, renderTransition, } from './app-frame-render.js';
|
|
25
|
+
import { applyFrameAction, scrollFocusedPane, switchTab, syncPageFrameState, } from './app-frame-actions.js';
|
|
26
|
+
import { handlePaletteKey, openCommandPalette, openSearchPalette, } from './app-frame-palette.js';
|
|
27
|
+
export { activeFrameLayer, describeFrameLayerStack, underlyingFrameLayer, } from './app-frame-layers.js';
|
|
19
28
|
// ---------------------------------------------------------------------------
|
|
20
29
|
// Frame Notification Helpers
|
|
21
30
|
// ---------------------------------------------------------------------------
|
|
22
31
|
const FRAME_NOTIFICATION_TICK_MS = 40;
|
|
23
32
|
const DEFAULT_FRAME_NOTIFICATION_DURATION_MS = 6_000;
|
|
33
|
+
const SETTINGS_FEEDBACK_TOAST_WIDTH = 40;
|
|
34
|
+
const DEFAULT_NOTIFICATION_CENTER_FILTERS = [
|
|
35
|
+
'ALL',
|
|
36
|
+
'ACTIONABLE',
|
|
37
|
+
'ERROR',
|
|
38
|
+
'WARNING',
|
|
39
|
+
'SUCCESS',
|
|
40
|
+
'INFO',
|
|
41
|
+
];
|
|
42
|
+
const quitHelpKeys = createKeyMap()
|
|
43
|
+
.group('Exit', (g) => g
|
|
44
|
+
.bind('q', 'Quit', { type: 'toggle-help' })
|
|
45
|
+
.bind('escape', 'Quit', { type: 'toggle-help' })
|
|
46
|
+
.bind('ctrl+c', 'Quit', { type: 'toggle-help' }));
|
|
47
|
+
const helpLayerHelpKeys = createKeyMap()
|
|
48
|
+
.group('Help', (g) => g
|
|
49
|
+
.bind('escape', 'Close help', { type: 'noop' })
|
|
50
|
+
.bind('?', 'Close help', { type: 'noop' })
|
|
51
|
+
.bind('up', 'Scroll up', { type: 'noop' })
|
|
52
|
+
.bind('down', 'Scroll down', { type: 'noop' })
|
|
53
|
+
.bind('j', 'Scroll down', { type: 'noop' })
|
|
54
|
+
.bind('k', 'Scroll up', { type: 'noop' })
|
|
55
|
+
.bind('d', 'Page down', { type: 'noop' })
|
|
56
|
+
.bind('u', 'Page up', { type: 'noop' })
|
|
57
|
+
.bind('g', 'Top', { type: 'noop' })
|
|
58
|
+
.bind('shift+g', 'Bottom', { type: 'noop' }));
|
|
59
|
+
const settingsHelpKeys = createKeyMap()
|
|
60
|
+
.group('Settings', (g) => g
|
|
61
|
+
.bind('escape', 'Close settings', { type: 'toggle-settings' })
|
|
62
|
+
.bind('f2', 'Close settings', { type: 'toggle-settings' })
|
|
63
|
+
.bind('up', 'Previous row', { type: 'scroll-up' })
|
|
64
|
+
.bind('down', 'Next row', { type: 'scroll-down' })
|
|
65
|
+
.bind('enter', 'Activate setting', { type: 'toggle-settings' })
|
|
66
|
+
.bind('space', 'Activate setting', { type: 'toggle-settings' })
|
|
67
|
+
.bind('j', 'Scroll down', { type: 'scroll-down' })
|
|
68
|
+
.bind('k', 'Scroll up', { type: 'scroll-up' })
|
|
69
|
+
.bind('d', 'Page down', { type: 'page-down' })
|
|
70
|
+
.bind('u', 'Page up', { type: 'page-up' })
|
|
71
|
+
.bind('g', 'Top', { type: 'top' })
|
|
72
|
+
.bind('shift+g', 'Bottom', { type: 'bottom' })
|
|
73
|
+
.bind('/', 'Search', { type: 'open-search' })
|
|
74
|
+
.bind('ctrl+p', 'Open command palette', { type: 'open-palette' })
|
|
75
|
+
.bind(':', 'Open command palette', { type: 'open-palette' })
|
|
76
|
+
.bind('?', 'Toggle help', { type: 'toggle-help' }));
|
|
77
|
+
const notificationCenterHelpKeys = createKeyMap()
|
|
78
|
+
.group('Notifications', (g) => g
|
|
79
|
+
.bind('shift+n', 'Close notification center', { type: 'noop' })
|
|
80
|
+
.bind('up', 'Scroll up', { type: 'noop' })
|
|
81
|
+
.bind('down', 'Scroll down', { type: 'noop' })
|
|
82
|
+
.bind('j', 'Scroll down', { type: 'noop' })
|
|
83
|
+
.bind('k', 'Scroll up', { type: 'noop' })
|
|
84
|
+
.bind('d', 'Page down', { type: 'noop' })
|
|
85
|
+
.bind('u', 'Page up', { type: 'noop' })
|
|
86
|
+
.bind('g', 'Top', { type: 'noop' })
|
|
87
|
+
.bind('shift+g', 'Bottom', { type: 'noop' })
|
|
88
|
+
.bind('f', 'Cycle filter', { type: 'noop' })
|
|
89
|
+
.bind('/', 'Search', { type: 'noop' })
|
|
90
|
+
.bind('ctrl+p', 'Open command palette', { type: 'noop' })
|
|
91
|
+
.bind(':', 'Open command palette', { type: 'noop' })
|
|
92
|
+
.bind('?', 'Toggle help', { type: 'noop' }));
|
|
93
|
+
const quitConfirmHelpKeys = createKeyMap()
|
|
94
|
+
.group('Quit', (g) => g
|
|
95
|
+
.bind('y', 'Quit', { type: 'noop' })
|
|
96
|
+
.bind('enter', 'Quit', { type: 'noop' })
|
|
97
|
+
.bind('n', 'Stay', { type: 'noop' })
|
|
98
|
+
.bind('escape', 'Stay', { type: 'noop' })
|
|
99
|
+
.bind('q', 'Stay', { type: 'noop' }));
|
|
24
100
|
function resolveFrameNotificationOptions(options) {
|
|
25
101
|
if (options.runtimeNotifications === false) {
|
|
26
102
|
return {
|
|
@@ -77,8 +153,13 @@ export function createFramedApp(options) {
|
|
|
77
153
|
if (!pagesById.has(defaultPageId)) {
|
|
78
154
|
throw new Error(`createFramedApp: defaultPageId "${defaultPageId}" not found in pages`);
|
|
79
155
|
}
|
|
80
|
-
const frameKeys = createFrameKeyMap(
|
|
156
|
+
const frameKeys = createFrameKeyMap({
|
|
157
|
+
enableSettings: options.settings != null,
|
|
158
|
+
enableNotifications: options.notificationCenter != null || options.runtimeNotifications !== false,
|
|
159
|
+
i18n: options.i18n,
|
|
160
|
+
});
|
|
81
161
|
const frameNotificationOptions = resolveFrameNotificationOptions(options);
|
|
162
|
+
let composedFrameScratch = null;
|
|
82
163
|
const paletteKeys = commandPaletteKeyMap({
|
|
83
164
|
focusNext: { type: 'cp-next' },
|
|
84
165
|
focusPrev: { type: 'cp-prev' },
|
|
@@ -87,69 +168,298 @@ export function createFramedApp(options) {
|
|
|
87
168
|
select: { type: 'cp-select' },
|
|
88
169
|
close: { type: 'cp-close' },
|
|
89
170
|
});
|
|
171
|
+
function getComposedFrameScratch(width, height) {
|
|
172
|
+
if (composedFrameScratch == null
|
|
173
|
+
|| composedFrameScratch.width !== width
|
|
174
|
+
|| composedFrameScratch.height !== height) {
|
|
175
|
+
composedFrameScratch = createSurface(width, height);
|
|
176
|
+
}
|
|
177
|
+
return composedFrameScratch;
|
|
178
|
+
}
|
|
90
179
|
function withObservedKey(model, cmds, msg, route) {
|
|
91
180
|
const observed = options.observeKey?.(msg, route);
|
|
92
181
|
if (observed === undefined)
|
|
93
182
|
return [...cmds];
|
|
94
183
|
return [emitMsgForPage(model.activePageId, observed), ...cmds];
|
|
95
184
|
}
|
|
185
|
+
function applyQuitRequest(model, msg) {
|
|
186
|
+
if (!shouldUseShellQuitConfirm()) {
|
|
187
|
+
return [model, withObservedKey(model, [quit()], msg, 'frame')];
|
|
188
|
+
}
|
|
189
|
+
if (model.quitConfirmOpen) {
|
|
190
|
+
return [model, withObservedKey(model, [], msg, 'frame')];
|
|
191
|
+
}
|
|
192
|
+
return [{
|
|
193
|
+
...model,
|
|
194
|
+
quitConfirmOpen: true,
|
|
195
|
+
helpOpen: false,
|
|
196
|
+
helpScrollY: 0,
|
|
197
|
+
settingsOpen: false,
|
|
198
|
+
notificationCenterOpen: false,
|
|
199
|
+
commandPalette: undefined,
|
|
200
|
+
commandPaletteEntries: undefined,
|
|
201
|
+
commandPaletteTitle: undefined,
|
|
202
|
+
commandPaletteKind: undefined,
|
|
203
|
+
}, withObservedKey(model, [], msg, 'frame')];
|
|
204
|
+
}
|
|
205
|
+
function closeCommandPalette(model) {
|
|
206
|
+
return {
|
|
207
|
+
...model,
|
|
208
|
+
commandPalette: undefined,
|
|
209
|
+
commandPaletteEntries: undefined,
|
|
210
|
+
commandPaletteTitle: undefined,
|
|
211
|
+
commandPaletteKind: undefined,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
function resolveLayerContext(model) {
|
|
215
|
+
const activePage = pagesById.get(model.activePageId);
|
|
216
|
+
const activePageModel = model.pageModels[model.activePageId];
|
|
217
|
+
const inputAreas = resolveInputAreas(activePage, activePageModel);
|
|
218
|
+
const activeInputArea = findInputAreaByPaneId(inputAreas, model.focusedPaneByPage[model.activePageId]);
|
|
219
|
+
const modalKeyMap = activePage.modalKeyMap?.(activePageModel);
|
|
220
|
+
const pageModalOpen = modalKeyMap != null;
|
|
221
|
+
const activeLayer = activeFrameLayer(model, { pageModalOpen });
|
|
222
|
+
return {
|
|
223
|
+
activePage,
|
|
224
|
+
activePageModel,
|
|
225
|
+
inputAreas,
|
|
226
|
+
activeInputArea,
|
|
227
|
+
modalKeyMap,
|
|
228
|
+
pageModalOpen,
|
|
229
|
+
activeLayer,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
function resolveWorkspaceHelpSource(activePage, activeInputArea) {
|
|
233
|
+
return mergeBindingSources(frameKeys, quitHelpKeys, options.globalKeys, activeInputArea?.helpSource ?? activeInputArea?.keyMap, activePage.helpSource ?? activePage.keyMap);
|
|
234
|
+
}
|
|
235
|
+
function resolveWorkspaceHintSource(model, activePage, activeInputArea) {
|
|
236
|
+
const helpLineOverride = options.helpLineSource?.({
|
|
237
|
+
model,
|
|
238
|
+
activePage,
|
|
239
|
+
frameKeys,
|
|
240
|
+
globalKeys: options.globalKeys,
|
|
241
|
+
});
|
|
242
|
+
if (typeof helpLineOverride === 'string') {
|
|
243
|
+
return helpLineOverride;
|
|
244
|
+
}
|
|
245
|
+
return helpLineOverride ?? mergeBindingSources(frameKeys, options.globalKeys, activeInputArea?.helpSource ?? activeInputArea?.keyMap, activePage.helpSource ?? activePage.keyMap);
|
|
246
|
+
}
|
|
247
|
+
function resolveLayerMetadata(model, activePage, activeInputArea, modalKeyMap) {
|
|
248
|
+
const settings = resolveFrameSettings(model, options, pagesById);
|
|
249
|
+
const notificationCenter = resolveFrameNotificationCenter(model, options, pagesById);
|
|
250
|
+
const workspaceHintSource = resolveWorkspaceHintSource(model, activePage, activeInputArea);
|
|
251
|
+
const workspaceHelpSource = resolveWorkspaceHelpSource(activePage, activeInputArea);
|
|
252
|
+
const paletteHint = frameMessage(options.i18n, 'palette.hint', 'Enter select • Esc close');
|
|
253
|
+
const helpHint = frameMessage(options.i18n, 'help.hint', 'j/k scroll • d/u page • g/G top/bottom • mouse wheel • ?/Esc close');
|
|
254
|
+
const settingsHint = frameMessage(options.i18n, 'settings.footer', 'F2/Esc close • ↑/↓ rows • Enter toggle • / search • q quit');
|
|
255
|
+
const notificationsHint = frameMessage(options.i18n, 'notifications.footer', 'Shift+N close • f filter • j/k scroll • q quit');
|
|
256
|
+
const quitHint = frameMessage(options.i18n, 'quit.footer', 'Y quit • N stay');
|
|
257
|
+
const paletteTitle = model.commandPaletteTitle
|
|
258
|
+
?? frameMessage(options.i18n, 'palette.title', 'Command Palette');
|
|
259
|
+
const searchTitle = model.commandPaletteTitle
|
|
260
|
+
?? activePage.searchTitle
|
|
261
|
+
?? frameMessage(options.i18n, 'search.title', 'Search');
|
|
262
|
+
const notificationsTitle = notificationCenter == null
|
|
263
|
+
? frameMessage(options.i18n, 'notifications.title', 'Notifications')
|
|
264
|
+
: `${notificationCenter.title} • ${frameNotificationFilterLabel(options.i18n, notificationCenter.activeFilter)}`;
|
|
265
|
+
return {
|
|
266
|
+
workspace: {
|
|
267
|
+
title: activePage.title,
|
|
268
|
+
hintSource: workspaceHintSource,
|
|
269
|
+
helpSource: workspaceHelpSource,
|
|
270
|
+
},
|
|
271
|
+
'page-modal': {
|
|
272
|
+
title: activePage.title,
|
|
273
|
+
hintSource: modalKeyMap ?? activePage.helpSource ?? activePage.keyMap,
|
|
274
|
+
helpSource: mergeBindingSources(quitHelpKeys, modalKeyMap, activePage.helpSource ?? activePage.keyMap),
|
|
275
|
+
},
|
|
276
|
+
settings: {
|
|
277
|
+
title: settings?.title ?? frameMessage(options.i18n, 'settings.title', 'Settings'),
|
|
278
|
+
hintSource: settingsHint,
|
|
279
|
+
helpSource: mergeBindingSources(settingsHelpKeys, quitHelpKeys),
|
|
280
|
+
},
|
|
281
|
+
help: {
|
|
282
|
+
title: frameMessage(options.i18n, 'help.title', 'Keyboard Help'),
|
|
283
|
+
hintSource: helpHint,
|
|
284
|
+
helpSource: helpLayerHelpKeys,
|
|
285
|
+
},
|
|
286
|
+
'notification-center': {
|
|
287
|
+
title: notificationsTitle,
|
|
288
|
+
hintSource: notificationsHint,
|
|
289
|
+
helpSource: mergeBindingSources(notificationCenterHelpKeys, quitHelpKeys),
|
|
290
|
+
},
|
|
291
|
+
search: {
|
|
292
|
+
title: searchTitle,
|
|
293
|
+
hintSource: paletteHint,
|
|
294
|
+
helpSource: mergeBindingSources(paletteKeys, quitHelpKeys),
|
|
295
|
+
},
|
|
296
|
+
'command-palette': {
|
|
297
|
+
title: paletteTitle,
|
|
298
|
+
hintSource: paletteHint,
|
|
299
|
+
helpSource: mergeBindingSources(paletteKeys, quitHelpKeys),
|
|
300
|
+
},
|
|
301
|
+
'quit-confirm': {
|
|
302
|
+
title: frameMessage(options.i18n, 'quit.title', 'Quit?'),
|
|
303
|
+
hintSource: quitHint,
|
|
304
|
+
helpSource: quitConfirmHelpKeys,
|
|
305
|
+
},
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
function resolvePresentedLayerContext(model) {
|
|
309
|
+
const { activePage, activePageModel, inputAreas, activeInputArea, modalKeyMap, pageModalOpen, } = resolveLayerContext(model);
|
|
310
|
+
const layerStack = describeFrameLayerStack(model, {
|
|
311
|
+
pageModalOpen,
|
|
312
|
+
layers: resolveLayerMetadata(model, activePage, activeInputArea, modalKeyMap),
|
|
313
|
+
});
|
|
314
|
+
const activeLayer = layerStack[layerStack.length - 1];
|
|
315
|
+
const underlyingLayer = layerStack.length > 1 ? layerStack[layerStack.length - 2] : undefined;
|
|
316
|
+
return {
|
|
317
|
+
activePage,
|
|
318
|
+
activePageModel,
|
|
319
|
+
inputAreas,
|
|
320
|
+
activeInputArea,
|
|
321
|
+
modalKeyMap,
|
|
322
|
+
pageModalOpen,
|
|
323
|
+
layerStack,
|
|
324
|
+
activeLayer,
|
|
325
|
+
underlyingLayer,
|
|
326
|
+
};
|
|
327
|
+
}
|
|
96
328
|
function updateTargetPage(model, targetPageId, targetMsg) {
|
|
97
329
|
const targetPage = pagesById.get(targetPageId);
|
|
98
330
|
if (targetPage == null)
|
|
99
331
|
return [model, []];
|
|
100
332
|
const pageModel = model.pageModels[targetPageId];
|
|
101
333
|
const updateResult = targetPage.update(targetMsg, pageModel);
|
|
102
|
-
|
|
103
|
-
let cmds = [];
|
|
104
|
-
if (updateResult !== undefined && updateResult !== null) {
|
|
105
|
-
if (Array.isArray(updateResult)) {
|
|
106
|
-
nextPageModel = (updateResult[0] ?? pageModel);
|
|
107
|
-
cmds = (updateResult[1] ?? []);
|
|
108
|
-
}
|
|
109
|
-
else {
|
|
110
|
-
nextPageModel = updateResult;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
334
|
+
const [nextPageModel, cmds = []] = updateResult;
|
|
113
335
|
const nextModels = { ...model.pageModels, [targetPageId]: nextPageModel };
|
|
114
336
|
const synced = syncPageFrameState({ ...model, pageModels: nextModels }, targetPageId, pagesById);
|
|
115
|
-
const wrappedCmds =
|
|
116
|
-
? cmds.map((cmd) => wrapCmdForPage(targetPageId, cmd))
|
|
117
|
-
: [];
|
|
337
|
+
const wrappedCmds = cmds.map((cmd) => wrapCmdForPage(targetPageId, cmd));
|
|
118
338
|
return [synced, wrappedCmds];
|
|
119
339
|
}
|
|
120
340
|
function handleFrameMouse(msg, model) {
|
|
121
|
-
|
|
341
|
+
const { activePage, activePageModel, inputAreas, activeLayer, } = resolveLayerContext(model);
|
|
342
|
+
if (activeLayer.kind === 'help') {
|
|
343
|
+
if (msg.action === 'scroll-up' || msg.action === 'scroll-down') {
|
|
344
|
+
return [applyHelpScroll(model, activePage, msg.action === 'scroll-down' ? 3 : -3, frameKeys, paletteKeys, options, pagesById), []];
|
|
345
|
+
}
|
|
346
|
+
return [model, []];
|
|
347
|
+
}
|
|
348
|
+
if (activeLayer.kind === 'search' || activeLayer.kind === 'command-palette') {
|
|
122
349
|
return [model, []];
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
350
|
+
}
|
|
351
|
+
if (activeLayer.kind === 'quit-confirm' || activeLayer.kind === 'page-modal') {
|
|
352
|
+
return [model, []];
|
|
353
|
+
}
|
|
354
|
+
if (activeLayer.kind === 'settings') {
|
|
355
|
+
const layout = resolveSettingsLayout(model, options, pagesById);
|
|
356
|
+
if (layout != null) {
|
|
357
|
+
if (msg.action === 'scroll-up' || msg.action === 'scroll-down') {
|
|
358
|
+
if (isInsideSettingsDrawer(msg.col, msg.row, layout, model)) {
|
|
359
|
+
return [
|
|
360
|
+
scrollSettingsBy(model, layout, msg.action === 'scroll-down' ? 3 : -3),
|
|
361
|
+
[],
|
|
362
|
+
];
|
|
363
|
+
}
|
|
364
|
+
return [model, []];
|
|
365
|
+
}
|
|
366
|
+
if (msg.action === 'press' && msg.button === 'left') {
|
|
367
|
+
if (!isInsideSettingsDrawer(msg.col, msg.row, layout, model)) {
|
|
368
|
+
return [model, []];
|
|
369
|
+
}
|
|
370
|
+
const hit = settingsRowAtPosition(msg.col, msg.row, model, layout);
|
|
371
|
+
if (hit == null)
|
|
372
|
+
return [model, []];
|
|
373
|
+
const focusedModel = { ...model, settingsFocusIndex: hit.index };
|
|
374
|
+
if (hit.row.action === undefined || hit.row.enabled === false || hit.row.kind === 'info') {
|
|
375
|
+
return [focusedModel, []];
|
|
376
|
+
}
|
|
377
|
+
return activateSettingsRow(focusedModel, hit.row);
|
|
378
|
+
}
|
|
379
|
+
return [model, []];
|
|
136
380
|
}
|
|
137
|
-
|
|
381
|
+
}
|
|
382
|
+
if (activeLayer.kind === 'notification-center') {
|
|
383
|
+
const layout = resolveNotificationCenterLayout(model, options, pagesById);
|
|
384
|
+
if (layout != null) {
|
|
385
|
+
if (msg.action === 'scroll-up' || msg.action === 'scroll-down') {
|
|
386
|
+
if (isInsideNotificationCenterDrawer(msg.col, msg.row, layout, model)) {
|
|
387
|
+
return [
|
|
388
|
+
scrollNotificationCenterBy(model, layout, msg.action === 'scroll-down' ? 3 : -3),
|
|
389
|
+
[],
|
|
390
|
+
];
|
|
391
|
+
}
|
|
392
|
+
return [model, []];
|
|
393
|
+
}
|
|
394
|
+
if (msg.action === 'press' && msg.button === 'left') {
|
|
395
|
+
return [model, []];
|
|
396
|
+
}
|
|
138
397
|
return [model, []];
|
|
139
398
|
}
|
|
140
399
|
}
|
|
141
|
-
if (msg.
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
400
|
+
if (msg.action === 'press' && msg.button === 'left') {
|
|
401
|
+
if (frameNotificationOptions.enabled) {
|
|
402
|
+
const nowMs = resolveClock(resolveSafeCtx()).now();
|
|
403
|
+
const notificationTarget = hitTestNotificationStack(model.runtimeNotifications, {
|
|
404
|
+
screenWidth: model.columns,
|
|
405
|
+
screenHeight: model.rows,
|
|
406
|
+
margin: frameNotificationOptions.margin,
|
|
407
|
+
gap: frameNotificationOptions.gap,
|
|
408
|
+
ctx: resolveSafeCtx() ?? undefined,
|
|
409
|
+
}, msg.col, msg.row);
|
|
410
|
+
if (notificationTarget?.kind === 'dismiss') {
|
|
411
|
+
return applyFrameNotificationState(model, dismissNotification(model.runtimeNotifications, notificationTarget.item.id, nowMs), nowMs);
|
|
412
|
+
}
|
|
413
|
+
if (notificationTarget != null) {
|
|
414
|
+
return [model, []];
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
if (msg.row === 0) {
|
|
418
|
+
const header = resolveHeaderLine(model, options, pagesById);
|
|
419
|
+
const tab = header.tabTargets.find((target) => msg.col >= target.startCol && msg.col <= target.endCol);
|
|
420
|
+
if (tab != null) {
|
|
421
|
+
const currentIndex = model.pageOrder.indexOf(model.activePageId);
|
|
422
|
+
const nextIndex = model.pageOrder.indexOf(tab.pageId);
|
|
423
|
+
if (currentIndex >= 0 && nextIndex >= 0 && nextIndex !== currentIndex) {
|
|
424
|
+
return switchTab(model, nextIndex - currentIndex, pagesById, options);
|
|
425
|
+
}
|
|
426
|
+
return [model, []];
|
|
149
427
|
}
|
|
150
428
|
return [model, []];
|
|
151
429
|
}
|
|
152
|
-
|
|
430
|
+
const clickedPane = paneHitAtPosition(model, msg.col, msg.row, pagesById, options);
|
|
431
|
+
if (clickedPane != null) {
|
|
432
|
+
const focusedModel = focusPane(model, clickedPane.paneId);
|
|
433
|
+
const inputArea = findInputAreaByPaneId(inputAreas, clickedPane.paneId);
|
|
434
|
+
const areaMsg = inputArea?.mouse?.({
|
|
435
|
+
msg,
|
|
436
|
+
model: activePageModel,
|
|
437
|
+
rect: clickedPane.rect,
|
|
438
|
+
});
|
|
439
|
+
if (areaMsg !== undefined) {
|
|
440
|
+
return [focusedModel, [emitMsgForPage(model.activePageId, areaMsg)]];
|
|
441
|
+
}
|
|
442
|
+
return [focusedModel, [emitMsgForPage(model.activePageId, msg)]];
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
if (msg.action === 'scroll-up' || msg.action === 'scroll-down') {
|
|
446
|
+
const hoveredPane = paneHitAtPosition(model, msg.col, msg.row, pagesById, options);
|
|
447
|
+
if (hoveredPane != null) {
|
|
448
|
+
const focusedModel = focusPane(model, hoveredPane.paneId);
|
|
449
|
+
const inputArea = findInputAreaByPaneId(inputAreas, hoveredPane.paneId);
|
|
450
|
+
const areaMsg = inputArea?.mouse?.({
|
|
451
|
+
msg,
|
|
452
|
+
model: activePageModel,
|
|
453
|
+
rect: hoveredPane.rect,
|
|
454
|
+
});
|
|
455
|
+
if (areaMsg !== undefined) {
|
|
456
|
+
return [focusedModel, [emitMsgForPage(model.activePageId, areaMsg)]];
|
|
457
|
+
}
|
|
458
|
+
const action = msg.action === 'scroll-down'
|
|
459
|
+
? { type: 'scroll-down' }
|
|
460
|
+
: { type: 'scroll-up' };
|
|
461
|
+
return [scrollFocusedPane(focusedModel, action, pagesById, options), []];
|
|
462
|
+
}
|
|
153
463
|
}
|
|
154
464
|
return undefined;
|
|
155
465
|
}
|
|
@@ -171,6 +481,32 @@ export function createFramedApp(options) {
|
|
|
171
481
|
}
|
|
172
482
|
return [nextModel, []];
|
|
173
483
|
}
|
|
484
|
+
function activateSettingsRow(model, row) {
|
|
485
|
+
if (row.action === undefined || row.enabled === false || row.kind === 'info') {
|
|
486
|
+
return [model, []];
|
|
487
|
+
}
|
|
488
|
+
const cmds = [emitMsgForPage(model.activePageId, row.action)];
|
|
489
|
+
if (!frameNotificationOptions.enabled) {
|
|
490
|
+
return [model, cmds];
|
|
491
|
+
}
|
|
492
|
+
const feedback = row.feedback ?? {
|
|
493
|
+
title: 'Setting updated',
|
|
494
|
+
message: `${row.label} updated.`,
|
|
495
|
+
};
|
|
496
|
+
const nowMs = resolveClock(resolveSafeCtx()).now();
|
|
497
|
+
const notifications = pushNotification(model.runtimeNotifications, {
|
|
498
|
+
title: feedback.title ?? 'Setting updated',
|
|
499
|
+
message: feedback.message,
|
|
500
|
+
variant: 'TOAST',
|
|
501
|
+
tone: feedback.tone ?? 'INFO',
|
|
502
|
+
width: SETTINGS_FEEDBACK_TOAST_WIDTH,
|
|
503
|
+
placement: frameNotificationOptions.placement,
|
|
504
|
+
durationMs: feedback.durationMs ?? 2_500,
|
|
505
|
+
overflow: frameNotificationOptions.overflow,
|
|
506
|
+
}, nowMs);
|
|
507
|
+
const [nextModel, notificationCmds] = applyFrameNotificationState(model, notifications, nowMs);
|
|
508
|
+
return [nextModel, [...cmds, ...notificationCmds]];
|
|
509
|
+
}
|
|
174
510
|
const app = {
|
|
175
511
|
init() {
|
|
176
512
|
const pageModels = {};
|
|
@@ -189,6 +525,14 @@ export function createFramedApp(options) {
|
|
|
189
525
|
columns: Math.max(1, options.initialColumns ?? 80),
|
|
190
526
|
rows: Math.max(1, options.initialRows ?? 24),
|
|
191
527
|
helpOpen: false,
|
|
528
|
+
helpScrollY: 0,
|
|
529
|
+
commandPaletteKind: undefined,
|
|
530
|
+
settingsOpen: false,
|
|
531
|
+
notificationCenterOpen: false,
|
|
532
|
+
quitConfirmOpen: false,
|
|
533
|
+
settingsFocusIndex: 0,
|
|
534
|
+
settingsScrollY: 0,
|
|
535
|
+
notificationCenterScrollY: 0,
|
|
192
536
|
transitionProgress: 1,
|
|
193
537
|
transitionGeneration: 0,
|
|
194
538
|
transitionFrame: 0,
|
|
@@ -197,6 +541,7 @@ export function createFramedApp(options) {
|
|
|
197
541
|
dockStateByPage: {},
|
|
198
542
|
splitRatioOverrides: {},
|
|
199
543
|
runtimeNotifications: createNotificationState(),
|
|
544
|
+
runtimeNotificationHistoryFilter: 'ALL',
|
|
200
545
|
runtimeNotificationLoopActive: false,
|
|
201
546
|
};
|
|
202
547
|
for (const pageId of pageOrder) {
|
|
@@ -297,32 +642,226 @@ export function createFramedApp(options) {
|
|
|
297
642
|
}, []];
|
|
298
643
|
}
|
|
299
644
|
if (isKeyMsg(msg)) {
|
|
300
|
-
|
|
645
|
+
const { activePage, activeInputArea, modalKeyMap, activeLayer, } = resolveLayerContext(model);
|
|
646
|
+
if (activeLayer.kind === 'search' || activeLayer.kind === 'command-palette') {
|
|
647
|
+
if (msg.ctrl && !msg.alt && msg.key === 'c') {
|
|
648
|
+
return applyQuitRequest(model, msg);
|
|
649
|
+
}
|
|
650
|
+
if (!msg.ctrl && !msg.alt && !msg.shift && msg.key === 'escape') {
|
|
651
|
+
return [closeCommandPalette(model), withObservedKey(model, [], msg, 'palette')];
|
|
652
|
+
}
|
|
653
|
+
const frameAction = frameKeys.handle(msg);
|
|
654
|
+
if (frameAction?.type === 'open-search') {
|
|
655
|
+
if (activeLayer.kind === 'search') {
|
|
656
|
+
return [closeCommandPalette(model), withObservedKey(model, [], msg, 'palette')];
|
|
657
|
+
}
|
|
658
|
+
return [openSearchPalette(model, frameKeys, options, pagesById), withObservedKey(model, [], msg, 'palette')];
|
|
659
|
+
}
|
|
660
|
+
if (frameAction?.type === 'open-palette') {
|
|
661
|
+
if (activeLayer.kind === 'command-palette') {
|
|
662
|
+
return [closeCommandPalette(model), withObservedKey(model, [], msg, 'palette')];
|
|
663
|
+
}
|
|
664
|
+
return [openCommandPalette(model, frameKeys, options, pagesById), withObservedKey(model, [], msg, 'palette')];
|
|
665
|
+
}
|
|
666
|
+
if (frameAction?.type === 'toggle-notifications') {
|
|
667
|
+
const [nextModel, cmds] = applyFrameAction(frameAction, closeCommandPalette(model), options, pagesById);
|
|
668
|
+
return [nextModel, withObservedKey(model, cmds, msg, 'palette')];
|
|
669
|
+
}
|
|
301
670
|
const [nextModel, cmds] = handlePaletteKey(msg, model, paletteKeys, options, pagesById);
|
|
302
671
|
return [nextModel, withObservedKey(model, cmds, msg, 'palette')];
|
|
303
672
|
}
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
673
|
+
if (activeLayer.kind === 'help') {
|
|
674
|
+
if (!msg.ctrl && !msg.alt && (msg.key === '?' || msg.key === 'escape')) {
|
|
675
|
+
return [{ ...model, helpOpen: false, helpScrollY: 0 }, withObservedKey(model, [], msg, 'help')];
|
|
676
|
+
}
|
|
677
|
+
if (isShellQuitRequest(msg)) {
|
|
678
|
+
return applyQuitRequest(model, msg);
|
|
679
|
+
}
|
|
680
|
+
const helpAction = frameKeys.handle(msg);
|
|
681
|
+
if (helpAction && isHelpScrollAction(helpAction)) {
|
|
682
|
+
return [
|
|
683
|
+
applyHelpScrollAction(model, activePage, helpAction, frameKeys, paletteKeys, options, pagesById),
|
|
684
|
+
withObservedKey(model, [], msg, 'help'),
|
|
685
|
+
];
|
|
308
686
|
}
|
|
309
687
|
return [model, withObservedKey(model, [], msg, 'help')];
|
|
310
688
|
}
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
689
|
+
if (activeLayer.kind === 'settings') {
|
|
690
|
+
const layout = resolveSettingsLayout(model, options, pagesById);
|
|
691
|
+
if (layout != null) {
|
|
692
|
+
const settingsFrameAction = frameKeys.handle(msg);
|
|
693
|
+
if (!msg.ctrl && !msg.alt && msg.key === 'escape') {
|
|
694
|
+
return [{
|
|
695
|
+
...model,
|
|
696
|
+
settingsOpen: false,
|
|
697
|
+
}, withObservedKey(model, [], msg, 'frame')];
|
|
698
|
+
}
|
|
699
|
+
if (msg.ctrl && !msg.alt && msg.key === ',') {
|
|
700
|
+
return [{
|
|
701
|
+
...model,
|
|
702
|
+
settingsOpen: false,
|
|
703
|
+
}, withObservedKey(model, [], msg, 'frame')];
|
|
704
|
+
}
|
|
705
|
+
if (!msg.ctrl && !msg.alt && msg.key === 'f2') {
|
|
706
|
+
return [{
|
|
707
|
+
...model,
|
|
708
|
+
settingsOpen: false,
|
|
709
|
+
}, withObservedKey(model, [], msg, 'frame')];
|
|
710
|
+
}
|
|
711
|
+
if (!msg.ctrl && !msg.alt && msg.key === '?') {
|
|
712
|
+
return [{ ...model, helpOpen: true }, withObservedKey(model, [], msg, 'frame')];
|
|
713
|
+
}
|
|
714
|
+
if (isShellQuitRequest(msg)) {
|
|
715
|
+
return applyQuitRequest(model, msg);
|
|
716
|
+
}
|
|
717
|
+
if (options.enableCommandPalette && !msg.ctrl && !msg.alt && msg.key === '/') {
|
|
718
|
+
return [openSearchPalette(model, frameKeys, options, pagesById), withObservedKey(model, [], msg, 'frame')];
|
|
719
|
+
}
|
|
720
|
+
if (options.enableCommandPalette && ((msg.ctrl && !msg.alt && msg.key === 'p') || (!msg.ctrl && !msg.alt && msg.key === ':'))) {
|
|
721
|
+
return [openCommandPalette(model, frameKeys, options, pagesById), withObservedKey(model, [], msg, 'frame')];
|
|
722
|
+
}
|
|
723
|
+
if (settingsFrameAction?.type === 'toggle-notifications') {
|
|
724
|
+
const [nextModel, cmds] = applyFrameAction(settingsFrameAction, model, options, pagesById);
|
|
725
|
+
return [nextModel, withObservedKey(model, cmds, msg, 'frame')];
|
|
726
|
+
}
|
|
727
|
+
if (!msg.ctrl && !msg.alt && msg.key === 'up') {
|
|
728
|
+
return [moveSettingsFocus(model, layout, -1), withObservedKey(model, [], msg, 'frame')];
|
|
729
|
+
}
|
|
730
|
+
if (!msg.ctrl && !msg.alt && msg.key === 'down') {
|
|
731
|
+
return [moveSettingsFocus(model, layout, 1), withObservedKey(model, [], msg, 'frame')];
|
|
732
|
+
}
|
|
733
|
+
if (!msg.ctrl && !msg.alt && msg.key === 'j') {
|
|
734
|
+
return [scrollSettingsBy(model, layout, 1), withObservedKey(model, [], msg, 'frame')];
|
|
735
|
+
}
|
|
736
|
+
if (!msg.ctrl && !msg.alt && msg.key === 'k') {
|
|
737
|
+
return [scrollSettingsBy(model, layout, -1), withObservedKey(model, [], msg, 'frame')];
|
|
738
|
+
}
|
|
739
|
+
if (!msg.ctrl && !msg.alt && msg.key === 'd') {
|
|
740
|
+
return [scrollSettingsBy(model, layout, Math.max(1, layout.contentHeight - 1)), withObservedKey(model, [], msg, 'frame')];
|
|
741
|
+
}
|
|
742
|
+
if (!msg.ctrl && !msg.alt && msg.key === 'u') {
|
|
743
|
+
return [scrollSettingsBy(model, layout, -Math.max(1, layout.contentHeight - 1)), withObservedKey(model, [], msg, 'frame')];
|
|
744
|
+
}
|
|
745
|
+
if (!msg.ctrl && !msg.alt && msg.key === 'g') {
|
|
746
|
+
return [{ ...model, settingsScrollY: 0 }, withObservedKey(model, [], msg, 'frame')];
|
|
747
|
+
}
|
|
748
|
+
if (!msg.ctrl && !msg.alt && msg.key === 'G') {
|
|
749
|
+
return [{ ...model, settingsScrollY: layout.maxScrollY }, withObservedKey(model, [], msg, 'frame')];
|
|
750
|
+
}
|
|
751
|
+
if (!msg.ctrl && !msg.alt && (msg.key === 'enter' || msg.key === 'space')) {
|
|
752
|
+
const row = layout.rows[clampSettingsFocus(model, layout)]?.row;
|
|
753
|
+
if (row?.action !== undefined && row.enabled !== false && row.kind !== 'info') {
|
|
754
|
+
const [nextModel, cmds] = activateSettingsRow(model, row);
|
|
755
|
+
return [nextModel, withObservedKey(model, cmds, msg, 'frame')];
|
|
756
|
+
}
|
|
757
|
+
return [model, withObservedKey(model, [], msg, 'frame')];
|
|
758
|
+
}
|
|
759
|
+
return [model, withObservedKey(model, [], msg, 'frame')];
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
if (activeLayer.kind === 'notification-center') {
|
|
763
|
+
const layout = resolveNotificationCenterLayout(model, options, pagesById);
|
|
764
|
+
if (layout != null) {
|
|
765
|
+
const centerFrameAction = frameKeys.handle(msg);
|
|
766
|
+
if (!msg.ctrl && !msg.alt && msg.key === 'escape') {
|
|
767
|
+
return [{
|
|
768
|
+
...model,
|
|
769
|
+
notificationCenterOpen: false,
|
|
770
|
+
notificationCenterScrollY: 0,
|
|
771
|
+
}, withObservedKey(model, [], msg, 'frame')];
|
|
772
|
+
}
|
|
773
|
+
if (isShellQuitRequest(msg)) {
|
|
774
|
+
return applyQuitRequest(model, msg);
|
|
775
|
+
}
|
|
776
|
+
if (centerFrameAction?.type === 'toggle-notifications') {
|
|
777
|
+
const [nextModel, cmds] = applyFrameAction(centerFrameAction, model, options, pagesById);
|
|
778
|
+
return [nextModel, withObservedKey(model, cmds, msg, 'frame')];
|
|
779
|
+
}
|
|
780
|
+
if (!msg.ctrl && !msg.alt && msg.key === 'f2') {
|
|
781
|
+
const [nextModel, cmds] = applyFrameAction({ type: 'toggle-settings' }, model, options, pagesById);
|
|
782
|
+
return [nextModel, withObservedKey(model, cmds, msg, 'frame')];
|
|
783
|
+
}
|
|
784
|
+
if (!msg.ctrl && !msg.alt && msg.key === '?') {
|
|
785
|
+
return [{
|
|
786
|
+
...model,
|
|
787
|
+
helpOpen: true,
|
|
788
|
+
notificationCenterOpen: false,
|
|
789
|
+
notificationCenterScrollY: 0,
|
|
790
|
+
}, withObservedKey(model, [], msg, 'frame')];
|
|
791
|
+
}
|
|
792
|
+
if (options.enableCommandPalette && !msg.ctrl && !msg.alt && msg.key === '/') {
|
|
793
|
+
return [openSearchPalette({
|
|
794
|
+
...model,
|
|
795
|
+
notificationCenterOpen: false,
|
|
796
|
+
notificationCenterScrollY: 0,
|
|
797
|
+
}, frameKeys, options, pagesById), withObservedKey(model, [], msg, 'frame')];
|
|
798
|
+
}
|
|
799
|
+
if (options.enableCommandPalette && ((msg.ctrl && !msg.alt && msg.key === 'p') || (!msg.ctrl && !msg.alt && msg.key === ':'))) {
|
|
800
|
+
return [openCommandPalette({
|
|
801
|
+
...model,
|
|
802
|
+
notificationCenterOpen: false,
|
|
803
|
+
notificationCenterScrollY: 0,
|
|
804
|
+
}, frameKeys, options, pagesById), withObservedKey(model, [], msg, 'frame')];
|
|
805
|
+
}
|
|
806
|
+
if (!msg.ctrl && !msg.alt && (msg.key === 'up' || msg.key === 'k')) {
|
|
807
|
+
return [scrollNotificationCenterBy(model, layout, -1), withObservedKey(model, [], msg, 'frame')];
|
|
808
|
+
}
|
|
809
|
+
if (!msg.ctrl && !msg.alt && (msg.key === 'down' || msg.key === 'j')) {
|
|
810
|
+
return [scrollNotificationCenterBy(model, layout, 1), withObservedKey(model, [], msg, 'frame')];
|
|
811
|
+
}
|
|
812
|
+
if (!msg.ctrl && !msg.alt && msg.key === 'd') {
|
|
813
|
+
return [scrollNotificationCenterBy(model, layout, Math.max(1, layout.contentHeight - 2)), withObservedKey(model, [], msg, 'frame')];
|
|
814
|
+
}
|
|
815
|
+
if (!msg.ctrl && !msg.alt && msg.key === 'u') {
|
|
816
|
+
return [scrollNotificationCenterBy(model, layout, -Math.max(1, layout.contentHeight - 2)), withObservedKey(model, [], msg, 'frame')];
|
|
817
|
+
}
|
|
818
|
+
if (!msg.ctrl && !msg.alt && msg.key === 'g') {
|
|
819
|
+
return [{ ...model, notificationCenterScrollY: 0 }, withObservedKey(model, [], msg, 'frame')];
|
|
820
|
+
}
|
|
821
|
+
if (!msg.ctrl && !msg.alt && msg.key === 'G') {
|
|
822
|
+
return [{ ...model, notificationCenterScrollY: layout.maxScrollY }, withObservedKey(model, [], msg, 'frame')];
|
|
823
|
+
}
|
|
824
|
+
if (!msg.ctrl && !msg.alt && msg.key === 'f') {
|
|
825
|
+
const [nextModel, cmds] = cycleNotificationCenterFilter(model, layout);
|
|
826
|
+
return [nextModel, withObservedKey(model, cmds, msg, 'frame')];
|
|
827
|
+
}
|
|
828
|
+
return [model, withObservedKey(model, [], msg, 'frame')];
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
if (activeLayer.kind === 'quit-confirm') {
|
|
832
|
+
if (isShellQuitConfirmAccept(msg)) {
|
|
833
|
+
return [{
|
|
834
|
+
...model,
|
|
835
|
+
quitConfirmOpen: false,
|
|
836
|
+
}, withObservedKey(model, [quit()], msg, 'frame')];
|
|
837
|
+
}
|
|
838
|
+
if (isShellQuitConfirmDismiss(msg)) {
|
|
839
|
+
return [{
|
|
840
|
+
...model,
|
|
841
|
+
quitConfirmOpen: false,
|
|
842
|
+
}, withObservedKey(model, [], msg, 'frame')];
|
|
843
|
+
}
|
|
844
|
+
return [model, withObservedKey(model, [], msg, 'frame')];
|
|
845
|
+
}
|
|
846
|
+
if (activeLayer.kind === 'page-modal' && modalKeyMap != null) {
|
|
315
847
|
const modalAction = modalKeyMap.handle(msg);
|
|
316
848
|
if (modalAction !== undefined) {
|
|
317
849
|
return [model, withObservedKey(model, [emitMsgForPage(model.activePageId, modalAction)], msg, 'page')];
|
|
318
850
|
}
|
|
319
851
|
return [model, withObservedKey(model, [], msg, 'page')];
|
|
320
852
|
}
|
|
853
|
+
if (isShellQuitRequest(msg)) {
|
|
854
|
+
return applyQuitRequest(model, msg);
|
|
855
|
+
}
|
|
856
|
+
const paneAction = activeInputArea?.keyMap?.handle(msg);
|
|
321
857
|
const pageAction = activePage.keyMap?.handle(msg);
|
|
322
858
|
const globalAction = options.globalKeys?.handle(msg);
|
|
323
859
|
const frameAction = frameKeys.handle(msg);
|
|
324
860
|
const keyPriority = options.keyPriority ?? 'frame-first';
|
|
325
861
|
if (keyPriority === 'page-first') {
|
|
862
|
+
if (paneAction !== undefined) {
|
|
863
|
+
return [model, withObservedKey(model, [emitMsgForPage(model.activePageId, paneAction)], msg, 'page')];
|
|
864
|
+
}
|
|
326
865
|
if (pageAction !== undefined) {
|
|
327
866
|
return [model, withObservedKey(model, [emitMsgForPage(model.activePageId, pageAction)], msg, 'page')];
|
|
328
867
|
}
|
|
@@ -330,6 +869,9 @@ export function createFramedApp(options) {
|
|
|
330
869
|
return [model, withObservedKey(model, [emitMsg(globalAction)], msg, 'global')];
|
|
331
870
|
}
|
|
332
871
|
if (frameAction !== undefined) {
|
|
872
|
+
if (frameAction.type === 'open-search' && options.enableCommandPalette) {
|
|
873
|
+
return [openSearchPalette(model, frameKeys, options, pagesById), withObservedKey(model, [], msg, 'frame')];
|
|
874
|
+
}
|
|
333
875
|
if (frameAction.type === 'open-palette' && options.enableCommandPalette) {
|
|
334
876
|
return [openCommandPalette(model, frameKeys, options, pagesById), withObservedKey(model, [], msg, 'frame')];
|
|
335
877
|
}
|
|
@@ -340,12 +882,18 @@ export function createFramedApp(options) {
|
|
|
340
882
|
}
|
|
341
883
|
if (frameAction !== undefined) {
|
|
342
884
|
// Handle palette opening here since applyFrameAction doesn't have access to palette deps
|
|
885
|
+
if (frameAction.type === 'open-search' && options.enableCommandPalette) {
|
|
886
|
+
return [openSearchPalette(model, frameKeys, options, pagesById), withObservedKey(model, [], msg, 'frame')];
|
|
887
|
+
}
|
|
343
888
|
if (frameAction.type === 'open-palette' && options.enableCommandPalette) {
|
|
344
889
|
return [openCommandPalette(model, frameKeys, options, pagesById), withObservedKey(model, [], msg, 'frame')];
|
|
345
890
|
}
|
|
346
891
|
const [nextModel, cmds] = applyFrameAction(frameAction, model, options, pagesById);
|
|
347
892
|
return [nextModel, withObservedKey(model, cmds, msg, 'frame')];
|
|
348
893
|
}
|
|
894
|
+
if (paneAction !== undefined) {
|
|
895
|
+
return [model, withObservedKey(model, [emitMsgForPage(model.activePageId, paneAction)], msg, 'page')];
|
|
896
|
+
}
|
|
349
897
|
if (globalAction !== undefined) {
|
|
350
898
|
return [model, withObservedKey(model, [emitMsg(globalAction)], msg, 'global')];
|
|
351
899
|
}
|
|
@@ -361,31 +909,45 @@ export function createFramedApp(options) {
|
|
|
361
909
|
return updateTargetPage(model, model.activePageId, msg);
|
|
362
910
|
}
|
|
363
911
|
// Custom message path: route to originating page when command messages are scoped.
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
return updateTargetPage(model,
|
|
912
|
+
if (isPageScopedMsg(msg)) {
|
|
913
|
+
return updateTargetPage(model, msg.pageId, msg.msg);
|
|
914
|
+
}
|
|
915
|
+
return updateTargetPage(model, model.activePageId, msg);
|
|
368
916
|
},
|
|
369
917
|
view(model) {
|
|
370
|
-
const activePage =
|
|
918
|
+
const { activePage, layerStack, activeLayer, } = resolvePresentedLayerContext(model);
|
|
371
919
|
const header = resolveHeaderLine(model, options, pagesById).surface;
|
|
372
|
-
const helpLine = renderHelpLine(model,
|
|
373
|
-
const bodyRect =
|
|
920
|
+
const helpLine = renderHelpLine(model, activeLayer, options.i18n, resolveNotificationFooterCue(model, options, pagesById));
|
|
921
|
+
const bodyRect = resolveBodyRect(model, options);
|
|
374
922
|
// Check for maximized pane — if set, render only that pane at full body rect
|
|
375
923
|
const maxState = model.maximizedPaneByPage[model.activePageId];
|
|
376
924
|
const maximizedPaneId = maxState?.maximizedPaneId;
|
|
377
|
-
const
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
925
|
+
const frameSurface = getComposedFrameScratch(model.columns, model.rows);
|
|
926
|
+
frameSurface.clear();
|
|
927
|
+
frameSurface.blit(header, 0, 0);
|
|
928
|
+
if (model.rows > 1) {
|
|
929
|
+
frameSurface.blit(helpLine, 0, model.rows - 1);
|
|
930
|
+
}
|
|
931
|
+
let activeResult;
|
|
932
|
+
let bodySurface;
|
|
381
933
|
const activeTransition = model.activeTransition ?? options.transition;
|
|
382
934
|
if (model.previousPageId != null && model.transitionProgress < 1 && activeTransition && activeTransition !== 'none') {
|
|
935
|
+
const activeBodyResult = maximizedPaneId
|
|
936
|
+
? renderMaximizedPane(model.activePageId, model, bodyRect, pagesById, maximizedPaneId)
|
|
937
|
+
: renderPageContent(model.activePageId, model, bodyRect, pagesById);
|
|
938
|
+
activeResult = activeBodyResult;
|
|
939
|
+
bodySurface = activeBodyResult.surface;
|
|
383
940
|
const ctx = resolveSafeCtx();
|
|
384
941
|
if (ctx) {
|
|
385
942
|
const prevResult = renderPageContent(model.previousPageId, model, bodyRect, pagesById);
|
|
386
|
-
bodySurface = renderTransition(prevResult.surface,
|
|
943
|
+
bodySurface = renderTransition(prevResult.surface, activeBodyResult.surface, activeTransition, model.transitionProgress, bodyRect.width, bodyRect.height, ctx, model.transitionFrame);
|
|
387
944
|
}
|
|
388
945
|
}
|
|
946
|
+
else {
|
|
947
|
+
activeResult = maximizedPaneId
|
|
948
|
+
? renderMaximizedPaneInto(model.activePageId, model, bodyRect, pagesById, maximizedPaneId, frameSurface)
|
|
949
|
+
: renderPageContentInto(model.activePageId, model, bodyRect, pagesById, frameSurface);
|
|
950
|
+
}
|
|
389
951
|
const overlays = [];
|
|
390
952
|
if (options.overlayFactory != null) {
|
|
391
953
|
overlays.push(...options.overlayFactory({
|
|
@@ -405,12 +967,31 @@ export function createFramedApp(options) {
|
|
|
405
967
|
ctx: ctx ?? undefined,
|
|
406
968
|
}));
|
|
407
969
|
}
|
|
970
|
+
if (model.settingsOpen) {
|
|
971
|
+
const settingsLayer = layerStack.find((layer) => layer.kind === 'settings');
|
|
972
|
+
const settingsOverlay = renderSettingsDrawer(model, options, pagesById, settingsLayer?.title);
|
|
973
|
+
if (settingsOverlay != null) {
|
|
974
|
+
overlays.push(settingsOverlay);
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
if (model.notificationCenterOpen) {
|
|
978
|
+
const notificationLayer = layerStack.find((layer) => layer.kind === 'notification-center');
|
|
979
|
+
const notificationCenterOverlay = renderNotificationCenterDrawer(model, options, pagesById, notificationLayer?.title);
|
|
980
|
+
if (notificationCenterOverlay != null) {
|
|
981
|
+
overlays.push(notificationCenterOverlay);
|
|
982
|
+
}
|
|
983
|
+
}
|
|
408
984
|
if (model.helpOpen) {
|
|
409
|
-
const
|
|
985
|
+
const helpOverlay = renderHelpOverlay(model, activePage, frameKeys, paletteKeys, options, pagesById);
|
|
410
986
|
overlays.push(modal({
|
|
411
|
-
title:
|
|
412
|
-
|
|
413
|
-
|
|
987
|
+
title: activeLayer.kind === 'help'
|
|
988
|
+
? (activeLayer.title ?? frameMessage(options.i18n, 'help.title', 'Keyboard Help'))
|
|
989
|
+
: frameMessage(options.i18n, 'help.title', 'Keyboard Help'),
|
|
990
|
+
body: helpOverlay.body,
|
|
991
|
+
hint: typeof activeLayer.hintSource === 'string'
|
|
992
|
+
? activeLayer.hintSource
|
|
993
|
+
: frameMessage(options.i18n, 'help.hint', 'j/k scroll • d/u page • g/G top/bottom • mouse wheel • ?/Esc close'),
|
|
994
|
+
width: helpOverlay.body.width + 4,
|
|
414
995
|
screenWidth: model.columns,
|
|
415
996
|
screenHeight: model.rows,
|
|
416
997
|
}));
|
|
@@ -418,25 +999,27 @@ export function createFramedApp(options) {
|
|
|
418
999
|
if (model.commandPalette != null) {
|
|
419
1000
|
const paletteWidth = Math.max(20, Math.min(80, model.columns - 4));
|
|
420
1001
|
const paletteBody = commandPalette(model.commandPalette, { width: Math.max(16, paletteWidth - 4) });
|
|
1002
|
+
const paletteLayer = activeLayer.kind === 'search' || activeLayer.kind === 'command-palette'
|
|
1003
|
+
? activeLayer
|
|
1004
|
+
: undefined;
|
|
421
1005
|
overlays.push(modal({
|
|
422
|
-
title: 'Command Palette',
|
|
1006
|
+
title: paletteLayer?.title ?? model.commandPaletteTitle ?? frameMessage(options.i18n, 'palette.title', 'Command Palette'),
|
|
423
1007
|
body: paletteBody,
|
|
424
|
-
hint:
|
|
1008
|
+
hint: typeof paletteLayer?.hintSource === 'string'
|
|
1009
|
+
? paletteLayer.hintSource
|
|
1010
|
+
: frameMessage(options.i18n, 'palette.hint', 'Enter select • Esc close'),
|
|
425
1011
|
width: paletteWidth,
|
|
426
1012
|
screenWidth: model.columns,
|
|
427
1013
|
screenHeight: model.rows,
|
|
428
1014
|
}));
|
|
429
1015
|
}
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
overlays,
|
|
438
|
-
dimBackground: overlays.length > 0,
|
|
439
|
-
});
|
|
1016
|
+
if (model.quitConfirmOpen) {
|
|
1017
|
+
overlays.push(renderShellQuitOverlay(model.columns, model.rows, options.i18n));
|
|
1018
|
+
}
|
|
1019
|
+
if (bodySurface != null && bodyRect.width > 0 && bodyRect.height > 0) {
|
|
1020
|
+
frameSurface.blit(bodySurface, bodyRect.col, bodyRect.row);
|
|
1021
|
+
}
|
|
1022
|
+
return compositeSurfaceInto(frameSurface, frameSurface, overlays, { dim: overlays.length > 0 });
|
|
440
1023
|
},
|
|
441
1024
|
routeRuntimeIssue(issue) {
|
|
442
1025
|
if (!frameNotificationOptions.enabled)
|
|
@@ -446,15 +1029,525 @@ export function createFramedApp(options) {
|
|
|
446
1029
|
};
|
|
447
1030
|
return app;
|
|
448
1031
|
}
|
|
449
|
-
function
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
1032
|
+
function focusPane(model, paneId) {
|
|
1033
|
+
if (model.focusedPaneByPage[model.activePageId] === paneId)
|
|
1034
|
+
return model;
|
|
1035
|
+
return {
|
|
1036
|
+
...model,
|
|
1037
|
+
focusedPaneByPage: {
|
|
1038
|
+
...model.focusedPaneByPage,
|
|
1039
|
+
[model.activePageId]: paneId,
|
|
1040
|
+
},
|
|
1041
|
+
};
|
|
1042
|
+
}
|
|
1043
|
+
function paneHitAtPosition(model, col, row, pagesById, options) {
|
|
1044
|
+
const bodyRect = resolveBodyRect(model, options);
|
|
1045
|
+
const maxState = model.maximizedPaneByPage[model.activePageId];
|
|
1046
|
+
const maximizedPaneId = maxState?.maximizedPaneId;
|
|
1047
|
+
const renderResult = maximizedPaneId
|
|
1048
|
+
? renderMaximizedPane(model.activePageId, model, bodyRect, pagesById, maximizedPaneId)
|
|
1049
|
+
: renderPageContent(model.activePageId, model, bodyRect, pagesById);
|
|
1050
|
+
for (const [paneId, rect] of renderResult.paneRects.entries()) {
|
|
1051
|
+
if (col >= rect.col
|
|
1052
|
+
&& col < rect.col + rect.width
|
|
1053
|
+
&& row >= rect.row
|
|
1054
|
+
&& row < rect.row + rect.height) {
|
|
1055
|
+
return { paneId, rect };
|
|
1056
|
+
}
|
|
454
1057
|
}
|
|
455
|
-
|
|
456
|
-
|
|
1058
|
+
return undefined;
|
|
1059
|
+
}
|
|
1060
|
+
function resolveBodyRect(model, options) {
|
|
1061
|
+
return frameBodyRect(model.columns, model.rows, options.bodyTopRows ?? 1, options.bodyBottomRows ?? 1);
|
|
1062
|
+
}
|
|
1063
|
+
function renderHelpOverlay(model, activePage, frameKeys, paletteKeys, options, pagesById) {
|
|
1064
|
+
const activePageModel = model.pageModels[model.activePageId];
|
|
1065
|
+
const activeInputArea = findInputAreaByPaneId(resolveInputAreas(activePage, activePageModel), model.focusedPaneByPage[model.activePageId]);
|
|
1066
|
+
const modalKeyMap = activePage.modalKeyMap?.(activePageModel);
|
|
1067
|
+
const settings = resolveFrameSettings(model, options, pagesById);
|
|
1068
|
+
const notificationCenter = resolveFrameNotificationCenter(model, options, pagesById);
|
|
1069
|
+
const workspaceHintSource = options.helpLineSource?.({
|
|
1070
|
+
model,
|
|
1071
|
+
activePage,
|
|
1072
|
+
frameKeys,
|
|
1073
|
+
globalKeys: options.globalKeys,
|
|
1074
|
+
});
|
|
1075
|
+
const workspaceHelpSource = mergeBindingSources(frameKeys, quitHelpKeys, options.globalKeys, activeInputArea?.helpSource ?? activeInputArea?.keyMap, activePage.helpSource ?? activePage.keyMap);
|
|
1076
|
+
const layerStack = describeFrameLayerStack(model, {
|
|
1077
|
+
pageModalOpen: modalKeyMap != null,
|
|
1078
|
+
layers: {
|
|
1079
|
+
workspace: {
|
|
1080
|
+
title: activePage.title,
|
|
1081
|
+
hintSource: typeof workspaceHintSource === 'string'
|
|
1082
|
+
? workspaceHintSource
|
|
1083
|
+
: workspaceHintSource ?? mergeBindingSources(frameKeys, options.globalKeys, activeInputArea?.helpSource ?? activeInputArea?.keyMap, activePage.helpSource ?? activePage.keyMap),
|
|
1084
|
+
helpSource: workspaceHelpSource,
|
|
1085
|
+
},
|
|
1086
|
+
'page-modal': {
|
|
1087
|
+
title: activePage.title,
|
|
1088
|
+
hintSource: modalKeyMap ?? activePage.helpSource ?? activePage.keyMap,
|
|
1089
|
+
helpSource: mergeBindingSources(quitHelpKeys, modalKeyMap, activePage.helpSource ?? activePage.keyMap),
|
|
1090
|
+
},
|
|
1091
|
+
settings: {
|
|
1092
|
+
title: settings?.title ?? frameMessage(options.i18n, 'settings.title', 'Settings'),
|
|
1093
|
+
hintSource: frameMessage(options.i18n, 'settings.footer', 'F2/Esc close • ↑/↓ rows • Enter toggle • / search • q quit'),
|
|
1094
|
+
helpSource: mergeBindingSources(settingsHelpKeys, quitHelpKeys),
|
|
1095
|
+
},
|
|
1096
|
+
help: {
|
|
1097
|
+
title: frameMessage(options.i18n, 'help.title', 'Keyboard Help'),
|
|
1098
|
+
hintSource: frameMessage(options.i18n, 'help.hint', 'j/k scroll • d/u page • g/G top/bottom • mouse wheel • ?/Esc close'),
|
|
1099
|
+
helpSource: helpLayerHelpKeys,
|
|
1100
|
+
},
|
|
1101
|
+
'notification-center': {
|
|
1102
|
+
title: notificationCenter == null
|
|
1103
|
+
? frameMessage(options.i18n, 'notifications.title', 'Notifications')
|
|
1104
|
+
: `${notificationCenter.title} • ${frameNotificationFilterLabel(options.i18n, notificationCenter.activeFilter)}`,
|
|
1105
|
+
hintSource: frameMessage(options.i18n, 'notifications.footer', 'Shift+N close • f filter • j/k scroll • q quit'),
|
|
1106
|
+
helpSource: mergeBindingSources(notificationCenterHelpKeys, quitHelpKeys),
|
|
1107
|
+
},
|
|
1108
|
+
search: {
|
|
1109
|
+
title: model.commandPaletteTitle ?? activePage.searchTitle ?? frameMessage(options.i18n, 'search.title', 'Search'),
|
|
1110
|
+
hintSource: frameMessage(options.i18n, 'palette.hint', 'Enter select • Esc close'),
|
|
1111
|
+
helpSource: mergeBindingSources(paletteKeys, quitHelpKeys),
|
|
1112
|
+
},
|
|
1113
|
+
'command-palette': {
|
|
1114
|
+
title: model.commandPaletteTitle ?? frameMessage(options.i18n, 'palette.title', 'Command Palette'),
|
|
1115
|
+
hintSource: frameMessage(options.i18n, 'palette.hint', 'Enter select • Esc close'),
|
|
1116
|
+
helpSource: mergeBindingSources(paletteKeys, quitHelpKeys),
|
|
1117
|
+
},
|
|
1118
|
+
'quit-confirm': {
|
|
1119
|
+
title: frameMessage(options.i18n, 'quit.title', 'Quit?'),
|
|
1120
|
+
hintSource: frameMessage(options.i18n, 'quit.footer', 'Y quit • N stay'),
|
|
1121
|
+
helpSource: quitConfirmHelpKeys,
|
|
1122
|
+
},
|
|
1123
|
+
},
|
|
1124
|
+
});
|
|
1125
|
+
const beneathHelpLayer = layerStack.length > 1 ? layerStack[layerStack.length - 2] : undefined;
|
|
1126
|
+
const source = beneathHelpLayer?.helpSource ?? workspaceHelpSource;
|
|
1127
|
+
const maxDialogWidth = Math.max(28, Math.min(model.columns - 4, 88));
|
|
1128
|
+
const bodyWidth = Math.max(20, maxDialogWidth - 4);
|
|
1129
|
+
const helpSurface = helpViewSurface(source, {
|
|
1130
|
+
title: undefined,
|
|
1131
|
+
width: bodyWidth,
|
|
1132
|
+
});
|
|
1133
|
+
const pagerHeight = Math.max(4, Math.min(helpSurface.height + 1, Math.max(4, model.rows - 8)));
|
|
1134
|
+
const pagerState = createPagerStateForSurface(helpSurface, {
|
|
1135
|
+
width: bodyWidth,
|
|
1136
|
+
height: pagerHeight,
|
|
1137
|
+
});
|
|
1138
|
+
const scrollY = Math.max(0, Math.min(model.helpScrollY, pagerState.scroll.maxY));
|
|
1139
|
+
const scrolledState = {
|
|
1140
|
+
...pagerState,
|
|
1141
|
+
scroll: {
|
|
1142
|
+
...pagerState.scroll,
|
|
1143
|
+
y: scrollY,
|
|
1144
|
+
},
|
|
1145
|
+
};
|
|
1146
|
+
return {
|
|
1147
|
+
body: pagerSurface(helpSurface, scrolledState, { showScrollbar: true, showStatus: true }),
|
|
1148
|
+
maxScrollY: pagerState.scroll.maxY,
|
|
1149
|
+
scrollY,
|
|
1150
|
+
};
|
|
1151
|
+
}
|
|
1152
|
+
function isHelpScrollAction(action) {
|
|
1153
|
+
return action.type === 'scroll-up'
|
|
1154
|
+
|| action.type === 'scroll-down'
|
|
1155
|
+
|| action.type === 'page-up'
|
|
1156
|
+
|| action.type === 'page-down'
|
|
1157
|
+
|| action.type === 'top'
|
|
1158
|
+
|| action.type === 'bottom';
|
|
1159
|
+
}
|
|
1160
|
+
function applyHelpScrollAction(model, activePage, action, frameKeys, paletteKeys, options, pagesById) {
|
|
1161
|
+
const overlay = renderHelpOverlay(model, activePage, frameKeys, paletteKeys, options, pagesById);
|
|
1162
|
+
const pagerState = {
|
|
1163
|
+
scroll: {
|
|
1164
|
+
y: overlay.scrollY,
|
|
1165
|
+
maxY: overlay.maxScrollY,
|
|
1166
|
+
x: 0,
|
|
1167
|
+
maxX: 0,
|
|
1168
|
+
totalLines: overlay.maxScrollY + Math.max(1, overlay.body.height - 1),
|
|
1169
|
+
visibleLines: Math.max(1, overlay.body.height - 1),
|
|
1170
|
+
},
|
|
1171
|
+
content: '',
|
|
1172
|
+
width: overlay.body.width,
|
|
1173
|
+
height: overlay.body.height,
|
|
1174
|
+
};
|
|
1175
|
+
let next = pagerState;
|
|
1176
|
+
switch (action.type) {
|
|
1177
|
+
case 'scroll-up':
|
|
1178
|
+
next = pagerScrollBy(pagerState, -1);
|
|
1179
|
+
break;
|
|
1180
|
+
case 'scroll-down':
|
|
1181
|
+
next = pagerScrollBy(pagerState, 1);
|
|
1182
|
+
break;
|
|
1183
|
+
case 'page-up':
|
|
1184
|
+
next = pagerPageUp(pagerState);
|
|
1185
|
+
break;
|
|
1186
|
+
case 'page-down':
|
|
1187
|
+
next = pagerPageDown(pagerState);
|
|
1188
|
+
break;
|
|
1189
|
+
case 'top':
|
|
1190
|
+
next = pagerScrollToTop(pagerState);
|
|
1191
|
+
break;
|
|
1192
|
+
case 'bottom':
|
|
1193
|
+
next = pagerScrollToBottom(pagerState);
|
|
1194
|
+
break;
|
|
1195
|
+
}
|
|
1196
|
+
return {
|
|
1197
|
+
...model,
|
|
1198
|
+
helpScrollY: next.scroll.y,
|
|
1199
|
+
};
|
|
1200
|
+
}
|
|
1201
|
+
function applyHelpScroll(model, activePage, delta, frameKeys, paletteKeys, options, pagesById) {
|
|
1202
|
+
const overlay = renderHelpOverlay(model, activePage, frameKeys, paletteKeys, options, pagesById);
|
|
1203
|
+
return {
|
|
1204
|
+
...model,
|
|
1205
|
+
helpScrollY: Math.max(0, Math.min(overlay.maxScrollY, overlay.scrollY + delta)),
|
|
1206
|
+
};
|
|
1207
|
+
}
|
|
1208
|
+
function resolveFrameSettings(model, options, pagesById) {
|
|
1209
|
+
const activePage = pagesById.get(model.activePageId);
|
|
1210
|
+
return options.settings?.({
|
|
1211
|
+
model,
|
|
1212
|
+
activePage,
|
|
1213
|
+
pageModel: model.pageModels[model.activePageId],
|
|
1214
|
+
});
|
|
1215
|
+
}
|
|
1216
|
+
function resolveFrameNotificationCenter(model, options, pagesById) {
|
|
1217
|
+
const activePage = pagesById.get(model.activePageId);
|
|
1218
|
+
const pageModel = model.pageModels[model.activePageId];
|
|
1219
|
+
const provided = options.notificationCenter?.({
|
|
1220
|
+
model,
|
|
1221
|
+
activePage,
|
|
1222
|
+
pageModel,
|
|
1223
|
+
runtimeNotifications: model.runtimeNotifications,
|
|
1224
|
+
});
|
|
1225
|
+
if (provided != null) {
|
|
1226
|
+
const filters = provided.filters != null && provided.filters.length > 0
|
|
1227
|
+
? provided.filters
|
|
1228
|
+
: DEFAULT_NOTIFICATION_CENTER_FILTERS;
|
|
1229
|
+
const activeFilter = filters.includes(provided.activeFilter ?? 'ALL')
|
|
1230
|
+
? (provided.activeFilter ?? 'ALL')
|
|
1231
|
+
: filters[0];
|
|
1232
|
+
return {
|
|
1233
|
+
title: provided.title ?? frameMessage(options.i18n, 'notifications.title', 'Notifications'),
|
|
1234
|
+
state: provided.state,
|
|
1235
|
+
filters,
|
|
1236
|
+
activeFilter,
|
|
1237
|
+
onFilterChange: provided.onFilterChange,
|
|
1238
|
+
};
|
|
1239
|
+
}
|
|
1240
|
+
if (options.runtimeNotifications === false)
|
|
1241
|
+
return undefined;
|
|
1242
|
+
return {
|
|
1243
|
+
title: frameMessage(options.i18n, 'notifications.title', 'Notifications'),
|
|
1244
|
+
state: model.runtimeNotifications,
|
|
1245
|
+
filters: DEFAULT_NOTIFICATION_CENTER_FILTERS,
|
|
1246
|
+
activeFilter: model.runtimeNotificationHistoryFilter,
|
|
1247
|
+
};
|
|
1248
|
+
}
|
|
1249
|
+
function resolveSettingsLayout(model, options, pagesById) {
|
|
1250
|
+
const settings = resolveFrameSettings(model, options, pagesById);
|
|
1251
|
+
if (settings == null)
|
|
1252
|
+
return undefined;
|
|
1253
|
+
const sections = settings.sections.filter((section) => section.rows.length > 0);
|
|
1254
|
+
if (sections.length === 0)
|
|
1255
|
+
return undefined;
|
|
1256
|
+
const drawerWidth = resolveSettingsDrawerWidth(model.columns);
|
|
1257
|
+
const anchor = frameStartAnchor(options.i18n);
|
|
1258
|
+
const startCol = anchor === 'left' ? 0 : Math.max(0, model.columns - drawerWidth);
|
|
1259
|
+
const contentWidth = Math.max(16, drawerWidth - 4);
|
|
1260
|
+
const preferenceSections = preparePreferenceSections(toPreferenceSections(sections));
|
|
1261
|
+
const rows = [];
|
|
1262
|
+
let line = 0;
|
|
1263
|
+
for (let sectionIndex = 0; sectionIndex < preferenceSections.length; sectionIndex++) {
|
|
1264
|
+
const section = preferenceSections[sectionIndex];
|
|
1265
|
+
if (sectionIndex > 0) {
|
|
1266
|
+
line += 1;
|
|
1267
|
+
}
|
|
1268
|
+
line += 1;
|
|
1269
|
+
line += 1;
|
|
1270
|
+
for (let rowIndex = 0; rowIndex < section.rows.length; rowIndex++) {
|
|
1271
|
+
const preparedRow = section.rows[rowIndex];
|
|
1272
|
+
const row = sections[sectionIndex].rows[rowIndex];
|
|
1273
|
+
const rowLayout = resolvePreferenceRowLayout(preparedRow, contentWidth);
|
|
1274
|
+
rows.push({
|
|
1275
|
+
index: rows.length,
|
|
1276
|
+
line,
|
|
1277
|
+
height: rowLayout.height,
|
|
1278
|
+
row,
|
|
1279
|
+
});
|
|
1280
|
+
line += rowLayout.height;
|
|
1281
|
+
if (rowIndex < section.rows.length - 1) {
|
|
1282
|
+
line += 1;
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
const contentHeight = Math.max(1, model.rows - 2);
|
|
1287
|
+
const totalLines = Math.max(1, line);
|
|
1288
|
+
const maxScrollY = Math.max(0, totalLines - contentHeight);
|
|
1289
|
+
return {
|
|
1290
|
+
settings: {
|
|
1291
|
+
...settings,
|
|
1292
|
+
sections,
|
|
1293
|
+
},
|
|
1294
|
+
preferenceSections,
|
|
1295
|
+
rows,
|
|
1296
|
+
anchor,
|
|
1297
|
+
startCol,
|
|
1298
|
+
drawerWidth,
|
|
1299
|
+
contentWidth,
|
|
1300
|
+
contentHeight,
|
|
1301
|
+
totalLines,
|
|
1302
|
+
maxScrollY,
|
|
1303
|
+
};
|
|
1304
|
+
}
|
|
1305
|
+
function resolveNotificationCenterDrawerWidth(columns) {
|
|
1306
|
+
const boundedColumns = Math.max(28, columns);
|
|
1307
|
+
return Math.min(Math.max(32, Math.floor(boundedColumns * 0.34)), Math.max(32, boundedColumns - 4), 52);
|
|
1308
|
+
}
|
|
1309
|
+
function resolveNotificationCenterLayout(model, options, pagesById) {
|
|
1310
|
+
const center = resolveFrameNotificationCenter(model, options, pagesById);
|
|
1311
|
+
if (center == null)
|
|
1312
|
+
return undefined;
|
|
1313
|
+
const drawerWidth = resolveNotificationCenterDrawerWidth(model.columns);
|
|
1314
|
+
const anchor = frameEndAnchor(options.i18n);
|
|
1315
|
+
const startCol = anchor === 'left' ? 0 : Math.max(0, model.columns - drawerWidth);
|
|
1316
|
+
const contentWidth = Math.max(18, drawerWidth - 4);
|
|
1317
|
+
const content = renderNotificationCenterSurface(center, contentWidth, options.i18n);
|
|
1318
|
+
const contentHeight = Math.max(1, model.rows - 2);
|
|
1319
|
+
const pagerState = createPagerStateForSurface(content, {
|
|
1320
|
+
width: contentWidth,
|
|
1321
|
+
height: contentHeight,
|
|
1322
|
+
});
|
|
1323
|
+
return {
|
|
1324
|
+
center,
|
|
1325
|
+
anchor,
|
|
1326
|
+
startCol,
|
|
1327
|
+
drawerWidth,
|
|
1328
|
+
contentWidth,
|
|
1329
|
+
contentHeight,
|
|
1330
|
+
content,
|
|
1331
|
+
maxScrollY: pagerState.scroll.maxY,
|
|
1332
|
+
};
|
|
1333
|
+
}
|
|
1334
|
+
function resolveSettingsDrawerWidth(columns) {
|
|
1335
|
+
const boundedColumns = Math.max(24, columns);
|
|
1336
|
+
return Math.min(Math.max(28, Math.floor(boundedColumns * 0.3)), Math.max(28, boundedColumns - 4), 42);
|
|
1337
|
+
}
|
|
1338
|
+
function clampSettingsFocus(model, layout) {
|
|
1339
|
+
if (layout.rows.length === 0)
|
|
1340
|
+
return 0;
|
|
1341
|
+
return Math.max(0, Math.min(model.settingsFocusIndex, layout.rows.length - 1));
|
|
1342
|
+
}
|
|
1343
|
+
function clampSettingsScroll(model, layout) {
|
|
1344
|
+
return Math.max(0, Math.min(model.settingsScrollY, layout.maxScrollY));
|
|
1345
|
+
}
|
|
1346
|
+
function resolveInputAreas(page, pageModel) {
|
|
1347
|
+
return page.inputAreas?.(pageModel) ?? [];
|
|
1348
|
+
}
|
|
1349
|
+
function findInputAreaByPaneId(inputAreas, paneId) {
|
|
1350
|
+
if (paneId == null)
|
|
1351
|
+
return undefined;
|
|
1352
|
+
return inputAreas.find((area) => area.paneId === paneId);
|
|
1353
|
+
}
|
|
1354
|
+
function ensureSettingsRangeVisible(startLine, height, scrollY, visibleLines, maxScrollY) {
|
|
1355
|
+
let next = scrollY;
|
|
1356
|
+
const endLine = startLine + Math.max(1, height) - 1;
|
|
1357
|
+
if (startLine < next) {
|
|
1358
|
+
next = startLine;
|
|
1359
|
+
}
|
|
1360
|
+
else if (endLine >= next + visibleLines) {
|
|
1361
|
+
next = endLine - visibleLines + 1;
|
|
1362
|
+
}
|
|
1363
|
+
return Math.max(0, Math.min(next, maxScrollY));
|
|
1364
|
+
}
|
|
1365
|
+
function moveSettingsFocus(model, layout, delta) {
|
|
1366
|
+
if (layout.rows.length === 0)
|
|
1367
|
+
return model;
|
|
1368
|
+
const nextFocus = Math.max(0, Math.min(clampSettingsFocus(model, layout) + delta, layout.rows.length - 1));
|
|
1369
|
+
const focusedRow = layout.rows[nextFocus];
|
|
1370
|
+
return {
|
|
1371
|
+
...model,
|
|
1372
|
+
settingsFocusIndex: nextFocus,
|
|
1373
|
+
settingsScrollY: ensureSettingsRangeVisible(focusedRow.line, focusedRow.height, clampSettingsScroll(model, layout), layout.contentHeight, layout.maxScrollY),
|
|
1374
|
+
};
|
|
1375
|
+
}
|
|
1376
|
+
function scrollSettingsBy(model, layout, delta) {
|
|
1377
|
+
return {
|
|
1378
|
+
...model,
|
|
1379
|
+
settingsScrollY: Math.max(0, Math.min(clampSettingsScroll(model, layout) + delta, layout.maxScrollY)),
|
|
1380
|
+
};
|
|
1381
|
+
}
|
|
1382
|
+
function scrollNotificationCenterBy(model, layout, delta) {
|
|
1383
|
+
return {
|
|
1384
|
+
...model,
|
|
1385
|
+
notificationCenterScrollY: Math.max(0, Math.min(model.notificationCenterScrollY + delta, layout.maxScrollY)),
|
|
1386
|
+
};
|
|
1387
|
+
}
|
|
1388
|
+
function cycleNotificationCenterFilter(model, layout) {
|
|
1389
|
+
const filters = layout.center.filters;
|
|
1390
|
+
if (filters.length < 2)
|
|
1391
|
+
return [model, []];
|
|
1392
|
+
const currentIndex = Math.max(0, filters.indexOf(layout.center.activeFilter));
|
|
1393
|
+
const nextFilter = filters[(currentIndex + 1) % filters.length];
|
|
1394
|
+
if (layout.center.onFilterChange != null) {
|
|
1395
|
+
const action = layout.center.onFilterChange(nextFilter);
|
|
1396
|
+
return [{
|
|
1397
|
+
...model,
|
|
1398
|
+
notificationCenterScrollY: 0,
|
|
1399
|
+
}, action === undefined ? [] : [emitMsgForPage(model.activePageId, action)]];
|
|
1400
|
+
}
|
|
1401
|
+
return [{
|
|
1402
|
+
...model,
|
|
1403
|
+
runtimeNotificationHistoryFilter: nextFilter,
|
|
1404
|
+
notificationCenterScrollY: 0,
|
|
1405
|
+
}, []];
|
|
1406
|
+
}
|
|
1407
|
+
function isInsideSettingsDrawer(col, row, layout, model) {
|
|
1408
|
+
return col >= layout.startCol
|
|
1409
|
+
&& col < layout.startCol + layout.drawerWidth
|
|
1410
|
+
&& row >= 0
|
|
1411
|
+
&& row < model.rows;
|
|
1412
|
+
}
|
|
1413
|
+
function settingsRowAtPosition(col, row, model, layout) {
|
|
1414
|
+
if (!isInsideSettingsDrawer(col, row, layout, model))
|
|
1415
|
+
return undefined;
|
|
1416
|
+
if (row <= 0 || row >= model.rows - 1)
|
|
1417
|
+
return undefined;
|
|
1418
|
+
const contentLine = (row - 1) + clampSettingsScroll(model, layout);
|
|
1419
|
+
return layout.rows.find((candidate) => contentLine >= candidate.line && contentLine < candidate.line + candidate.height);
|
|
1420
|
+
}
|
|
1421
|
+
function isInsideNotificationCenterDrawer(col, row, layout, model) {
|
|
1422
|
+
return col >= layout.startCol
|
|
1423
|
+
&& col < layout.startCol + layout.drawerWidth
|
|
1424
|
+
&& row >= 0
|
|
1425
|
+
&& row < model.rows;
|
|
1426
|
+
}
|
|
1427
|
+
function renderSettingsDrawer(model, options, pagesById, titleOverride) {
|
|
1428
|
+
const layout = resolveSettingsLayout(model, options, pagesById);
|
|
1429
|
+
if (layout == null)
|
|
1430
|
+
return undefined;
|
|
1431
|
+
const scrollY = clampSettingsScroll(model, layout);
|
|
1432
|
+
const content = renderSettingsSurface(layout, model);
|
|
1433
|
+
const pagerState = createPagerStateForSurface(content, {
|
|
1434
|
+
width: layout.contentWidth,
|
|
1435
|
+
height: layout.contentHeight,
|
|
1436
|
+
});
|
|
1437
|
+
const scrolledState = {
|
|
1438
|
+
...pagerState,
|
|
1439
|
+
scroll: {
|
|
1440
|
+
...pagerState.scroll,
|
|
1441
|
+
y: scrollY,
|
|
1442
|
+
},
|
|
1443
|
+
};
|
|
1444
|
+
const body = pagerSurface(content, scrolledState, {
|
|
1445
|
+
showScrollbar: layout.maxScrollY > 0,
|
|
1446
|
+
showStatus: false,
|
|
1447
|
+
});
|
|
1448
|
+
return drawer({
|
|
1449
|
+
anchor: layout.anchor,
|
|
1450
|
+
title: titleOverride ?? layout.settings.title ?? frameMessage(options.i18n, 'settings.title', 'Settings'),
|
|
1451
|
+
content: body,
|
|
1452
|
+
borderToken: layout.settings.borderToken,
|
|
1453
|
+
bgToken: layout.settings.bgToken,
|
|
1454
|
+
ctx: resolveSafeCtx() ?? undefined,
|
|
1455
|
+
width: layout.drawerWidth,
|
|
1456
|
+
screenWidth: model.columns,
|
|
1457
|
+
screenHeight: model.rows,
|
|
1458
|
+
});
|
|
1459
|
+
}
|
|
1460
|
+
function renderNotificationCenterDrawer(model, options, pagesById, titleOverride) {
|
|
1461
|
+
const layout = resolveNotificationCenterLayout(model, options, pagesById);
|
|
1462
|
+
if (layout == null)
|
|
1463
|
+
return undefined;
|
|
1464
|
+
const pagerState = createPagerStateForSurface(layout.content, {
|
|
1465
|
+
width: layout.contentWidth,
|
|
1466
|
+
height: layout.contentHeight,
|
|
1467
|
+
});
|
|
1468
|
+
const scrolledState = {
|
|
1469
|
+
...pagerState,
|
|
1470
|
+
scroll: {
|
|
1471
|
+
...pagerState.scroll,
|
|
1472
|
+
y: Math.max(0, Math.min(model.notificationCenterScrollY, layout.maxScrollY)),
|
|
1473
|
+
},
|
|
1474
|
+
};
|
|
1475
|
+
const body = pagerSurface(layout.content, scrolledState, {
|
|
1476
|
+
showScrollbar: layout.maxScrollY > 0,
|
|
1477
|
+
showStatus: false,
|
|
1478
|
+
});
|
|
1479
|
+
return drawer({
|
|
1480
|
+
anchor: layout.anchor,
|
|
1481
|
+
title: titleOverride ?? `${layout.center.title} • ${frameNotificationFilterLabel(options.i18n, layout.center.activeFilter)}`,
|
|
1482
|
+
content: body,
|
|
1483
|
+
width: layout.drawerWidth,
|
|
1484
|
+
screenWidth: model.columns,
|
|
1485
|
+
screenHeight: model.rows,
|
|
1486
|
+
});
|
|
1487
|
+
}
|
|
1488
|
+
function renderSettingsSurface(layout, model) {
|
|
1489
|
+
const focusedIndex = clampSettingsFocus(model, layout);
|
|
1490
|
+
return preferenceListSurface(layout.preferenceSections, {
|
|
1491
|
+
width: layout.contentWidth,
|
|
1492
|
+
selectedRowId: layout.rows[focusedIndex]?.row.id,
|
|
1493
|
+
ctx: resolveSafeCtx() ?? undefined,
|
|
1494
|
+
theme: layout.settings.listTheme,
|
|
1495
|
+
});
|
|
1496
|
+
}
|
|
1497
|
+
function toPreferenceSections(sections) {
|
|
1498
|
+
return sections.map((section) => ({
|
|
1499
|
+
id: section.id,
|
|
1500
|
+
title: section.title,
|
|
1501
|
+
rows: section.rows.map((row) => toPreferenceRow(row)),
|
|
1502
|
+
}));
|
|
1503
|
+
}
|
|
1504
|
+
function toPreferenceRow(row) {
|
|
1505
|
+
return {
|
|
1506
|
+
id: row.id,
|
|
1507
|
+
label: row.label,
|
|
1508
|
+
description: row.description,
|
|
1509
|
+
valueLabel: row.valueLabel,
|
|
1510
|
+
kind: row.kind,
|
|
1511
|
+
checked: row.checked,
|
|
1512
|
+
enabled: row.enabled,
|
|
1513
|
+
};
|
|
1514
|
+
}
|
|
1515
|
+
function resolveNotificationFooterCue(model, options, pagesById) {
|
|
1516
|
+
const center = resolveFrameNotificationCenter(model, options, pagesById);
|
|
1517
|
+
if (center == null)
|
|
1518
|
+
return undefined;
|
|
1519
|
+
const liveCount = center.state.items.length;
|
|
1520
|
+
const archivedCount = countNotificationHistory(center.state, center.activeFilter);
|
|
1521
|
+
return frameNotificationCue(options.i18n, liveCount, archivedCount);
|
|
1522
|
+
}
|
|
1523
|
+
function renderNotificationCenterSurface(center, width, i18n) {
|
|
1524
|
+
const ctx = resolveSafeCtx() ?? undefined;
|
|
1525
|
+
const rows = [
|
|
1526
|
+
insetLineSurface(`Live: ${center.state.items.length} • Archived: ${center.state.history.length}`, width),
|
|
1527
|
+
insetLineSurface(`Filter: ${frameNotificationFilterLabel(i18n, center.activeFilter)}`, width),
|
|
1528
|
+
];
|
|
1529
|
+
const liveItems = [...center.state.items].sort((left, right) => right.updatedAtMs - left.updatedAtMs || right.id - left.id);
|
|
1530
|
+
if (liveItems.length > 0) {
|
|
1531
|
+
rows.push(createSurface(width, 1));
|
|
1532
|
+
rows.push(insetLineSurface(ctx == null ? 'Current stack' : ctx.style.bold('Current stack'), width));
|
|
1533
|
+
rows.push(createSurface(width, 1));
|
|
1534
|
+
for (let index = 0; index < liveItems.length; index++) {
|
|
1535
|
+
rows.push(renderNotificationReviewEntrySurface(liveItems[index], {
|
|
1536
|
+
width,
|
|
1537
|
+
ctx,
|
|
1538
|
+
metaLabel: `${liveItems[index].variant} • live`,
|
|
1539
|
+
}));
|
|
1540
|
+
if (index < liveItems.length - 1)
|
|
1541
|
+
rows.push(createSurface(width, 1));
|
|
1542
|
+
}
|
|
457
1543
|
}
|
|
458
|
-
|
|
1544
|
+
rows.push(createSurface(width, 1));
|
|
1545
|
+
rows.push(renderNotificationHistorySurface(center.state, {
|
|
1546
|
+
width,
|
|
1547
|
+
height: Number.MAX_SAFE_INTEGER,
|
|
1548
|
+
filter: center.activeFilter,
|
|
1549
|
+
ctx,
|
|
1550
|
+
}));
|
|
1551
|
+
return vstackSurface(...rows);
|
|
459
1552
|
}
|
|
460
1553
|
//# sourceMappingURL=app-frame.js.map
|