@flyingrobots/bijou-tui 4.1.0 → 4.4.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 +6 -1
- package/dist/app-frame-layers.d.ts +4 -0
- package/dist/app-frame-layers.d.ts.map +1 -1
- package/dist/app-frame-layers.js +44 -12
- package/dist/app-frame-layers.js.map +1 -1
- package/dist/app-frame-render.d.ts +8 -6
- package/dist/app-frame-render.d.ts.map +1 -1
- package/dist/app-frame-render.js +29 -25
- package/dist/app-frame-render.js.map +1 -1
- package/dist/app-frame-types.d.ts +80 -0
- package/dist/app-frame-types.d.ts.map +1 -1
- package/dist/app-frame-types.js.map +1 -1
- package/dist/app-frame.d.ts +2 -2
- package/dist/app-frame.d.ts.map +1 -1
- package/dist/app-frame.js +669 -505
- package/dist/app-frame.js.map +1 -1
- package/dist/canvas.d.ts.map +1 -1
- package/dist/canvas.js +25 -4
- package/dist/canvas.js.map +1 -1
- package/dist/css/text-style.d.ts +6 -0
- package/dist/css/text-style.d.ts.map +1 -1
- package/dist/css/text-style.js +59 -15
- package/dist/css/text-style.js.map +1 -1
- package/dist/flex.d.ts.map +1 -1
- package/dist/flex.js +28 -4
- package/dist/flex.js.map +1 -1
- package/dist/focus-area.d.ts.map +1 -1
- package/dist/focus-area.js +31 -4
- package/dist/focus-area.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/notification.d.ts.map +1 -1
- package/dist/notification.js +56 -16
- package/dist/notification.js.map +1 -1
- package/dist/overlay.d.ts.map +1 -1
- package/dist/overlay.js +93 -16
- package/dist/overlay.js.map +1 -1
- package/dist/pipeline/middleware/grayscale.d.ts +3 -0
- package/dist/pipeline/middleware/grayscale.d.ts.map +1 -1
- package/dist/pipeline/middleware/grayscale.js +63 -10
- package/dist/pipeline/middleware/grayscale.js.map +1 -1
- package/dist/pipeline/pipeline.d.ts +7 -0
- package/dist/pipeline/pipeline.d.ts.map +1 -1
- package/dist/pipeline/pipeline.js.map +1 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +33 -1
- package/dist/runtime.js.map +1 -1
- package/dist/screen.d.ts +5 -1
- package/dist/screen.d.ts.map +1 -1
- package/dist/screen.js +6 -2
- package/dist/screen.js.map +1 -1
- package/dist/transition-shaders.d.ts.map +1 -1
- package/dist/transition-shaders.js +6 -1
- package/dist/transition-shaders.js.map +1 -1
- package/package.json +3 -3
package/dist/app-frame.js
CHANGED
|
@@ -12,25 +12,27 @@ import { quit } from './commands.js';
|
|
|
12
12
|
import { compositeSurfaceInto, drawer, modal } from './overlay.js';
|
|
13
13
|
import { isShellQuitConfirmAccept, isShellQuitConfirmDismiss, isShellQuitRequest, renderShellQuitOverlay, shouldUseShellQuitConfirm, } from './shell-quit.js';
|
|
14
14
|
import { commandPalette, commandPaletteKeyMap, } from './command-palette.js';
|
|
15
|
-
import { createPagerStateForSurface,
|
|
15
|
+
import { createPagerStateForSurface, pagerSurface, } from './pager.js';
|
|
16
16
|
import { restoreLayoutState } from './layout-preset.js';
|
|
17
17
|
import { countNotificationHistory, createNotificationState, dismissNotification, hitTestNotificationStack, notificationsNeedTick, pushNotification, renderNotificationHistorySurface, renderNotificationReviewEntrySurface, renderNotificationStack, tickNotifications, trimNotificationsToViewport, } from './notification.js';
|
|
18
18
|
import { insetLineSurface } from './collection-surface.js';
|
|
19
19
|
import { vstackSurface } from './surface-layout.js';
|
|
20
20
|
import { isFrameScopedMsg, isPageScopedMsg, wrapCmdForPage, emitMsg, emitMsgForPage, wrapFrameMsg, } from './app-frame-types.js';
|
|
21
21
|
import { createFrameKeyMap, frameBodyRect, mergeBindingSources, } from './app-frame-utils.js';
|
|
22
|
-
import { activeFrameLayer, describeFrameLayerStack, } from './app-frame-layers.js';
|
|
22
|
+
import { activeFrameLayer, describeFrameLayerStack, describeFrameRuntimeViewStack, } from './app-frame-layers.js';
|
|
23
|
+
import { applyRuntimeCommandBuffer, bufferRuntimeRouteResult, createRuntimeBuffers, createRuntimeRetainedLayouts, retainRuntimeLayout, routeRuntimeInput, } from './runtime-engine.js';
|
|
23
24
|
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 { resolveHeaderLine, renderHelpLine, renderPageContent, renderPageContentInto, renderMaximizedPane, renderMaximizedPaneInto, renderTransition, createFramePaneScratchPool, } from './app-frame-render.js';
|
|
25
26
|
import { applyFrameAction, scrollFocusedPane, switchTab, syncPageFrameState, } from './app-frame-actions.js';
|
|
26
27
|
import { handlePaletteKey, openCommandPalette, openSearchPalette, } from './app-frame-palette.js';
|
|
27
|
-
export { activeFrameLayer, describeFrameLayerStack, underlyingFrameLayer, } from './app-frame-layers.js';
|
|
28
|
+
export { activeFrameLayer, describeFrameLayerStack, describeFrameRuntimeViewStack, underlyingFrameLayer, } from './app-frame-layers.js';
|
|
28
29
|
// ---------------------------------------------------------------------------
|
|
29
30
|
// Frame Notification Helpers
|
|
30
31
|
// ---------------------------------------------------------------------------
|
|
31
32
|
const FRAME_NOTIFICATION_TICK_MS = 40;
|
|
32
33
|
const DEFAULT_FRAME_NOTIFICATION_DURATION_MS = 6_000;
|
|
33
34
|
const SETTINGS_FEEDBACK_TOAST_WIDTH = 40;
|
|
35
|
+
const EMPTY_RUNTIME_LAYOUTS = createRuntimeRetainedLayouts();
|
|
34
36
|
const DEFAULT_NOTIFICATION_CENTER_FILTERS = [
|
|
35
37
|
'ALL',
|
|
36
38
|
'ACTIONABLE',
|
|
@@ -160,6 +162,9 @@ export function createFramedApp(options) {
|
|
|
160
162
|
});
|
|
161
163
|
const frameNotificationOptions = resolveFrameNotificationOptions(options);
|
|
162
164
|
let composedFrameScratch = null;
|
|
165
|
+
let headerScratch;
|
|
166
|
+
let helpLineScratch;
|
|
167
|
+
const paneScratchPool = createFramePaneScratchPool();
|
|
163
168
|
const paletteKeys = commandPaletteKeyMap({
|
|
164
169
|
focusNext: { type: 'cp-next' },
|
|
165
170
|
focusPrev: { type: 'cp-prev' },
|
|
@@ -176,20 +181,29 @@ export function createFramedApp(options) {
|
|
|
176
181
|
}
|
|
177
182
|
return composedFrameScratch;
|
|
178
183
|
}
|
|
179
|
-
function
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
+
function closeCommandPalette(model) {
|
|
185
|
+
return {
|
|
186
|
+
...model,
|
|
187
|
+
commandPalette: undefined,
|
|
188
|
+
commandPaletteEntries: undefined,
|
|
189
|
+
commandPaletteTitle: undefined,
|
|
190
|
+
commandPaletteKind: undefined,
|
|
191
|
+
};
|
|
184
192
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
+
const shellCommandHandlers = {
|
|
194
|
+
// --- overlay lifecycle ---
|
|
195
|
+
'close-help': (model) => ({ ...model, helpOpen: false, helpScrollY: 0 }),
|
|
196
|
+
'close-settings': (model) => ({ ...model, settingsOpen: false }),
|
|
197
|
+
'close-notification-center': (model) => ({ ...model, notificationCenterOpen: false, notificationCenterScrollY: 0 }),
|
|
198
|
+
'close-palette': (model) => closeCommandPalette(model),
|
|
199
|
+
'close-quit-confirm': (model) => ({ ...model, quitConfirmOpen: false }),
|
|
200
|
+
'open-help': (model) => ({ ...model, helpOpen: true }),
|
|
201
|
+
'open-quit-confirm': (model) => {
|
|
202
|
+
if (!shouldUseShellQuitConfirm())
|
|
203
|
+
return model;
|
|
204
|
+
if (model.quitConfirmOpen)
|
|
205
|
+
return model;
|
|
206
|
+
return {
|
|
193
207
|
...model,
|
|
194
208
|
quitConfirmOpen: true,
|
|
195
209
|
helpOpen: false,
|
|
@@ -200,16 +214,149 @@ export function createFramedApp(options) {
|
|
|
200
214
|
commandPaletteEntries: undefined,
|
|
201
215
|
commandPaletteTitle: undefined,
|
|
202
216
|
commandPaletteKind: undefined,
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
}
|
|
217
|
+
};
|
|
218
|
+
},
|
|
219
|
+
'open-search-palette': (model) => openSearchPalette(model, frameKeys, options, pagesById),
|
|
220
|
+
'open-command-palette': (model) => openCommandPalette(model, frameKeys, options, pagesById),
|
|
221
|
+
// --- settings ---
|
|
222
|
+
'settings-focus-move': (model, cmd) => {
|
|
223
|
+
const c = cmd;
|
|
224
|
+
const layout = resolveSettingsLayout(model, options, pagesById);
|
|
225
|
+
return layout != null ? moveSettingsFocus(model, layout, c.delta) : model;
|
|
226
|
+
},
|
|
227
|
+
'settings-scroll': (model, cmd) => {
|
|
228
|
+
const c = cmd;
|
|
229
|
+
const layout = resolveSettingsLayout(model, options, pagesById);
|
|
230
|
+
return layout != null ? scrollSettingsBy(model, layout, c.delta) : model;
|
|
231
|
+
},
|
|
232
|
+
'settings-scroll-to': (model, cmd) => {
|
|
233
|
+
const c = cmd;
|
|
234
|
+
const layout = resolveSettingsLayout(model, options, pagesById);
|
|
235
|
+
if (layout == null)
|
|
236
|
+
return model;
|
|
237
|
+
return { ...model, settingsScrollY: c.position === 'top' ? 0 : layout.maxScrollY };
|
|
238
|
+
},
|
|
239
|
+
'activate-settings-row': (model, cmd, teaCmds) => {
|
|
240
|
+
const c = cmd;
|
|
241
|
+
const layout = resolveSettingsLayout(model, options, pagesById);
|
|
242
|
+
if (layout == null)
|
|
243
|
+
return model;
|
|
244
|
+
const hitRow = layout.rows.find((r) => r.index === c.rowIndex);
|
|
245
|
+
if (hitRow == null)
|
|
246
|
+
return model;
|
|
247
|
+
const focusedModel = { ...model, settingsFocusIndex: hitRow.index };
|
|
248
|
+
if (hitRow.row.action === undefined || hitRow.row.enabled === false || hitRow.row.kind === 'info') {
|
|
249
|
+
return focusedModel;
|
|
250
|
+
}
|
|
251
|
+
const [nextModel, cmds] = activateSettingsRow(focusedModel, hitRow.row);
|
|
252
|
+
teaCmds.push(...cmds);
|
|
253
|
+
return nextModel;
|
|
254
|
+
},
|
|
255
|
+
// --- notification center ---
|
|
256
|
+
'notification-center-scroll': (model, cmd) => {
|
|
257
|
+
const c = cmd;
|
|
258
|
+
const layout = resolveNotificationCenterLayout(model, options, pagesById);
|
|
259
|
+
return layout != null ? scrollNotificationCenterBy(model, layout, c.delta) : model;
|
|
260
|
+
},
|
|
261
|
+
'notification-center-scroll-to': (model, cmd) => {
|
|
262
|
+
const c = cmd;
|
|
263
|
+
const layout = resolveNotificationCenterLayout(model, options, pagesById);
|
|
264
|
+
if (layout == null)
|
|
265
|
+
return model;
|
|
266
|
+
return { ...model, notificationCenterScrollY: c.position === 'top' ? 0 : layout.maxScrollY };
|
|
267
|
+
},
|
|
268
|
+
'cycle-notification-filter': (model, _cmd, teaCmds) => {
|
|
269
|
+
const layout = resolveNotificationCenterLayout(model, options, pagesById);
|
|
270
|
+
if (layout == null)
|
|
271
|
+
return model;
|
|
272
|
+
const [nextModel, cmds] = cycleNotificationCenterFilter(model, layout);
|
|
273
|
+
teaCmds.push(...cmds);
|
|
274
|
+
return nextModel;
|
|
275
|
+
},
|
|
276
|
+
// --- help ---
|
|
277
|
+
'help-scroll': (model, cmd) => {
|
|
278
|
+
const c = cmd;
|
|
279
|
+
const activePage = pagesById.get(model.activePageId);
|
|
280
|
+
const overlay = renderHelpOverlay(model, activePage, frameKeys, paletteKeys, options, pagesById);
|
|
281
|
+
const viewportHeight = Math.max(1, overlay.body.height - 1);
|
|
282
|
+
const delta = c.action === 'down' ? 3
|
|
283
|
+
: c.action === 'up' ? -3
|
|
284
|
+
: c.action === 'page-down' ? viewportHeight
|
|
285
|
+
: c.action === 'page-up' ? -viewportHeight
|
|
286
|
+
: c.action === 'bottom' ? Infinity
|
|
287
|
+
: /* top */ -Infinity;
|
|
288
|
+
return {
|
|
289
|
+
...model,
|
|
290
|
+
helpScrollY: Math.max(0, Math.min(overlay.maxScrollY, overlay.scrollY + delta)),
|
|
291
|
+
};
|
|
292
|
+
},
|
|
293
|
+
// --- workspace ---
|
|
294
|
+
'focus-pane': (model, cmd) => {
|
|
295
|
+
const c = cmd;
|
|
296
|
+
return focusPane(model, c.paneId);
|
|
297
|
+
},
|
|
298
|
+
'scroll-focused-pane': (model, cmd) => {
|
|
299
|
+
const c = cmd;
|
|
300
|
+
return scrollFocusedPane(model, { type: c.direction === 'down' ? 'scroll-down' : 'scroll-up' }, pagesById, options);
|
|
301
|
+
},
|
|
302
|
+
'switch-tab': (model, cmd, teaCmds) => {
|
|
303
|
+
const c = cmd;
|
|
304
|
+
const [nextModel, cmds] = switchTab(model, c.delta, pagesById, options);
|
|
305
|
+
teaCmds.push(...cmds);
|
|
306
|
+
return nextModel;
|
|
307
|
+
},
|
|
308
|
+
// --- delegation ---
|
|
309
|
+
'apply-frame-action': (model, cmd, teaCmds) => {
|
|
310
|
+
const c = cmd;
|
|
311
|
+
const [nextModel, cmds] = applyFrameAction(c.action, model, options, pagesById);
|
|
312
|
+
teaCmds.push(...cmds);
|
|
313
|
+
return nextModel;
|
|
314
|
+
},
|
|
315
|
+
'palette-key': (model, cmd, teaCmds) => {
|
|
316
|
+
const c = cmd;
|
|
317
|
+
const [nextModel, cmds] = handlePaletteKey(c.msg, model, paletteKeys, options, pagesById);
|
|
318
|
+
teaCmds.push(...cmds);
|
|
319
|
+
return nextModel;
|
|
320
|
+
},
|
|
321
|
+
// --- TEA command emissions ---
|
|
322
|
+
'emit-page-msg': (model, cmd, teaCmds) => {
|
|
323
|
+
const c = cmd;
|
|
324
|
+
teaCmds.push(emitMsgForPage(c.pageId, c.msg));
|
|
325
|
+
return model;
|
|
326
|
+
},
|
|
327
|
+
'emit-global-msg': (model, cmd, teaCmds) => {
|
|
328
|
+
const c = cmd;
|
|
329
|
+
teaCmds.push(emitMsg(c.msg));
|
|
330
|
+
return model;
|
|
331
|
+
},
|
|
332
|
+
'quit': (_model, _cmd, teaCmds) => {
|
|
333
|
+
teaCmds.push(quit());
|
|
334
|
+
return _model;
|
|
335
|
+
},
|
|
336
|
+
'dismiss-notification': (model, cmd, teaCmds) => {
|
|
337
|
+
const c = cmd;
|
|
338
|
+
if (!frameNotificationOptions.enabled)
|
|
339
|
+
return model;
|
|
340
|
+
const nowMs = resolveClock(resolveSafeCtx()).now();
|
|
341
|
+
const [nextModel, cmds] = applyFrameNotificationState(model, dismissNotification(model.runtimeNotifications, c.notificationId, nowMs), nowMs);
|
|
342
|
+
teaCmds.push(...cmds);
|
|
343
|
+
return nextModel;
|
|
344
|
+
},
|
|
345
|
+
// --- observation ---
|
|
346
|
+
'observed-key': (model, cmd, teaCmds) => {
|
|
347
|
+
const c = cmd;
|
|
348
|
+
const observed = options.observeKey?.(c.msg, c.route);
|
|
349
|
+
if (observed !== undefined) {
|
|
350
|
+
teaCmds.push(emitMsgForPage(model.activePageId, observed));
|
|
351
|
+
}
|
|
352
|
+
return model;
|
|
353
|
+
},
|
|
354
|
+
};
|
|
355
|
+
function drainShellCommandBuffer(model, routeResult) {
|
|
356
|
+
const buffers = bufferRuntimeRouteResult(createRuntimeBuffers(), routeResult);
|
|
357
|
+
const teaCmds = [];
|
|
358
|
+
const { state } = applyRuntimeCommandBuffer(model, buffers.commands, (s, cmd) => shellCommandHandlers[cmd.type](s, cmd, teaCmds));
|
|
359
|
+
return [state, teaCmds];
|
|
213
360
|
}
|
|
214
361
|
function resolveLayerContext(model) {
|
|
215
362
|
const activePage = pagesById.get(model.activePageId);
|
|
@@ -229,6 +376,486 @@ export function createFramedApp(options) {
|
|
|
229
376
|
activeLayer,
|
|
230
377
|
};
|
|
231
378
|
}
|
|
379
|
+
function quitRequestCommands(msg, route) {
|
|
380
|
+
if (!shouldUseShellQuitConfirm()) {
|
|
381
|
+
return [{ type: 'observed-key', msg, route }, { type: 'quit' }];
|
|
382
|
+
}
|
|
383
|
+
return [{ type: 'observed-key', msg, route }, { type: 'open-quit-confirm' }];
|
|
384
|
+
}
|
|
385
|
+
function resolveFrameActionCommands(msg, action, route) {
|
|
386
|
+
if (action.type === 'open-search' && options.enableCommandPalette) {
|
|
387
|
+
return [{ type: 'observed-key', msg, route }, { type: 'open-search-palette' }];
|
|
388
|
+
}
|
|
389
|
+
if (action.type === 'open-palette' && options.enableCommandPalette) {
|
|
390
|
+
return [{ type: 'observed-key', msg, route }, { type: 'open-command-palette' }];
|
|
391
|
+
}
|
|
392
|
+
return [{ type: 'observed-key', msg, route }, { type: 'apply-frame-action', action }];
|
|
393
|
+
}
|
|
394
|
+
function handlePaletteLayerKeyCommands(msg, routedLayerKind) {
|
|
395
|
+
const obs = { type: 'observed-key', msg, route: 'palette' };
|
|
396
|
+
if (msg.ctrl && !msg.alt && msg.key === 'c') {
|
|
397
|
+
return quitRequestCommands(msg, 'palette');
|
|
398
|
+
}
|
|
399
|
+
if (!msg.ctrl && !msg.alt && !msg.shift && msg.key === 'escape') {
|
|
400
|
+
return [obs, { type: 'close-palette' }];
|
|
401
|
+
}
|
|
402
|
+
const frameAction = frameKeys.handle(msg);
|
|
403
|
+
if (frameAction?.type === 'open-search') {
|
|
404
|
+
return routedLayerKind === 'search'
|
|
405
|
+
? [obs, { type: 'close-palette' }]
|
|
406
|
+
: [obs, { type: 'open-search-palette' }];
|
|
407
|
+
}
|
|
408
|
+
if (frameAction?.type === 'open-palette') {
|
|
409
|
+
return routedLayerKind === 'command-palette'
|
|
410
|
+
? [obs, { type: 'close-palette' }]
|
|
411
|
+
: [obs, { type: 'open-command-palette' }];
|
|
412
|
+
}
|
|
413
|
+
if (frameAction?.type === 'toggle-notifications') {
|
|
414
|
+
return [obs, { type: 'close-palette' }, { type: 'apply-frame-action', action: frameAction }];
|
|
415
|
+
}
|
|
416
|
+
return [obs, { type: 'palette-key', msg }];
|
|
417
|
+
}
|
|
418
|
+
function handleHelpLayerKeyCommands(msg) {
|
|
419
|
+
const obs = { type: 'observed-key', msg, route: 'help' };
|
|
420
|
+
if (!msg.ctrl && !msg.alt && (msg.key === '?' || msg.key === 'escape')) {
|
|
421
|
+
return [obs, { type: 'close-help' }];
|
|
422
|
+
}
|
|
423
|
+
if (isShellQuitRequest(msg)) {
|
|
424
|
+
return quitRequestCommands(msg, 'help');
|
|
425
|
+
}
|
|
426
|
+
const helpAction = frameKeys.handle(msg);
|
|
427
|
+
if (helpAction && isHelpScrollAction(helpAction)) {
|
|
428
|
+
const action = helpAction.type === 'scroll-down' ? 'down'
|
|
429
|
+
: helpAction.type === 'scroll-up' ? 'up'
|
|
430
|
+
: helpAction.type === 'page-down' ? 'page-down'
|
|
431
|
+
: helpAction.type === 'page-up' ? 'page-up'
|
|
432
|
+
: helpAction.type === 'bottom' ? 'bottom'
|
|
433
|
+
: 'top';
|
|
434
|
+
return [obs, { type: 'help-scroll', action }];
|
|
435
|
+
}
|
|
436
|
+
return [obs];
|
|
437
|
+
}
|
|
438
|
+
function handleSettingsLayerKeyCommands(msg, model) {
|
|
439
|
+
const layout = resolveSettingsLayout(model, options, pagesById);
|
|
440
|
+
if (layout == null)
|
|
441
|
+
return undefined;
|
|
442
|
+
const obs = { type: 'observed-key', msg, route: 'frame' };
|
|
443
|
+
if (!msg.ctrl && !msg.alt && (msg.key === 'escape' || msg.key === 'f2')) {
|
|
444
|
+
return [obs, { type: 'close-settings' }];
|
|
445
|
+
}
|
|
446
|
+
if (msg.ctrl && !msg.alt && msg.key === ',') {
|
|
447
|
+
return [obs, { type: 'close-settings' }];
|
|
448
|
+
}
|
|
449
|
+
if (!msg.ctrl && !msg.alt && msg.key === '?') {
|
|
450
|
+
return [obs, { type: 'open-help' }];
|
|
451
|
+
}
|
|
452
|
+
if (isShellQuitRequest(msg)) {
|
|
453
|
+
return quitRequestCommands(msg, 'frame');
|
|
454
|
+
}
|
|
455
|
+
if (options.enableCommandPalette && !msg.ctrl && !msg.alt && msg.key === '/') {
|
|
456
|
+
return [obs, { type: 'open-search-palette' }];
|
|
457
|
+
}
|
|
458
|
+
if (options.enableCommandPalette && ((msg.ctrl && !msg.alt && msg.key === 'p') || (!msg.ctrl && !msg.alt && msg.key === ':'))) {
|
|
459
|
+
return [obs, { type: 'open-command-palette' }];
|
|
460
|
+
}
|
|
461
|
+
const settingsFrameAction = frameKeys.handle(msg);
|
|
462
|
+
if (settingsFrameAction?.type === 'toggle-notifications') {
|
|
463
|
+
return [obs, { type: 'apply-frame-action', action: settingsFrameAction }];
|
|
464
|
+
}
|
|
465
|
+
if (!msg.ctrl && !msg.alt && msg.key === 'up') {
|
|
466
|
+
return [obs, { type: 'settings-focus-move', delta: -1 }];
|
|
467
|
+
}
|
|
468
|
+
if (!msg.ctrl && !msg.alt && msg.key === 'down') {
|
|
469
|
+
return [obs, { type: 'settings-focus-move', delta: 1 }];
|
|
470
|
+
}
|
|
471
|
+
if (!msg.ctrl && !msg.alt && msg.key === 'j') {
|
|
472
|
+
return [obs, { type: 'settings-scroll', delta: 1 }];
|
|
473
|
+
}
|
|
474
|
+
if (!msg.ctrl && !msg.alt && msg.key === 'k') {
|
|
475
|
+
return [obs, { type: 'settings-scroll', delta: -1 }];
|
|
476
|
+
}
|
|
477
|
+
if (!msg.ctrl && !msg.alt && msg.key === 'd') {
|
|
478
|
+
return [obs, { type: 'settings-scroll', delta: Math.max(1, layout.contentHeight - 1) }];
|
|
479
|
+
}
|
|
480
|
+
if (!msg.ctrl && !msg.alt && msg.key === 'u') {
|
|
481
|
+
return [obs, { type: 'settings-scroll', delta: -Math.max(1, layout.contentHeight - 1) }];
|
|
482
|
+
}
|
|
483
|
+
if (!msg.ctrl && !msg.alt && msg.key === 'g') {
|
|
484
|
+
return [obs, { type: 'settings-scroll-to', position: 'top' }];
|
|
485
|
+
}
|
|
486
|
+
if (!msg.ctrl && !msg.alt && msg.key === 'G') {
|
|
487
|
+
return [obs, { type: 'settings-scroll-to', position: 'bottom' }];
|
|
488
|
+
}
|
|
489
|
+
if (!msg.ctrl && !msg.alt && (msg.key === 'enter' || msg.key === 'space')) {
|
|
490
|
+
const rowIndex = clampSettingsFocus(model, layout);
|
|
491
|
+
const row = layout.rows[rowIndex];
|
|
492
|
+
if (row?.row.action !== undefined && row.row.enabled !== false && row.row.kind !== 'info') {
|
|
493
|
+
return [obs, { type: 'activate-settings-row', rowIndex: row.index }];
|
|
494
|
+
}
|
|
495
|
+
return [obs];
|
|
496
|
+
}
|
|
497
|
+
return [obs];
|
|
498
|
+
}
|
|
499
|
+
function handleNotificationCenterLayerKeyCommands(msg, model) {
|
|
500
|
+
const layout = resolveNotificationCenterLayout(model, options, pagesById);
|
|
501
|
+
if (layout == null)
|
|
502
|
+
return undefined;
|
|
503
|
+
const obs = { type: 'observed-key', msg, route: 'frame' };
|
|
504
|
+
if (!msg.ctrl && !msg.alt && msg.key === 'escape') {
|
|
505
|
+
return [obs, { type: 'close-notification-center' }];
|
|
506
|
+
}
|
|
507
|
+
if (isShellQuitRequest(msg)) {
|
|
508
|
+
return quitRequestCommands(msg, 'frame');
|
|
509
|
+
}
|
|
510
|
+
const centerFrameAction = frameKeys.handle(msg);
|
|
511
|
+
if (centerFrameAction?.type === 'toggle-notifications') {
|
|
512
|
+
return [obs, { type: 'apply-frame-action', action: centerFrameAction }];
|
|
513
|
+
}
|
|
514
|
+
if (!msg.ctrl && !msg.alt && msg.key === 'f2') {
|
|
515
|
+
return [obs, { type: 'close-notification-center' }, { type: 'apply-frame-action', action: { type: 'toggle-settings' } }];
|
|
516
|
+
}
|
|
517
|
+
if (!msg.ctrl && !msg.alt && msg.key === '?') {
|
|
518
|
+
return [obs, { type: 'close-notification-center' }, { type: 'open-help' }];
|
|
519
|
+
}
|
|
520
|
+
if (options.enableCommandPalette && !msg.ctrl && !msg.alt && msg.key === '/') {
|
|
521
|
+
return [obs, { type: 'close-notification-center' }, { type: 'open-search-palette' }];
|
|
522
|
+
}
|
|
523
|
+
if (options.enableCommandPalette && ((msg.ctrl && !msg.alt && msg.key === 'p') || (!msg.ctrl && !msg.alt && msg.key === ':'))) {
|
|
524
|
+
return [obs, { type: 'close-notification-center' }, { type: 'open-command-palette' }];
|
|
525
|
+
}
|
|
526
|
+
if (!msg.ctrl && !msg.alt && (msg.key === 'up' || msg.key === 'k')) {
|
|
527
|
+
return [obs, { type: 'notification-center-scroll', delta: -1 }];
|
|
528
|
+
}
|
|
529
|
+
if (!msg.ctrl && !msg.alt && (msg.key === 'down' || msg.key === 'j')) {
|
|
530
|
+
return [obs, { type: 'notification-center-scroll', delta: 1 }];
|
|
531
|
+
}
|
|
532
|
+
if (!msg.ctrl && !msg.alt && msg.key === 'd') {
|
|
533
|
+
return [obs, { type: 'notification-center-scroll', delta: Math.max(1, layout.contentHeight - 2) }];
|
|
534
|
+
}
|
|
535
|
+
if (!msg.ctrl && !msg.alt && msg.key === 'u') {
|
|
536
|
+
return [obs, { type: 'notification-center-scroll', delta: -Math.max(1, layout.contentHeight - 2) }];
|
|
537
|
+
}
|
|
538
|
+
if (!msg.ctrl && !msg.alt && msg.key === 'g') {
|
|
539
|
+
return [obs, { type: 'notification-center-scroll-to', position: 'top' }];
|
|
540
|
+
}
|
|
541
|
+
if (!msg.ctrl && !msg.alt && msg.key === 'G') {
|
|
542
|
+
return [obs, { type: 'notification-center-scroll-to', position: 'bottom' }];
|
|
543
|
+
}
|
|
544
|
+
if (!msg.ctrl && !msg.alt && msg.key === 'f') {
|
|
545
|
+
return [obs, { type: 'cycle-notification-filter' }];
|
|
546
|
+
}
|
|
547
|
+
return [obs];
|
|
548
|
+
}
|
|
549
|
+
function handleWorkspaceLayerKeyCommands(msg, model) {
|
|
550
|
+
if (isShellQuitRequest(msg)) {
|
|
551
|
+
return quitRequestCommands(msg, 'frame');
|
|
552
|
+
}
|
|
553
|
+
const context = resolveLayerContext(model);
|
|
554
|
+
const { activePage, activeInputArea } = context;
|
|
555
|
+
const paneAction = activeInputArea?.keyMap?.handle(msg);
|
|
556
|
+
const pageAction = activePage.keyMap?.handle(msg);
|
|
557
|
+
const globalAction = options.globalKeys?.handle(msg);
|
|
558
|
+
const frameAction = frameKeys.handle(msg);
|
|
559
|
+
const keyPriority = options.keyPriority ?? 'frame-first';
|
|
560
|
+
if (keyPriority === 'page-first') {
|
|
561
|
+
if (paneAction !== undefined) {
|
|
562
|
+
return [{ type: 'observed-key', msg, route: 'page' }, { type: 'emit-page-msg', pageId: model.activePageId, msg: paneAction }];
|
|
563
|
+
}
|
|
564
|
+
if (pageAction !== undefined) {
|
|
565
|
+
return [{ type: 'observed-key', msg, route: 'page' }, { type: 'emit-page-msg', pageId: model.activePageId, msg: pageAction }];
|
|
566
|
+
}
|
|
567
|
+
if (globalAction !== undefined) {
|
|
568
|
+
return [{ type: 'observed-key', msg, route: 'global' }, { type: 'emit-global-msg', msg: globalAction }];
|
|
569
|
+
}
|
|
570
|
+
if (frameAction !== undefined) {
|
|
571
|
+
return resolveFrameActionCommands(msg, frameAction, 'frame');
|
|
572
|
+
}
|
|
573
|
+
return [{ type: 'observed-key', msg, route: 'unhandled' }];
|
|
574
|
+
}
|
|
575
|
+
// frame-first (default)
|
|
576
|
+
if (frameAction !== undefined) {
|
|
577
|
+
return resolveFrameActionCommands(msg, frameAction, 'frame');
|
|
578
|
+
}
|
|
579
|
+
if (paneAction !== undefined) {
|
|
580
|
+
return [{ type: 'observed-key', msg, route: 'page' }, { type: 'emit-page-msg', pageId: model.activePageId, msg: paneAction }];
|
|
581
|
+
}
|
|
582
|
+
if (globalAction !== undefined) {
|
|
583
|
+
return [{ type: 'observed-key', msg, route: 'global' }, { type: 'emit-global-msg', msg: globalAction }];
|
|
584
|
+
}
|
|
585
|
+
if (pageAction !== undefined) {
|
|
586
|
+
return [{ type: 'observed-key', msg, route: 'page' }, { type: 'emit-page-msg', pageId: model.activePageId, msg: pageAction }];
|
|
587
|
+
}
|
|
588
|
+
return [{ type: 'observed-key', msg, route: 'unhandled' }];
|
|
589
|
+
}
|
|
590
|
+
function resolveRoutedKeyLayer(msg, model) {
|
|
591
|
+
const context = resolveLayerContext(model);
|
|
592
|
+
const runtimeStack = describeFrameRuntimeViewStack(model, {
|
|
593
|
+
pageModalOpen: context.pageModalOpen,
|
|
594
|
+
});
|
|
595
|
+
return routeRuntimeInput(runtimeStack, EMPTY_RUNTIME_LAYOUTS, { kind: 'key', key: msg.key }, ({ layer }) => {
|
|
596
|
+
const frameLayer = layer.model;
|
|
597
|
+
if (frameLayer == null)
|
|
598
|
+
return undefined;
|
|
599
|
+
if (frameLayer.kind === 'search' || frameLayer.kind === 'command-palette') {
|
|
600
|
+
return { handled: true, commands: handlePaletteLayerKeyCommands(msg, frameLayer.kind) };
|
|
601
|
+
}
|
|
602
|
+
if (frameLayer.kind === 'help') {
|
|
603
|
+
return { handled: true, commands: handleHelpLayerKeyCommands(msg) };
|
|
604
|
+
}
|
|
605
|
+
if (frameLayer.kind === 'settings') {
|
|
606
|
+
const cmds = handleSettingsLayerKeyCommands(msg, model);
|
|
607
|
+
return cmds != null ? { handled: true, commands: cmds } : { bubble: true };
|
|
608
|
+
}
|
|
609
|
+
if (frameLayer.kind === 'notification-center') {
|
|
610
|
+
const cmds = handleNotificationCenterLayerKeyCommands(msg, model);
|
|
611
|
+
return cmds != null ? { handled: true, commands: cmds } : { bubble: true };
|
|
612
|
+
}
|
|
613
|
+
if (frameLayer.kind === 'quit-confirm') {
|
|
614
|
+
const obs = { type: 'observed-key', msg, route: 'frame' };
|
|
615
|
+
if (isShellQuitConfirmAccept(msg)) {
|
|
616
|
+
return { handled: true, commands: [obs, { type: 'close-quit-confirm' }, { type: 'quit' }] };
|
|
617
|
+
}
|
|
618
|
+
if (isShellQuitConfirmDismiss(msg)) {
|
|
619
|
+
return { handled: true, commands: [obs, { type: 'close-quit-confirm' }] };
|
|
620
|
+
}
|
|
621
|
+
return { handled: true, commands: [obs] };
|
|
622
|
+
}
|
|
623
|
+
if (frameLayer.kind === 'page-modal') {
|
|
624
|
+
const { modalKeyMap } = context;
|
|
625
|
+
const obs = { type: 'observed-key', msg, route: 'page' };
|
|
626
|
+
if (modalKeyMap != null) {
|
|
627
|
+
const modalAction = modalKeyMap.handle(msg);
|
|
628
|
+
if (modalAction !== undefined) {
|
|
629
|
+
return { handled: true, commands: [obs, { type: 'emit-page-msg', pageId: model.activePageId, msg: modalAction }] };
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
return { handled: true, commands: [obs] };
|
|
633
|
+
}
|
|
634
|
+
// workspace (root layer)
|
|
635
|
+
return { handled: true, commands: handleWorkspaceLayerKeyCommands(msg, model) };
|
|
636
|
+
});
|
|
637
|
+
}
|
|
638
|
+
function createShellRetainedLayoutNode(id, rect, children) {
|
|
639
|
+
return {
|
|
640
|
+
id,
|
|
641
|
+
rect: {
|
|
642
|
+
x: rect.col,
|
|
643
|
+
y: rect.row,
|
|
644
|
+
width: rect.width,
|
|
645
|
+
height: rect.height,
|
|
646
|
+
},
|
|
647
|
+
children: children ?? [],
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
function resolveWorkspacePaneRects(model) {
|
|
651
|
+
const bodyRect = resolveBodyRect(model, options);
|
|
652
|
+
const maxState = model.maximizedPaneByPage[model.activePageId];
|
|
653
|
+
const maximizedPaneId = maxState?.maximizedPaneId;
|
|
654
|
+
const renderResult = maximizedPaneId
|
|
655
|
+
? renderMaximizedPane(model.activePageId, model, bodyRect, pagesById, maximizedPaneId, paneScratchPool)
|
|
656
|
+
: renderPageContent(model.activePageId, model, bodyRect, pagesById);
|
|
657
|
+
return renderResult.paneRects;
|
|
658
|
+
}
|
|
659
|
+
function buildWorkspaceLayoutTree(model) {
|
|
660
|
+
const header = resolveHeaderLine(model, options, pagesById, headerScratch);
|
|
661
|
+
headerScratch = header.surface;
|
|
662
|
+
const tabChildren = header.tabTargets.map((target) => createShellRetainedLayoutNode(`tab:${target.pageId}`, {
|
|
663
|
+
row: 0,
|
|
664
|
+
col: target.startCol,
|
|
665
|
+
width: target.endCol - target.startCol + 1,
|
|
666
|
+
height: 1,
|
|
667
|
+
}));
|
|
668
|
+
const bodyRect = resolveBodyRect(model, options);
|
|
669
|
+
const paneRects = resolveWorkspacePaneRects(model);
|
|
670
|
+
const paneChildren = [];
|
|
671
|
+
for (const [paneId, rect] of paneRects.entries()) {
|
|
672
|
+
paneChildren.push(createShellRetainedLayoutNode(`pane:${paneId}`, rect));
|
|
673
|
+
}
|
|
674
|
+
return createShellRetainedLayoutNode('workspace', { row: 0, col: 0, width: model.columns, height: model.rows }, [
|
|
675
|
+
createShellRetainedLayoutNode('header-bar', { row: 0, col: 0, width: model.columns, height: 1 }, tabChildren),
|
|
676
|
+
createShellRetainedLayoutNode('workspace-body', bodyRect, paneChildren),
|
|
677
|
+
]);
|
|
678
|
+
}
|
|
679
|
+
function buildSettingsRowChildren(model, layout) {
|
|
680
|
+
const scrollY = clampSettingsScroll(model, layout);
|
|
681
|
+
const viewportTop = 1;
|
|
682
|
+
const viewportBottom = model.rows - 1;
|
|
683
|
+
const children = [];
|
|
684
|
+
for (const flatRow of layout.rows) {
|
|
685
|
+
const screenRow = flatRow.line - scrollY + viewportTop;
|
|
686
|
+
const clippedTop = Math.max(viewportTop, screenRow);
|
|
687
|
+
const clippedBottom = Math.min(viewportBottom, screenRow + flatRow.height);
|
|
688
|
+
if (clippedTop >= clippedBottom)
|
|
689
|
+
continue;
|
|
690
|
+
children.push(createShellRetainedLayoutNode(`settings-row:${flatRow.index}`, {
|
|
691
|
+
row: clippedTop,
|
|
692
|
+
col: layout.startCol,
|
|
693
|
+
width: layout.drawerWidth,
|
|
694
|
+
height: clippedBottom - clippedTop,
|
|
695
|
+
}));
|
|
696
|
+
}
|
|
697
|
+
return children;
|
|
698
|
+
}
|
|
699
|
+
function resolveFrameMouseRuntimeLayouts(model) {
|
|
700
|
+
let layouts = EMPTY_RUNTIME_LAYOUTS;
|
|
701
|
+
const settingsLayout = model.settingsOpen ? resolveSettingsLayout(model, options, pagesById) : undefined;
|
|
702
|
+
if (settingsLayout != null) {
|
|
703
|
+
layouts = retainRuntimeLayout(layouts, {
|
|
704
|
+
viewId: 'settings',
|
|
705
|
+
tree: createShellRetainedLayoutNode('settings-drawer', {
|
|
706
|
+
row: 0,
|
|
707
|
+
col: settingsLayout.startCol,
|
|
708
|
+
width: settingsLayout.drawerWidth,
|
|
709
|
+
height: model.rows,
|
|
710
|
+
}, buildSettingsRowChildren(model, settingsLayout)),
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
const notificationCenterLayout = model.notificationCenterOpen ? resolveNotificationCenterLayout(model, options, pagesById) : undefined;
|
|
714
|
+
if (notificationCenterLayout != null) {
|
|
715
|
+
layouts = retainRuntimeLayout(layouts, {
|
|
716
|
+
viewId: 'notification-center',
|
|
717
|
+
tree: createShellRetainedLayoutNode('notification-center-drawer', {
|
|
718
|
+
row: 0,
|
|
719
|
+
col: notificationCenterLayout.startCol,
|
|
720
|
+
width: notificationCenterLayout.drawerWidth,
|
|
721
|
+
height: model.rows,
|
|
722
|
+
}),
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
layouts = retainRuntimeLayout(layouts, {
|
|
726
|
+
viewId: 'workspace',
|
|
727
|
+
tree: buildWorkspaceLayoutTree(model),
|
|
728
|
+
});
|
|
729
|
+
return layouts;
|
|
730
|
+
}
|
|
731
|
+
function resolveRoutedMouseLayer(msg, model) {
|
|
732
|
+
const context = resolveLayerContext(model);
|
|
733
|
+
const { activePageModel, inputAreas } = context;
|
|
734
|
+
const runtimeStack = describeFrameRuntimeViewStack(model, {
|
|
735
|
+
pageModalOpen: context.pageModalOpen,
|
|
736
|
+
});
|
|
737
|
+
return routeRuntimeInput(runtimeStack, resolveFrameMouseRuntimeLayouts(model), {
|
|
738
|
+
kind: 'pointer',
|
|
739
|
+
action: msg.action,
|
|
740
|
+
x: msg.col,
|
|
741
|
+
y: msg.row,
|
|
742
|
+
button: msg.button === 'none' ? undefined : msg.button,
|
|
743
|
+
}, ({ layer, hit }) => {
|
|
744
|
+
const frameLayer = layer.model;
|
|
745
|
+
if (frameLayer == null)
|
|
746
|
+
return undefined;
|
|
747
|
+
const cmds = [];
|
|
748
|
+
if (frameLayer.kind === 'help') {
|
|
749
|
+
if (msg.action === 'scroll-up' || msg.action === 'scroll-down') {
|
|
750
|
+
cmds.push({ type: 'help-scroll', action: msg.action === 'scroll-down' ? 'down' : 'up' });
|
|
751
|
+
}
|
|
752
|
+
return { handled: true, commands: cmds };
|
|
753
|
+
}
|
|
754
|
+
if (frameLayer.kind === 'search' || frameLayer.kind === 'command-palette'
|
|
755
|
+
|| frameLayer.kind === 'quit-confirm' || frameLayer.kind === 'page-modal') {
|
|
756
|
+
return { handled: true };
|
|
757
|
+
}
|
|
758
|
+
if (frameLayer.kind === 'settings') {
|
|
759
|
+
if (hit == null)
|
|
760
|
+
return { handled: true };
|
|
761
|
+
if (msg.action === 'scroll-up' || msg.action === 'scroll-down') {
|
|
762
|
+
cmds.push({ type: 'settings-scroll', delta: msg.action === 'scroll-down' ? 3 : -3 });
|
|
763
|
+
return { handled: true, commands: cmds };
|
|
764
|
+
}
|
|
765
|
+
if (msg.action === 'press' && msg.button === 'left') {
|
|
766
|
+
const rowNode = hit.path.find((n) => n.id?.startsWith('settings-row:'));
|
|
767
|
+
if (rowNode != null) {
|
|
768
|
+
const rowIndex = parseInt(rowNode.id.slice('settings-row:'.length), 10);
|
|
769
|
+
cmds.push({ type: 'activate-settings-row', rowIndex });
|
|
770
|
+
}
|
|
771
|
+
return { handled: true, commands: cmds };
|
|
772
|
+
}
|
|
773
|
+
return { handled: true };
|
|
774
|
+
}
|
|
775
|
+
if (frameLayer.kind === 'notification-center') {
|
|
776
|
+
if (hit == null)
|
|
777
|
+
return { handled: true };
|
|
778
|
+
if (msg.action === 'scroll-up' || msg.action === 'scroll-down') {
|
|
779
|
+
cmds.push({ type: 'notification-center-scroll', delta: msg.action === 'scroll-down' ? 3 : -3 });
|
|
780
|
+
return { handled: true, commands: cmds };
|
|
781
|
+
}
|
|
782
|
+
return { handled: true };
|
|
783
|
+
}
|
|
784
|
+
// workspace layer
|
|
785
|
+
if (msg.action === 'press' && msg.button === 'left') {
|
|
786
|
+
// notification toast hit-testing (outside retained layouts)
|
|
787
|
+
if (frameNotificationOptions.enabled) {
|
|
788
|
+
const notificationTarget = hitTestNotificationStack(model.runtimeNotifications, {
|
|
789
|
+
screenWidth: model.columns,
|
|
790
|
+
screenHeight: model.rows,
|
|
791
|
+
margin: frameNotificationOptions.margin,
|
|
792
|
+
gap: frameNotificationOptions.gap,
|
|
793
|
+
ctx: resolveSafeCtx() ?? undefined,
|
|
794
|
+
}, msg.col, msg.row);
|
|
795
|
+
if (notificationTarget?.kind === 'dismiss') {
|
|
796
|
+
cmds.push({ type: 'dismiss-notification', notificationId: notificationTarget.item.id });
|
|
797
|
+
return { handled: true, commands: cmds };
|
|
798
|
+
}
|
|
799
|
+
if (notificationTarget != null) {
|
|
800
|
+
return { handled: true };
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
// tab click
|
|
804
|
+
const tabNode = hit?.path.find((n) => n.id?.startsWith('tab:'));
|
|
805
|
+
if (tabNode != null) {
|
|
806
|
+
const pageId = tabNode.id.slice('tab:'.length);
|
|
807
|
+
const currentIndex = model.pageOrder.indexOf(model.activePageId);
|
|
808
|
+
const nextIndex = model.pageOrder.indexOf(pageId);
|
|
809
|
+
if (currentIndex >= 0 && nextIndex >= 0 && nextIndex !== currentIndex) {
|
|
810
|
+
cmds.push({ type: 'switch-tab', delta: nextIndex - currentIndex });
|
|
811
|
+
}
|
|
812
|
+
return { handled: true, commands: cmds };
|
|
813
|
+
}
|
|
814
|
+
if (msg.row === 0) {
|
|
815
|
+
return { handled: true };
|
|
816
|
+
}
|
|
817
|
+
// pane click
|
|
818
|
+
const clickedPaneNode = hit?.path.find((n) => n.id?.startsWith('pane:'));
|
|
819
|
+
if (clickedPaneNode != null) {
|
|
820
|
+
const paneId = clickedPaneNode.id.slice('pane:'.length);
|
|
821
|
+
const paneRects = resolveWorkspacePaneRects(model);
|
|
822
|
+
const paneRect = paneRects.get(paneId);
|
|
823
|
+
if (paneRect != null) {
|
|
824
|
+
cmds.push({ type: 'focus-pane', paneId });
|
|
825
|
+
const inputArea = findInputAreaByPaneId(inputAreas, paneId);
|
|
826
|
+
const areaMsg = inputArea?.mouse?.({ msg, model: activePageModel, rect: paneRect });
|
|
827
|
+
cmds.push({
|
|
828
|
+
type: 'emit-page-msg',
|
|
829
|
+
pageId: model.activePageId,
|
|
830
|
+
msg: areaMsg !== undefined ? areaMsg : msg,
|
|
831
|
+
});
|
|
832
|
+
return { handled: true, commands: cmds };
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
if (msg.action === 'scroll-up' || msg.action === 'scroll-down') {
|
|
837
|
+
const scrollPaneNode = hit?.path.find((n) => n.id?.startsWith('pane:'));
|
|
838
|
+
if (scrollPaneNode != null) {
|
|
839
|
+
const paneId = scrollPaneNode.id.slice('pane:'.length);
|
|
840
|
+
const paneRects = resolveWorkspacePaneRects(model);
|
|
841
|
+
const paneRect = paneRects.get(paneId);
|
|
842
|
+
if (paneRect != null) {
|
|
843
|
+
cmds.push({ type: 'focus-pane', paneId });
|
|
844
|
+
const inputArea = findInputAreaByPaneId(inputAreas, paneId);
|
|
845
|
+
const areaMsg = inputArea?.mouse?.({ msg, model: activePageModel, rect: paneRect });
|
|
846
|
+
if (areaMsg !== undefined) {
|
|
847
|
+
cmds.push({ type: 'emit-page-msg', pageId: model.activePageId, msg: areaMsg });
|
|
848
|
+
}
|
|
849
|
+
else {
|
|
850
|
+
cmds.push({ type: 'scroll-focused-pane', direction: msg.action === 'scroll-down' ? 'down' : 'up' });
|
|
851
|
+
}
|
|
852
|
+
return { handled: true, commands: cmds };
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
return { handled: true };
|
|
857
|
+
});
|
|
858
|
+
}
|
|
232
859
|
function resolveWorkspaceHelpSource(activePage, activeInputArea) {
|
|
233
860
|
return mergeBindingSources(frameKeys, quitHelpKeys, options.globalKeys, activeInputArea?.helpSource ?? activeInputArea?.keyMap, activePage.helpSource ?? activePage.keyMap);
|
|
234
861
|
}
|
|
@@ -337,132 +964,6 @@ export function createFramedApp(options) {
|
|
|
337
964
|
const wrappedCmds = cmds.map((cmd) => wrapCmdForPage(targetPageId, cmd));
|
|
338
965
|
return [synced, wrappedCmds];
|
|
339
966
|
}
|
|
340
|
-
function handleFrameMouse(msg, model) {
|
|
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') {
|
|
349
|
-
return [model, []];
|
|
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, []];
|
|
380
|
-
}
|
|
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
|
-
}
|
|
397
|
-
return [model, []];
|
|
398
|
-
}
|
|
399
|
-
}
|
|
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, []];
|
|
427
|
-
}
|
|
428
|
-
return [model, []];
|
|
429
|
-
}
|
|
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
|
-
}
|
|
463
|
-
}
|
|
464
|
-
return undefined;
|
|
465
|
-
}
|
|
466
967
|
function applyFrameNotificationState(model, notifications, nowMs, forceTick = false) {
|
|
467
968
|
const trimmed = trimNotificationsToViewport(notifications, {
|
|
468
969
|
screenWidth: model.columns,
|
|
@@ -642,270 +1143,13 @@ export function createFramedApp(options) {
|
|
|
642
1143
|
}, []];
|
|
643
1144
|
}
|
|
644
1145
|
if (isKeyMsg(msg)) {
|
|
645
|
-
|
|
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
|
-
}
|
|
670
|
-
const [nextModel, cmds] = handlePaletteKey(msg, model, paletteKeys, options, pagesById);
|
|
671
|
-
return [nextModel, withObservedKey(model, cmds, msg, 'palette')];
|
|
672
|
-
}
|
|
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
|
-
];
|
|
686
|
-
}
|
|
687
|
-
return [model, withObservedKey(model, [], msg, 'help')];
|
|
688
|
-
}
|
|
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) {
|
|
847
|
-
const modalAction = modalKeyMap.handle(msg);
|
|
848
|
-
if (modalAction !== undefined) {
|
|
849
|
-
return [model, withObservedKey(model, [emitMsgForPage(model.activePageId, modalAction)], msg, 'page')];
|
|
850
|
-
}
|
|
851
|
-
return [model, withObservedKey(model, [], msg, 'page')];
|
|
852
|
-
}
|
|
853
|
-
if (isShellQuitRequest(msg)) {
|
|
854
|
-
return applyQuitRequest(model, msg);
|
|
855
|
-
}
|
|
856
|
-
const paneAction = activeInputArea?.keyMap?.handle(msg);
|
|
857
|
-
const pageAction = activePage.keyMap?.handle(msg);
|
|
858
|
-
const globalAction = options.globalKeys?.handle(msg);
|
|
859
|
-
const frameAction = frameKeys.handle(msg);
|
|
860
|
-
const keyPriority = options.keyPriority ?? 'frame-first';
|
|
861
|
-
if (keyPriority === 'page-first') {
|
|
862
|
-
if (paneAction !== undefined) {
|
|
863
|
-
return [model, withObservedKey(model, [emitMsgForPage(model.activePageId, paneAction)], msg, 'page')];
|
|
864
|
-
}
|
|
865
|
-
if (pageAction !== undefined) {
|
|
866
|
-
return [model, withObservedKey(model, [emitMsgForPage(model.activePageId, pageAction)], msg, 'page')];
|
|
867
|
-
}
|
|
868
|
-
if (globalAction !== undefined) {
|
|
869
|
-
return [model, withObservedKey(model, [emitMsg(globalAction)], msg, 'global')];
|
|
870
|
-
}
|
|
871
|
-
if (frameAction !== undefined) {
|
|
872
|
-
if (frameAction.type === 'open-search' && options.enableCommandPalette) {
|
|
873
|
-
return [openSearchPalette(model, frameKeys, options, pagesById), withObservedKey(model, [], msg, 'frame')];
|
|
874
|
-
}
|
|
875
|
-
if (frameAction.type === 'open-palette' && options.enableCommandPalette) {
|
|
876
|
-
return [openCommandPalette(model, frameKeys, options, pagesById), withObservedKey(model, [], msg, 'frame')];
|
|
877
|
-
}
|
|
878
|
-
const [nextModel, cmds] = applyFrameAction(frameAction, model, options, pagesById);
|
|
879
|
-
return [nextModel, withObservedKey(model, cmds, msg, 'frame')];
|
|
880
|
-
}
|
|
881
|
-
return [model, withObservedKey(model, [], msg, 'unhandled')];
|
|
882
|
-
}
|
|
883
|
-
if (frameAction !== undefined) {
|
|
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
|
-
}
|
|
888
|
-
if (frameAction.type === 'open-palette' && options.enableCommandPalette) {
|
|
889
|
-
return [openCommandPalette(model, frameKeys, options, pagesById), withObservedKey(model, [], msg, 'frame')];
|
|
890
|
-
}
|
|
891
|
-
const [nextModel, cmds] = applyFrameAction(frameAction, model, options, pagesById);
|
|
892
|
-
return [nextModel, withObservedKey(model, cmds, msg, 'frame')];
|
|
893
|
-
}
|
|
894
|
-
if (paneAction !== undefined) {
|
|
895
|
-
return [model, withObservedKey(model, [emitMsgForPage(model.activePageId, paneAction)], msg, 'page')];
|
|
896
|
-
}
|
|
897
|
-
if (globalAction !== undefined) {
|
|
898
|
-
return [model, withObservedKey(model, [emitMsg(globalAction)], msg, 'global')];
|
|
899
|
-
}
|
|
900
|
-
if (pageAction !== undefined) {
|
|
901
|
-
return [model, withObservedKey(model, [emitMsgForPage(model.activePageId, pageAction)], msg, 'page')];
|
|
902
|
-
}
|
|
903
|
-
return [model, withObservedKey(model, [], msg, 'unhandled')];
|
|
1146
|
+
return drainShellCommandBuffer(model, resolveRoutedKeyLayer(msg, model));
|
|
904
1147
|
}
|
|
905
1148
|
if (isMouseMsg(msg)) {
|
|
906
|
-
const
|
|
907
|
-
if (
|
|
908
|
-
return
|
|
1149
|
+
const mouseRouteResult = resolveRoutedMouseLayer(msg, model);
|
|
1150
|
+
if (mouseRouteResult.handled) {
|
|
1151
|
+
return drainShellCommandBuffer(model, mouseRouteResult);
|
|
1152
|
+
}
|
|
909
1153
|
return updateTargetPage(model, model.activePageId, msg);
|
|
910
1154
|
}
|
|
911
1155
|
// Custom message path: route to originating page when command messages are scoped.
|
|
@@ -916,13 +1160,18 @@ export function createFramedApp(options) {
|
|
|
916
1160
|
},
|
|
917
1161
|
view(model) {
|
|
918
1162
|
const { activePage, layerStack, activeLayer, } = resolvePresentedLayerContext(model);
|
|
919
|
-
const
|
|
920
|
-
|
|
1163
|
+
const headerResult = resolveHeaderLine(model, options, pagesById, headerScratch);
|
|
1164
|
+
headerScratch = headerResult.surface;
|
|
1165
|
+
const header = headerResult.surface;
|
|
1166
|
+
helpLineScratch = renderHelpLine(model, activeLayer, options.i18n, resolveNotificationFooterCue(model, options, pagesById), helpLineScratch);
|
|
1167
|
+
const helpLine = helpLineScratch;
|
|
921
1168
|
const bodyRect = resolveBodyRect(model, options);
|
|
922
1169
|
// Check for maximized pane — if set, render only that pane at full body rect
|
|
923
1170
|
const maxState = model.maximizedPaneByPage[model.activePageId];
|
|
924
1171
|
const maximizedPaneId = maxState?.maximizedPaneId;
|
|
925
1172
|
const frameSurface = getComposedFrameScratch(model.columns, model.rows);
|
|
1173
|
+
// clear() is load-bearing: it resets dim flags left by overlay compositing
|
|
1174
|
+
// on the previous frame. Do not skip or defer this call.
|
|
926
1175
|
frameSurface.clear();
|
|
927
1176
|
frameSurface.blit(header, 0, 0);
|
|
928
1177
|
if (model.rows > 1) {
|
|
@@ -933,7 +1182,7 @@ export function createFramedApp(options) {
|
|
|
933
1182
|
const activeTransition = model.activeTransition ?? options.transition;
|
|
934
1183
|
if (model.previousPageId != null && model.transitionProgress < 1 && activeTransition && activeTransition !== 'none') {
|
|
935
1184
|
const activeBodyResult = maximizedPaneId
|
|
936
|
-
? renderMaximizedPane(model.activePageId, model, bodyRect, pagesById, maximizedPaneId)
|
|
1185
|
+
? renderMaximizedPane(model.activePageId, model, bodyRect, pagesById, maximizedPaneId, paneScratchPool)
|
|
937
1186
|
: renderPageContent(model.activePageId, model, bodyRect, pagesById);
|
|
938
1187
|
activeResult = activeBodyResult;
|
|
939
1188
|
bodySurface = activeBodyResult.surface;
|
|
@@ -945,8 +1194,8 @@ export function createFramedApp(options) {
|
|
|
945
1194
|
}
|
|
946
1195
|
else {
|
|
947
1196
|
activeResult = maximizedPaneId
|
|
948
|
-
? renderMaximizedPaneInto(model.activePageId, model, bodyRect, pagesById, maximizedPaneId, frameSurface)
|
|
949
|
-
: renderPageContentInto(model.activePageId, model, bodyRect, pagesById, frameSurface);
|
|
1197
|
+
? renderMaximizedPaneInto(model.activePageId, model, bodyRect, pagesById, maximizedPaneId, frameSurface, bodyRect.row, bodyRect.col, paneScratchPool)
|
|
1198
|
+
: renderPageContentInto(model.activePageId, model, bodyRect, pagesById, frameSurface, bodyRect.row, bodyRect.col, paneScratchPool);
|
|
950
1199
|
}
|
|
951
1200
|
const overlays = [];
|
|
952
1201
|
if (options.overlayFactory != null) {
|
|
@@ -1040,23 +1289,6 @@ function focusPane(model, paneId) {
|
|
|
1040
1289
|
},
|
|
1041
1290
|
};
|
|
1042
1291
|
}
|
|
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
|
-
}
|
|
1057
|
-
}
|
|
1058
|
-
return undefined;
|
|
1059
|
-
}
|
|
1060
1292
|
function resolveBodyRect(model, options) {
|
|
1061
1293
|
return frameBodyRect(model.columns, model.rows, options.bodyTopRows ?? 1, options.bodyBottomRows ?? 1);
|
|
1062
1294
|
}
|
|
@@ -1157,54 +1389,6 @@ function isHelpScrollAction(action) {
|
|
|
1157
1389
|
|| action.type === 'top'
|
|
1158
1390
|
|| action.type === 'bottom';
|
|
1159
1391
|
}
|
|
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
1392
|
function resolveFrameSettings(model, options, pagesById) {
|
|
1209
1393
|
const activePage = pagesById.get(model.activePageId);
|
|
1210
1394
|
return options.settings?.({
|
|
@@ -1404,26 +1588,6 @@ function cycleNotificationCenterFilter(model, layout) {
|
|
|
1404
1588
|
notificationCenterScrollY: 0,
|
|
1405
1589
|
}, []];
|
|
1406
1590
|
}
|
|
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
1591
|
function renderSettingsDrawer(model, options, pagesById, titleOverride) {
|
|
1428
1592
|
const layout = resolveSettingsLayout(model, options, pagesById);
|
|
1429
1593
|
if (layout == null)
|