@softov/scena 0.1.2
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/LICENSE +21 -0
- package/README.md +42 -0
- package/dist/controls/command.d.ts +18 -0
- package/dist/controls/command.js +150 -0
- package/dist/controls/events.d.ts +3 -0
- package/dist/controls/events.js +43 -0
- package/dist/controls/index.d.ts +10 -0
- package/dist/controls/index.js +10 -0
- package/dist/controls/keybinding.d.ts +9 -0
- package/dist/controls/keybinding.js +94 -0
- package/dist/controls/layout.d.ts +16 -0
- package/dist/controls/layout.js +201 -0
- package/dist/controls/manifest.d.ts +21 -0
- package/dist/controls/manifest.js +219 -0
- package/dist/controls/session.d.ts +16 -0
- package/dist/controls/session.js +95 -0
- package/dist/controls/shell.d.ts +8 -0
- package/dist/controls/shell.js +48 -0
- package/dist/controls/storage/layout-local.d.ts +6 -0
- package/dist/controls/storage/layout-local.js +47 -0
- package/dist/controls/storage/session-local.d.ts +5 -0
- package/dist/controls/storage/session-local.js +38 -0
- package/dist/controls/when.d.ts +7 -0
- package/dist/controls/when.js +226 -0
- package/dist/converters/a2ui-v0.10.d.ts +23 -0
- package/dist/converters/a2ui-v0.10.js +136 -0
- package/dist/converters/index.d.ts +1 -0
- package/dist/converters/index.js +1 -0
- package/dist/core/backends/modus-backend.d.ts +13 -0
- package/dist/core/backends/modus-backend.js +115 -0
- package/dist/core/backends/storage-backend.d.ts +7 -0
- package/dist/core/backends/storage-backend.js +73 -0
- package/dist/core/backends/yjs-backend.d.ts +3 -0
- package/dist/core/backends/yjs-backend.js +44 -0
- package/dist/core/binding-resolver.d.ts +7 -0
- package/dist/core/binding-resolver.js +106 -0
- package/dist/core/component-graph.d.ts +6 -0
- package/dist/core/component-graph.js +77 -0
- package/dist/core/component-registry.d.ts +7 -0
- package/dist/core/component-registry.js +87 -0
- package/dist/core/computed-dsl.d.ts +2 -0
- package/dist/core/computed-dsl.js +267 -0
- package/dist/core/converter-registry.d.ts +2 -0
- package/dist/core/converter-registry.js +37 -0
- package/dist/core/disposable.d.ts +3 -0
- package/dist/core/disposable.js +19 -0
- package/dist/core/dynamic-resolver.d.ts +12 -0
- package/dist/core/dynamic-resolver.js +134 -0
- package/dist/core/index.d.ts +44 -0
- package/dist/core/index.js +48 -0
- package/dist/core/label.d.ts +2 -0
- package/dist/core/label.js +14 -0
- package/dist/core/mount-menu.d.ts +2 -0
- package/dist/core/mount-menu.js +23 -0
- package/dist/core/mount-surface.d.ts +15 -0
- package/dist/core/mount-surface.js +214 -0
- package/dist/core/path-resolver.d.ts +14 -0
- package/dist/core/path-resolver.js +169 -0
- package/dist/core/permissions.d.ts +2 -0
- package/dist/core/permissions.js +50 -0
- package/dist/core/reactive-store.d.ts +17 -0
- package/dist/core/reactive-store.js +496 -0
- package/dist/core/scena.d.ts +2 -0
- package/dist/core/scena.js +115 -0
- package/dist/core/surface-presentation.d.ts +6 -0
- package/dist/core/surface-presentation.js +12 -0
- package/dist/i18n/i18n-backend.d.ts +2 -0
- package/dist/i18n/i18n-backend.js +35 -0
- package/dist/i18n/registry.d.ts +32 -0
- package/dist/i18n/registry.js +200 -0
- package/dist/porta/Limen.css +50 -0
- package/dist/porta/Limen.d.ts +16 -0
- package/dist/porta/Limen.js +14 -0
- package/dist/porta/PortaContext.d.ts +10 -0
- package/dist/porta/PortaContext.js +20 -0
- package/dist/porta/PortaLock.css +14 -0
- package/dist/porta/PortaLock.d.ts +10 -0
- package/dist/porta/PortaLock.js +13 -0
- package/dist/porta/SigillumGate.d.ts +8 -0
- package/dist/porta/SigillumGate.js +7 -0
- package/dist/porta/commands.d.ts +3 -0
- package/dist/porta/commands.js +125 -0
- package/dist/porta/index.d.ts +18 -0
- package/dist/porta/index.js +22 -0
- package/dist/porta/login-form.block.d.ts +2 -0
- package/dist/porta/login-form.block.js +7 -0
- package/dist/porta/match.d.ts +2 -0
- package/dist/porta/match.js +20 -0
- package/dist/porta/provider.d.ts +59 -0
- package/dist/porta/provider.js +1 -0
- package/dist/porta/providers/examples/google-oauth.d.ts +6 -0
- package/dist/porta/providers/examples/google-oauth.js +21 -0
- package/dist/porta/providers/examples/index.d.ts +6 -0
- package/dist/porta/providers/examples/index.js +3 -0
- package/dist/porta/providers/examples/magic-link.d.ts +8 -0
- package/dist/porta/providers/examples/magic-link.js +31 -0
- package/dist/porta/providers/examples/sms-otp.d.ts +9 -0
- package/dist/porta/providers/examples/sms-otp.js +31 -0
- package/dist/porta/providers/password.d.ts +12 -0
- package/dist/porta/providers/password.js +46 -0
- package/dist/porta/register-blocks.d.ts +3 -0
- package/dist/porta/register-blocks.js +31 -0
- package/dist/porta/register.d.ts +15 -0
- package/dist/porta/register.js +31 -0
- package/dist/porta/sigillum.d.ts +19 -0
- package/dist/porta/sigillum.js +41 -0
- package/dist/react/DefaultShell.d.ts +1 -0
- package/dist/react/DefaultShell.js +37 -0
- package/dist/react/Scena.d.ts +9 -0
- package/dist/react/Scena.js +20 -0
- package/dist/react/ScenaProvider.d.ts +8 -0
- package/dist/react/ScenaProvider.js +13 -0
- package/dist/react/ShellSplitter.d.ts +13 -0
- package/dist/react/ShellSplitter.js +88 -0
- package/dist/react/SurfaceArea.d.ts +13 -0
- package/dist/react/SurfaceArea.js +75 -0
- package/dist/react/Translate.d.ts +8 -0
- package/dist/react/Translate.js +10 -0
- package/dist/react/ViewMount.d.ts +12 -0
- package/dist/react/ViewMount.js +268 -0
- package/dist/react/core.d.ts +6 -0
- package/dist/react/core.js +10 -0
- package/dist/react/hooks/useCommand.d.ts +1 -0
- package/dist/react/hooks/useCommand.js +6 -0
- package/dist/react/hooks/useI18n.d.ts +9 -0
- package/dist/react/hooks/useI18n.js +16 -0
- package/dist/react/hooks/useLabel.d.ts +2 -0
- package/dist/react/hooks/useLabel.js +19 -0
- package/dist/react/hooks/useLayout.d.ts +2 -0
- package/dist/react/hooks/useLayout.js +10 -0
- package/dist/react/hooks/useMounts.d.ts +2 -0
- package/dist/react/hooks/useMounts.js +17 -0
- package/dist/react/hooks/useStore.d.ts +3 -0
- package/dist/react/hooks/useStore.js +40 -0
- package/dist/react/hooks/useStoreLabel.d.ts +3 -0
- package/dist/react/hooks/useStoreLabel.js +25 -0
- package/dist/react/hooks/useSurfacePresentation.d.ts +4 -0
- package/dist/react/hooks/useSurfacePresentation.js +16 -0
- package/dist/react/index.d.ts +20 -0
- package/dist/react/index.js +20 -0
- package/dist/react/mount-context.d.ts +8 -0
- package/dist/react/mount-context.js +20 -0
- package/dist/react/useSurfaceResize.d.ts +39 -0
- package/dist/react/useSurfaceResize.js +135 -0
- package/dist/runtime/args-schema.d.ts +11 -0
- package/dist/runtime/args-schema.js +45 -0
- package/dist/runtime/builtins.d.ts +3 -0
- package/dist/runtime/builtins.js +193 -0
- package/dist/runtime/client-handlers.d.ts +3 -0
- package/dist/runtime/client-handlers.js +19 -0
- package/dist/runtime/in-memory-socket.d.ts +7 -0
- package/dist/runtime/in-memory-socket.js +53 -0
- package/dist/runtime/index.d.ts +10 -0
- package/dist/runtime/index.js +7 -0
- package/dist/runtime/layout-commands.d.ts +5 -0
- package/dist/runtime/layout-commands.js +548 -0
- package/dist/runtime/opener-commands.d.ts +17 -0
- package/dist/runtime/opener-commands.js +96 -0
- package/dist/runtime/surface-bridge.d.ts +15 -0
- package/dist/runtime/surface-bridge.js +116 -0
- package/dist/styles/bar.css +155 -0
- package/dist/styles/base.css +285 -0
- package/dist/styles/geometry.css +32 -0
- package/dist/styles/index.d.ts +29 -0
- package/dist/styles/index.js +109 -0
- package/dist/styles/scrollbar.css +45 -0
- package/dist/styles/surface.css +179 -0
- package/dist/styles/theme.css +3 -0
- package/dist/styles/themes/dark.css +30 -0
- package/dist/styles/themes/light.css +1 -0
- package/dist/styles/themes/tokens.css +222 -0
- package/dist/types/binding-resolver.d.ts +30 -0
- package/dist/types/binding-resolver.js +1 -0
- package/dist/types/colors.d.ts +7 -0
- package/dist/types/colors.js +59 -0
- package/dist/types/command.d.ts +62 -0
- package/dist/types/command.js +1 -0
- package/dist/types/component-graph.d.ts +72 -0
- package/dist/types/component-graph.js +40 -0
- package/dist/types/component-registry.d.ts +70 -0
- package/dist/types/component-registry.js +1 -0
- package/dist/types/converter-registry.d.ts +17 -0
- package/dist/types/converter-registry.js +1 -0
- package/dist/types/disposable.d.ts +3 -0
- package/dist/types/disposable.js +1 -0
- package/dist/types/events.d.ts +75 -0
- package/dist/types/events.js +1 -0
- package/dist/types/host.d.ts +50 -0
- package/dist/types/host.js +1 -0
- package/dist/types/index.d.ts +23 -0
- package/dist/types/index.js +23 -0
- package/dist/types/keybinding.d.ts +36 -0
- package/dist/types/keybinding.js +1 -0
- package/dist/types/label.d.ts +9 -0
- package/dist/types/label.js +1 -0
- package/dist/types/layout.d.ts +117 -0
- package/dist/types/layout.js +1 -0
- package/dist/types/manifest.d.ts +152 -0
- package/dist/types/manifest.js +1 -0
- package/dist/types/mount-menu.d.ts +8 -0
- package/dist/types/mount-menu.js +1 -0
- package/dist/types/mount-surface.d.ts +87 -0
- package/dist/types/mount-surface.js +1 -0
- package/dist/types/permissions.d.ts +18 -0
- package/dist/types/permissions.js +1 -0
- package/dist/types/query.d.ts +34 -0
- package/dist/types/query.js +7 -0
- package/dist/types/reactive-store.d.ts +39 -0
- package/dist/types/reactive-store.js +1 -0
- package/dist/types/scena.d.ts +53 -0
- package/dist/types/scena.js +1 -0
- package/dist/types/scope-backend.d.ts +16 -0
- package/dist/types/scope-backend.js +1 -0
- package/dist/types/session.d.ts +39 -0
- package/dist/types/session.js +1 -0
- package/dist/types/shell.d.ts +21 -0
- package/dist/types/shell.js +1 -0
- package/dist/types/when.d.ts +10 -0
- package/dist/types/when.js +1 -0
- package/dist/ui/_utils.d.ts +8 -0
- package/dist/ui/_utils.js +53 -0
- package/dist/ui/campus/CampusMappa.d.ts +18 -0
- package/dist/ui/campus/CampusMappa.js +106 -0
- package/dist/ui/campus/CampusNodus.d.ts +25 -0
- package/dist/ui/campus/CampusNodus.js +211 -0
- package/dist/ui/campus/CampusStratum.d.ts +13 -0
- package/dist/ui/campus/CampusStratum.js +20 -0
- package/dist/ui/campus/CampusVelum.d.ts +7 -0
- package/dist/ui/campus/CampusVelum.js +12 -0
- package/dist/ui/campus/CampusView.d.ts +27 -0
- package/dist/ui/campus/CampusView.js +420 -0
- package/dist/ui/campus/context.d.ts +13 -0
- package/dist/ui/campus/context.js +12 -0
- package/dist/ui/campus/index.d.ts +14 -0
- package/dist/ui/campus/index.js +11 -0
- package/dist/ui/campus/stratum-context.d.ts +2 -0
- package/dist/ui/campus/stratum-context.js +10 -0
- package/dist/ui/campus/types.d.ts +47 -0
- package/dist/ui/campus/types.js +4 -0
- package/dist/ui/chart/BarChart.css +53 -0
- package/dist/ui/chart/BarChart.d.ts +24 -0
- package/dist/ui/chart/BarChart.js +32 -0
- package/dist/ui/chart/Donut.css +15 -0
- package/dist/ui/chart/Donut.d.ts +15 -0
- package/dist/ui/chart/Donut.js +18 -0
- package/dist/ui/chart/LineChart.css +8 -0
- package/dist/ui/chart/LineChart.d.ts +24 -0
- package/dist/ui/chart/LineChart.js +29 -0
- package/dist/ui/chart/catalog.d.ts +2 -0
- package/dist/ui/chart/catalog.js +7 -0
- package/dist/ui/chart/index.d.ts +6 -0
- package/dist/ui/chart/index.js +4 -0
- package/dist/ui/control/Button.css +7 -0
- package/dist/ui/control/Button.d.ts +17 -0
- package/dist/ui/control/Button.js +17 -0
- package/dist/ui/control/CheckBox.css +16 -0
- package/dist/ui/control/CheckBox.d.ts +15 -0
- package/dist/ui/control/CheckBox.js +23 -0
- package/dist/ui/control/Checks.css +6 -0
- package/dist/ui/control/Checks.d.ts +9 -0
- package/dist/ui/control/Checks.js +10 -0
- package/dist/ui/control/ChoicePicker.css +16 -0
- package/dist/ui/control/ChoicePicker.d.ts +25 -0
- package/dist/ui/control/ChoicePicker.js +38 -0
- package/dist/ui/control/DateTimeInput.css +1 -0
- package/dist/ui/control/DateTimeInput.d.ts +22 -0
- package/dist/ui/control/DateTimeInput.js +41 -0
- package/dist/ui/control/LocaleToggle.css +38 -0
- package/dist/ui/control/LocaleToggle.d.ts +11 -0
- package/dist/ui/control/LocaleToggle.js +23 -0
- package/dist/ui/control/ReloadButton.d.ts +6 -0
- package/dist/ui/control/ReloadButton.js +9 -0
- package/dist/ui/control/Slider.css +2 -0
- package/dist/ui/control/Slider.d.ts +21 -0
- package/dist/ui/control/Slider.js +37 -0
- package/dist/ui/control/TextField.css +0 -0
- package/dist/ui/control/TextField.d.ts +25 -0
- package/dist/ui/control/TextField.js +36 -0
- package/dist/ui/control/_field-parts.css +102 -0
- package/dist/ui/control/_field-parts.d.ts +15 -0
- package/dist/ui/control/_field-parts.js +17 -0
- package/dist/ui/control/catalog.d.ts +2 -0
- package/dist/ui/control/catalog.js +10 -0
- package/dist/ui/control/index.d.ts +13 -0
- package/dist/ui/control/index.js +11 -0
- package/dist/ui/data/DataTable.d.ts +19 -0
- package/dist/ui/data/DataTable.js +20 -0
- package/dist/ui/data/Filter.d.ts +13 -0
- package/dist/ui/data/Filter.js +56 -0
- package/dist/ui/data/List.css +16 -0
- package/dist/ui/data/List.d.ts +15 -0
- package/dist/ui/data/List.js +18 -0
- package/dist/ui/data/Listable.css +245 -0
- package/dist/ui/data/Listable.d.ts +60 -0
- package/dist/ui/data/Listable.js +209 -0
- package/dist/ui/data/Pagination.d.ts +5 -0
- package/dist/ui/data/Pagination.js +43 -0
- package/dist/ui/data/Tree.css +118 -0
- package/dist/ui/data/Tree.d.ts +45 -0
- package/dist/ui/data/Tree.js +170 -0
- package/dist/ui/data/catalog.d.ts +2 -0
- package/dist/ui/data/catalog.js +7 -0
- package/dist/ui/data/index.d.ts +11 -0
- package/dist/ui/data/index.js +7 -0
- package/dist/ui/display/AddPlaceholder.d.ts +6 -0
- package/dist/ui/display/AddPlaceholder.js +24 -0
- package/dist/ui/display/Alert.css +42 -0
- package/dist/ui/display/Alert.d.ts +14 -0
- package/dist/ui/display/Alert.js +13 -0
- package/dist/ui/display/Avatar.css +19 -0
- package/dist/ui/display/Avatar.d.ts +16 -0
- package/dist/ui/display/Avatar.js +52 -0
- package/dist/ui/display/Badge.css +26 -0
- package/dist/ui/display/Badge.d.ts +16 -0
- package/dist/ui/display/Badge.js +9 -0
- package/dist/ui/display/Card.css +15 -0
- package/dist/ui/display/Card.d.ts +11 -0
- package/dist/ui/display/Card.js +8 -0
- package/dist/ui/display/DetailContainer.d.ts +6 -0
- package/dist/ui/display/DetailContainer.js +7 -0
- package/dist/ui/display/DetailHeader.css +53 -0
- package/dist/ui/display/DetailHeader.d.ts +22 -0
- package/dist/ui/display/DetailHeader.js +8 -0
- package/dist/ui/display/DetailList.css +26 -0
- package/dist/ui/display/DetailList.d.ts +13 -0
- package/dist/ui/display/DetailList.js +5 -0
- package/dist/ui/display/DetailNotFound.d.ts +17 -0
- package/dist/ui/display/DetailNotFound.js +39 -0
- package/dist/ui/display/Divider.css +6 -0
- package/dist/ui/display/Divider.d.ts +8 -0
- package/dist/ui/display/Divider.js +16 -0
- package/dist/ui/display/Divisor.css +30 -0
- package/dist/ui/display/Divisor.d.ts +11 -0
- package/dist/ui/display/Divisor.js +43 -0
- package/dist/ui/display/Icon.css +10 -0
- package/dist/ui/display/Icon.d.ts +14 -0
- package/dist/ui/display/Icon.js +144 -0
- package/dist/ui/display/Image.css +1 -0
- package/dist/ui/display/Image.d.ts +16 -0
- package/dist/ui/display/Image.js +19 -0
- package/dist/ui/display/Markdown.css +38 -0
- package/dist/ui/display/Markdown.d.ts +8 -0
- package/dist/ui/display/Markdown.js +140 -0
- package/dist/ui/display/Progress.css +39 -0
- package/dist/ui/display/Progress.d.ts +15 -0
- package/dist/ui/display/Progress.js +17 -0
- package/dist/ui/display/SectionTitle.d.ts +6 -0
- package/dist/ui/display/SectionTitle.js +6 -0
- package/dist/ui/display/Skeleton.css +20 -0
- package/dist/ui/display/Skeleton.d.ts +11 -0
- package/dist/ui/display/Skeleton.js +11 -0
- package/dist/ui/display/Spinner.css +20 -0
- package/dist/ui/display/Spinner.d.ts +12 -0
- package/dist/ui/display/Spinner.js +12 -0
- package/dist/ui/display/Svg.d.ts +13 -0
- package/dist/ui/display/Svg.js +17 -0
- package/dist/ui/display/Text.css +20 -0
- package/dist/ui/display/Text.d.ts +13 -0
- package/dist/ui/display/Text.js +15 -0
- package/dist/ui/display/catalog.d.ts +2 -0
- package/dist/ui/display/catalog.js +12 -0
- package/dist/ui/display/index.d.ts +22 -0
- package/dist/ui/display/index.js +14 -0
- package/dist/ui/embed/HtmlEmbed.d.ts +12 -0
- package/dist/ui/embed/HtmlEmbed.js +16 -0
- package/dist/ui/embed/index.d.ts +2 -0
- package/dist/ui/embed/index.js +1 -0
- package/dist/ui/forms/DangerZone.d.ts +8 -0
- package/dist/ui/forms/DangerZone.js +31 -0
- package/dist/ui/forms/Field.css +15 -0
- package/dist/ui/forms/Field.d.ts +28 -0
- package/dist/ui/forms/Field.js +25 -0
- package/dist/ui/forms/FieldGroup.css +15 -0
- package/dist/ui/forms/FieldGroup.d.ts +7 -0
- package/dist/ui/forms/FieldGroup.js +7 -0
- package/dist/ui/forms/Form.css +14 -0
- package/dist/ui/forms/Form.d.ts +18 -0
- package/dist/ui/forms/Form.js +17 -0
- package/dist/ui/forms/FormActions.css +11 -0
- package/dist/ui/forms/FormActions.d.ts +18 -0
- package/dist/ui/forms/FormActions.js +10 -0
- package/dist/ui/forms/FormSection.css +19 -0
- package/dist/ui/forms/FormSection.d.ts +7 -0
- package/dist/ui/forms/FormSection.js +7 -0
- package/dist/ui/forms/LoginForm.css +105 -0
- package/dist/ui/forms/LoginForm.d.ts +12 -0
- package/dist/ui/forms/LoginForm.js +137 -0
- package/dist/ui/forms/SchemaForm.css +81 -0
- package/dist/ui/forms/SchemaForm.d.ts +26 -0
- package/dist/ui/forms/SchemaForm.js +186 -0
- package/dist/ui/forms/SettingsContainer.d.ts +8 -0
- package/dist/ui/forms/SettingsContainer.js +8 -0
- package/dist/ui/forms/catalog.d.ts +2 -0
- package/dist/ui/forms/catalog.js +11 -0
- package/dist/ui/forms/index.d.ts +19 -0
- package/dist/ui/forms/index.js +12 -0
- package/dist/ui/forms/schema-walk.d.ts +50 -0
- package/dist/ui/forms/schema-walk.js +116 -0
- package/dist/ui/index.d.ts +58 -0
- package/dist/ui/index.js +63 -0
- package/dist/ui/layout/BarLayout.d.ts +2 -0
- package/dist/ui/layout/BarLayout.js +18 -0
- package/dist/ui/layout/Column.css +6 -0
- package/dist/ui/layout/Column.d.ts +12 -0
- package/dist/ui/layout/Column.js +13 -0
- package/dist/ui/layout/FloatingLayout.d.ts +2 -0
- package/dist/ui/layout/FloatingLayout.js +12 -0
- package/dist/ui/layout/Grid.css +5 -0
- package/dist/ui/layout/Grid.d.ts +13 -0
- package/dist/ui/layout/Grid.js +19 -0
- package/dist/ui/layout/InlineLayout.d.ts +2 -0
- package/dist/ui/layout/InlineLayout.js +13 -0
- package/dist/ui/layout/MountTitle.d.ts +12 -0
- package/dist/ui/layout/MountTitle.js +42 -0
- package/dist/ui/layout/RailLayout.d.ts +2 -0
- package/dist/ui/layout/RailLayout.js +38 -0
- package/dist/ui/layout/Row.css +16 -0
- package/dist/ui/layout/Row.d.ts +13 -0
- package/dist/ui/layout/Row.js +21 -0
- package/dist/ui/layout/SectionHeader.d.ts +10 -0
- package/dist/ui/layout/SectionHeader.js +65 -0
- package/dist/ui/layout/SingleLayout.d.ts +2 -0
- package/dist/ui/layout/SingleLayout.js +25 -0
- package/dist/ui/layout/SpatialCard.d.ts +9 -0
- package/dist/ui/layout/SpatialCard.js +30 -0
- package/dist/ui/layout/SpatialLayout.d.ts +2 -0
- package/dist/ui/layout/SpatialLayout.js +446 -0
- package/dist/ui/layout/SplitLayout.d.ts +2 -0
- package/dist/ui/layout/SplitLayout.js +159 -0
- package/dist/ui/layout/Splitter.d.ts +12 -0
- package/dist/ui/layout/Splitter.js +64 -0
- package/dist/ui/layout/StackHeader.d.ts +9 -0
- package/dist/ui/layout/StackHeader.js +87 -0
- package/dist/ui/layout/StackLayout.d.ts +2 -0
- package/dist/ui/layout/StackLayout.js +137 -0
- package/dist/ui/layout/TabLayout.d.ts +2 -0
- package/dist/ui/layout/TabLayout.js +145 -0
- package/dist/ui/layout/TabPanelLayout.d.ts +20 -0
- package/dist/ui/layout/TabPanelLayout.js +615 -0
- package/dist/ui/layout/catalog.d.ts +2 -0
- package/dist/ui/layout/catalog.js +10 -0
- package/dist/ui/layout/index.d.ts +17 -0
- package/dist/ui/layout/index.js +17 -0
- package/dist/ui/layout/register.d.ts +3 -0
- package/dist/ui/layout/register.js +25 -0
- package/dist/ui/media/Audio.css +1 -0
- package/dist/ui/media/Audio.d.ts +12 -0
- package/dist/ui/media/Audio.js +8 -0
- package/dist/ui/media/Video.css +1 -0
- package/dist/ui/media/Video.d.ts +16 -0
- package/dist/ui/media/Video.js +9 -0
- package/dist/ui/media/catalog.d.ts +2 -0
- package/dist/ui/media/catalog.js +5 -0
- package/dist/ui/media/index.d.ts +2 -0
- package/dist/ui/media/index.js +3 -0
- package/dist/ui/menu/ActionList.css +262 -0
- package/dist/ui/menu/ActionList.d.ts +19 -0
- package/dist/ui/menu/ActionList.js +369 -0
- package/dist/ui/menu/ContainerMenu.d.ts +13 -0
- package/dist/ui/menu/ContainerMenu.js +57 -0
- package/dist/ui/menu/ContextMenu.d.ts +27 -0
- package/dist/ui/menu/ContextMenu.js +93 -0
- package/dist/ui/menu/index.d.ts +10 -0
- package/dist/ui/menu/index.js +7 -0
- package/dist/ui/menu/token.d.ts +22 -0
- package/dist/ui/menu/token.js +145 -0
- package/dist/ui/menu/useChatPicker.d.ts +24 -0
- package/dist/ui/menu/useChatPicker.js +210 -0
- package/dist/ui/navigation/Breadcrumb.css +30 -0
- package/dist/ui/navigation/Breadcrumb.d.ts +14 -0
- package/dist/ui/navigation/Breadcrumb.js +12 -0
- package/dist/ui/navigation/Link.css +13 -0
- package/dist/ui/navigation/Link.d.ts +18 -0
- package/dist/ui/navigation/Link.js +43 -0
- package/dist/ui/navigation/Tabs.css +39 -0
- package/dist/ui/navigation/Tabs.d.ts +18 -0
- package/dist/ui/navigation/Tabs.js +55 -0
- package/dist/ui/navigation/Toolbar.css +25 -0
- package/dist/ui/navigation/Toolbar.d.ts +12 -0
- package/dist/ui/navigation/Toolbar.js +21 -0
- package/dist/ui/navigation/catalog.d.ts +2 -0
- package/dist/ui/navigation/catalog.js +5 -0
- package/dist/ui/navigation/index.d.ts +8 -0
- package/dist/ui/navigation/index.js +5 -0
- package/dist/ui/overlay/Modal.css +35 -0
- package/dist/ui/overlay/Modal.d.ts +12 -0
- package/dist/ui/overlay/Modal.js +57 -0
- package/dist/ui/overlay/Popup.d.ts +12 -0
- package/dist/ui/overlay/Popup.js +74 -0
- package/dist/ui/overlay/catalog.d.ts +2 -0
- package/dist/ui/overlay/catalog.js +4 -0
- package/dist/ui/overlay/index.d.ts +1 -0
- package/dist/ui/overlay/index.js +2 -0
- package/dist/ui/register.d.ts +6 -0
- package/dist/ui/register.js +26 -0
- package/package.json +115 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Luiz Fernando Softov
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# @softov/scena
|
|
2
|
+
|
|
3
|
+
Graph-mounted reactive UI runtime.
|
|
4
|
+
|
|
5
|
+
Scena renders UI from data, not from hand-written component trees.
|
|
6
|
+
A page is a graph of component nodes held in a layered reactive store; components resolve through a late-binding registry and mount lazily.
|
|
7
|
+
|
|
8
|
+
> Status: pre-1.0. The public surface is still moving.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pnpm add @softov/scena
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
React is an optional peer dependency - the core runtime and store work without it:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pnpm add react react-dom
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Entry points
|
|
23
|
+
|
|
24
|
+
| Import | Contents |
|
|
25
|
+
| --- | --- |
|
|
26
|
+
| `@softov/scena` | Core runtime and reactive store |
|
|
27
|
+
| `@softov/scena/types` | Contracts and type definitions |
|
|
28
|
+
| `@softov/scena/react` | React bindings (full barrel) |
|
|
29
|
+
| `@softov/scena/react/core` | Narrow boot entry - provider and hook only |
|
|
30
|
+
| `@softov/scena/ui` | All UI components |
|
|
31
|
+
| `@softov/scena/ui/builtins` | Registry catalog, no component code |
|
|
32
|
+
| `@softov/scena/ui/<section>` | One section: `forms`, `layout`, `display`, `control`, `data`, `navigation`, `media`, `overlay`, `chart`, `menu`, `campus` |
|
|
33
|
+
| `@softov/scena/styles` | Stylesheets and themes |
|
|
34
|
+
| `@softov/scena/runtime` | Runtime helpers |
|
|
35
|
+
| `@softov/scena/porta` | Auth gate - login UI and provider registry |
|
|
36
|
+
|
|
37
|
+
Prefer the narrow entries (`/react/core`, `/ui/builtins`, `/ui/<section>`) over the
|
|
38
|
+
barrels in application code - the barrels pull in every component.
|
|
39
|
+
|
|
40
|
+
## License
|
|
41
|
+
|
|
42
|
+
MIT
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { EventBus } from '../types/events.js';
|
|
2
|
+
import type { ReactiveStore, SocketBridge } from '../types/reactive-store.js';
|
|
3
|
+
import type { MountSurfaceRegistry } from '../types/mount-surface.js';
|
|
4
|
+
import type { CommandRegistry } from '../types/command.js';
|
|
5
|
+
import type { WhenEngine } from '../types/when.js';
|
|
6
|
+
import type { Scena } from '../types/scena.js';
|
|
7
|
+
import type { KeybindingRegistry } from '../types/keybinding.js';
|
|
8
|
+
interface Deps {
|
|
9
|
+
events: EventBus;
|
|
10
|
+
store: ReactiveStore;
|
|
11
|
+
surfaces: MountSurfaceRegistry;
|
|
12
|
+
when: WhenEngine;
|
|
13
|
+
socket?: SocketBridge;
|
|
14
|
+
keybindings: KeybindingRegistry;
|
|
15
|
+
getScena: () => Scena;
|
|
16
|
+
}
|
|
17
|
+
export declare function createCommandRegistry(deps: Deps): CommandRegistry;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { disposableFrom } from '../core/disposable.js';
|
|
2
|
+
import { validateArgs } from '../runtime/args-schema.js';
|
|
3
|
+
export function createCommandRegistry(deps) {
|
|
4
|
+
const { events, store, surfaces, when, socket, keybindings, getScena } = deps;
|
|
5
|
+
const commands = new Map();
|
|
6
|
+
function announce() {
|
|
7
|
+
events.emit('scena:registry:changed', { registry: 'commands' });
|
|
8
|
+
}
|
|
9
|
+
function dispatchModeOf(cmd) {
|
|
10
|
+
if (cmd.dispatch)
|
|
11
|
+
return cmd.dispatch;
|
|
12
|
+
return cmd.run ? 'client' : 'remote';
|
|
13
|
+
}
|
|
14
|
+
function contextFor(source, opts) {
|
|
15
|
+
const scena = getScena();
|
|
16
|
+
return {
|
|
17
|
+
scena,
|
|
18
|
+
store,
|
|
19
|
+
surfaces,
|
|
20
|
+
commands: scena.commands,
|
|
21
|
+
events,
|
|
22
|
+
source,
|
|
23
|
+
dataContext: opts?.dataContext,
|
|
24
|
+
host: opts?.host,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
async function runRemote(id, args) {
|
|
28
|
+
if (!socket) {
|
|
29
|
+
throw new Error(`Cannot execute remote command "${id}": no socket bridge configured. ` +
|
|
30
|
+
`Pass a SocketBridge via createScena options or provide a local 'run' on the command.`);
|
|
31
|
+
}
|
|
32
|
+
socket.emit('command:execute', { commandId: id, args: [args] });
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
async function executeFrom(source, id, args, opts) {
|
|
36
|
+
const cmd = commands.get(id);
|
|
37
|
+
if (!cmd)
|
|
38
|
+
throw new Error(`Command "${id}" not registered`);
|
|
39
|
+
// NOTE: cmd.when is intentionally NOT gated here. `when` filters menu
|
|
40
|
+
// visibility (via commands.list({enabled:true})), not invocability. A
|
|
41
|
+
// command shown as enabled and then becoming disabled mid-dispatch is
|
|
42
|
+
// a logic error in the caller — the run() body is responsible for any
|
|
43
|
+
// last-mile guard it actually needs.
|
|
44
|
+
let normalizedArgs = args;
|
|
45
|
+
if (cmd.args) {
|
|
46
|
+
const result = validateArgs(cmd.args, args);
|
|
47
|
+
if (!result.ok) {
|
|
48
|
+
throw new Error(`Command "${id}" args invalid: ${result.errors.join('; ')}`);
|
|
49
|
+
}
|
|
50
|
+
normalizedArgs = result.value;
|
|
51
|
+
}
|
|
52
|
+
const mode = dispatchModeOf(cmd);
|
|
53
|
+
if (mode === 'remote')
|
|
54
|
+
return runRemote(id, normalizedArgs);
|
|
55
|
+
if (mode === 'either' && !cmd.run)
|
|
56
|
+
return runRemote(id, normalizedArgs);
|
|
57
|
+
if (!cmd.run)
|
|
58
|
+
throw new Error(`Command "${id}" has no local 'run'`);
|
|
59
|
+
return cmd.run(contextFor(source, { ...opts, source }), normalizedArgs);
|
|
60
|
+
}
|
|
61
|
+
// Title resolver for q matching — dynamic titles get a minimal stub ctx.
|
|
62
|
+
function resolveTitle(cmd) {
|
|
63
|
+
if (typeof cmd.title === 'string')
|
|
64
|
+
return cmd.title;
|
|
65
|
+
try {
|
|
66
|
+
return cmd.title(contextFor('programmatic'));
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return cmd.id;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function matchesQuery(cmd, q) {
|
|
73
|
+
if (!q)
|
|
74
|
+
return true;
|
|
75
|
+
const needle = q.toLowerCase();
|
|
76
|
+
if (cmd.id.toLowerCase().includes(needle))
|
|
77
|
+
return true;
|
|
78
|
+
if (resolveTitle(cmd).toLowerCase().includes(needle))
|
|
79
|
+
return true;
|
|
80
|
+
if (cmd.keywords?.some((k) => k.toLowerCase().includes(needle)))
|
|
81
|
+
return true;
|
|
82
|
+
const shortcuts = Array.isArray(cmd.shortcut)
|
|
83
|
+
? cmd.shortcut
|
|
84
|
+
: cmd.shortcut !== undefined
|
|
85
|
+
? [cmd.shortcut]
|
|
86
|
+
: [];
|
|
87
|
+
if (shortcuts.some((s) => s.toLowerCase().includes(needle)))
|
|
88
|
+
return true;
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
function list(filter) {
|
|
92
|
+
let out = [...commands.values()];
|
|
93
|
+
if (filter?.category)
|
|
94
|
+
out = out.filter((c) => c.category === filter.category);
|
|
95
|
+
if (filter?.slot) {
|
|
96
|
+
const slots = Array.isArray(filter.slot) ? filter.slot : [filter.slot];
|
|
97
|
+
// commands.values() iteration order is insertion order, so the array
|
|
98
|
+
// is naturally deduplicated by id even when a command lists multiple
|
|
99
|
+
// matching slots (no need to track seen ids here).
|
|
100
|
+
out = out.filter((c) => c.slots?.some((s) => slots.includes(s)));
|
|
101
|
+
}
|
|
102
|
+
if (filter?.enabled !== undefined) {
|
|
103
|
+
out = out.filter((c) => {
|
|
104
|
+
const enabled = c.when === undefined || when.evaluate(c.when);
|
|
105
|
+
return enabled === filter.enabled;
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
if (filter?.q) {
|
|
109
|
+
const q = filter.q;
|
|
110
|
+
out = out.filter((c) => matchesQuery(c, q));
|
|
111
|
+
}
|
|
112
|
+
return out;
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
register(cmd) {
|
|
116
|
+
commands.set(cmd.id, cmd);
|
|
117
|
+
const kbDisposables = [];
|
|
118
|
+
if (cmd.keys !== undefined) {
|
|
119
|
+
const keysList = Array.isArray(cmd.keys) ? cmd.keys : [cmd.keys];
|
|
120
|
+
for (const keys of keysList) {
|
|
121
|
+
kbDisposables.push(keybindings.register({ keys, commandId: cmd.id, when: cmd.when }));
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
announce();
|
|
125
|
+
return disposableFrom(() => {
|
|
126
|
+
if (commands.get(cmd.id) === cmd) {
|
|
127
|
+
commands.delete(cmd.id);
|
|
128
|
+
for (const d of kbDisposables)
|
|
129
|
+
d.dispose();
|
|
130
|
+
announce();
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
},
|
|
134
|
+
unregister(id) {
|
|
135
|
+
if (commands.delete(id))
|
|
136
|
+
announce();
|
|
137
|
+
},
|
|
138
|
+
get(id) {
|
|
139
|
+
return commands.get(id);
|
|
140
|
+
},
|
|
141
|
+
list,
|
|
142
|
+
listEnabled(filter) {
|
|
143
|
+
return list({ ...filter, enabled: true });
|
|
144
|
+
},
|
|
145
|
+
execute(id, args, opts) {
|
|
146
|
+
return executeFrom(opts?.source ?? 'programmatic', id, args, opts);
|
|
147
|
+
},
|
|
148
|
+
executeFrom,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { disposableFrom } from '../core/disposable.js';
|
|
2
|
+
export function createEventBus() {
|
|
3
|
+
const subs = new Map();
|
|
4
|
+
function emit(event, payload) {
|
|
5
|
+
const set = subs.get(event);
|
|
6
|
+
if (!set || set.size === 0)
|
|
7
|
+
return;
|
|
8
|
+
for (const fn of [...set]) {
|
|
9
|
+
try {
|
|
10
|
+
fn(payload);
|
|
11
|
+
}
|
|
12
|
+
catch (err) {
|
|
13
|
+
console.error(`[scena.events] listener for "${event}" threw:`, err);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function on(event, fn) {
|
|
18
|
+
let set = subs.get(event);
|
|
19
|
+
if (!set) {
|
|
20
|
+
set = new Set();
|
|
21
|
+
subs.set(event, set);
|
|
22
|
+
}
|
|
23
|
+
set.add(fn);
|
|
24
|
+
return disposableFrom(() => {
|
|
25
|
+
set.delete(fn);
|
|
26
|
+
if (set.size === 0)
|
|
27
|
+
subs.delete(event);
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
function off(event, fn) {
|
|
31
|
+
const set = subs.get(event);
|
|
32
|
+
if (!set)
|
|
33
|
+
return;
|
|
34
|
+
set.delete(fn);
|
|
35
|
+
if (set.size === 0)
|
|
36
|
+
subs.delete(event);
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
emit: emit,
|
|
40
|
+
on: on,
|
|
41
|
+
off,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { createEventBus } from './events.js';
|
|
2
|
+
export { createWhenEngine } from './when.js';
|
|
3
|
+
export { createCommandRegistry } from './command.js';
|
|
4
|
+
export { createKeybindingRegistry } from './keybinding.js';
|
|
5
|
+
export { createLayoutAPI, createLayoutRegistry } from './layout.js';
|
|
6
|
+
export { createSessionAPI } from './session.js';
|
|
7
|
+
export { createShellRegistry } from './shell.js';
|
|
8
|
+
export { createManifestAPI } from './manifest.js';
|
|
9
|
+
export { createLocalStorageLayoutStorage, createNoopLayoutStorage, } from './storage/layout-local.js';
|
|
10
|
+
export { createLocalStorageSessionStorage } from './storage/session-local.js';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { createEventBus } from './events.js';
|
|
2
|
+
export { createWhenEngine } from './when.js';
|
|
3
|
+
export { createCommandRegistry } from './command.js';
|
|
4
|
+
export { createKeybindingRegistry } from './keybinding.js';
|
|
5
|
+
export { createLayoutAPI, createLayoutRegistry } from './layout.js';
|
|
6
|
+
export { createSessionAPI } from './session.js';
|
|
7
|
+
export { createShellRegistry } from './shell.js';
|
|
8
|
+
export { createManifestAPI } from './manifest.js';
|
|
9
|
+
export { createLocalStorageLayoutStorage, createNoopLayoutStorage, } from './storage/layout-local.js';
|
|
10
|
+
export { createLocalStorageSessionStorage } from './storage/session-local.js';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { EventBus } from '../types/events.js';
|
|
2
|
+
import type { KeybindingRegistry } from '../types/keybinding.js';
|
|
3
|
+
import type { WhenEngine } from '../types/when.js';
|
|
4
|
+
interface Deps {
|
|
5
|
+
events: EventBus;
|
|
6
|
+
when: WhenEngine;
|
|
7
|
+
}
|
|
8
|
+
export declare function createKeybindingRegistry(deps: Deps): KeybindingRegistry;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { disposableFrom } from '../core/disposable.js';
|
|
2
|
+
function normalizeKey(input) {
|
|
3
|
+
return input
|
|
4
|
+
.trim()
|
|
5
|
+
.toLowerCase()
|
|
6
|
+
.replace(/\s*\+\s*/g, '+');
|
|
7
|
+
}
|
|
8
|
+
function eventKeyOf(e) {
|
|
9
|
+
const parts = [];
|
|
10
|
+
if (e.ctrlKey)
|
|
11
|
+
parts.push('ctrl');
|
|
12
|
+
if (e.altKey)
|
|
13
|
+
parts.push('alt');
|
|
14
|
+
if (e.shiftKey)
|
|
15
|
+
parts.push('shift');
|
|
16
|
+
if (e.metaKey)
|
|
17
|
+
parts.push('meta');
|
|
18
|
+
parts.push(e.key.toLowerCase());
|
|
19
|
+
return parts.join('+');
|
|
20
|
+
}
|
|
21
|
+
export function createKeybindingRegistry(deps) {
|
|
22
|
+
const { events, when } = deps;
|
|
23
|
+
const bindings = [];
|
|
24
|
+
let chordBuffer = [];
|
|
25
|
+
let chordTimer = null;
|
|
26
|
+
function announce() {
|
|
27
|
+
events.emit('scena:registry:changed', { registry: 'keybindings' });
|
|
28
|
+
}
|
|
29
|
+
function clearChord() {
|
|
30
|
+
chordBuffer = [];
|
|
31
|
+
if (chordTimer) {
|
|
32
|
+
clearTimeout(chordTimer);
|
|
33
|
+
chordTimer = null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function specificityOf(b) {
|
|
37
|
+
// Higher = wins. viewId > surfaceName > none.
|
|
38
|
+
if (b.scope?.viewId)
|
|
39
|
+
return 2;
|
|
40
|
+
if (b.scope?.surfaceName)
|
|
41
|
+
return 1;
|
|
42
|
+
return 0;
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
register(binding) {
|
|
46
|
+
bindings.push(binding);
|
|
47
|
+
announce();
|
|
48
|
+
return disposableFrom(() => {
|
|
49
|
+
const idx = bindings.indexOf(binding);
|
|
50
|
+
if (idx >= 0) {
|
|
51
|
+
bindings.splice(idx, 1);
|
|
52
|
+
announce();
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
},
|
|
56
|
+
unregister(keys, commandId) {
|
|
57
|
+
const normalized = normalizeKey(keys);
|
|
58
|
+
for (let i = bindings.length - 1; i >= 0; i--) {
|
|
59
|
+
const b = bindings[i];
|
|
60
|
+
if (normalizeKey(b.keys) === normalized && b.commandId === commandId) {
|
|
61
|
+
bindings.splice(i, 1);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
announce();
|
|
65
|
+
},
|
|
66
|
+
list() {
|
|
67
|
+
return [...bindings];
|
|
68
|
+
},
|
|
69
|
+
resolve(event) {
|
|
70
|
+
chordBuffer.push(eventKeyOf(event));
|
|
71
|
+
const enabled = bindings.filter((b) => b.when === undefined || when.evaluate(b.when));
|
|
72
|
+
const currentChord = chordBuffer.join(' ');
|
|
73
|
+
// Exact match — fire.
|
|
74
|
+
const matches = enabled.filter((b) => normalizeKey(b.keys) === currentChord);
|
|
75
|
+
if (matches.length > 0) {
|
|
76
|
+
matches.sort((a, b) => specificityOf(b) - specificityOf(a));
|
|
77
|
+
const winner = matches[0];
|
|
78
|
+
clearChord();
|
|
79
|
+
return { kind: 'fire', commandId: winner.commandId, args: winner.args };
|
|
80
|
+
}
|
|
81
|
+
// Pending — some binding starts with the current chord prefix.
|
|
82
|
+
const prefix = `${currentChord} `;
|
|
83
|
+
const partial = enabled.some((b) => normalizeKey(b.keys).startsWith(prefix));
|
|
84
|
+
if (partial) {
|
|
85
|
+
if (chordTimer)
|
|
86
|
+
clearTimeout(chordTimer);
|
|
87
|
+
chordTimer = setTimeout(clearChord, 1500);
|
|
88
|
+
return { kind: 'chord-pending', depth: chordBuffer.length };
|
|
89
|
+
}
|
|
90
|
+
clearChord();
|
|
91
|
+
return { kind: 'none' };
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { EventBus } from '../types/events.js';
|
|
2
|
+
import type { ReactiveStore } from '../types/reactive-store.js';
|
|
3
|
+
import type { LayoutAPI, LayoutRegistry, LayoutStorage, ScenaLayout } from '../types/layout.js';
|
|
4
|
+
interface LayoutDeps {
|
|
5
|
+
events: EventBus;
|
|
6
|
+
store: ReactiveStore;
|
|
7
|
+
initial?: ScenaLayout;
|
|
8
|
+
storage?: LayoutStorage;
|
|
9
|
+
}
|
|
10
|
+
export declare function createLayoutAPI(deps: LayoutDeps): LayoutAPI & {
|
|
11
|
+
setStorage(s: LayoutStorage | null): void;
|
|
12
|
+
};
|
|
13
|
+
export declare function createLayoutRegistry(deps: {
|
|
14
|
+
events: EventBus;
|
|
15
|
+
}): LayoutRegistry;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { disposableFrom } from '../core/disposable.js';
|
|
2
|
+
const DEFAULT_SURFACE_LAYOUTS = {
|
|
3
|
+
titlebar: { visible: true, layout: 'bar' },
|
|
4
|
+
activitybar: { visible: true, layout: 'rail' },
|
|
5
|
+
'sidebar:left': { visible: true, layout: 'single', size: 240 },
|
|
6
|
+
'sidebar:right': { visible: false, layout: 'stack', size: 280 },
|
|
7
|
+
main: { visible: true, layout: 'tab' },
|
|
8
|
+
'panel:bottom': { visible: false, layout: 'tab', size: 200 },
|
|
9
|
+
statusbar: { visible: true, layout: 'bar' },
|
|
10
|
+
overlay: { visible: true, layout: 'floating' },
|
|
11
|
+
detached: { visible: false, layout: 'single' },
|
|
12
|
+
};
|
|
13
|
+
function defaultLayout() {
|
|
14
|
+
return { surfaces: { ...DEFAULT_SURFACE_LAYOUTS } };
|
|
15
|
+
}
|
|
16
|
+
// Long enough to swallow a gesture, short enough that letting go of a splitter
|
|
17
|
+
// and closing the tab keeps the size.
|
|
18
|
+
const SAVE_DEBOUNCE_MS = 150;
|
|
19
|
+
function applyDeep(base, patch) {
|
|
20
|
+
if (patch === null || typeof patch !== 'object')
|
|
21
|
+
return patch;
|
|
22
|
+
if (Array.isArray(patch))
|
|
23
|
+
return patch;
|
|
24
|
+
const out = { ...base };
|
|
25
|
+
for (const [k, v] of Object.entries(patch)) {
|
|
26
|
+
out[k] = applyDeep((out[k] ?? {}), v);
|
|
27
|
+
}
|
|
28
|
+
return out;
|
|
29
|
+
}
|
|
30
|
+
function surfaceBase(surface) {
|
|
31
|
+
// Surface names can contain `:` (sidebar:left). Per RFC 6901, `:` is a
|
|
32
|
+
// legal segment character, so we use it as-is.
|
|
33
|
+
return `$/layout/surfaces/${surface}`;
|
|
34
|
+
}
|
|
35
|
+
export function createLayoutAPI(deps) {
|
|
36
|
+
const { events, store } = deps;
|
|
37
|
+
let storage = deps.storage ?? null;
|
|
38
|
+
let state = deps.initial
|
|
39
|
+
? applyDeep(defaultLayout(), deps.initial)
|
|
40
|
+
: defaultLayout();
|
|
41
|
+
const subscribers = new Set();
|
|
42
|
+
let saveTimer = null;
|
|
43
|
+
// Only the surfaces that could have changed. `set` and `patch` can touch any
|
|
44
|
+
// of them and pass nothing; `setSurface` names the one it wrote, which keeps a
|
|
45
|
+
// splitter drag from re-announcing all nine every frame.
|
|
46
|
+
function changedSurfaces(only) {
|
|
47
|
+
if (only !== undefined) {
|
|
48
|
+
const one = state.surfaces[only];
|
|
49
|
+
return one ? [[only, one]] : [];
|
|
50
|
+
}
|
|
51
|
+
return Object.entries(state.surfaces).filter((entry) => entry[1] !== undefined);
|
|
52
|
+
}
|
|
53
|
+
// `store.set` queues a change notification unconditionally, so mirroring a
|
|
54
|
+
// value back over itself still wakes every subscriber on that path. Most of
|
|
55
|
+
// what this writes is unchanged on any given call.
|
|
56
|
+
function setIfChanged(path, value) {
|
|
57
|
+
if (Object.is(store.get(path), value))
|
|
58
|
+
return;
|
|
59
|
+
store.set(path, value);
|
|
60
|
+
}
|
|
61
|
+
function mirrorToStore(only) {
|
|
62
|
+
for (const [surface, surfaceState] of changedSurfaces(only)) {
|
|
63
|
+
const base = surfaceBase(surface);
|
|
64
|
+
setIfChanged(`${base}/visible`, surfaceState.visible);
|
|
65
|
+
if (surfaceState.size !== undefined) {
|
|
66
|
+
setIfChanged(`${base}/size`, surfaceState.size);
|
|
67
|
+
}
|
|
68
|
+
if (surfaceState.layout !== undefined) {
|
|
69
|
+
setIfChanged(`${base}/layout`, surfaceState.layout);
|
|
70
|
+
}
|
|
71
|
+
if (surfaceState.activeContainerKey !== undefined) {
|
|
72
|
+
setIfChanged(`${base}/activeContainerKey`, surfaceState.activeContainerKey);
|
|
73
|
+
}
|
|
74
|
+
if (surfaceState.section !== undefined) {
|
|
75
|
+
setIfChanged(`${base}/section`, surfaceState.section);
|
|
76
|
+
}
|
|
77
|
+
if (surfaceState.split) {
|
|
78
|
+
setIfChanged(`${base}/split`, surfaceState.split);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function mirrorToCss(only) {
|
|
83
|
+
if (typeof document === 'undefined')
|
|
84
|
+
return;
|
|
85
|
+
const root = document.documentElement;
|
|
86
|
+
for (const [surface, surfaceState] of changedSurfaces(only)) {
|
|
87
|
+
const slug = surface.replace(':', '-');
|
|
88
|
+
if (surfaceState.size !== undefined) {
|
|
89
|
+
root.style.setProperty(`--oo-${slug}-size`, `${surfaceState.size}px`);
|
|
90
|
+
}
|
|
91
|
+
root.style.setProperty(`--oo-${slug}-visible`, surfaceState.visible ? '1' : '0');
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// Writing the layout means serialising all of it, so a caller that changes
|
|
95
|
+
// something per animation frame would otherwise run a synchronous
|
|
96
|
+
// JSON.stringify + localStorage write on the main thread at 60Hz. Coalesced,
|
|
97
|
+
// the last value of a burst is the one that lands, which is the only one
|
|
98
|
+
// anybody wanted saved.
|
|
99
|
+
function scheduleSave() {
|
|
100
|
+
if (!storage)
|
|
101
|
+
return;
|
|
102
|
+
if (saveTimer !== null)
|
|
103
|
+
clearTimeout(saveTimer);
|
|
104
|
+
saveTimer = setTimeout(() => {
|
|
105
|
+
saveTimer = null;
|
|
106
|
+
if (storage)
|
|
107
|
+
void storage.save(state);
|
|
108
|
+
}, SAVE_DEBOUNCE_MS);
|
|
109
|
+
}
|
|
110
|
+
function notify(opts) {
|
|
111
|
+
events.emit('scena:layout:changed', state);
|
|
112
|
+
for (const fn of [...subscribers]) {
|
|
113
|
+
try {
|
|
114
|
+
fn(state);
|
|
115
|
+
}
|
|
116
|
+
catch (err) {
|
|
117
|
+
console.error('[scena.layout] subscriber threw:', err);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
// The CSS variables move with the gesture: they are one write per changed
|
|
121
|
+
// property on one element, and a shell that sizes from them rather than
|
|
122
|
+
// from React state gets the frame for free.
|
|
123
|
+
mirrorToCss(opts?.surface);
|
|
124
|
+
// A frame of a drag is not a value anybody should be reading or storing.
|
|
125
|
+
// The next settled call mirrors and persists whatever it ended on.
|
|
126
|
+
if (opts?.transient === true)
|
|
127
|
+
return;
|
|
128
|
+
mirrorToStore(opts?.surface);
|
|
129
|
+
scheduleSave();
|
|
130
|
+
}
|
|
131
|
+
mirrorToStore();
|
|
132
|
+
mirrorToCss();
|
|
133
|
+
if (storage) {
|
|
134
|
+
void storage.load().then((loaded) => {
|
|
135
|
+
if (loaded) {
|
|
136
|
+
state = applyDeep(defaultLayout(), loaded);
|
|
137
|
+
notify();
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
get() {
|
|
143
|
+
return state;
|
|
144
|
+
},
|
|
145
|
+
set(layout) {
|
|
146
|
+
state = layout;
|
|
147
|
+
notify();
|
|
148
|
+
},
|
|
149
|
+
patch(patch) {
|
|
150
|
+
state = applyDeep(state, patch);
|
|
151
|
+
notify();
|
|
152
|
+
},
|
|
153
|
+
setSurface(surface, surfaceState, opts) {
|
|
154
|
+
const current = state.surfaces[surface] ?? { visible: false };
|
|
155
|
+
state = {
|
|
156
|
+
...state,
|
|
157
|
+
surfaces: {
|
|
158
|
+
...state.surfaces,
|
|
159
|
+
[surface]: { ...current, ...surfaceState },
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
notify({ surface, ...(opts?.transient === true ? { transient: true } : {}) });
|
|
163
|
+
},
|
|
164
|
+
subscribe(fn) {
|
|
165
|
+
subscribers.add(fn);
|
|
166
|
+
return disposableFrom(() => subscribers.delete(fn));
|
|
167
|
+
},
|
|
168
|
+
setStorage(s) {
|
|
169
|
+
storage = s;
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
export function createLayoutRegistry(deps) {
|
|
174
|
+
const { events } = deps;
|
|
175
|
+
const layouts = new Map();
|
|
176
|
+
function announce() {
|
|
177
|
+
events.emit('scena:registry:changed', { registry: 'layouts' });
|
|
178
|
+
}
|
|
179
|
+
return {
|
|
180
|
+
register(layout) {
|
|
181
|
+
layouts.set(layout.id, layout);
|
|
182
|
+
announce();
|
|
183
|
+
return disposableFrom(() => {
|
|
184
|
+
if (layouts.get(layout.id) === layout) {
|
|
185
|
+
layouts.delete(layout.id);
|
|
186
|
+
announce();
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
},
|
|
190
|
+
unregister(id) {
|
|
191
|
+
if (layouts.delete(id))
|
|
192
|
+
announce();
|
|
193
|
+
},
|
|
194
|
+
get(id) {
|
|
195
|
+
return layouts.get(id);
|
|
196
|
+
},
|
|
197
|
+
list() {
|
|
198
|
+
return [...layouts.values()];
|
|
199
|
+
},
|
|
200
|
+
};
|
|
201
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { EventBus } from '../types/events.js';
|
|
2
|
+
import type { ReactiveStore } from '../types/reactive-store.js';
|
|
3
|
+
import type { ComponentRegistry } from '../types/component-registry.js';
|
|
4
|
+
import type { CommandRegistry } from '../types/command.js';
|
|
5
|
+
import type { KeybindingRegistry } from '../types/keybinding.js';
|
|
6
|
+
import type { MountSurfaceRegistry } from '../types/mount-surface.js';
|
|
7
|
+
import type { ConverterRegistry } from '../types/converter-registry.js';
|
|
8
|
+
import type { PermissionEngine } from '../types/permissions.js';
|
|
9
|
+
import type { ManifestAPI } from '../types/manifest.js';
|
|
10
|
+
interface Deps {
|
|
11
|
+
events: EventBus;
|
|
12
|
+
store: ReactiveStore;
|
|
13
|
+
components: ComponentRegistry;
|
|
14
|
+
commands: CommandRegistry;
|
|
15
|
+
keybindings: KeybindingRegistry;
|
|
16
|
+
surfaces: MountSurfaceRegistry;
|
|
17
|
+
converters: ConverterRegistry;
|
|
18
|
+
permissions: PermissionEngine;
|
|
19
|
+
}
|
|
20
|
+
export declare function createManifestAPI(deps: Deps): ManifestAPI;
|
|
21
|
+
export {};
|