@rezi-ui/core 0.1.0-alpha.60 → 0.1.0-alpha.63
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/dist/app/createApp/breadcrumbs.d.ts +38 -0
- package/dist/app/createApp/breadcrumbs.d.ts.map +1 -0
- package/dist/app/createApp/breadcrumbs.js +65 -0
- package/dist/app/createApp/breadcrumbs.js.map +1 -0
- package/dist/app/createApp/config.d.ts +25 -0
- package/dist/app/createApp/config.d.ts.map +1 -0
- package/dist/app/createApp/config.js +130 -0
- package/dist/app/createApp/config.js.map +1 -0
- package/dist/app/createApp/eventLoop.d.ts +95 -0
- package/dist/app/createApp/eventLoop.d.ts.map +1 -0
- package/dist/app/createApp/eventLoop.js +384 -0
- package/dist/app/createApp/eventLoop.js.map +1 -0
- package/dist/app/createApp/guards.d.ts +21 -0
- package/dist/app/createApp/guards.d.ts.map +1 -0
- package/dist/app/createApp/guards.js +54 -0
- package/dist/app/createApp/guards.js.map +1 -0
- package/dist/app/createApp/keybindings.d.ts +28 -0
- package/dist/app/createApp/keybindings.d.ts.map +1 -0
- package/dist/app/createApp/keybindings.js +113 -0
- package/dist/app/createApp/keybindings.js.map +1 -0
- package/dist/app/createApp/renderLoop.d.ts +64 -0
- package/dist/app/createApp/renderLoop.d.ts.map +1 -0
- package/dist/app/createApp/renderLoop.js +305 -0
- package/dist/app/createApp/renderLoop.js.map +1 -0
- package/dist/app/createApp.d.ts +3 -39
- package/dist/app/createApp.d.ts.map +1 -1
- package/dist/app/createApp.js +403 -1205
- package/dist/app/createApp.js.map +1 -1
- package/dist/app/widgetRenderer/constraintState.d.ts +98 -0
- package/dist/app/widgetRenderer/constraintState.d.ts.map +1 -0
- package/dist/app/widgetRenderer/constraintState.js +563 -0
- package/dist/app/widgetRenderer/constraintState.js.map +1 -0
- package/dist/app/widgetRenderer/fileNodeCache.d.ts +2 -0
- package/dist/app/widgetRenderer/fileNodeCache.d.ts.map +1 -1
- package/dist/app/widgetRenderer/fileNodeCache.js +31 -0
- package/dist/app/widgetRenderer/fileNodeCache.js.map +1 -1
- package/dist/app/widgetRenderer/filePickerRouting.d.ts +12 -1
- package/dist/app/widgetRenderer/filePickerRouting.d.ts.map +1 -1
- package/dist/app/widgetRenderer/filePickerRouting.js +63 -14
- package/dist/app/widgetRenderer/filePickerRouting.js.map +1 -1
- package/dist/app/widgetRenderer/focusState.d.ts +46 -0
- package/dist/app/widgetRenderer/focusState.d.ts.map +1 -0
- package/dist/app/widgetRenderer/focusState.js +122 -0
- package/dist/app/widgetRenderer/focusState.js.map +1 -0
- package/dist/app/widgetRenderer/keyboardRouting.d.ts.map +1 -1
- package/dist/app/widgetRenderer/keyboardRouting.js.map +1 -1
- package/dist/app/widgetRenderer/mouseRouting.d.ts +5 -0
- package/dist/app/widgetRenderer/mouseRouting.d.ts.map +1 -1
- package/dist/app/widgetRenderer/mouseRouting.js +78 -8
- package/dist/app/widgetRenderer/mouseRouting.js.map +1 -1
- package/dist/app/widgetRenderer/overlayShortcuts.d.ts.map +1 -1
- package/dist/app/widgetRenderer/overlayShortcuts.js +14 -4
- package/dist/app/widgetRenderer/overlayShortcuts.js.map +1 -1
- package/dist/app/widgetRenderer/overlayState.d.ts +198 -0
- package/dist/app/widgetRenderer/overlayState.d.ts.map +1 -0
- package/dist/app/widgetRenderer/overlayState.js +590 -0
- package/dist/app/widgetRenderer/overlayState.js.map +1 -0
- package/dist/app/widgetRenderer/routeEngineEvent.d.ts +189 -0
- package/dist/app/widgetRenderer/routeEngineEvent.d.ts.map +1 -0
- package/dist/app/widgetRenderer/routeEngineEvent.js +527 -0
- package/dist/app/widgetRenderer/routeEngineEvent.js.map +1 -0
- package/dist/app/widgetRenderer.d.ts +2 -1
- package/dist/app/widgetRenderer.d.ts.map +1 -1
- package/dist/app/widgetRenderer.js +334 -1707
- package/dist/app/widgetRenderer.js.map +1 -1
- package/dist/forms/internal/arrayState.d.ts +35 -0
- package/dist/forms/internal/arrayState.d.ts.map +1 -0
- package/dist/forms/internal/arrayState.js +238 -0
- package/dist/forms/internal/arrayState.js.map +1 -0
- package/dist/forms/internal/bindings.d.ts +46 -0
- package/dist/forms/internal/bindings.d.ts.map +1 -0
- package/dist/forms/internal/bindings.js +161 -0
- package/dist/forms/internal/bindings.js.map +1 -0
- package/dist/forms/internal/dev.d.ts +4 -0
- package/dist/forms/internal/dev.d.ts.map +1 -0
- package/dist/forms/internal/dev.js +21 -0
- package/dist/forms/internal/dev.js.map +1 -0
- package/dist/forms/internal/state.d.ts +52 -0
- package/dist/forms/internal/state.d.ts.map +1 -0
- package/dist/forms/internal/state.js +240 -0
- package/dist/forms/internal/state.js.map +1 -0
- package/dist/forms/internal/submit.d.ts +43 -0
- package/dist/forms/internal/submit.d.ts.map +1 -0
- package/dist/forms/internal/submit.js +165 -0
- package/dist/forms/internal/submit.js.map +1 -0
- package/dist/forms/internal/wizard.d.ts +53 -0
- package/dist/forms/internal/wizard.d.ts.map +1 -0
- package/dist/forms/internal/wizard.js +311 -0
- package/dist/forms/internal/wizard.js.map +1 -0
- package/dist/forms/useForm.d.ts.map +1 -1
- package/dist/forms/useForm.js +90 -1117
- package/dist/forms/useForm.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/keybindings/manager.d.ts.map +1 -1
- package/dist/keybindings/manager.js.map +1 -1
- package/dist/keybindings/parser.d.ts.map +1 -1
- package/dist/keybindings/parser.js +10 -5
- package/dist/keybindings/parser.js.map +1 -1
- package/dist/layout/dropdownGeometry.d.ts +8 -0
- package/dist/layout/dropdownGeometry.d.ts.map +1 -1
- package/dist/layout/dropdownGeometry.js +40 -0
- package/dist/layout/dropdownGeometry.js.map +1 -1
- package/dist/layout/engine/layoutEngine.js +1 -1
- package/dist/layout/engine/layoutEngine.js.map +1 -1
- package/dist/layout/kinds/overlays.d.ts.map +1 -1
- package/dist/layout/kinds/stack.d.ts +1 -3
- package/dist/layout/kinds/stack.d.ts.map +1 -1
- package/dist/layout/kinds/stack.js +11 -1523
- package/dist/layout/kinds/stack.js.map +1 -1
- package/dist/layout/kinds/stackParts/axis.d.ts +32 -0
- package/dist/layout/kinds/stackParts/axis.d.ts.map +1 -0
- package/dist/layout/kinds/stackParts/axis.js +61 -0
- package/dist/layout/kinds/stackParts/axis.js.map +1 -0
- package/dist/layout/kinds/stackParts/constraintPlan.d.ts +18 -0
- package/dist/layout/kinds/stackParts/constraintPlan.d.ts.map +1 -0
- package/dist/layout/kinds/stackParts/constraintPlan.js +434 -0
- package/dist/layout/kinds/stackParts/constraintPlan.js.map +1 -0
- package/dist/layout/kinds/stackParts/layout.d.ts +6 -0
- package/dist/layout/kinds/stackParts/layout.d.ts.map +1 -0
- package/dist/layout/kinds/stackParts/layout.js +376 -0
- package/dist/layout/kinds/stackParts/layout.js.map +1 -0
- package/dist/layout/kinds/stackParts/measure.d.ts +6 -0
- package/dist/layout/kinds/stackParts/measure.d.ts.map +1 -0
- package/dist/layout/kinds/stackParts/measure.js +212 -0
- package/dist/layout/kinds/stackParts/measure.js.map +1 -0
- package/dist/layout/kinds/stackParts/shared.d.ts +31 -0
- package/dist/layout/kinds/stackParts/shared.d.ts.map +1 -0
- package/dist/layout/kinds/stackParts/shared.js +94 -0
- package/dist/layout/kinds/stackParts/shared.js.map +1 -0
- package/dist/layout/kinds/stackParts/wrap.d.ts +26 -0
- package/dist/layout/kinds/stackParts/wrap.d.ts.map +1 -0
- package/dist/layout/kinds/stackParts/wrap.js +374 -0
- package/dist/layout/kinds/stackParts/wrap.js.map +1 -0
- package/dist/layout/validate/interactive.d.ts +106 -0
- package/dist/layout/validate/interactive.d.ts.map +1 -0
- package/dist/layout/validate/interactive.js +430 -0
- package/dist/layout/validate/interactive.js.map +1 -0
- package/dist/layout/validate/layoutConstraints.d.ts +51 -0
- package/dist/layout/validate/layoutConstraints.d.ts.map +1 -0
- package/dist/layout/validate/layoutConstraints.js +100 -0
- package/dist/layout/validate/layoutConstraints.js.map +1 -0
- package/dist/layout/validate/primitives.d.ts +31 -0
- package/dist/layout/validate/primitives.d.ts.map +1 -0
- package/dist/layout/validate/primitives.js +299 -0
- package/dist/layout/validate/primitives.js.map +1 -0
- package/dist/layout/validate/shared.d.ts +23 -0
- package/dist/layout/validate/shared.d.ts.map +1 -0
- package/dist/layout/validate/shared.js +32 -0
- package/dist/layout/validate/shared.js.map +1 -0
- package/dist/layout/validate/spacing.d.ts +21 -0
- package/dist/layout/validate/spacing.d.ts.map +1 -0
- package/dist/layout/validate/spacing.js +33 -0
- package/dist/layout/validate/spacing.js.map +1 -0
- package/dist/layout/validateProps.d.ts +5 -159
- package/dist/layout/validateProps.d.ts.map +1 -1
- package/dist/layout/validateProps.js +1 -832
- package/dist/layout/validateProps.js.map +1 -1
- package/dist/pipeline.js +1 -1
- package/dist/pipeline.js.map +1 -1
- package/dist/renderer/renderToDrawlist/renderTree.d.ts +1 -1
- package/dist/renderer/renderToDrawlist/renderTree.d.ts.map +1 -1
- package/dist/renderer/renderToDrawlist/renderTree.js +3 -3
- package/dist/renderer/renderToDrawlist/renderTree.js.map +1 -1
- package/dist/renderer/renderToDrawlist/types.d.ts +2 -0
- package/dist/renderer/renderToDrawlist/types.d.ts.map +1 -1
- package/dist/renderer/renderToDrawlist/widgets/containers.d.ts.map +1 -1
- package/dist/renderer/renderToDrawlist/widgets/containers.js +1 -1
- package/dist/renderer/renderToDrawlist/widgets/containers.js.map +1 -1
- package/dist/renderer/renderToDrawlist/widgets/files.d.ts +2 -1
- package/dist/renderer/renderToDrawlist/widgets/files.d.ts.map +1 -1
- package/dist/renderer/renderToDrawlist/widgets/files.js +30 -3
- package/dist/renderer/renderToDrawlist/widgets/files.js.map +1 -1
- package/dist/renderer/renderToDrawlist/widgets/overlays.d.ts +1 -1
- package/dist/renderer/renderToDrawlist/widgets/overlays.d.ts.map +1 -1
- package/dist/renderer/renderToDrawlist/widgets/overlays.js +26 -6
- package/dist/renderer/renderToDrawlist/widgets/overlays.js.map +1 -1
- package/dist/renderer/renderToDrawlist/widgets/renderFormWidgets.d.ts.map +1 -1
- package/dist/renderer/renderToDrawlist/widgets/renderFormWidgets.js +52 -18
- package/dist/renderer/renderToDrawlist/widgets/renderFormWidgets.js.map +1 -1
- package/dist/renderer/renderToDrawlist.d.ts +0 -8
- package/dist/renderer/renderToDrawlist.d.ts.map +1 -1
- package/dist/renderer/renderToDrawlist.js +1 -9
- package/dist/renderer/renderToDrawlist.js.map +1 -1
- package/dist/runtime/commit/composite.d.ts +11 -0
- package/dist/runtime/commit/composite.d.ts.map +1 -0
- package/dist/runtime/commit/composite.js +238 -0
- package/dist/runtime/commit/composite.js.map +1 -0
- package/dist/runtime/commit/container.d.ts +7 -0
- package/dist/runtime/commit/container.d.ts.map +1 -0
- package/dist/runtime/commit/container.js +350 -0
- package/dist/runtime/commit/container.js.map +1 -0
- package/dist/runtime/commit/equality.d.ts +20 -0
- package/dist/runtime/commit/equality.d.ts.map +1 -0
- package/dist/runtime/commit/equality.js +436 -0
- package/dist/runtime/commit/equality.js.map +1 -0
- package/dist/runtime/commit/errorBoundary.d.ts +7 -0
- package/dist/runtime/commit/errorBoundary.d.ts.map +1 -0
- package/dist/runtime/commit/errorBoundary.js +53 -0
- package/dist/runtime/commit/errorBoundary.js.map +1 -0
- package/dist/runtime/commit/shared.d.ts +138 -0
- package/dist/runtime/commit/shared.d.ts.map +1 -0
- package/dist/runtime/commit/shared.js +11 -0
- package/dist/runtime/commit/shared.js.map +1 -0
- package/dist/runtime/commit/transitions.d.ts +9 -0
- package/dist/runtime/commit/transitions.d.ts.map +1 -0
- package/dist/runtime/commit/transitions.js +93 -0
- package/dist/runtime/commit/transitions.js.map +1 -0
- package/dist/runtime/commit/validation.d.ts +16 -0
- package/dist/runtime/commit/validation.d.ts.map +1 -0
- package/dist/runtime/commit/validation.js +157 -0
- package/dist/runtime/commit/validation.js.map +1 -0
- package/dist/runtime/commit.d.ts +7 -117
- package/dist/runtime/commit.d.ts.map +1 -1
- package/dist/runtime/commit.js +13 -1394
- package/dist/runtime/commit.js.map +1 -1
- package/dist/runtime/localState.d.ts +4 -0
- package/dist/runtime/localState.d.ts.map +1 -1
- package/dist/runtime/localState.js.map +1 -1
- package/dist/runtime/widgetMeta/collector.d.ts +77 -0
- package/dist/runtime/widgetMeta/collector.d.ts.map +1 -0
- package/dist/runtime/widgetMeta/collector.js +293 -0
- package/dist/runtime/widgetMeta/collector.js.map +1 -0
- package/dist/runtime/widgetMeta/focusContainers.d.ts +44 -0
- package/dist/runtime/widgetMeta/focusContainers.d.ts.map +1 -0
- package/dist/runtime/widgetMeta/focusContainers.js +190 -0
- package/dist/runtime/widgetMeta/focusContainers.js.map +1 -0
- package/dist/runtime/widgetMeta/focusInfo.d.ts +19 -0
- package/dist/runtime/widgetMeta/focusInfo.d.ts.map +1 -0
- package/dist/runtime/widgetMeta/focusInfo.js +172 -0
- package/dist/runtime/widgetMeta/focusInfo.js.map +1 -0
- package/dist/runtime/widgetMeta/helpers.d.ts +47 -0
- package/dist/runtime/widgetMeta/helpers.d.ts.map +1 -0
- package/dist/runtime/widgetMeta/helpers.js +182 -0
- package/dist/runtime/widgetMeta/helpers.js.map +1 -0
- package/dist/runtime/widgetMeta.d.ts +12 -175
- package/dist/runtime/widgetMeta.d.ts.map +1 -1
- package/dist/runtime/widgetMeta.js +6 -847
- package/dist/runtime/widgetMeta.js.map +1 -1
- package/dist/ui/capabilities.d.ts.map +1 -1
- package/dist/ui/designTokens.d.ts.map +1 -1
- package/dist/widgets/accordion.d.ts.map +1 -1
- package/dist/widgets/accordion.js +8 -13
- package/dist/widgets/accordion.js.map +1 -1
- package/dist/widgets/factories/advanced.d.ts +20 -0
- package/dist/widgets/factories/advanced.d.ts.map +1 -0
- package/dist/widgets/factories/advanced.js +75 -0
- package/dist/widgets/factories/advanced.js.map +1 -0
- package/dist/widgets/factories/basic.d.ts +14 -0
- package/dist/widgets/factories/basic.d.ts.map +1 -0
- package/dist/widgets/factories/basic.js +44 -0
- package/dist/widgets/factories/basic.js.map +1 -0
- package/dist/widgets/factories/feedback.d.ts +20 -0
- package/dist/widgets/factories/feedback.d.ts.map +1 -0
- package/dist/widgets/factories/feedback.js +102 -0
- package/dist/widgets/factories/feedback.js.map +1 -0
- package/dist/widgets/factories/helpers.d.ts +41 -0
- package/dist/widgets/factories/helpers.d.ts.map +1 -0
- package/dist/widgets/factories/helpers.js +72 -0
- package/dist/widgets/factories/helpers.js.map +1 -0
- package/dist/widgets/factories/interactive.d.ts +15 -0
- package/dist/widgets/factories/interactive.d.ts.map +1 -0
- package/dist/widgets/factories/interactive.js +46 -0
- package/dist/widgets/factories/interactive.js.map +1 -0
- package/dist/widgets/factories/layoutShell.d.ts +22 -0
- package/dist/widgets/factories/layoutShell.d.ts.map +1 -0
- package/dist/widgets/factories/layoutShell.js +190 -0
- package/dist/widgets/factories/layoutShell.js.map +1 -0
- package/dist/widgets/factories/media.d.ts +14 -0
- package/dist/widgets/factories/media.d.ts.map +1 -0
- package/dist/widgets/factories/media.js +25 -0
- package/dist/widgets/factories/media.js.map +1 -0
- package/dist/widgets/factories/navigation.d.ts +10 -0
- package/dist/widgets/factories/navigation.d.ts.map +1 -0
- package/dist/widgets/factories/navigation.js +24 -0
- package/dist/widgets/factories/navigation.js.map +1 -0
- package/dist/widgets/field.d.ts +6 -1
- package/dist/widgets/field.d.ts.map +1 -1
- package/dist/widgets/field.js +8 -2
- package/dist/widgets/field.js.map +1 -1
- package/dist/widgets/filePicker.d.ts +5 -0
- package/dist/widgets/filePicker.d.ts.map +1 -0
- package/dist/widgets/filePicker.js +136 -0
- package/dist/widgets/filePicker.js.map +1 -0
- package/dist/widgets/protocol.d.ts +0 -6
- package/dist/widgets/protocol.d.ts.map +1 -1
- package/dist/widgets/protocol.js +0 -6
- package/dist/widgets/protocol.js.map +1 -1
- package/dist/widgets/select.js +1 -1
- package/dist/widgets/select.js.map +1 -1
- package/dist/widgets/splitPane.d.ts.map +1 -1
- package/dist/widgets/splitPane.js.map +1 -1
- package/dist/widgets/table.d.ts.map +1 -1
- package/dist/widgets/table.js +43 -1
- package/dist/widgets/table.js.map +1 -1
- package/dist/widgets/tree.d.ts.map +1 -1
- package/dist/widgets/types/advanced.d.ts +611 -0
- package/dist/widgets/types/advanced.d.ts.map +1 -0
- package/dist/widgets/types/advanced.js +2 -0
- package/dist/widgets/types/advanced.js.map +1 -0
- package/dist/widgets/types/base.d.ts +933 -0
- package/dist/widgets/types/base.d.ts.map +1 -0
- package/dist/widgets/types/base.js +2 -0
- package/dist/widgets/types/base.js.map +1 -0
- package/dist/widgets/types/forms.d.ts +136 -0
- package/dist/widgets/types/forms.d.ts.map +1 -0
- package/dist/widgets/types/forms.js +2 -0
- package/dist/widgets/types/forms.js.map +1 -0
- package/dist/widgets/types/navigation.d.ts +83 -0
- package/dist/widgets/types/navigation.d.ts.map +1 -0
- package/dist/widgets/types/navigation.js +2 -0
- package/dist/widgets/types/navigation.js.map +1 -0
- package/dist/widgets/types/overlaysShell.d.ts +223 -0
- package/dist/widgets/types/overlaysShell.d.ts.map +1 -0
- package/dist/widgets/types/overlaysShell.js +2 -0
- package/dist/widgets/types/overlaysShell.js.map +1 -0
- package/dist/widgets/types/table.d.ts +104 -0
- package/dist/widgets/types/table.d.ts.map +1 -0
- package/dist/widgets/types/table.js +2 -0
- package/dist/widgets/types/table.js.map +1 -0
- package/dist/widgets/types/tree.d.ts +64 -0
- package/dist/widgets/types/tree.d.ts.map +1 -0
- package/dist/widgets/types/tree.js +2 -0
- package/dist/widgets/types/tree.js.map +1 -0
- package/dist/widgets/types.d.ts +14 -2123
- package/dist/widgets/types.d.ts.map +1 -1
- package/dist/widgets/ui.d.ts +37 -843
- package/dist/widgets/ui.d.ts.map +1 -1
- package/dist/widgets/ui.js +37 -1262
- package/dist/widgets/ui.js.map +1 -1
- package/package.json +2 -2
- package/dist/constraints/aggregation.d.ts +0 -17
- package/dist/constraints/aggregation.d.ts.map +0 -1
- package/dist/constraints/aggregation.js +0 -59
- package/dist/constraints/aggregation.js.map +0 -1
- package/dist/renderer/renderToDrawlist/overflowCulling.d.ts +0 -3
- package/dist/renderer/renderToDrawlist/overflowCulling.d.ts.map +0 -1
- package/dist/renderer/renderToDrawlist/overflowCulling.js +0 -81
- package/dist/renderer/renderToDrawlist/overflowCulling.js.map +0 -1
- package/dist/widgets/tests/protocol.test.d.ts +0 -2
- package/dist/widgets/tests/protocol.test.d.ts.map +0 -1
- package/dist/widgets/tests/protocol.test.js +0 -120
- package/dist/widgets/tests/protocol.test.js.map +0 -1
package/dist/app/createApp.js
CHANGED
|
@@ -22,237 +22,35 @@
|
|
|
22
22
|
* @see docs/guide/lifecycle-and-updates.md
|
|
23
23
|
*/
|
|
24
24
|
import { ZrUiError } from "../abi.js";
|
|
25
|
-
import {
|
|
25
|
+
import { BACKEND_FPS_CAP_MARKER, BACKEND_MAX_EVENT_BYTES_MARKER, } from "../backend.js";
|
|
26
26
|
import { describeThrown } from "../debug/describeThrown.js";
|
|
27
|
-
import { createManagerState, getBindings, getMode, getPendingChord,
|
|
28
|
-
import {
|
|
29
|
-
import { normalizeBreakpointThresholds, } from "../layout/responsive.js";
|
|
30
|
-
import { PERF_ENABLED, perfMarkEnd, perfMarkStart, perfNow, perfRecord } from "../perf/perf.js";
|
|
31
|
-
import { parseEventBatchV1 } from "../protocol/zrev_v1.js";
|
|
27
|
+
import { createManagerState, getBindings, getMode, getPendingChord, setMode, } from "../keybindings/index.js";
|
|
28
|
+
import { PERF_ENABLED, perfMarkStart } from "../perf/perf.js";
|
|
32
29
|
import { createRouterIntegration } from "../router/integration.js";
|
|
33
|
-
import { DEFAULT_TERMINAL_PROFILE
|
|
30
|
+
import { DEFAULT_TERMINAL_PROFILE } from "../terminalProfile.js";
|
|
34
31
|
import { defaultTheme } from "../theme/defaultTheme.js";
|
|
35
|
-
import {
|
|
36
|
-
import {
|
|
37
|
-
import {
|
|
32
|
+
import { compileTheme } from "../theme/theme.js";
|
|
33
|
+
import { createRuntimeBreadcrumbHelpers, } from "./createApp/breadcrumbs.js";
|
|
34
|
+
import { loadTerminalProfile, readBackendDrawlistVersionMarker, readBackendPositiveIntMarker, requirePositiveInt, resolveAppConfig as resolveAppConfigImpl, } from "./createApp/config.js";
|
|
35
|
+
import { DIRTY_LAYOUT, DIRTY_RENDER, DIRTY_VIEW, createDirtyTracker, } from "./createApp/dirtyPlan.js";
|
|
36
|
+
import { createEventLoop } from "./createApp/eventLoop.js";
|
|
38
37
|
import { createFocusDispatcher } from "./createApp/focusDispatcher.js";
|
|
38
|
+
import { createAppGuards } from "./createApp/guards.js";
|
|
39
|
+
import { computeKeybindingsEnabled, createAppKeybindingHelpers } from "./createApp/keybindings.js";
|
|
40
|
+
import { createRenderLoop } from "./createApp/renderLoop.js";
|
|
39
41
|
import { createRunSignalController, readProcessLike } from "./createApp/runSignals.js";
|
|
40
|
-
import { buildTopLevelViewErrorScreen, captureTopLevelViewError, isTopLevelQuitEvent, isTopLevelRetryEvent, isUnhandledCtrlCKeyEvent, isUnmodifiedTextQuitEvent, } from "./createApp/topLevelViewError.js";
|
|
41
42
|
import { RawRenderer } from "./rawRenderer.js";
|
|
42
|
-
import { isRuntimeBreadcrumbEventKind, mergeRuntimeBreadcrumbSnapshot, toRuntimeBreadcrumbAction, } from "./runtimeBreadcrumbs.js";
|
|
43
43
|
import { AppStateMachine } from "./stateMachine.js";
|
|
44
44
|
import { TurnScheduler } from "./turnScheduler.js";
|
|
45
45
|
import { UpdateQueue } from "./updateQueue.js";
|
|
46
|
-
import { WidgetRenderer
|
|
47
|
-
const ROUTE_KEYBINDING_SOURCE = "__rezi:router";
|
|
48
|
-
/** Default configuration values. */
|
|
49
|
-
const DEFAULT_CONFIG = Object.freeze({
|
|
50
|
-
fpsCap: 60,
|
|
51
|
-
maxEventBytes: 1 << 20 /* 1 MiB */,
|
|
52
|
-
maxDrawlistBytes: 2 << 20 /* 2 MiB */,
|
|
53
|
-
rootPadding: 0,
|
|
54
|
-
breakpointThresholds: normalizeBreakpointThresholds(undefined),
|
|
55
|
-
drawlistValidateParams: true,
|
|
56
|
-
drawlistReuseOutputBuffer: true,
|
|
57
|
-
drawlistEncodedStringCacheCap: 131072,
|
|
58
|
-
maxFramesInFlight: 1,
|
|
59
|
-
themeTransitionFrames: 0,
|
|
60
|
-
internal_onRender: undefined,
|
|
61
|
-
internal_onLayout: undefined,
|
|
62
|
-
});
|
|
63
|
-
const MAX_SAFE_FPS_CAP = 1000;
|
|
64
|
-
const MAX_SAFE_EVENT_BYTES = 4 << 20; /* 4 MiB */
|
|
65
|
-
const SYNC_FRAME_ACK_MARKER = "__reziSyncFrameAck";
|
|
46
|
+
import { WidgetRenderer } from "./widgetRenderer.js";
|
|
66
47
|
export const APP_INTERNAL_REQUEST_VIEW_LAYOUT_MARKER = "__reziRequestViewLayout";
|
|
67
48
|
export const APP_INTERNAL_SET_RUNTIME_BREADCRUMB_HOOKS_MARKER = "__reziSetRuntimeBreadcrumbHooks";
|
|
68
49
|
function invalidProps(detail) {
|
|
69
50
|
throw new ZrUiError("ZRUI_INVALID_PROPS", detail);
|
|
70
51
|
}
|
|
71
|
-
function requirePositiveInt(name, v) {
|
|
72
|
-
if (!Number.isInteger(v) || v <= 0)
|
|
73
|
-
invalidProps(`${name} must be a positive integer`);
|
|
74
|
-
return v;
|
|
75
|
-
}
|
|
76
|
-
function requirePositiveIntAtMost(name, v, max) {
|
|
77
|
-
const parsed = requirePositiveInt(name, v);
|
|
78
|
-
if (parsed > max)
|
|
79
|
-
invalidProps(`${name} must be <= ${String(max)}`);
|
|
80
|
-
return parsed;
|
|
81
|
-
}
|
|
82
|
-
function requireNonNegativeInt(name, v) {
|
|
83
|
-
if (!Number.isInteger(v) || v < 0)
|
|
84
|
-
invalidProps(`${name} must be a non-negative integer`);
|
|
85
|
-
return v;
|
|
86
|
-
}
|
|
87
|
-
function isSyncFrameAck(p) {
|
|
88
|
-
return (typeof p === "object" &&
|
|
89
|
-
p !== null &&
|
|
90
|
-
p[SYNC_FRAME_ACK_MARKER] === true);
|
|
91
|
-
}
|
|
92
|
-
function getAcceptedFrameAck(p) {
|
|
93
|
-
if (typeof p !== "object" || p === null)
|
|
94
|
-
return null;
|
|
95
|
-
const marker = p[FRAME_ACCEPTED_ACK_MARKER];
|
|
96
|
-
if (typeof marker !== "object" || marker === null)
|
|
97
|
-
return null;
|
|
98
|
-
if (typeof marker.then !== "function")
|
|
99
|
-
return null;
|
|
100
|
-
return marker;
|
|
101
|
-
}
|
|
102
|
-
function readBackendPositiveIntMarker(backend, marker) {
|
|
103
|
-
const value = backend[marker];
|
|
104
|
-
if (value === undefined)
|
|
105
|
-
return null;
|
|
106
|
-
if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
|
|
107
|
-
invalidProps(`backend marker ${marker} must be a positive integer when present`);
|
|
108
|
-
}
|
|
109
|
-
return value;
|
|
110
|
-
}
|
|
111
|
-
function readBackendDrawlistVersionMarker(backend) {
|
|
112
|
-
const value = backend[BACKEND_DRAWLIST_VERSION_MARKER];
|
|
113
|
-
if (value === undefined)
|
|
114
|
-
return null;
|
|
115
|
-
if (value !== 1) {
|
|
116
|
-
invalidProps(`backend marker ${BACKEND_DRAWLIST_VERSION_MARKER} must be 1 (received ${String(value)})`);
|
|
117
|
-
}
|
|
118
|
-
return 1;
|
|
119
|
-
}
|
|
120
|
-
function monotonicNowMs() {
|
|
121
|
-
const perf = globalThis.performance;
|
|
122
|
-
const perfNow = perf?.now;
|
|
123
|
-
if (typeof perfNow === "function")
|
|
124
|
-
return perfNow.call(perf);
|
|
125
|
-
return Date.now();
|
|
126
|
-
}
|
|
127
|
-
async function loadTerminalProfile(backend) {
|
|
128
|
-
try {
|
|
129
|
-
if (typeof backend.getTerminalProfile === "function") {
|
|
130
|
-
return await backend.getTerminalProfile();
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
catch {
|
|
134
|
-
// fall through to caps-derived profile
|
|
135
|
-
}
|
|
136
|
-
try {
|
|
137
|
-
const caps = await backend.getCaps();
|
|
138
|
-
return terminalProfileFromCaps(caps);
|
|
139
|
-
}
|
|
140
|
-
catch {
|
|
141
|
-
return DEFAULT_TERMINAL_PROFILE;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
function buildLayoutDebugOverlay(rectById) {
|
|
145
|
-
if (rectById.size === 0)
|
|
146
|
-
return null;
|
|
147
|
-
const rows = [...rectById.entries()]
|
|
148
|
-
.sort((a, b) => a[0].localeCompare(b[0]))
|
|
149
|
-
.slice(0, 18)
|
|
150
|
-
.map(([id, rect]) => ui.text(`${id} ${String(rect.x)},${String(rect.y)} ${String(rect.w)}x${String(rect.h)}`));
|
|
151
|
-
const panel = ui.box({ border: "single", title: `Layout (${String(rectById.size)})`, p: 1 }, [
|
|
152
|
-
ui.column({ gap: 0 }, rows),
|
|
153
|
-
]);
|
|
154
|
-
return ui.layer({
|
|
155
|
-
id: "rezi.layout.debug.overlay",
|
|
156
|
-
zIndex: 2_000_000_000,
|
|
157
|
-
modal: false,
|
|
158
|
-
backdrop: "none",
|
|
159
|
-
closeOnEscape: false,
|
|
160
|
-
content: ui.column({ width: "full", height: "full", justify: "end", p: 1 }, [
|
|
161
|
-
ui.row({ width: "full", justify: "start" }, [panel]),
|
|
162
|
-
]),
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
/** Apply defaults to user-provided config, validating all values. */
|
|
166
52
|
export function resolveAppConfig(config) {
|
|
167
|
-
|
|
168
|
-
return DEFAULT_CONFIG;
|
|
169
|
-
const fpsCap = config.fpsCap === undefined
|
|
170
|
-
? DEFAULT_CONFIG.fpsCap
|
|
171
|
-
: requirePositiveIntAtMost("fpsCap", config.fpsCap, MAX_SAFE_FPS_CAP);
|
|
172
|
-
const maxEventBytes = config.maxEventBytes === undefined
|
|
173
|
-
? DEFAULT_CONFIG.maxEventBytes
|
|
174
|
-
: requirePositiveIntAtMost("maxEventBytes", config.maxEventBytes, MAX_SAFE_EVENT_BYTES);
|
|
175
|
-
const maxDrawlistBytes = config.maxDrawlistBytes === undefined
|
|
176
|
-
? DEFAULT_CONFIG.maxDrawlistBytes
|
|
177
|
-
: requirePositiveInt("maxDrawlistBytes", config.maxDrawlistBytes);
|
|
178
|
-
const rootPadding = config.rootPadding === undefined
|
|
179
|
-
? DEFAULT_CONFIG.rootPadding
|
|
180
|
-
: requireNonNegativeInt("rootPadding", config.rootPadding);
|
|
181
|
-
const breakpointThresholds = normalizeBreakpointThresholds(config.breakpoints);
|
|
182
|
-
const drawlistValidateParams = config.drawlistValidateParams === undefined
|
|
183
|
-
? DEFAULT_CONFIG.drawlistValidateParams
|
|
184
|
-
: config.drawlistValidateParams !== false;
|
|
185
|
-
const drawlistReuseOutputBuffer = config.drawlistReuseOutputBuffer === undefined
|
|
186
|
-
? DEFAULT_CONFIG.drawlistReuseOutputBuffer
|
|
187
|
-
: config.drawlistReuseOutputBuffer === true;
|
|
188
|
-
const drawlistEncodedStringCacheCap = config.drawlistEncodedStringCacheCap === undefined
|
|
189
|
-
? DEFAULT_CONFIG.drawlistEncodedStringCacheCap
|
|
190
|
-
: requireNonNegativeInt("drawlistEncodedStringCacheCap", config.drawlistEncodedStringCacheCap);
|
|
191
|
-
const maxFramesInFlight = config.maxFramesInFlight === undefined
|
|
192
|
-
? DEFAULT_CONFIG.maxFramesInFlight
|
|
193
|
-
: Math.min(4, Math.max(1, requirePositiveInt("maxFramesInFlight", config.maxFramesInFlight)));
|
|
194
|
-
const themeTransitionFrames = config.themeTransitionFrames === undefined
|
|
195
|
-
? DEFAULT_CONFIG.themeTransitionFrames
|
|
196
|
-
: requireNonNegativeInt("themeTransitionFrames", config.themeTransitionFrames);
|
|
197
|
-
const internal_onRender = typeof config.internal_onRender === "function" ? config.internal_onRender : undefined;
|
|
198
|
-
const internal_onLayout = typeof config.internal_onLayout === "function" ? config.internal_onLayout : undefined;
|
|
199
|
-
return Object.freeze({
|
|
200
|
-
fpsCap,
|
|
201
|
-
maxEventBytes,
|
|
202
|
-
maxDrawlistBytes,
|
|
203
|
-
rootPadding,
|
|
204
|
-
breakpointThresholds,
|
|
205
|
-
drawlistValidateParams,
|
|
206
|
-
drawlistReuseOutputBuffer,
|
|
207
|
-
drawlistEncodedStringCacheCap,
|
|
208
|
-
maxFramesInFlight,
|
|
209
|
-
themeTransitionFrames,
|
|
210
|
-
internal_onRender,
|
|
211
|
-
internal_onLayout,
|
|
212
|
-
});
|
|
213
|
-
}
|
|
214
|
-
/**
|
|
215
|
-
* Convert a text codepoint to a key code for keybinding matching.
|
|
216
|
-
* Letters are normalized to uppercase (A-Z = 65-90).
|
|
217
|
-
* Returns null if codepoint is not matchable.
|
|
218
|
-
*/
|
|
219
|
-
function codepointToKeyCode(codepoint) {
|
|
220
|
-
// Lowercase letters -> uppercase
|
|
221
|
-
if (codepoint >= 97 && codepoint <= 122) {
|
|
222
|
-
return codepoint - 32; // 'a' (97) -> 'A' (65)
|
|
223
|
-
}
|
|
224
|
-
// Uppercase letters
|
|
225
|
-
if (codepoint >= 65 && codepoint <= 90) {
|
|
226
|
-
return codepoint;
|
|
227
|
-
}
|
|
228
|
-
// Digits and printable ASCII
|
|
229
|
-
if (codepoint >= 32 && codepoint <= 126) {
|
|
230
|
-
return codepoint;
|
|
231
|
-
}
|
|
232
|
-
return null;
|
|
233
|
-
}
|
|
234
|
-
/**
|
|
235
|
-
* Convert text control characters into Ctrl+key key codes.
|
|
236
|
-
*
|
|
237
|
-
* Terminals without kitty/CSI-u often emit Ctrl+letter as text bytes:
|
|
238
|
-
* 0x01-0x1A for Ctrl+A..Ctrl+Z, and 0x1C-0x1F for Ctrl+\..Ctrl+_.
|
|
239
|
-
* We intentionally exclude 0x09 (Tab), 0x0D (Enter), and 0x1B (Escape)
|
|
240
|
-
* because they have dedicated key semantics in the engine.
|
|
241
|
-
*/
|
|
242
|
-
function codepointToCtrlKeyCode(codepoint) {
|
|
243
|
-
if (codepoint === 9 || codepoint === 13) {
|
|
244
|
-
return null;
|
|
245
|
-
}
|
|
246
|
-
if (codepoint >= 1 && codepoint <= 26) {
|
|
247
|
-
return codepoint + 64;
|
|
248
|
-
}
|
|
249
|
-
if (codepoint >= 28 && codepoint <= 31) {
|
|
250
|
-
return codepoint + 64;
|
|
251
|
-
}
|
|
252
|
-
return null;
|
|
253
|
-
}
|
|
254
|
-
function blendThemeColors(from, to, t) {
|
|
255
|
-
return blendTheme(from, to, t);
|
|
53
|
+
return resolveAppConfigImpl(config);
|
|
256
54
|
}
|
|
257
55
|
export function createApp(opts) {
|
|
258
56
|
const backend = opts.backend;
|
|
@@ -314,14 +112,26 @@ export function createApp(opts) {
|
|
|
314
112
|
let settleActiveRun = null;
|
|
315
113
|
let renderRequestQueuedForCurrentTurn = false;
|
|
316
114
|
let userCommitScheduled = false;
|
|
317
|
-
// Perf tracking: submit time for backend_ack calculation
|
|
318
|
-
let submitFrameStartMs = null;
|
|
319
|
-
// Perf tracking: schedule_wait measures time from render request to render start
|
|
320
115
|
let scheduleWaitStartMs = null;
|
|
321
|
-
const
|
|
116
|
+
const baseInternalOnRender = config.internal_onRender;
|
|
117
|
+
const baseInternalOnLayout = config.internal_onLayout;
|
|
118
|
+
let inspectorInternalOnRender;
|
|
119
|
+
let inspectorInternalOnLayout;
|
|
120
|
+
let runtimeBreadcrumbsEnabled = baseInternalOnRender !== undefined || baseInternalOnLayout !== undefined;
|
|
121
|
+
let keybindingState = createManagerState();
|
|
122
|
+
let keybindingsEnabled = false;
|
|
123
|
+
let breadcrumbLastEventKind = null;
|
|
124
|
+
let breadcrumbLastConsumptionPath = null;
|
|
125
|
+
let breadcrumbLastAction = null;
|
|
126
|
+
let breadcrumbEventTracked = false;
|
|
127
|
+
let deferredInlineFatal = null;
|
|
128
|
+
let processTurnImpl = () => undefined;
|
|
129
|
+
let tryRenderOnceImpl = () => undefined;
|
|
130
|
+
const scheduler = new TurnScheduler((items) => processTurnImpl(items));
|
|
131
|
+
const enqueueWorkItem = (item) => {
|
|
132
|
+
scheduler.enqueue(item);
|
|
133
|
+
};
|
|
322
134
|
function markDirty(flags, schedule = true) {
|
|
323
|
-
// Track when dirty flags are first set for schedule_wait measurement.
|
|
324
|
-
// This captures time from "render needed" to "render started".
|
|
325
135
|
const { wasDirty, flags: nextFlags } = dirtyTracker.markDirty(flags);
|
|
326
136
|
if (PERF_ENABLED && !wasDirty && nextFlags !== 0 && scheduleWaitStartMs === null) {
|
|
327
137
|
scheduleWaitStartMs = perfMarkStart("schedule_wait");
|
|
@@ -333,63 +143,128 @@ export function createApp(opts) {
|
|
|
333
143
|
if (scheduler.isExecuting) {
|
|
334
144
|
if (!renderRequestQueuedForCurrentTurn) {
|
|
335
145
|
renderRequestQueuedForCurrentTurn = true;
|
|
336
|
-
|
|
146
|
+
enqueueWorkItem({ kind: "renderRequest" });
|
|
337
147
|
}
|
|
338
148
|
return;
|
|
339
149
|
}
|
|
340
150
|
if (scheduler.isScheduled)
|
|
341
151
|
return;
|
|
342
|
-
|
|
152
|
+
enqueueWorkItem({ kind: "renderRequest" });
|
|
343
153
|
}
|
|
344
|
-
function
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
154
|
+
function requestRenderFromRenderer() {
|
|
155
|
+
markDirty(DIRTY_RENDER);
|
|
156
|
+
}
|
|
157
|
+
function requestViewFromRenderer() {
|
|
158
|
+
markDirty(DIRTY_VIEW);
|
|
159
|
+
}
|
|
160
|
+
const guards = createAppGuards({
|
|
161
|
+
getEventHandlerDepth: () => inEventHandlerDepth,
|
|
162
|
+
getLifecycleBusy: () => lifecycleBusy,
|
|
163
|
+
getRuntimeState: () => sm.state,
|
|
164
|
+
isInCommit: () => inCommit,
|
|
165
|
+
isInRender: () => inRender,
|
|
166
|
+
});
|
|
167
|
+
function enqueueFatal(code, detail) {
|
|
168
|
+
enqueueWorkItem({ kind: "fatal", code, detail });
|
|
169
|
+
}
|
|
170
|
+
function doFatal(code, detail) {
|
|
171
|
+
if (sm.state !== "Running")
|
|
348
172
|
return;
|
|
173
|
+
lifecycleBusy = null;
|
|
174
|
+
lifecycleGeneration++;
|
|
175
|
+
backendStarted = false;
|
|
176
|
+
const fatalEv = { kind: "fatal", code, detail };
|
|
177
|
+
const snapshot = [];
|
|
178
|
+
for (const slot of handlers) {
|
|
179
|
+
if (slot.active.value)
|
|
180
|
+
snapshot.push(slot.fn);
|
|
181
|
+
}
|
|
182
|
+
for (const fn of snapshot) {
|
|
183
|
+
try {
|
|
184
|
+
fn(fatalEv);
|
|
185
|
+
}
|
|
186
|
+
catch {
|
|
187
|
+
// ignore
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
try {
|
|
191
|
+
sm.toFaulted();
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
// ignore
|
|
195
|
+
}
|
|
196
|
+
pollToken++;
|
|
197
|
+
try {
|
|
198
|
+
void backend
|
|
199
|
+
.stop()
|
|
200
|
+
.catch(() => undefined)
|
|
201
|
+
.finally(() => {
|
|
202
|
+
try {
|
|
203
|
+
backend.dispose();
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
// ignore
|
|
207
|
+
}
|
|
208
|
+
settleActiveRun?.();
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
try {
|
|
213
|
+
backend.dispose();
|
|
214
|
+
}
|
|
215
|
+
catch {
|
|
216
|
+
// ignore
|
|
217
|
+
}
|
|
218
|
+
settleActiveRun?.();
|
|
349
219
|
}
|
|
350
|
-
themeTransition = Object.freeze({
|
|
351
|
-
from: theme,
|
|
352
|
-
to: nextTheme,
|
|
353
|
-
frame: 0,
|
|
354
|
-
totalFrames: config.themeTransitionFrames,
|
|
355
|
-
});
|
|
356
220
|
}
|
|
357
|
-
function
|
|
358
|
-
|
|
359
|
-
if (!active)
|
|
221
|
+
function flushDeferredInlineFatal() {
|
|
222
|
+
if (deferredInlineFatal === null || inEventHandlerDepth !== 0)
|
|
360
223
|
return;
|
|
361
|
-
const
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
224
|
+
const fatal = deferredInlineFatal;
|
|
225
|
+
deferredInlineFatal = null;
|
|
226
|
+
doFatal(fatal.code, fatal.detail);
|
|
227
|
+
}
|
|
228
|
+
function fatalNowOrEnqueue(code, detail) {
|
|
229
|
+
const canFailFastInline = scheduler.isExecuting && !inRender && !inCommit;
|
|
230
|
+
if (canFailFastInline && inEventHandlerDepth > 0) {
|
|
231
|
+
if (deferredInlineFatal === null) {
|
|
232
|
+
deferredInlineFatal = Object.freeze({ code, detail });
|
|
233
|
+
}
|
|
365
234
|
return;
|
|
366
235
|
}
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
...active,
|
|
370
|
-
frame: nextFrame,
|
|
371
|
-
});
|
|
372
|
-
}
|
|
373
|
-
function scheduleThemeTransitionContinuation() {
|
|
374
|
-
if (!themeTransition || sm.state !== "Running")
|
|
236
|
+
if (canFailFastInline) {
|
|
237
|
+
doFatal(code, detail);
|
|
375
238
|
return;
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
markDirty(DIRTY_VIEW, false);
|
|
379
|
-
renderRequestQueuedForCurrentTurn = true;
|
|
380
|
-
scheduler.enqueue({ kind: "renderRequest" });
|
|
381
|
-
}
|
|
382
|
-
function requestRenderFromRenderer() {
|
|
383
|
-
markDirty(DIRTY_RENDER);
|
|
239
|
+
}
|
|
240
|
+
enqueueFatal(code, detail);
|
|
384
241
|
}
|
|
385
|
-
function
|
|
386
|
-
|
|
242
|
+
function cleanupStartedBackendAfterAbort() {
|
|
243
|
+
if (!backendStarted)
|
|
244
|
+
return;
|
|
245
|
+
backendStarted = false;
|
|
246
|
+
try {
|
|
247
|
+
void backend
|
|
248
|
+
.stop()
|
|
249
|
+
.catch(() => undefined)
|
|
250
|
+
.finally(() => {
|
|
251
|
+
try {
|
|
252
|
+
backend.dispose();
|
|
253
|
+
}
|
|
254
|
+
catch {
|
|
255
|
+
// ignore
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
catch {
|
|
260
|
+
try {
|
|
261
|
+
backend.dispose();
|
|
262
|
+
}
|
|
263
|
+
catch {
|
|
264
|
+
// ignore
|
|
265
|
+
}
|
|
266
|
+
}
|
|
387
267
|
}
|
|
388
|
-
const baseInternalOnRender = config.internal_onRender;
|
|
389
|
-
const baseInternalOnLayout = config.internal_onLayout;
|
|
390
|
-
let inspectorInternalOnRender;
|
|
391
|
-
let inspectorInternalOnLayout;
|
|
392
|
-
let runtimeBreadcrumbsEnabled = baseInternalOnRender !== undefined || baseInternalOnLayout !== undefined;
|
|
393
268
|
const rawRenderer = new RawRenderer({
|
|
394
269
|
backend,
|
|
395
270
|
maxDrawlistBytes: config.maxDrawlistBytes,
|
|
@@ -434,129 +309,52 @@ export function createApp(opts) {
|
|
|
434
309
|
return routerIntegration.renderCurrentScreen(state, routeStateUpdater);
|
|
435
310
|
};
|
|
436
311
|
}
|
|
437
|
-
|
|
438
|
-
let keybindingState = createManagerState();
|
|
439
|
-
let keybindingsEnabled = false;
|
|
440
|
-
let breadcrumbLastEventKind = null;
|
|
441
|
-
let breadcrumbLastConsumptionPath = null;
|
|
442
|
-
let breadcrumbLastAction = null;
|
|
443
|
-
let breadcrumbEventTracked = false;
|
|
444
|
-
let deferredInlineFatal = null;
|
|
445
|
-
function recomputeRuntimeBreadcrumbCollection() {
|
|
446
|
-
const next = baseInternalOnRender !== undefined ||
|
|
447
|
-
baseInternalOnLayout !== undefined ||
|
|
448
|
-
inspectorInternalOnRender !== undefined ||
|
|
449
|
-
inspectorInternalOnLayout !== undefined;
|
|
450
|
-
if (next === runtimeBreadcrumbsEnabled)
|
|
451
|
-
return;
|
|
452
|
-
runtimeBreadcrumbsEnabled = next;
|
|
453
|
-
widgetRenderer.setRuntimeBreadcrumbCaptureEnabled(next);
|
|
454
|
-
if (!next) {
|
|
455
|
-
breadcrumbLastEventKind = null;
|
|
456
|
-
breadcrumbLastConsumptionPath = null;
|
|
457
|
-
breadcrumbLastAction = null;
|
|
458
|
-
breadcrumbEventTracked = false;
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
function computeKeybindingsEnabled(state) {
|
|
462
|
-
for (const m of state.modes.values()) {
|
|
463
|
-
if (m.bindings.length > 0)
|
|
464
|
-
return true;
|
|
465
|
-
}
|
|
466
|
-
return false;
|
|
467
|
-
}
|
|
468
|
-
function formatInvalidKeybindingDetail(invalidKeys) {
|
|
469
|
-
return invalidKeys.map((invalid) => `"${invalid.key}": ${invalid.detail}`).join("; ");
|
|
470
|
-
}
|
|
471
|
-
function applyKeybindingState(nextState) {
|
|
312
|
+
const applyKeybindingState = (nextState) => {
|
|
472
313
|
keybindingState = nextState;
|
|
473
314
|
keybindingsEnabled = computeKeybindingsEnabled(keybindingState);
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
if (keybindingState.chordState !== previousChordState) {
|
|
518
|
-
markDirty(DIRTY_VIEW);
|
|
519
|
-
}
|
|
520
|
-
return;
|
|
521
|
-
}
|
|
522
|
-
// Preserve handler-triggered mode changes (for example app.setMode() in a binding).
|
|
523
|
-
if (keybindingState.currentMode !== routeInputState.currentMode) {
|
|
524
|
-
return;
|
|
525
|
-
}
|
|
526
|
-
// For non-mode mutations (e.g. app.keys/app.modes in a handler), keep those
|
|
527
|
-
// edits but still advance chord state from the routed event.
|
|
528
|
-
keybindingState = Object.freeze({
|
|
529
|
-
...keybindingState,
|
|
530
|
-
chordState: routeNextState.chordState,
|
|
531
|
-
});
|
|
532
|
-
if (keybindingState.chordState !== previousChordState) {
|
|
533
|
-
markDirty(DIRTY_VIEW);
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
function noteBreadcrumbEvent(kind) {
|
|
537
|
-
breadcrumbEventTracked = false;
|
|
538
|
-
if (!runtimeBreadcrumbsEnabled)
|
|
539
|
-
return;
|
|
540
|
-
if (!isRuntimeBreadcrumbEventKind(kind))
|
|
541
|
-
return;
|
|
542
|
-
breadcrumbLastEventKind = kind;
|
|
543
|
-
breadcrumbLastConsumptionPath = null;
|
|
544
|
-
breadcrumbEventTracked = true;
|
|
545
|
-
}
|
|
546
|
-
function noteBreadcrumbConsumptionPath(path) {
|
|
547
|
-
if (!runtimeBreadcrumbsEnabled)
|
|
548
|
-
return;
|
|
549
|
-
if (!breadcrumbEventTracked)
|
|
550
|
-
return;
|
|
551
|
-
breadcrumbLastConsumptionPath = path;
|
|
552
|
-
}
|
|
553
|
-
function noteBreadcrumbAction(action) {
|
|
554
|
-
if (!runtimeBreadcrumbsEnabled)
|
|
555
|
-
return;
|
|
556
|
-
if (!breadcrumbEventTracked)
|
|
557
|
-
return;
|
|
558
|
-
breadcrumbLastAction = toRuntimeBreadcrumbAction(action);
|
|
559
|
-
}
|
|
315
|
+
};
|
|
316
|
+
const keybindingHelpers = createAppKeybindingHelpers({
|
|
317
|
+
getState: () => keybindingState,
|
|
318
|
+
markDirty: (flags) => markDirty(flags),
|
|
319
|
+
setState: applyKeybindingState,
|
|
320
|
+
throwCode: guards.throwCode,
|
|
321
|
+
});
|
|
322
|
+
const runtimeBreadcrumbHelpers = createRuntimeBreadcrumbHelpers({
|
|
323
|
+
getBaseInternalOnLayout: () => baseInternalOnLayout,
|
|
324
|
+
getBaseInternalOnRender: () => baseInternalOnRender,
|
|
325
|
+
getInspectorInternalOnLayout: () => inspectorInternalOnLayout,
|
|
326
|
+
getInspectorInternalOnRender: () => inspectorInternalOnRender,
|
|
327
|
+
getLastAction: () => breadcrumbLastAction,
|
|
328
|
+
getLastConsumptionPath: () => breadcrumbLastConsumptionPath,
|
|
329
|
+
getLastEventKind: () => breadcrumbLastEventKind,
|
|
330
|
+
getWidgetRuntimeBreadcrumbSnapshot: () => widgetRenderer.getRuntimeBreadcrumbSnapshot(),
|
|
331
|
+
isEnabled: () => runtimeBreadcrumbsEnabled,
|
|
332
|
+
isEventTracked: () => breadcrumbEventTracked,
|
|
333
|
+
setEnabled: (enabled) => {
|
|
334
|
+
runtimeBreadcrumbsEnabled = enabled;
|
|
335
|
+
},
|
|
336
|
+
setEventTracked: (tracked) => {
|
|
337
|
+
breadcrumbEventTracked = tracked;
|
|
338
|
+
},
|
|
339
|
+
setInspectorInternalOnLayout: (callback) => {
|
|
340
|
+
inspectorInternalOnLayout = callback;
|
|
341
|
+
},
|
|
342
|
+
setInspectorInternalOnRender: (callback) => {
|
|
343
|
+
inspectorInternalOnRender = callback;
|
|
344
|
+
},
|
|
345
|
+
setLastAction: (action) => {
|
|
346
|
+
breadcrumbLastAction = action;
|
|
347
|
+
},
|
|
348
|
+
setLastConsumptionPath: (path) => {
|
|
349
|
+
breadcrumbLastConsumptionPath = path;
|
|
350
|
+
},
|
|
351
|
+
setLastEventKind: (kind) => {
|
|
352
|
+
breadcrumbLastEventKind = kind;
|
|
353
|
+
},
|
|
354
|
+
setWidgetRuntimeBreadcrumbCaptureEnabled: (enabled) => {
|
|
355
|
+
widgetRenderer.setRuntimeBreadcrumbCaptureEnabled(enabled);
|
|
356
|
+
},
|
|
357
|
+
});
|
|
560
358
|
function retryTopLevelViewError() {
|
|
561
359
|
topLevelViewError = null;
|
|
562
360
|
markDirty(DIRTY_VIEW | DIRTY_LAYOUT);
|
|
@@ -589,88 +387,16 @@ export function createApp(opts) {
|
|
|
589
387
|
try {
|
|
590
388
|
stopPromise = app.stop();
|
|
591
389
|
}
|
|
592
|
-
catch (
|
|
593
|
-
// Late events can race while a stop is already in-flight; avoid double-fatal.
|
|
390
|
+
catch (error) {
|
|
594
391
|
if (lifecycleBusy === "stop")
|
|
595
392
|
return;
|
|
596
|
-
fatalNowOrEnqueue("ZRUI_BACKEND_ERROR", `stop threw after unhandled quit input: ${describeThrown(
|
|
393
|
+
fatalNowOrEnqueue("ZRUI_BACKEND_ERROR", `stop threw after unhandled quit input: ${describeThrown(error)}`);
|
|
597
394
|
return;
|
|
598
395
|
}
|
|
599
|
-
void stopPromise.catch((
|
|
600
|
-
fatalNowOrEnqueue("ZRUI_BACKEND_ERROR", `stop rejected after unhandled quit input: ${describeThrown(
|
|
396
|
+
void stopPromise.catch((error) => {
|
|
397
|
+
fatalNowOrEnqueue("ZRUI_BACKEND_ERROR", `stop rejected after unhandled quit input: ${describeThrown(error)}`);
|
|
601
398
|
});
|
|
602
399
|
}
|
|
603
|
-
function buildRuntimeBreadcrumbSnapshot(renderTimeMs) {
|
|
604
|
-
if (!runtimeBreadcrumbsEnabled)
|
|
605
|
-
return null;
|
|
606
|
-
const widgetSnapshot = widgetRenderer.getRuntimeBreadcrumbSnapshot();
|
|
607
|
-
if (!widgetSnapshot)
|
|
608
|
-
return null;
|
|
609
|
-
return mergeRuntimeBreadcrumbSnapshot(widgetSnapshot, breadcrumbLastEventKind, breadcrumbLastConsumptionPath, breadcrumbLastAction, renderTimeMs);
|
|
610
|
-
}
|
|
611
|
-
function throwCode(code, detail) {
|
|
612
|
-
throw new ZrUiError(code, detail);
|
|
613
|
-
}
|
|
614
|
-
function assertOperational(method) {
|
|
615
|
-
const st = sm.state;
|
|
616
|
-
if (st === "Disposed" || st === "Faulted") {
|
|
617
|
-
throwCode("ZRUI_INVALID_STATE", `${method}: app is ${st}`);
|
|
618
|
-
}
|
|
619
|
-
if (lifecycleBusy !== null) {
|
|
620
|
-
throwCode("ZRUI_INVALID_STATE", `${method}: lifecycle operation already in flight`);
|
|
621
|
-
}
|
|
622
|
-
}
|
|
623
|
-
function assertLifecycleIdle(method) {
|
|
624
|
-
if (lifecycleBusy !== null) {
|
|
625
|
-
throwCode("ZRUI_INVALID_STATE", `${method}: lifecycle operation already in flight`);
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
function assertNotReentrant(method) {
|
|
629
|
-
if (inCommit || inRender || inEventHandlerDepth > 0) {
|
|
630
|
-
throwCode("ZRUI_REENTRANT_CALL", `${method}: re-entrant call`);
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
function enqueueFatal(code, detail) {
|
|
634
|
-
scheduler.enqueue({ kind: "fatal", code, detail });
|
|
635
|
-
}
|
|
636
|
-
function flushDeferredInlineFatal() {
|
|
637
|
-
if (deferredInlineFatal === null || inEventHandlerDepth !== 0)
|
|
638
|
-
return;
|
|
639
|
-
const fatal = deferredInlineFatal;
|
|
640
|
-
deferredInlineFatal = null;
|
|
641
|
-
doFatal(fatal.code, fatal.detail);
|
|
642
|
-
}
|
|
643
|
-
function fatalNowOrEnqueue(code, detail) {
|
|
644
|
-
const canFailFastInline = scheduler.isExecuting && !inRender && !inCommit;
|
|
645
|
-
if (canFailFastInline && inEventHandlerDepth > 0) {
|
|
646
|
-
if (deferredInlineFatal === null) {
|
|
647
|
-
deferredInlineFatal = Object.freeze({ code, detail });
|
|
648
|
-
}
|
|
649
|
-
return;
|
|
650
|
-
}
|
|
651
|
-
if (canFailFastInline) {
|
|
652
|
-
doFatal(code, detail);
|
|
653
|
-
return;
|
|
654
|
-
}
|
|
655
|
-
enqueueFatal(code, detail);
|
|
656
|
-
}
|
|
657
|
-
function updateDuringRenderDetail(method) {
|
|
658
|
-
return `${method}: called during render. Hint: This usually means an onPress/onChange callback calls app.update() synchronously during the render phase. Move state updates to event handlers or useEffect.`;
|
|
659
|
-
}
|
|
660
|
-
function assertRouterMutationAllowed(method) {
|
|
661
|
-
assertOperational(method);
|
|
662
|
-
if (inCommit)
|
|
663
|
-
throwCode("ZRUI_REENTRANT_CALL", `${method}: called during commit`);
|
|
664
|
-
if (inRender)
|
|
665
|
-
throwCode("ZRUI_UPDATE_DURING_RENDER", updateDuringRenderDetail(method));
|
|
666
|
-
}
|
|
667
|
-
function assertKeybindingMutationAllowed(method) {
|
|
668
|
-
assertOperational(method);
|
|
669
|
-
if (inCommit)
|
|
670
|
-
throwCode("ZRUI_REENTRANT_CALL", `${method}: called during commit`);
|
|
671
|
-
if (inRender)
|
|
672
|
-
throwCode("ZRUI_UPDATE_DURING_RENDER", updateDuringRenderDetail(method));
|
|
673
|
-
}
|
|
674
400
|
if (routes !== undefined) {
|
|
675
401
|
routerIntegration = createRouterIntegration({
|
|
676
402
|
routes,
|
|
@@ -679,12 +405,10 @@ export function createApp(opts) {
|
|
|
679
405
|
? {}
|
|
680
406
|
: { maxHistoryDepth: opts.routeHistoryMaxDepth }),
|
|
681
407
|
getState: () => committedState,
|
|
682
|
-
// Route transitions can swap the entire screen tree; force both commit
|
|
683
|
-
// and layout so id->rect indexes and focus metadata stay in sync.
|
|
684
408
|
requestRouteRender: () => markDirty(DIRTY_VIEW | DIRTY_LAYOUT),
|
|
685
409
|
captureFocusSnapshot: () => widgetRenderer.captureFocusSnapshot(),
|
|
686
410
|
restoreFocusSnapshot: (snapshot) => widgetRenderer.restoreFocusSnapshot(snapshot),
|
|
687
|
-
assertCanMutate: assertRouterMutationAllowed,
|
|
411
|
+
assertCanMutate: guards.assertRouterMutationAllowed,
|
|
688
412
|
});
|
|
689
413
|
}
|
|
690
414
|
function emit(ev) {
|
|
@@ -699,8 +423,8 @@ export function createApp(opts) {
|
|
|
699
423
|
try {
|
|
700
424
|
fn(ev);
|
|
701
425
|
}
|
|
702
|
-
catch (
|
|
703
|
-
fatalNowOrEnqueue("ZRUI_USER_CODE_THROW", `onEvent handler threw: ${describeThrown(
|
|
426
|
+
catch (error) {
|
|
427
|
+
fatalNowOrEnqueue("ZRUI_USER_CODE_THROW", `onEvent handler threw: ${describeThrown(error)}`);
|
|
704
428
|
return false;
|
|
705
429
|
}
|
|
706
430
|
}
|
|
@@ -714,676 +438,154 @@ export function createApp(opts) {
|
|
|
714
438
|
function emitFocusChangeIfNeeded() {
|
|
715
439
|
return focusDispatcher.emitIfChanged();
|
|
716
440
|
}
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
interactiveBudget = 2;
|
|
843
|
-
}
|
|
844
|
-
noteBreadcrumbEvent(ev.kind);
|
|
845
|
-
if (!emit({ kind: "engine", event: ev }))
|
|
846
|
-
return;
|
|
847
|
-
if (sm.state !== "Running")
|
|
848
|
-
return;
|
|
849
|
-
if (ev.kind === "resize") {
|
|
850
|
-
const prev = viewport;
|
|
851
|
-
if (prev === null || prev.cols !== ev.cols || prev.rows !== ev.rows) {
|
|
852
|
-
viewport = Object.freeze({ cols: ev.cols, rows: ev.rows });
|
|
853
|
-
if (widgetRenderer.hasViewportAwareComposites()) {
|
|
854
|
-
widgetRenderer.invalidateCompositeWidgets();
|
|
855
|
-
markDirty(DIRTY_LAYOUT | DIRTY_VIEW);
|
|
856
|
-
}
|
|
857
|
-
else {
|
|
858
|
-
markDirty(DIRTY_LAYOUT);
|
|
859
|
-
}
|
|
860
|
-
}
|
|
861
|
-
}
|
|
862
|
-
if (ev.kind === "tick" && mode === "widget") {
|
|
863
|
-
// Tick events drive render-only animation frames for animated widgets
|
|
864
|
-
// (currently spinner). Throttle to avoid repaint storms/flicker.
|
|
865
|
-
//
|
|
866
|
-
// Prefer backend tick timestamps when they advance, but fall back to
|
|
867
|
-
// local monotonic time for runtimes/terminals where tick time is
|
|
868
|
-
// constant or non-monotonic.
|
|
869
|
-
if (widgetRenderer.hasAnimatedWidgets()) {
|
|
870
|
-
const tickMs = ev.timeMs;
|
|
871
|
-
const perfMs = perfNow();
|
|
872
|
-
const eventClockAdvances = tickMs > lastObservedSpinnerTickEventMs;
|
|
873
|
-
if (eventClockAdvances)
|
|
874
|
-
lastObservedSpinnerTickEventMs = tickMs;
|
|
875
|
-
const elapsedMs = eventClockAdvances
|
|
876
|
-
? tickMs - lastSpinnerRenderTickMs
|
|
877
|
-
: perfMs - lastSpinnerRenderPerfMs;
|
|
878
|
-
if (elapsedMs >= spinnerTickMinIntervalMs) {
|
|
879
|
-
lastSpinnerRenderTickMs = tickMs;
|
|
880
|
-
lastSpinnerRenderPerfMs = perfMs;
|
|
881
|
-
markDirty(DIRTY_RENDER);
|
|
882
|
-
}
|
|
883
|
-
}
|
|
884
|
-
}
|
|
885
|
-
if (mode === "widget" && topLevelViewError !== null) {
|
|
886
|
-
if (isTopLevelRetryEvent(ev)) {
|
|
887
|
-
noteBreadcrumbConsumptionPath("widgetRouting");
|
|
888
|
-
retryTopLevelViewError();
|
|
889
|
-
continue;
|
|
890
|
-
}
|
|
891
|
-
if (isTopLevelQuitEvent(ev)) {
|
|
892
|
-
noteBreadcrumbConsumptionPath("widgetRouting");
|
|
893
|
-
quitFromTopLevelViewError();
|
|
894
|
-
continue;
|
|
895
|
-
}
|
|
896
|
-
if (ev.kind === "key" ||
|
|
897
|
-
ev.kind === "text" ||
|
|
898
|
-
ev.kind === "paste" ||
|
|
899
|
-
ev.kind === "mouse") {
|
|
900
|
-
noteBreadcrumbConsumptionPath("widgetRouting");
|
|
901
|
-
continue;
|
|
902
|
-
}
|
|
903
|
-
}
|
|
904
|
-
const isWidgetRoutableEvent = ev.kind === "key" || ev.kind === "text" || ev.kind === "paste" || ev.kind === "mouse";
|
|
905
|
-
if (mode === "widget" && isWidgetRoutableEvent) {
|
|
906
|
-
if (keybindingsEnabled) {
|
|
907
|
-
if (ev.kind === "mouse" &&
|
|
908
|
-
ev.mouseKind === 3 &&
|
|
909
|
-
keybindingState.chordState.pendingKeys.length > 0) {
|
|
910
|
-
keybindingState = Object.freeze({
|
|
911
|
-
...keybindingState,
|
|
912
|
-
chordState: resetChordState(),
|
|
913
|
-
});
|
|
914
|
-
}
|
|
915
|
-
// Route key events through keybinding system first
|
|
916
|
-
if (ev.kind === "key") {
|
|
917
|
-
const bypass = widgetRenderer.shouldBypassKeybindings(ev);
|
|
918
|
-
if (!bypass) {
|
|
919
|
-
const keyCtx = Object.freeze({
|
|
920
|
-
state: committedState,
|
|
921
|
-
update: app.update,
|
|
922
|
-
focusedId: widgetRenderer.getFocusedId(),
|
|
923
|
-
});
|
|
924
|
-
const routeInputState = keybindingState;
|
|
925
|
-
const keyResult = routeKeyEvent(routeInputState, ev, keyCtx);
|
|
926
|
-
applyRoutedKeybindingState(routeInputState, keyResult.nextState);
|
|
927
|
-
if (keyResult.handlerError !== undefined) {
|
|
928
|
-
fatalNowOrEnqueue("ZRUI_USER_CODE_THROW", `keybinding handler threw: ${describeThrown(keyResult.handlerError)}`);
|
|
929
|
-
return;
|
|
930
|
-
}
|
|
931
|
-
if (keyResult.consumed) {
|
|
932
|
-
noteBreadcrumbConsumptionPath("keybindings");
|
|
933
|
-
continue; // Skip default widget routing
|
|
934
|
-
}
|
|
935
|
-
}
|
|
936
|
-
}
|
|
937
|
-
// Also route text events through keybinding system for single-character bindings.
|
|
938
|
-
// Printable text is guarded during overlays, but Ctrl+text control chars are not.
|
|
939
|
-
if (ev.kind === "text") {
|
|
940
|
-
const ctrlKeyCode = codepointToCtrlKeyCode(ev.codepoint);
|
|
941
|
-
const shouldRouteCtrlText = ctrlKeyCode !== null;
|
|
942
|
-
const shouldRoutePrintableText = !shouldRouteCtrlText && !widgetRenderer.hasActiveOverlay();
|
|
943
|
-
if (shouldRouteCtrlText || shouldRoutePrintableText) {
|
|
944
|
-
const keyCode = shouldRouteCtrlText
|
|
945
|
-
? ctrlKeyCode
|
|
946
|
-
: codepointToKeyCode(ev.codepoint);
|
|
947
|
-
const mods = shouldRouteCtrlText ? ZR_MOD_CTRL : 0;
|
|
948
|
-
if (keyCode !== null) {
|
|
949
|
-
// Create a synthetic key event for keybinding matching
|
|
950
|
-
const syntheticKeyEvent = {
|
|
951
|
-
kind: "key",
|
|
952
|
-
action: "down",
|
|
953
|
-
key: keyCode,
|
|
954
|
-
mods,
|
|
955
|
-
timeMs: ev.timeMs,
|
|
956
|
-
};
|
|
957
|
-
const keyCtx = Object.freeze({
|
|
958
|
-
state: committedState,
|
|
959
|
-
update: app.update,
|
|
960
|
-
focusedId: widgetRenderer.getFocusedId(),
|
|
961
|
-
});
|
|
962
|
-
const routeInputState = keybindingState;
|
|
963
|
-
const keyResult = routeKeyEvent(routeInputState, syntheticKeyEvent, keyCtx);
|
|
964
|
-
applyRoutedKeybindingState(routeInputState, keyResult.nextState);
|
|
965
|
-
if (keyResult.handlerError !== undefined) {
|
|
966
|
-
fatalNowOrEnqueue("ZRUI_USER_CODE_THROW", `keybinding handler threw: ${describeThrown(keyResult.handlerError)}`);
|
|
967
|
-
return;
|
|
968
|
-
}
|
|
969
|
-
if (keyResult.consumed) {
|
|
970
|
-
noteBreadcrumbConsumptionPath("keybindings");
|
|
971
|
-
continue; // Skip default widget routing
|
|
972
|
-
}
|
|
973
|
-
}
|
|
974
|
-
}
|
|
975
|
-
}
|
|
976
|
-
}
|
|
977
|
-
let routed;
|
|
978
|
-
try {
|
|
979
|
-
noteBreadcrumbConsumptionPath("widgetRouting");
|
|
980
|
-
routed = widgetRenderer.routeEngineEvent(ev);
|
|
981
|
-
}
|
|
982
|
-
catch (e) {
|
|
983
|
-
fatalNowOrEnqueue("ZRUI_USER_CODE_THROW", `widget routing threw: ${describeThrown(e)}`);
|
|
984
|
-
return;
|
|
985
|
-
}
|
|
986
|
-
if (sm.state !== "Running")
|
|
987
|
-
return;
|
|
988
|
-
if (!emitFocusChangeIfNeeded())
|
|
989
|
-
return;
|
|
990
|
-
if (routed.needsRender)
|
|
991
|
-
markDirty(DIRTY_RENDER);
|
|
992
|
-
if (routed.action) {
|
|
993
|
-
noteBreadcrumbAction(routed.action);
|
|
994
|
-
if (!emit({ kind: "action", ...routed.action }))
|
|
995
|
-
return;
|
|
996
|
-
if (sm.state !== "Running")
|
|
997
|
-
return;
|
|
998
|
-
}
|
|
999
|
-
if (routed.action === undefined &&
|
|
1000
|
-
!routed.needsRender &&
|
|
1001
|
-
routed.consumed !== true &&
|
|
1002
|
-
(isUnmodifiedTextQuitEvent(ev) || isUnhandledCtrlCKeyEvent(ev))) {
|
|
1003
|
-
noteBreadcrumbConsumptionPath("widgetRouting");
|
|
1004
|
-
stopFromUnhandledQuitEvent();
|
|
1005
|
-
}
|
|
1006
|
-
}
|
|
1007
|
-
}
|
|
1008
|
-
}
|
|
1009
|
-
finally {
|
|
1010
|
-
release();
|
|
1011
|
-
}
|
|
1012
|
-
}
|
|
1013
|
-
function commitUpdates() {
|
|
1014
|
-
const drained = updates.drain();
|
|
1015
|
-
if (drained.length === 0)
|
|
1016
|
-
return;
|
|
1017
|
-
const commitToken = perfMarkStart("commit");
|
|
1018
|
-
inCommit = true;
|
|
1019
|
-
try {
|
|
1020
|
-
let next = committedState;
|
|
1021
|
-
for (const u of drained) {
|
|
1022
|
-
if (typeof u === "function") {
|
|
1023
|
-
next = u(next);
|
|
1024
|
-
}
|
|
1025
|
-
else {
|
|
1026
|
-
next = u;
|
|
1027
|
-
}
|
|
1028
|
-
}
|
|
1029
|
-
if (next !== committedState) {
|
|
1030
|
-
committedState = next;
|
|
1031
|
-
markDirty(DIRTY_VIEW, false);
|
|
1032
|
-
}
|
|
1033
|
-
}
|
|
1034
|
-
catch (e) {
|
|
1035
|
-
fatalNowOrEnqueue("ZRUI_USER_CODE_THROW", `state updater threw: ${describeThrown(e)}`);
|
|
1036
|
-
}
|
|
1037
|
-
finally {
|
|
1038
|
-
inCommit = false;
|
|
1039
|
-
perfMarkEnd("commit", commitToken);
|
|
1040
|
-
}
|
|
1041
|
-
}
|
|
1042
|
-
function scheduleFrameSettlement(p, submitStart, submitEnd) {
|
|
1043
|
-
if (isSyncFrameAck(p)) {
|
|
1044
|
-
if (PERF_ENABLED && submitStart !== null) {
|
|
1045
|
-
const ackNow = perfNow();
|
|
1046
|
-
perfRecord("backend_ack", ackNow - submitStart);
|
|
1047
|
-
if (submitEnd !== null) {
|
|
1048
|
-
perfRecord("frame_build", submitEnd - submitStart);
|
|
1049
|
-
perfRecord("worker_roundtrip", ackNow - submitEnd);
|
|
1050
|
-
}
|
|
1051
|
-
}
|
|
1052
|
-
framesInFlight = Math.max(0, framesInFlight - 1);
|
|
1053
|
-
return;
|
|
1054
|
-
}
|
|
1055
|
-
const acceptedAck = getAcceptedFrameAck(p);
|
|
1056
|
-
const ackPromise = acceptedAck ?? p;
|
|
1057
|
-
void ackPromise.then(() => {
|
|
1058
|
-
if (PERF_ENABLED && submitStart !== null) {
|
|
1059
|
-
const ackNow = perfNow();
|
|
1060
|
-
// backend_ack: total time from frame build start to backend ack.
|
|
1061
|
-
// Equals frame_build + worker_roundtrip (kept for backward compat).
|
|
1062
|
-
perfRecord("backend_ack", ackNow - submitStart);
|
|
1063
|
-
if (submitEnd !== null) {
|
|
1064
|
-
// frame_build: synchronous TS pipeline (view/commit/layout/render/build).
|
|
1065
|
-
perfRecord("frame_build", submitEnd - submitStart);
|
|
1066
|
-
// worker_roundtrip: async transport from requestFrame to backend ack.
|
|
1067
|
-
perfRecord("worker_roundtrip", ackNow - submitEnd);
|
|
1068
|
-
}
|
|
1069
|
-
}
|
|
1070
|
-
scheduler.enqueue({ kind: "frameDone" });
|
|
1071
|
-
}, (err) => scheduler.enqueue({ kind: "frameError", error: err }));
|
|
1072
|
-
if (acceptedAck !== null) {
|
|
1073
|
-
void p.then(() => { }, (err) => scheduler.enqueue({
|
|
1074
|
-
kind: "fatal",
|
|
1075
|
-
code: "ZRUI_BACKEND_ERROR",
|
|
1076
|
-
detail: `requestFrame completion rejected after accepted ack: ${describeThrown(err)}`,
|
|
1077
|
-
}));
|
|
1078
|
-
}
|
|
1079
|
-
}
|
|
1080
|
-
function emitInternalRenderMetrics(renderTime, runtimeBreadcrumbs = null) {
|
|
1081
|
-
if (baseInternalOnRender === undefined && inspectorInternalOnRender === undefined)
|
|
1082
|
-
return true;
|
|
1083
|
-
try {
|
|
1084
|
-
const clampedRenderTime = Math.max(0, renderTime);
|
|
1085
|
-
if (runtimeBreadcrumbs) {
|
|
1086
|
-
const payload = {
|
|
1087
|
-
renderTime: clampedRenderTime,
|
|
1088
|
-
runtimeBreadcrumbs,
|
|
1089
|
-
};
|
|
1090
|
-
baseInternalOnRender?.(payload);
|
|
1091
|
-
inspectorInternalOnRender?.(payload);
|
|
1092
|
-
}
|
|
1093
|
-
else {
|
|
1094
|
-
const payload = { renderTime: clampedRenderTime };
|
|
1095
|
-
baseInternalOnRender?.(payload);
|
|
1096
|
-
inspectorInternalOnRender?.(payload);
|
|
1097
|
-
}
|
|
1098
|
-
return true;
|
|
1099
|
-
}
|
|
1100
|
-
catch (e) {
|
|
1101
|
-
fatalNowOrEnqueue("ZRUI_USER_CODE_THROW", `onRender callback threw: ${describeThrown(e)}`);
|
|
1102
|
-
return false;
|
|
1103
|
-
}
|
|
1104
|
-
}
|
|
1105
|
-
function emitInternalLayoutSnapshot(runtimeBreadcrumbs = null) {
|
|
1106
|
-
if (baseInternalOnLayout === undefined && inspectorInternalOnLayout === undefined)
|
|
1107
|
-
return true;
|
|
1108
|
-
try {
|
|
1109
|
-
const idRects = widgetRenderer.getRectByIdIndex();
|
|
1110
|
-
if (runtimeBreadcrumbs) {
|
|
1111
|
-
const payload = {
|
|
1112
|
-
idRects,
|
|
1113
|
-
runtimeBreadcrumbs,
|
|
1114
|
-
};
|
|
1115
|
-
baseInternalOnLayout?.(payload);
|
|
1116
|
-
inspectorInternalOnLayout?.(payload);
|
|
1117
|
-
}
|
|
1118
|
-
else {
|
|
1119
|
-
const payload = { idRects };
|
|
1120
|
-
baseInternalOnLayout?.(payload);
|
|
1121
|
-
inspectorInternalOnLayout?.(payload);
|
|
1122
|
-
}
|
|
1123
|
-
return true;
|
|
1124
|
-
}
|
|
1125
|
-
catch (e) {
|
|
1126
|
-
fatalNowOrEnqueue("ZRUI_USER_CODE_THROW", `onLayout callback threw: ${describeThrown(e)}`);
|
|
1127
|
-
return false;
|
|
1128
|
-
}
|
|
1129
|
-
}
|
|
1130
|
-
function tryRenderOnce() {
|
|
1131
|
-
if (sm.state !== "Running")
|
|
1132
|
-
return;
|
|
1133
|
-
// During stop(), we may still receive a few late event batches, but we must not
|
|
1134
|
-
// submit new frames (backend may be tearing down).
|
|
1135
|
-
if (lifecycleBusy === "stop")
|
|
1136
|
-
return;
|
|
1137
|
-
if (dirtyTracker.getFlags() === 0)
|
|
1138
|
-
return;
|
|
1139
|
-
const maxInFlight = config.maxFramesInFlight + (interactiveBudget > 0 ? 1 : 0);
|
|
1140
|
-
if (framesInFlight >= maxInFlight)
|
|
1141
|
-
return;
|
|
1142
|
-
if (mode === null)
|
|
1143
|
-
return;
|
|
1144
|
-
// Record schedule_wait: time from render request to render start
|
|
1145
|
-
if (PERF_ENABLED && scheduleWaitStartMs !== null) {
|
|
1146
|
-
perfMarkEnd("schedule_wait", scheduleWaitStartMs);
|
|
1147
|
-
scheduleWaitStartMs = null;
|
|
1148
|
-
}
|
|
1149
|
-
const dirtyVersionStart = dirtyTracker.snapshotVersions();
|
|
1150
|
-
const snapshot = committedState;
|
|
1151
|
-
const hooks = {
|
|
1152
|
-
enterRender: () => {
|
|
1153
|
-
inRender = true;
|
|
1154
|
-
},
|
|
1155
|
-
exitRender: () => {
|
|
1156
|
-
inRender = false;
|
|
1157
|
-
},
|
|
1158
|
-
};
|
|
1159
|
-
if (mode === "raw") {
|
|
1160
|
-
const df = drawFn;
|
|
1161
|
-
if (!df)
|
|
1162
|
-
return;
|
|
1163
|
-
const renderStart = perfNow();
|
|
1164
|
-
const submitToken = perfMarkStart("submit_frame");
|
|
1165
|
-
const res = rawRenderer.submitFrame(df, hooks);
|
|
1166
|
-
perfMarkEnd("submit_frame", submitToken);
|
|
1167
|
-
if (!res.ok) {
|
|
1168
|
-
fatalNowOrEnqueue(res.code, res.detail);
|
|
1169
|
-
return;
|
|
1170
|
-
}
|
|
1171
|
-
if (!emitInternalRenderMetrics(perfNow() - renderStart))
|
|
1172
|
-
return;
|
|
1173
|
-
submitFrameStartMs = PERF_ENABLED ? submitToken : null;
|
|
1174
|
-
const buildEndMs = PERF_ENABLED ? perfNow() : null;
|
|
1175
|
-
framesInFlight++;
|
|
1176
|
-
if (interactiveBudget > 0)
|
|
1177
|
-
interactiveBudget--;
|
|
1178
|
-
scheduleFrameSettlement(res.inFlight, submitFrameStartMs, buildEndMs);
|
|
1179
|
-
dirtyTracker.clearConsumedFlags(DIRTY_RENDER | DIRTY_LAYOUT | DIRTY_VIEW, dirtyVersionStart);
|
|
1180
|
-
return;
|
|
1181
|
-
}
|
|
1182
|
-
const vf = viewFn;
|
|
1183
|
-
if (!vf)
|
|
1184
|
-
return;
|
|
1185
|
-
if (!viewport)
|
|
1186
|
-
return;
|
|
1187
|
-
const pendingDirtyFlags = dirtyTracker.getFlags();
|
|
1188
|
-
if ((pendingDirtyFlags & (DIRTY_VIEW | DIRTY_LAYOUT | DIRTY_RENDER)) === 0)
|
|
1189
|
-
return;
|
|
1190
|
-
// Compute render plan from dirty flags. Render-only turns (e.g., focus change)
|
|
1191
|
-
// skip view/commit/layout. Layout-only turns (e.g., resize without state change)
|
|
1192
|
-
// skip view/commit. Commit turns now rely on WidgetRenderer layout signatures
|
|
1193
|
-
// to decide whether relayout is required, instead of forcing layout by default.
|
|
1194
|
-
// First-frame/bootstrap safety is handled inside submitFrame(): it falls back
|
|
1195
|
-
// to full pipeline when committedRoot or layoutTree is null.
|
|
1196
|
-
const frameNowMs = monotonicNowMs();
|
|
1197
|
-
const plan = buildWidgetRenderPlan(pendingDirtyFlags, frameNowMs);
|
|
1198
|
-
advanceThemeTransitionFrame();
|
|
1199
|
-
const resilientView = (state) => {
|
|
1200
|
-
if (topLevelViewError !== null) {
|
|
1201
|
-
return buildTopLevelViewErrorScreen(topLevelViewError);
|
|
1202
|
-
}
|
|
1203
|
-
try {
|
|
1204
|
-
return vf(state);
|
|
1205
|
-
}
|
|
1206
|
-
catch (e) {
|
|
1207
|
-
topLevelViewError = captureTopLevelViewError(e);
|
|
1208
|
-
return buildTopLevelViewErrorScreen(topLevelViewError);
|
|
1209
|
-
}
|
|
1210
|
-
};
|
|
1211
|
-
const renderStart = perfNow();
|
|
1212
|
-
const submitToken = perfMarkStart("submit_frame");
|
|
1213
|
-
const frameView = debugLayoutEnabled
|
|
1214
|
-
? (state) => {
|
|
1215
|
-
const root = resilientView(state);
|
|
1216
|
-
const overlay = buildLayoutDebugOverlay(widgetRenderer.getRectByIdIndex());
|
|
1217
|
-
if (!overlay)
|
|
1218
|
-
return root;
|
|
1219
|
-
return ui.layers([root, overlay]);
|
|
1220
|
-
}
|
|
1221
|
-
: resilientView;
|
|
1222
|
-
const res = widgetRenderer.submitFrame(frameView, snapshot, viewport, theme, hooks, plan);
|
|
1223
|
-
perfMarkEnd("submit_frame", submitToken);
|
|
1224
|
-
if (!res.ok) {
|
|
1225
|
-
fatalNowOrEnqueue(res.code, res.detail);
|
|
1226
|
-
return;
|
|
1227
|
-
}
|
|
1228
|
-
if (!emitFocusChangeIfNeeded())
|
|
1229
|
-
return;
|
|
1230
|
-
const renderTime = perfNow() - renderStart;
|
|
1231
|
-
const runtimeBreadcrumbs = buildRuntimeBreadcrumbSnapshot(Math.max(0, renderTime));
|
|
1232
|
-
if (!emitInternalRenderMetrics(renderTime, runtimeBreadcrumbs))
|
|
1233
|
-
return;
|
|
1234
|
-
if (!emitInternalLayoutSnapshot(runtimeBreadcrumbs))
|
|
1235
|
-
return;
|
|
1236
|
-
submitFrameStartMs = PERF_ENABLED ? submitToken : null;
|
|
1237
|
-
const buildEndMs = PERF_ENABLED ? perfNow() : null;
|
|
1238
|
-
framesInFlight++;
|
|
1239
|
-
if (interactiveBudget > 0)
|
|
1240
|
-
interactiveBudget--;
|
|
1241
|
-
scheduleFrameSettlement(res.inFlight, submitFrameStartMs, buildEndMs);
|
|
1242
|
-
let consumedDirtyFlags = DIRTY_RENDER;
|
|
1243
|
-
if (plan.layout)
|
|
1244
|
-
consumedDirtyFlags |= DIRTY_LAYOUT;
|
|
1245
|
-
if (plan.commit)
|
|
1246
|
-
consumedDirtyFlags |= DIRTY_VIEW;
|
|
1247
|
-
dirtyTracker.clearConsumedFlags(consumedDirtyFlags, dirtyVersionStart);
|
|
1248
|
-
scheduleThemeTransitionContinuation();
|
|
1249
|
-
if (dirtyTracker.getFlags() !== 0 && !renderRequestQueuedForCurrentTurn) {
|
|
1250
|
-
renderRequestQueuedForCurrentTurn = true;
|
|
1251
|
-
scheduler.enqueue({ kind: "renderRequest" });
|
|
1252
|
-
}
|
|
1253
|
-
}
|
|
1254
|
-
function drainIgnored(items, releasedBatches) {
|
|
1255
|
-
for (const it of items) {
|
|
1256
|
-
if (it.kind === "eventBatch" && !releasedBatches.has(it.batch)) {
|
|
1257
|
-
releasedBatches.add(it.batch);
|
|
1258
|
-
try {
|
|
1259
|
-
it.batch.release();
|
|
1260
|
-
}
|
|
1261
|
-
catch {
|
|
1262
|
-
// ignore
|
|
1263
|
-
}
|
|
1264
|
-
}
|
|
1265
|
-
}
|
|
1266
|
-
}
|
|
1267
|
-
function processTurn(items) {
|
|
1268
|
-
renderRequestQueuedForCurrentTurn = false;
|
|
1269
|
-
const releasedBatches = new Set();
|
|
1270
|
-
const st = sm.state;
|
|
1271
|
-
if (st === "Disposed" || st === "Faulted") {
|
|
1272
|
-
drainIgnored(items, releasedBatches);
|
|
1273
|
-
return;
|
|
1274
|
-
}
|
|
1275
|
-
let sawKick = false;
|
|
1276
|
-
for (const item of items) {
|
|
1277
|
-
if (sm.state === "Faulted" || sm.state === "Disposed") {
|
|
1278
|
-
drainIgnored(items, releasedBatches);
|
|
1279
|
-
return;
|
|
1280
|
-
}
|
|
1281
|
-
switch (item.kind) {
|
|
1282
|
-
case "fatal": {
|
|
1283
|
-
doFatal(item.code, item.detail);
|
|
1284
|
-
drainIgnored(items, releasedBatches);
|
|
1285
|
-
return;
|
|
1286
|
-
}
|
|
1287
|
-
case "eventBatch": {
|
|
1288
|
-
if (sm.state !== "Running") {
|
|
1289
|
-
releasedBatches.add(item.batch);
|
|
1290
|
-
try {
|
|
1291
|
-
item.batch.release();
|
|
1292
|
-
}
|
|
1293
|
-
catch {
|
|
1294
|
-
// ignore
|
|
1295
|
-
}
|
|
1296
|
-
break;
|
|
1297
|
-
}
|
|
1298
|
-
processEventBatch(item.batch, releasedBatches);
|
|
1299
|
-
if (sm.state !== "Running") {
|
|
1300
|
-
drainIgnored(items, releasedBatches);
|
|
1301
|
-
return;
|
|
1302
|
-
}
|
|
1303
|
-
commitUpdates();
|
|
1304
|
-
break;
|
|
1305
|
-
}
|
|
1306
|
-
case "userCommit": {
|
|
1307
|
-
userCommitScheduled = false;
|
|
1308
|
-
if (sm.state === "Running")
|
|
1309
|
-
commitUpdates();
|
|
1310
|
-
break;
|
|
1311
|
-
}
|
|
1312
|
-
case "kick": {
|
|
1313
|
-
sawKick = true;
|
|
1314
|
-
break;
|
|
1315
|
-
}
|
|
1316
|
-
case "renderRequest": {
|
|
1317
|
-
break;
|
|
1318
|
-
}
|
|
1319
|
-
case "frameDone": {
|
|
1320
|
-
framesInFlight = Math.max(0, framesInFlight - 1);
|
|
1321
|
-
break;
|
|
1322
|
-
}
|
|
1323
|
-
case "frameError": {
|
|
1324
|
-
framesInFlight = Math.max(0, framesInFlight - 1);
|
|
1325
|
-
// If we are intentionally stopping, treat requestFrame rejections as
|
|
1326
|
-
// part of shutdown (not a fatal backend error).
|
|
1327
|
-
if (lifecycleBusy === "stop")
|
|
1328
|
-
break;
|
|
1329
|
-
doFatal("ZRUI_BACKEND_ERROR", `requestFrame rejected: ${describeThrown(item.error)}`);
|
|
1330
|
-
break;
|
|
1331
|
-
}
|
|
1332
|
-
}
|
|
1333
|
-
}
|
|
1334
|
-
if (sm.state !== "Running")
|
|
1335
|
-
return;
|
|
1336
|
-
if (sawKick)
|
|
1337
|
-
commitUpdates();
|
|
1338
|
-
tryRenderOnce();
|
|
1339
|
-
}
|
|
1340
|
-
async function pollLoop(token) {
|
|
1341
|
-
while (sm.state === "Running" && token === pollToken) {
|
|
1342
|
-
let batch;
|
|
1343
|
-
try {
|
|
1344
|
-
batch = await backend.pollEvents();
|
|
1345
|
-
}
|
|
1346
|
-
catch (e) {
|
|
1347
|
-
if (sm.state === "Running" && token === pollToken) {
|
|
1348
|
-
fatalNowOrEnqueue("ZRUI_BACKEND_ERROR", `pollEvents rejected: ${describeThrown(e)}`);
|
|
1349
|
-
}
|
|
1350
|
-
return;
|
|
1351
|
-
}
|
|
1352
|
-
if (token !== pollToken || sm.state !== "Running") {
|
|
1353
|
-
try {
|
|
1354
|
-
batch.release();
|
|
1355
|
-
}
|
|
1356
|
-
catch {
|
|
1357
|
-
// ignore
|
|
1358
|
-
}
|
|
1359
|
-
return;
|
|
1360
|
-
}
|
|
1361
|
-
scheduler.enqueue({ kind: "eventBatch", batch });
|
|
1362
|
-
}
|
|
1363
|
-
}
|
|
441
|
+
const renderLoop = createRenderLoop({
|
|
442
|
+
buildRuntimeBreadcrumbSnapshot: runtimeBreadcrumbHelpers.buildRuntimeBreadcrumbSnapshot,
|
|
443
|
+
config,
|
|
444
|
+
dirtyTracker,
|
|
445
|
+
emitFocusChangeIfNeeded,
|
|
446
|
+
enqueueWorkItem,
|
|
447
|
+
fatalNowOrEnqueue,
|
|
448
|
+
getBaseInternalOnLayout: () => baseInternalOnLayout,
|
|
449
|
+
getBaseInternalOnRender: () => baseInternalOnRender,
|
|
450
|
+
getCommittedState: () => committedState,
|
|
451
|
+
getDebugLayoutEnabled: () => debugLayoutEnabled,
|
|
452
|
+
getDrawFn: () => drawFn,
|
|
453
|
+
getFramesInFlight: () => framesInFlight,
|
|
454
|
+
getInspectorInternalOnLayout: () => inspectorInternalOnLayout,
|
|
455
|
+
getInspectorInternalOnRender: () => inspectorInternalOnRender,
|
|
456
|
+
getInteractiveBudget: () => interactiveBudget,
|
|
457
|
+
getLifecycleBusy: () => lifecycleBusy,
|
|
458
|
+
getMode: () => mode,
|
|
459
|
+
getRenderRequestQueuedForCurrentTurn: () => renderRequestQueuedForCurrentTurn,
|
|
460
|
+
getScheduleWaitStartMs: () => scheduleWaitStartMs,
|
|
461
|
+
getTheme: () => theme,
|
|
462
|
+
getThemeTransition: () => themeTransition,
|
|
463
|
+
getTopLevelViewError: () => topLevelViewError,
|
|
464
|
+
getViewFn: () => viewFn,
|
|
465
|
+
getViewport: () => viewport,
|
|
466
|
+
isRunning: () => sm.state === "Running",
|
|
467
|
+
markDirty,
|
|
468
|
+
rawRenderer,
|
|
469
|
+
setFramesInFlight: (next) => {
|
|
470
|
+
framesInFlight = next;
|
|
471
|
+
},
|
|
472
|
+
setInRender: (next) => {
|
|
473
|
+
inRender = next;
|
|
474
|
+
},
|
|
475
|
+
setInteractiveBudget: (next) => {
|
|
476
|
+
interactiveBudget = next;
|
|
477
|
+
},
|
|
478
|
+
setRenderRequestQueuedForCurrentTurn: (next) => {
|
|
479
|
+
renderRequestQueuedForCurrentTurn = next;
|
|
480
|
+
},
|
|
481
|
+
setScheduleWaitStartMs: (next) => {
|
|
482
|
+
scheduleWaitStartMs = next;
|
|
483
|
+
},
|
|
484
|
+
setTheme: (next) => {
|
|
485
|
+
theme = next;
|
|
486
|
+
},
|
|
487
|
+
setThemeTransition: (next) => {
|
|
488
|
+
themeTransition = next;
|
|
489
|
+
},
|
|
490
|
+
setTopLevelViewError: (next) => {
|
|
491
|
+
topLevelViewError = next;
|
|
492
|
+
},
|
|
493
|
+
widgetRenderer,
|
|
494
|
+
});
|
|
495
|
+
tryRenderOnceImpl = renderLoop.tryRenderOnce;
|
|
496
|
+
const eventLoop = createEventLoop({
|
|
497
|
+
backend,
|
|
498
|
+
config,
|
|
499
|
+
doFatal,
|
|
500
|
+
emit,
|
|
501
|
+
emitFocusChangeIfNeeded,
|
|
502
|
+
enqueueWorkItem,
|
|
503
|
+
fatalNowOrEnqueue,
|
|
504
|
+
getAppUpdate: () => app.update,
|
|
505
|
+
getCommittedState: () => committedState,
|
|
506
|
+
getFramesInFlight: () => framesInFlight,
|
|
507
|
+
getInteractiveBudget: () => interactiveBudget,
|
|
508
|
+
getKeybindingState: () => keybindingState,
|
|
509
|
+
getKeybindingsEnabled: () => keybindingsEnabled,
|
|
510
|
+
getLastObservedSpinnerTickEventMs: () => lastObservedSpinnerTickEventMs,
|
|
511
|
+
getLastSpinnerRenderPerfMs: () => lastSpinnerRenderPerfMs,
|
|
512
|
+
getLastSpinnerRenderTickMs: () => lastSpinnerRenderTickMs,
|
|
513
|
+
getLifecycleBusy: () => lifecycleBusy,
|
|
514
|
+
getMode: () => mode,
|
|
515
|
+
getPollToken: () => pollToken,
|
|
516
|
+
getRenderRequestQueuedForCurrentTurn: () => renderRequestQueuedForCurrentTurn,
|
|
517
|
+
getRuntimeState: () => sm.state,
|
|
518
|
+
getTopLevelViewError: () => topLevelViewError,
|
|
519
|
+
getViewport: () => viewport,
|
|
520
|
+
keybindingHelpers,
|
|
521
|
+
markDirty,
|
|
522
|
+
noteBreadcrumbAction: runtimeBreadcrumbHelpers.noteBreadcrumbAction,
|
|
523
|
+
noteBreadcrumbConsumptionPath: runtimeBreadcrumbHelpers.noteBreadcrumbConsumptionPath,
|
|
524
|
+
noteBreadcrumbEvent: runtimeBreadcrumbHelpers.noteBreadcrumbEvent,
|
|
525
|
+
quitFromTopLevelViewError,
|
|
526
|
+
retryTopLevelViewError,
|
|
527
|
+
setCommittedState: (next) => {
|
|
528
|
+
committedState = next;
|
|
529
|
+
},
|
|
530
|
+
setFramesInFlight: (next) => {
|
|
531
|
+
framesInFlight = next;
|
|
532
|
+
},
|
|
533
|
+
setInCommit: (next) => {
|
|
534
|
+
inCommit = next;
|
|
535
|
+
},
|
|
536
|
+
setInteractiveBudget: (next) => {
|
|
537
|
+
interactiveBudget = next;
|
|
538
|
+
},
|
|
539
|
+
setKeybindingState: applyKeybindingState,
|
|
540
|
+
setLastObservedSpinnerTickEventMs: (next) => {
|
|
541
|
+
lastObservedSpinnerTickEventMs = next;
|
|
542
|
+
},
|
|
543
|
+
setLastSpinnerRenderPerfMs: (next) => {
|
|
544
|
+
lastSpinnerRenderPerfMs = next;
|
|
545
|
+
},
|
|
546
|
+
setLastSpinnerRenderTickMs: (next) => {
|
|
547
|
+
lastSpinnerRenderTickMs = next;
|
|
548
|
+
},
|
|
549
|
+
setRenderRequestQueuedForCurrentTurn: (next) => {
|
|
550
|
+
renderRequestQueuedForCurrentTurn = next;
|
|
551
|
+
},
|
|
552
|
+
setUserCommitScheduled: (next) => {
|
|
553
|
+
userCommitScheduled = next;
|
|
554
|
+
},
|
|
555
|
+
setViewport: (next) => {
|
|
556
|
+
viewport = next;
|
|
557
|
+
},
|
|
558
|
+
spinnerTickMinIntervalMs,
|
|
559
|
+
stopFromUnhandledQuitEvent,
|
|
560
|
+
timeUnwrap,
|
|
561
|
+
tryRenderOnce: () => tryRenderOnceImpl(),
|
|
562
|
+
updates,
|
|
563
|
+
widgetRenderer,
|
|
564
|
+
});
|
|
565
|
+
processTurnImpl = eventLoop.processTurn;
|
|
1364
566
|
const app = {
|
|
1365
567
|
view(fn) {
|
|
1366
|
-
assertOperational("view");
|
|
1367
|
-
assertLifecycleIdle("view");
|
|
568
|
+
guards.assertOperational("view");
|
|
569
|
+
guards.assertLifecycleIdle("view");
|
|
1368
570
|
sm.assertOneOf(["Created", "Stopped"], "view: must be Created or Stopped");
|
|
1369
|
-
assertNotReentrant("view");
|
|
571
|
+
guards.assertNotReentrant("view");
|
|
1370
572
|
if (routes !== undefined) {
|
|
1371
|
-
throwCode("ZRUI_MODE_CONFLICT", "view: routes are configured in createApp(); screen rendering is managed by router");
|
|
573
|
+
guards.throwCode("ZRUI_MODE_CONFLICT", "view: routes are configured in createApp(); screen rendering is managed by router");
|
|
1372
574
|
}
|
|
1373
575
|
if (mode === "raw")
|
|
1374
|
-
throwCode("ZRUI_MODE_CONFLICT", "view: draw mode already selected");
|
|
576
|
+
guards.throwCode("ZRUI_MODE_CONFLICT", "view: draw mode already selected");
|
|
1375
577
|
mode = "widget";
|
|
1376
578
|
viewFn = fn;
|
|
1377
579
|
},
|
|
1378
580
|
replaceView(fn) {
|
|
1379
|
-
assertOperational("replaceView");
|
|
1380
|
-
assertLifecycleIdle("replaceView");
|
|
1381
|
-
assertNotReentrant("replaceView");
|
|
581
|
+
guards.assertOperational("replaceView");
|
|
582
|
+
guards.assertLifecycleIdle("replaceView");
|
|
583
|
+
guards.assertNotReentrant("replaceView");
|
|
1382
584
|
if (routes !== undefined) {
|
|
1383
|
-
throwCode("ZRUI_MODE_CONFLICT", "replaceView: routes are configured in createApp(); screen rendering is managed by router");
|
|
585
|
+
guards.throwCode("ZRUI_MODE_CONFLICT", "replaceView: routes are configured in createApp(); screen rendering is managed by router");
|
|
1384
586
|
}
|
|
1385
587
|
if (mode === "raw") {
|
|
1386
|
-
throwCode("ZRUI_MODE_CONFLICT", "replaceView: draw mode already selected");
|
|
588
|
+
guards.throwCode("ZRUI_MODE_CONFLICT", "replaceView: draw mode already selected");
|
|
1387
589
|
}
|
|
1388
590
|
if (mode === null)
|
|
1389
591
|
mode = "widget";
|
|
@@ -1395,17 +597,18 @@ export function createApp(opts) {
|
|
|
1395
597
|
}
|
|
1396
598
|
},
|
|
1397
599
|
replaceRoutes(nextRoutes) {
|
|
1398
|
-
assertOperational("replaceRoutes");
|
|
1399
|
-
assertLifecycleIdle("replaceRoutes");
|
|
1400
|
-
assertNotReentrant("replaceRoutes");
|
|
1401
|
-
|
|
1402
|
-
|
|
600
|
+
guards.assertOperational("replaceRoutes");
|
|
601
|
+
guards.assertLifecycleIdle("replaceRoutes");
|
|
602
|
+
guards.assertNotReentrant("replaceRoutes");
|
|
603
|
+
const activeRouterIntegration = routerIntegration;
|
|
604
|
+
if (activeRouterIntegration === null || routes === undefined) {
|
|
605
|
+
throw new ZrUiError("ZRUI_MODE_CONFLICT", "replaceRoutes: app was created without routes; use replaceView for view-mode apps");
|
|
1403
606
|
}
|
|
1404
607
|
if (mode === "raw") {
|
|
1405
|
-
throwCode("ZRUI_MODE_CONFLICT", "replaceRoutes: draw mode already selected");
|
|
608
|
+
guards.throwCode("ZRUI_MODE_CONFLICT", "replaceRoutes: draw mode already selected");
|
|
1406
609
|
}
|
|
1407
|
-
const nextRouteKeybindings =
|
|
1408
|
-
replaceRouteBindings(nextRouteKeybindings);
|
|
610
|
+
const nextRouteKeybindings = activeRouterIntegration.replaceRoutes(nextRoutes);
|
|
611
|
+
keybindingHelpers.replaceRouteBindings(nextRouteKeybindings);
|
|
1409
612
|
topLevelViewError = null;
|
|
1410
613
|
if (sm.state === "Running") {
|
|
1411
614
|
widgetRenderer.forceFullRenderNextFrame();
|
|
@@ -1413,19 +616,19 @@ export function createApp(opts) {
|
|
|
1413
616
|
}
|
|
1414
617
|
},
|
|
1415
618
|
draw(fn) {
|
|
1416
|
-
assertOperational("draw");
|
|
1417
|
-
assertLifecycleIdle("draw");
|
|
619
|
+
guards.assertOperational("draw");
|
|
620
|
+
guards.assertLifecycleIdle("draw");
|
|
1418
621
|
sm.assertOneOf(["Created", "Stopped"], "draw: must be Created or Stopped");
|
|
1419
|
-
assertNotReentrant("draw");
|
|
622
|
+
guards.assertNotReentrant("draw");
|
|
1420
623
|
if (mode === "widget")
|
|
1421
|
-
throwCode("ZRUI_MODE_CONFLICT", "draw: view mode already selected");
|
|
624
|
+
guards.throwCode("ZRUI_MODE_CONFLICT", "draw: view mode already selected");
|
|
1422
625
|
mode = "raw";
|
|
1423
626
|
drawFn = fn;
|
|
1424
627
|
},
|
|
1425
628
|
onEvent(handler) {
|
|
1426
|
-
assertOperational("onEvent");
|
|
629
|
+
guards.assertOperational("onEvent");
|
|
1427
630
|
if (inCommit || inRender)
|
|
1428
|
-
throwCode("ZRUI_REENTRANT_CALL", "onEvent: re-entrant call");
|
|
631
|
+
guards.throwCode("ZRUI_REENTRANT_CALL", "onEvent: re-entrant call");
|
|
1429
632
|
const active = { value: true };
|
|
1430
633
|
handlers.push({ fn: handler, active });
|
|
1431
634
|
return () => {
|
|
@@ -1433,32 +636,34 @@ export function createApp(opts) {
|
|
|
1433
636
|
};
|
|
1434
637
|
},
|
|
1435
638
|
onFocusChange(handler) {
|
|
1436
|
-
assertOperational("onFocusChange");
|
|
639
|
+
guards.assertOperational("onFocusChange");
|
|
1437
640
|
if (inCommit || inRender) {
|
|
1438
|
-
throwCode("ZRUI_REENTRANT_CALL", "onFocusChange: re-entrant call");
|
|
641
|
+
guards.throwCode("ZRUI_REENTRANT_CALL", "onFocusChange: re-entrant call");
|
|
1439
642
|
}
|
|
1440
643
|
return focusDispatcher.register(handler);
|
|
1441
644
|
},
|
|
1442
645
|
update(updater) {
|
|
1443
|
-
assertOperational("update");
|
|
1444
|
-
assertLifecycleIdle("update");
|
|
646
|
+
guards.assertOperational("update");
|
|
647
|
+
guards.assertLifecycleIdle("update");
|
|
1445
648
|
if (inCommit)
|
|
1446
|
-
throwCode("ZRUI_REENTRANT_CALL", "update: called during commit");
|
|
1447
|
-
if (inRender)
|
|
1448
|
-
throwCode("ZRUI_UPDATE_DURING_RENDER", updateDuringRenderDetail("update"));
|
|
649
|
+
guards.throwCode("ZRUI_REENTRANT_CALL", "update: called during commit");
|
|
650
|
+
if (inRender) {
|
|
651
|
+
guards.throwCode("ZRUI_UPDATE_DURING_RENDER", guards.updateDuringRenderDetail("update"));
|
|
652
|
+
}
|
|
1449
653
|
updates.enqueue(updater);
|
|
1450
654
|
if (sm.state === "Running" && inEventHandlerDepth === 0 && !userCommitScheduled) {
|
|
1451
655
|
userCommitScheduled = true;
|
|
1452
|
-
|
|
656
|
+
enqueueWorkItem({ kind: "userCommit" });
|
|
1453
657
|
}
|
|
1454
658
|
},
|
|
1455
659
|
setTheme(next) {
|
|
1456
|
-
assertOperational("setTheme");
|
|
1457
|
-
assertLifecycleIdle("setTheme");
|
|
660
|
+
guards.assertOperational("setTheme");
|
|
661
|
+
guards.assertLifecycleIdle("setTheme");
|
|
1458
662
|
if (inCommit)
|
|
1459
|
-
throwCode("ZRUI_REENTRANT_CALL", "setTheme: called during commit");
|
|
1460
|
-
if (inRender)
|
|
1461
|
-
throwCode("ZRUI_UPDATE_DURING_RENDER", updateDuringRenderDetail("setTheme"));
|
|
663
|
+
guards.throwCode("ZRUI_REENTRANT_CALL", "setTheme: called during commit");
|
|
664
|
+
if (inRender) {
|
|
665
|
+
guards.throwCode("ZRUI_UPDATE_DURING_RENDER", guards.updateDuringRenderDetail("setTheme"));
|
|
666
|
+
}
|
|
1462
667
|
const nextTheme = compileTheme(next);
|
|
1463
668
|
if (nextTheme === themeTransition?.to)
|
|
1464
669
|
return;
|
|
@@ -1466,14 +671,14 @@ export function createApp(opts) {
|
|
|
1466
671
|
themeTransition = null;
|
|
1467
672
|
return;
|
|
1468
673
|
}
|
|
1469
|
-
beginThemeTransition(nextTheme);
|
|
674
|
+
renderLoop.beginThemeTransition(nextTheme);
|
|
1470
675
|
requestViewFromRenderer();
|
|
1471
676
|
},
|
|
1472
677
|
debugLayout(enabled) {
|
|
1473
|
-
assertOperational("debugLayout");
|
|
1474
|
-
assertLifecycleIdle("debugLayout");
|
|
678
|
+
guards.assertOperational("debugLayout");
|
|
679
|
+
guards.assertLifecycleIdle("debugLayout");
|
|
1475
680
|
if (mode === "raw") {
|
|
1476
|
-
throwCode("ZRUI_MODE_CONFLICT", "debugLayout: not available in draw mode");
|
|
681
|
+
guards.throwCode("ZRUI_MODE_CONFLICT", "debugLayout: not available in draw mode");
|
|
1477
682
|
}
|
|
1478
683
|
const next = enabled === undefined ? !debugLayoutEnabled : enabled === true;
|
|
1479
684
|
if (next === debugLayoutEnabled)
|
|
@@ -1483,23 +688,25 @@ export function createApp(opts) {
|
|
|
1483
688
|
return debugLayoutEnabled;
|
|
1484
689
|
},
|
|
1485
690
|
start() {
|
|
1486
|
-
assertOperational("start");
|
|
1487
|
-
assertNotReentrant("start");
|
|
691
|
+
guards.assertOperational("start");
|
|
692
|
+
guards.assertNotReentrant("start");
|
|
1488
693
|
sm.assertOneOf(["Created", "Stopped"], "start: must be Created or Stopped");
|
|
1489
694
|
if (mode === null)
|
|
1490
|
-
throwCode("ZRUI_NO_RENDER_MODE", "start: no render mode selected");
|
|
695
|
+
guards.throwCode("ZRUI_NO_RENDER_MODE", "start: no render mode selected");
|
|
1491
696
|
lifecycleBusy = "start";
|
|
1492
697
|
const startGeneration = ++lifecycleGeneration;
|
|
1493
|
-
let
|
|
698
|
+
let promise = null;
|
|
1494
699
|
try {
|
|
1495
|
-
|
|
700
|
+
promise = backend.start();
|
|
1496
701
|
}
|
|
1497
|
-
catch (
|
|
702
|
+
catch (error) {
|
|
1498
703
|
if (lifecycleGeneration === startGeneration)
|
|
1499
704
|
lifecycleBusy = null;
|
|
1500
|
-
throwCode("ZRUI_BACKEND_ERROR", `backend.start threw: ${describeThrown(
|
|
705
|
+
guards.throwCode("ZRUI_BACKEND_ERROR", `backend.start threw: ${describeThrown(error)}`);
|
|
1501
706
|
}
|
|
1502
|
-
|
|
707
|
+
if (promise === null)
|
|
708
|
+
throw new Error("start: backend.start did not return a promise");
|
|
709
|
+
return promise.then(async () => {
|
|
1503
710
|
try {
|
|
1504
711
|
backendStarted = true;
|
|
1505
712
|
if (lifecycleGeneration !== startGeneration) {
|
|
@@ -1517,27 +724,27 @@ export function createApp(opts) {
|
|
|
1517
724
|
sm.toRunning();
|
|
1518
725
|
markDirty(DIRTY_VIEW, false);
|
|
1519
726
|
pollToken++;
|
|
1520
|
-
void pollLoop(pollToken);
|
|
1521
|
-
|
|
727
|
+
void eventLoop.pollLoop(pollToken);
|
|
728
|
+
enqueueWorkItem({ kind: "kick" });
|
|
1522
729
|
}
|
|
1523
730
|
finally {
|
|
1524
731
|
if (lifecycleGeneration === startGeneration && lifecycleBusy === "start") {
|
|
1525
732
|
lifecycleBusy = null;
|
|
1526
733
|
}
|
|
1527
734
|
}
|
|
1528
|
-
}, (
|
|
735
|
+
}, (error) => {
|
|
1529
736
|
if (lifecycleGeneration !== startGeneration)
|
|
1530
737
|
return;
|
|
1531
738
|
lifecycleBusy = null;
|
|
1532
|
-
throw new ZrUiError("ZRUI_BACKEND_ERROR", `backend.start rejected: ${describeThrown(
|
|
739
|
+
throw new ZrUiError("ZRUI_BACKEND_ERROR", `backend.start rejected: ${describeThrown(error)}`);
|
|
1533
740
|
});
|
|
1534
741
|
},
|
|
1535
742
|
run() {
|
|
1536
|
-
assertOperational("run");
|
|
1537
|
-
assertNotReentrant("run");
|
|
743
|
+
guards.assertOperational("run");
|
|
744
|
+
guards.assertNotReentrant("run");
|
|
1538
745
|
sm.assertOneOf(["Created", "Stopped"], "run: must be Created or Stopped");
|
|
1539
746
|
if (mode === null)
|
|
1540
|
-
throwCode("ZRUI_NO_RENDER_MODE", "run: no render mode selected");
|
|
747
|
+
guards.throwCode("ZRUI_NO_RENDER_MODE", "run: no render mode selected");
|
|
1541
748
|
const proc = readProcessLike();
|
|
1542
749
|
let runSettle = null;
|
|
1543
750
|
const runController = createRunSignalController({
|
|
@@ -1575,42 +782,40 @@ export function createApp(opts) {
|
|
|
1575
782
|
try {
|
|
1576
783
|
startPromise = app.start();
|
|
1577
784
|
}
|
|
1578
|
-
catch (
|
|
785
|
+
catch (error) {
|
|
1579
786
|
runController.detach();
|
|
1580
|
-
throw
|
|
787
|
+
throw error;
|
|
1581
788
|
}
|
|
1582
789
|
return startPromise.then(() => {
|
|
1583
790
|
if (!runController.canRegisterSignals) {
|
|
1584
791
|
runController.settle();
|
|
1585
792
|
}
|
|
1586
793
|
return runController.promise;
|
|
1587
|
-
}, (
|
|
794
|
+
}, (error) => {
|
|
1588
795
|
runController.detach();
|
|
1589
|
-
throw
|
|
796
|
+
throw error;
|
|
1590
797
|
});
|
|
1591
798
|
},
|
|
1592
799
|
stop() {
|
|
1593
|
-
assertOperational("stop");
|
|
1594
|
-
assertNotReentrant("stop");
|
|
800
|
+
guards.assertOperational("stop");
|
|
801
|
+
guards.assertNotReentrant("stop");
|
|
1595
802
|
sm.assertOneOf(["Running"], "stop: must be Running");
|
|
1596
803
|
lifecycleBusy = "stop";
|
|
1597
804
|
const stopGeneration = ++lifecycleGeneration;
|
|
1598
|
-
// Stop polling immediately so in-flight pollEvents rejections from backend.stop()
|
|
1599
|
-
// are treated as part of shutdown (not a fatal backend error).
|
|
1600
805
|
pollToken++;
|
|
1601
|
-
// Clear any in-flight frames so a shutdown doesn't strand the app in a state
|
|
1602
|
-
// where a future start() cannot submit frames.
|
|
1603
806
|
framesInFlight = 0;
|
|
1604
|
-
let
|
|
807
|
+
let promise = null;
|
|
1605
808
|
try {
|
|
1606
|
-
|
|
809
|
+
promise = backend.stop();
|
|
1607
810
|
}
|
|
1608
|
-
catch (
|
|
811
|
+
catch (error) {
|
|
1609
812
|
if (lifecycleGeneration === stopGeneration)
|
|
1610
813
|
lifecycleBusy = null;
|
|
1611
|
-
throwCode("ZRUI_BACKEND_ERROR", `backend.stop threw: ${describeThrown(
|
|
814
|
+
guards.throwCode("ZRUI_BACKEND_ERROR", `backend.stop threw: ${describeThrown(error)}`);
|
|
1612
815
|
}
|
|
1613
|
-
|
|
816
|
+
if (promise === null)
|
|
817
|
+
throw new Error("stop: backend.stop did not return a promise");
|
|
818
|
+
return promise.then(() => {
|
|
1614
819
|
try {
|
|
1615
820
|
if (lifecycleGeneration !== stopGeneration)
|
|
1616
821
|
return;
|
|
@@ -1624,16 +829,16 @@ export function createApp(opts) {
|
|
|
1624
829
|
lifecycleBusy = null;
|
|
1625
830
|
}
|
|
1626
831
|
}
|
|
1627
|
-
}, (
|
|
832
|
+
}, (error) => {
|
|
1628
833
|
if (lifecycleGeneration !== stopGeneration)
|
|
1629
834
|
return;
|
|
1630
835
|
lifecycleBusy = null;
|
|
1631
|
-
throw new ZrUiError("ZRUI_BACKEND_ERROR", `backend.stop rejected: ${describeThrown(
|
|
836
|
+
throw new ZrUiError("ZRUI_BACKEND_ERROR", `backend.stop rejected: ${describeThrown(error)}`);
|
|
1632
837
|
});
|
|
1633
838
|
},
|
|
1634
839
|
dispose() {
|
|
1635
840
|
if (inCommit || inRender || inEventHandlerDepth > 0) {
|
|
1636
|
-
throwCode("ZRUI_REENTRANT_CALL", "dispose: re-entrant call");
|
|
841
|
+
guards.throwCode("ZRUI_REENTRANT_CALL", "dispose: re-entrant call");
|
|
1637
842
|
}
|
|
1638
843
|
const st0 = sm.state;
|
|
1639
844
|
if (st0 === "Disposed")
|
|
@@ -1665,22 +870,20 @@ export function createApp(opts) {
|
|
|
1665
870
|
}
|
|
1666
871
|
settleActiveRun?.();
|
|
1667
872
|
},
|
|
1668
|
-
/* --- Keybinding API --- */
|
|
1669
873
|
keys(bindings) {
|
|
1670
|
-
assertKeybindingMutationAllowed("keys");
|
|
1671
|
-
assertLifecycleIdle("keys");
|
|
1672
|
-
registerAppBindings(bindings);
|
|
874
|
+
guards.assertKeybindingMutationAllowed("keys");
|
|
875
|
+
guards.assertLifecycleIdle("keys");
|
|
876
|
+
keybindingHelpers.registerAppBindings(bindings);
|
|
1673
877
|
},
|
|
1674
878
|
modes(modes) {
|
|
1675
|
-
assertKeybindingMutationAllowed("modes");
|
|
1676
|
-
assertLifecycleIdle("modes");
|
|
1677
|
-
registerAppModes(modes);
|
|
879
|
+
guards.assertKeybindingMutationAllowed("modes");
|
|
880
|
+
guards.assertLifecycleIdle("modes");
|
|
881
|
+
keybindingHelpers.registerAppModes(modes);
|
|
1678
882
|
},
|
|
1679
883
|
setMode(modeName) {
|
|
1680
|
-
assertKeybindingMutationAllowed("setMode");
|
|
1681
|
-
assertLifecycleIdle("setMode");
|
|
1682
|
-
|
|
1683
|
-
keybindingsEnabled = computeKeybindingsEnabled(keybindingState);
|
|
884
|
+
guards.assertKeybindingMutationAllowed("setMode");
|
|
885
|
+
guards.assertLifecycleIdle("setMode");
|
|
886
|
+
applyKeybindingState(setMode(keybindingState, modeName));
|
|
1684
887
|
},
|
|
1685
888
|
getMode() {
|
|
1686
889
|
return getMode(keybindingState);
|
|
@@ -1703,8 +906,7 @@ export function createApp(opts) {
|
|
|
1703
906
|
};
|
|
1704
907
|
routeStateUpdater = app.update;
|
|
1705
908
|
if (routerIntegration) {
|
|
1706
|
-
|
|
1707
|
-
replaceRouteBindings(routeKeybindings);
|
|
909
|
+
keybindingHelpers.replaceRouteBindings(routerIntegration.routeKeybindings);
|
|
1708
910
|
}
|
|
1709
911
|
Object.defineProperty(app, APP_INTERNAL_REQUEST_VIEW_LAYOUT_MARKER, {
|
|
1710
912
|
value: () => {
|
|
@@ -1718,11 +920,7 @@ export function createApp(opts) {
|
|
|
1718
920
|
});
|
|
1719
921
|
Object.defineProperty(app, APP_INTERNAL_SET_RUNTIME_BREADCRUMB_HOOKS_MARKER, {
|
|
1720
922
|
value: (hooks) => {
|
|
1721
|
-
|
|
1722
|
-
typeof hooks?.onRender === "function" ? hooks.onRender : undefined;
|
|
1723
|
-
inspectorInternalOnLayout =
|
|
1724
|
-
typeof hooks?.onLayout === "function" ? hooks.onLayout : undefined;
|
|
1725
|
-
recomputeRuntimeBreadcrumbCollection();
|
|
923
|
+
runtimeBreadcrumbHelpers.setInspectorHooks(hooks);
|
|
1726
924
|
},
|
|
1727
925
|
enumerable: false,
|
|
1728
926
|
configurable: false,
|