@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
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState, } from 'react';
|
|
3
|
+
import { useScena } from '../../react/ScenaProvider.js';
|
|
4
|
+
import { canonicalShortcut } from './token.js';
|
|
5
|
+
import './ActionList.css';
|
|
6
|
+
import { resolveColorVar } from '../../types/colors.js';
|
|
7
|
+
// Debounce (ms) for async provider re-resolves while results are already on
|
|
8
|
+
// screen — collapses a burst of keystrokes into one network call.
|
|
9
|
+
const PROVIDER_DEBOUNCE_MS = 180;
|
|
10
|
+
export function ActionList({ spec, hostCtx, onBack, dataContext, manageKeys = true, ref, }) {
|
|
11
|
+
const scena = useScena();
|
|
12
|
+
const [selected, setSelected] = useState(0);
|
|
13
|
+
const [resolved, setResolved] = useState({ kind: 'loading' });
|
|
14
|
+
// True while an async provider is re-resolving but we're still showing the
|
|
15
|
+
// previous rows (so the list doesn't flash to a "Loading…" screen on every
|
|
16
|
+
// keystroke — see the provider branch below). Drives the header spinner.
|
|
17
|
+
const [pending, setPending] = useState(false);
|
|
18
|
+
const resolvedRef = useRef(resolved);
|
|
19
|
+
resolvedRef.current = resolved;
|
|
20
|
+
const itemsRef = useRef(null);
|
|
21
|
+
// Title / icon / active / disabled for a Command — evaluated against the
|
|
22
|
+
// panel's dataContext so per-instance reads (e.g. a chat command's `active`
|
|
23
|
+
// reading `$/chat/<id>/mode`) resolve to THIS panel's root, not the global
|
|
24
|
+
// fallback. Re-resolved when the registry/store announces a change.
|
|
25
|
+
const stubCtx = useMemo(() => ({
|
|
26
|
+
scena,
|
|
27
|
+
store: scena.store,
|
|
28
|
+
surfaces: scena.surfaces,
|
|
29
|
+
commands: scena.commands,
|
|
30
|
+
events: scena.events,
|
|
31
|
+
source: 'menu',
|
|
32
|
+
dataContext,
|
|
33
|
+
}), [scena, dataContext]);
|
|
34
|
+
const rowsFromCommands = useCallback((cmds) => {
|
|
35
|
+
return cmds.map((c) => {
|
|
36
|
+
const title = typeof c.title === 'function' ? c.title(stubCtx) : c.title;
|
|
37
|
+
const description = typeof c.description === 'function' ? c.description(stubCtx) : c.description;
|
|
38
|
+
const active = c.active ? !!c.active(stubCtx) : undefined;
|
|
39
|
+
const disabled = c.disabled ? !!c.disabled(stubCtx) : undefined;
|
|
40
|
+
return {
|
|
41
|
+
key: `cmd:${c.id}`,
|
|
42
|
+
title,
|
|
43
|
+
description,
|
|
44
|
+
icon: c.icon,
|
|
45
|
+
color: c.color,
|
|
46
|
+
shortcut: canonicalShortcut(c.shortcut),
|
|
47
|
+
keys: canonicalShortcut(c.keys),
|
|
48
|
+
category: c.category,
|
|
49
|
+
active,
|
|
50
|
+
disabled,
|
|
51
|
+
invoke: (host) => {
|
|
52
|
+
// Autocomplete the typed token to the command's canonical shortcut
|
|
53
|
+
// (e.g. '/mod' → '/model') so the input matches the submenu it opens
|
|
54
|
+
// and the picker's sentinel keeps the submenu open. No-op on hosts
|
|
55
|
+
// without a text input (context menus stub replaceActiveToken).
|
|
56
|
+
const sc = canonicalShortcut(c.shortcut);
|
|
57
|
+
if (sc)
|
|
58
|
+
host.replaceActiveToken(sc);
|
|
59
|
+
void scena.commands.execute(c.id, undefined, {
|
|
60
|
+
source: 'menu',
|
|
61
|
+
dataContext,
|
|
62
|
+
host,
|
|
63
|
+
});
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
});
|
|
67
|
+
}, [scena, stubCtx, dataContext]);
|
|
68
|
+
const rowsFromActions = useCallback((actions) => {
|
|
69
|
+
return actions.map((a, i) => {
|
|
70
|
+
if ('commandId' in a) {
|
|
71
|
+
const cmd = scena.commands.get(a.commandId);
|
|
72
|
+
if (!cmd) {
|
|
73
|
+
return {
|
|
74
|
+
key: `cmd:${a.commandId}:${i}`,
|
|
75
|
+
title: `[${a.commandId}]`,
|
|
76
|
+
disabled: true,
|
|
77
|
+
invoke: () => undefined,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
const row = rowsFromCommands([cmd])[0];
|
|
81
|
+
const args = a.args;
|
|
82
|
+
return {
|
|
83
|
+
...row,
|
|
84
|
+
key: `cmd:${a.commandId}:${i}`,
|
|
85
|
+
invoke: (host) => {
|
|
86
|
+
void scena.commands.execute(cmd.id, args, {
|
|
87
|
+
source: 'menu',
|
|
88
|
+
dataContext,
|
|
89
|
+
host,
|
|
90
|
+
});
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
key: `act:${i}:${a.title}`,
|
|
96
|
+
title: a.title,
|
|
97
|
+
description: a.description,
|
|
98
|
+
icon: a.icon,
|
|
99
|
+
color: a.color,
|
|
100
|
+
shortcut: a.shortcut,
|
|
101
|
+
keys: a.keys,
|
|
102
|
+
group: a.group,
|
|
103
|
+
active: a.active,
|
|
104
|
+
disabled: a.disabled,
|
|
105
|
+
invoke: a.onSelect,
|
|
106
|
+
};
|
|
107
|
+
});
|
|
108
|
+
}, [scena, rowsFromCommands, dataContext]);
|
|
109
|
+
// Resolve `spec` to a list of rows. Three modes: items, provider, query.
|
|
110
|
+
// Provider may return a Promise — loading state shown until it resolves.
|
|
111
|
+
// `spec.extraItems` (registry-injected rows) are prepended in every mode.
|
|
112
|
+
useEffect(() => {
|
|
113
|
+
let cancelled = false;
|
|
114
|
+
const extra = spec.extraItems ? rowsFromActions(spec.extraItems) : [];
|
|
115
|
+
if (spec.items) {
|
|
116
|
+
setPending(false);
|
|
117
|
+
setResolved({ kind: 'ready', rows: [...extra, ...rowsFromActions(spec.items)] });
|
|
118
|
+
return () => {
|
|
119
|
+
cancelled = true;
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
if (spec.query) {
|
|
123
|
+
setPending(false);
|
|
124
|
+
const querySlot = spec.query.slot;
|
|
125
|
+
const queryQ = spec.query.q;
|
|
126
|
+
const resolveQuery = () => {
|
|
127
|
+
const cmds = scena.commands.list({
|
|
128
|
+
slot: querySlot,
|
|
129
|
+
q: queryQ,
|
|
130
|
+
enabled: true,
|
|
131
|
+
});
|
|
132
|
+
if (!cancelled)
|
|
133
|
+
setResolved({ kind: 'ready', rows: [...extra, ...rowsFromCommands(cmds)] });
|
|
134
|
+
};
|
|
135
|
+
resolveQuery();
|
|
136
|
+
// Re-resolve on registry changes (newly-registered commands appear)
|
|
137
|
+
// AND on any store path change (so toggle commands like /plan show
|
|
138
|
+
// their ✓ live without waiting for the next menu open). Store
|
|
139
|
+
// updates can fire densely — coalesce to one rAF.
|
|
140
|
+
let scheduled = false;
|
|
141
|
+
function schedule() {
|
|
142
|
+
if (scheduled || cancelled)
|
|
143
|
+
return;
|
|
144
|
+
scheduled = true;
|
|
145
|
+
requestAnimationFrame(() => {
|
|
146
|
+
scheduled = false;
|
|
147
|
+
if (!cancelled)
|
|
148
|
+
resolveQuery();
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
const subRegistry = scena.events.on('scena:registry:changed', (payload) => {
|
|
152
|
+
const reg = payload.registry;
|
|
153
|
+
if (reg !== 'commands' && reg !== 'components')
|
|
154
|
+
return;
|
|
155
|
+
schedule();
|
|
156
|
+
});
|
|
157
|
+
const subStore = scena.events.on('scena:store:changed', () => {
|
|
158
|
+
schedule();
|
|
159
|
+
});
|
|
160
|
+
return () => {
|
|
161
|
+
cancelled = true;
|
|
162
|
+
subRegistry.dispose();
|
|
163
|
+
subStore.dispose();
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
if (spec.provider) {
|
|
167
|
+
const provider = spec.provider;
|
|
168
|
+
const run = () => {
|
|
169
|
+
if (cancelled)
|
|
170
|
+
return;
|
|
171
|
+
let out;
|
|
172
|
+
try {
|
|
173
|
+
out = provider(hostCtx);
|
|
174
|
+
}
|
|
175
|
+
catch (err) {
|
|
176
|
+
setResolved({ kind: 'error', message: err instanceof Error ? err.message : String(err) });
|
|
177
|
+
setPending(false);
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
if (out instanceof Promise) {
|
|
181
|
+
out
|
|
182
|
+
.then((actions) => {
|
|
183
|
+
if (cancelled)
|
|
184
|
+
return;
|
|
185
|
+
setResolved({ kind: 'ready', rows: [...extra, ...rowsFromActions(actions)] });
|
|
186
|
+
setPending(false);
|
|
187
|
+
})
|
|
188
|
+
.catch((err) => {
|
|
189
|
+
if (cancelled)
|
|
190
|
+
return;
|
|
191
|
+
setResolved({ kind: 'error', message: err instanceof Error ? err.message : String(err) });
|
|
192
|
+
setPending(false);
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
setResolved({ kind: 'ready', rows: [...extra, ...rowsFromActions(out)] });
|
|
197
|
+
setPending(false);
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
// Keep the prior rows, show the header spinner, and debounce — used for
|
|
201
|
+
// re-resolves (typing, or a store change below).
|
|
202
|
+
let timer = null;
|
|
203
|
+
const scheduleRun = () => {
|
|
204
|
+
if (cancelled)
|
|
205
|
+
return;
|
|
206
|
+
setPending(true);
|
|
207
|
+
if (timer)
|
|
208
|
+
clearTimeout(timer);
|
|
209
|
+
timer = setTimeout(run, PROVIDER_DEBOUNCE_MS);
|
|
210
|
+
};
|
|
211
|
+
// First resolve (nothing shown yet) → full "Loading…" + run immediately.
|
|
212
|
+
if (resolvedRef.current.kind !== 'ready') {
|
|
213
|
+
setResolved({ kind: 'loading' });
|
|
214
|
+
run();
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
scheduleRun();
|
|
218
|
+
}
|
|
219
|
+
// Re-resolve on store changes so a provider whose rows depend on store
|
|
220
|
+
// state (e.g. the /mode submenu's active ✓) reflects external updates —
|
|
221
|
+
// like the composer's mode button toggling `$/chat/<id>/mode` while the
|
|
222
|
+
// menu is open. Debounced + keep-rows so network providers stay smooth.
|
|
223
|
+
const subStore = scena.events.on('scena:store:changed', () => scheduleRun());
|
|
224
|
+
return () => {
|
|
225
|
+
cancelled = true;
|
|
226
|
+
if (timer)
|
|
227
|
+
clearTimeout(timer);
|
|
228
|
+
subStore.dispose();
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
setPending(false);
|
|
232
|
+
setResolved({ kind: 'ready', rows: extra });
|
|
233
|
+
return () => {
|
|
234
|
+
cancelled = true;
|
|
235
|
+
};
|
|
236
|
+
}, [spec, hostCtx, rowsFromActions, rowsFromCommands, scena]);
|
|
237
|
+
// Flat list of selectable rows (skip group headers and disabled rows).
|
|
238
|
+
const interactiveRows = useMemo(() => {
|
|
239
|
+
if (resolved.kind !== 'ready')
|
|
240
|
+
return [];
|
|
241
|
+
return resolved.rows.filter((r) => !r.disabled);
|
|
242
|
+
}, [resolved]);
|
|
243
|
+
// Reset highlight when the row set changes.
|
|
244
|
+
useEffect(() => {
|
|
245
|
+
setSelected(0);
|
|
246
|
+
}, [interactiveRows.length]);
|
|
247
|
+
// Scroll selected into view.
|
|
248
|
+
useEffect(() => {
|
|
249
|
+
const el = itemsRef.current;
|
|
250
|
+
if (!el)
|
|
251
|
+
return;
|
|
252
|
+
const selectedEl = el.querySelector('[data-selected="true"]');
|
|
253
|
+
if (selectedEl)
|
|
254
|
+
selectedEl.scrollIntoView({ block: 'nearest' });
|
|
255
|
+
}, [selected]);
|
|
256
|
+
// Track focused row for layout='info-list' detail panel.
|
|
257
|
+
const focusedRow = interactiveRows[selected];
|
|
258
|
+
// ---------- Group rows by category --------------------------------------
|
|
259
|
+
const grouped = useMemo(() => {
|
|
260
|
+
if (resolved.kind !== 'ready')
|
|
261
|
+
return [];
|
|
262
|
+
const order = spec.groupOrder ?? [];
|
|
263
|
+
const buckets = new Map();
|
|
264
|
+
const orderSeen = [];
|
|
265
|
+
for (const r of resolved.rows) {
|
|
266
|
+
const k = r.category ?? r.group ?? '';
|
|
267
|
+
if (!buckets.has(k)) {
|
|
268
|
+
buckets.set(k, []);
|
|
269
|
+
if (!orderSeen.includes(k))
|
|
270
|
+
orderSeen.push(k);
|
|
271
|
+
}
|
|
272
|
+
buckets.get(k).push(r);
|
|
273
|
+
}
|
|
274
|
+
const sortedKeys = [
|
|
275
|
+
...order.filter((k) => buckets.has(k)),
|
|
276
|
+
...orderSeen.filter((k) => !order.includes(k)),
|
|
277
|
+
];
|
|
278
|
+
return sortedKeys.map((k) => ({
|
|
279
|
+
label: k === '' ? null : k,
|
|
280
|
+
rows: buckets.get(k),
|
|
281
|
+
}));
|
|
282
|
+
}, [resolved, spec.groupOrder]);
|
|
283
|
+
// ---------- Keyboard handler --------------------------------------------
|
|
284
|
+
const handleKeyDown = useCallback((e) => {
|
|
285
|
+
if (interactiveRows.length === 0 && e.key !== 'Escape' && e.key !== 'ArrowLeft')
|
|
286
|
+
return false;
|
|
287
|
+
if (e.key === 'ArrowDown') {
|
|
288
|
+
e.preventDefault();
|
|
289
|
+
setSelected((p) => (p + 1) % interactiveRows.length);
|
|
290
|
+
return true;
|
|
291
|
+
}
|
|
292
|
+
if (e.key === 'ArrowUp') {
|
|
293
|
+
e.preventDefault();
|
|
294
|
+
setSelected((p) => (p - 1 + interactiveRows.length) % interactiveRows.length);
|
|
295
|
+
return true;
|
|
296
|
+
}
|
|
297
|
+
if (e.key === 'Enter' || e.key === 'Tab') {
|
|
298
|
+
e.preventDefault();
|
|
299
|
+
const row = interactiveRows[selected];
|
|
300
|
+
if (row)
|
|
301
|
+
void row.invoke(hostCtx);
|
|
302
|
+
return true;
|
|
303
|
+
}
|
|
304
|
+
if (e.key === 'Escape') {
|
|
305
|
+
e.preventDefault();
|
|
306
|
+
hostCtx.closeMenu();
|
|
307
|
+
return true;
|
|
308
|
+
}
|
|
309
|
+
if (e.key === 'ArrowLeft' && onBack) {
|
|
310
|
+
e.preventDefault();
|
|
311
|
+
onBack();
|
|
312
|
+
return true;
|
|
313
|
+
}
|
|
314
|
+
return false;
|
|
315
|
+
}, [interactiveRows, selected, hostCtx, onBack]);
|
|
316
|
+
// Document-level key listener for popup hosts (ContextMenu). Chat hosts
|
|
317
|
+
// disable it via `manageKeys: false` and route keys through the
|
|
318
|
+
// `controller.handleKey` exposed via ref — otherwise two visible pickers
|
|
319
|
+
// would both react to a single ArrowDown press.
|
|
320
|
+
useEffect(() => {
|
|
321
|
+
if (!manageKeys)
|
|
322
|
+
return;
|
|
323
|
+
function onDocKey(e) {
|
|
324
|
+
const synth = {
|
|
325
|
+
key: e.key,
|
|
326
|
+
preventDefault: () => e.preventDefault(),
|
|
327
|
+
};
|
|
328
|
+
handleKeyDown(synth);
|
|
329
|
+
}
|
|
330
|
+
document.addEventListener('keydown', onDocKey);
|
|
331
|
+
return () => document.removeEventListener('keydown', onDocKey);
|
|
332
|
+
}, [handleKeyDown, manageKeys]);
|
|
333
|
+
// Imperative controller — only consumed when `manageKeys: false`.
|
|
334
|
+
useImperativeHandle(ref, () => ({
|
|
335
|
+
handleKey: (e) => handleKeyDown(e),
|
|
336
|
+
}), [handleKeyDown]);
|
|
337
|
+
// ---------- Render -------------------------------------------------------
|
|
338
|
+
let visibleIndex = -1;
|
|
339
|
+
const rich = spec.rich === true;
|
|
340
|
+
function renderRow(row) {
|
|
341
|
+
const isInteractive = !row.disabled;
|
|
342
|
+
if (isInteractive)
|
|
343
|
+
visibleIndex += 1;
|
|
344
|
+
const isSelected = isInteractive && visibleIndex === selected;
|
|
345
|
+
const capturedIndex = visibleIndex;
|
|
346
|
+
const inlineStyle = row.color
|
|
347
|
+
? { ['--oo-color']: resolveColorVar(row.color) }
|
|
348
|
+
: undefined;
|
|
349
|
+
return (_jsxs("button", { className: "oo-action-list__row", type: "button", disabled: row.disabled, "data-cmd-group": row.group, "data-cmd-category": row.category, "data-color": row.color, "data-active": row.active ? 'true' : undefined, "data-disabled": row.disabled ? 'true' : undefined, "data-selected": isSelected ? 'true' : undefined, style: inlineStyle, onMouseEnter: () => { if (isInteractive)
|
|
350
|
+
setSelected(capturedIndex); }, onClick: () => { if (isInteractive)
|
|
351
|
+
void row.invoke(hostCtx); }, children: [_jsx("span", { className: "oo-icon", children: row.icon ?? '·' }), rich ? (_jsxs("span", { className: "oo-action-list__text", children: [_jsx("span", { className: "oo-action-list__title", children: row.title }), row.description ? (_jsx("span", { className: "oo-action-list__subtitle", children: row.description })) : null] })) : (_jsxs(_Fragment, { children: [_jsx("span", { className: "oo-action-list__title", children: row.title }), row.description ? (_jsx("span", { className: "oo-action-list__description", children: row.description })) : null] })), row.active ? _jsx("span", { className: "oo-action-list__check", children: "\u2713" }) : null, row.shortcut ? (_jsx("kbd", { className: "oo-action-list__shortcut", children: row.shortcut })) : null, row.keys ? (_jsx("kbd", { className: "oo-action-list__shortcut", children: row.keys })) : null] }, row.key));
|
|
352
|
+
}
|
|
353
|
+
function renderBody() {
|
|
354
|
+
if (resolved.kind === 'loading') {
|
|
355
|
+
return _jsx("div", { className: "oo-action-list__loading", children: "Loading\u2026" });
|
|
356
|
+
}
|
|
357
|
+
if (resolved.kind === 'error') {
|
|
358
|
+
return _jsx("div", { className: "oo-action-list__error", children: resolved.message });
|
|
359
|
+
}
|
|
360
|
+
if (grouped.length === 0 || resolved.rows.length === 0) {
|
|
361
|
+
return _jsx("div", { className: "oo-action-list__empty", children: "No matches" });
|
|
362
|
+
}
|
|
363
|
+
return (_jsx(_Fragment, { children: grouped.map((group, gi) => (_jsxs("div", { children: [gi > 0 ? _jsx("div", { className: "oo-action-list__divider" }) : null, group.label ? (_jsx("div", { className: "oo-action-list__group-label", children: group.label })) : null, group.rows.map(renderRow)] }, group.label ?? `__g${gi}`))) }));
|
|
364
|
+
}
|
|
365
|
+
const layout = spec.layout ?? 'list';
|
|
366
|
+
const showHeader = layout === 'header-list' && spec.header;
|
|
367
|
+
const showDetail = layout === 'info-list' && spec.detail;
|
|
368
|
+
return (_jsxs("div", { className: "oo-action-list", "data-layout": layout, children: [pending ? _jsx("div", { className: "oo-action-list__pending", "aria-hidden": true, children: "searching\u2026" }) : null, onBack ? (_jsxs("button", { type: "button", className: "oo-action-list__back", onClick: onBack, children: ["\u2190 ", spec.title ?? 'Back'] })) : null, spec.customHeader ? (_jsx("div", { className: "oo-action-list__custom-header", children: spec.customHeader(hostCtx) })) : null, showHeader ? (_jsx("div", { className: "oo-action-list__header", children: spec.header(hostCtx) })) : null, _jsxs("div", { className: "oo-action-list__body", children: [_jsx("div", { className: "oo-action-list__items", ref: itemsRef, children: renderBody() }), showDetail ? (_jsx("div", { className: "oo-action-list__detail", children: spec.detail(hostCtx, focusedRow) })) : null] }), spec.footerHints ? (_jsxs("div", { className: "oo-action-list__footer", children: ["\u2191\u2193 navigate \u00B7 \u21B5 select", onBack ? ' · ← back' : '', " \u00B7 \u238B close"] })) : null] }));
|
|
369
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import type { BindingPath } from '../../types/component-graph.js';
|
|
3
|
+
import type { ListSpec } from '../../types/host.js';
|
|
4
|
+
export interface ContainerMenuProps {
|
|
5
|
+
spec: ListSpec;
|
|
6
|
+
context?: Record<string, unknown>;
|
|
7
|
+
dataContext?: BindingPath;
|
|
8
|
+
manageKeys?: boolean;
|
|
9
|
+
inline?: {
|
|
10
|
+
component: ReactNode;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export declare function ContainerMenu({ spec, context, dataContext, manageKeys, inline, }: ContainerMenuProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useMemo, useRef, useState } from 'react';
|
|
3
|
+
import { useScena } from '../../react/ScenaProvider.js';
|
|
4
|
+
import { ActionList } from './ActionList.js';
|
|
5
|
+
// Inline twin of ContextMenu: same picker engine (ActionList + the host stack
|
|
6
|
+
// with push/back), but NO Popup wrapper. Use when the menu should sit inside
|
|
7
|
+
// the layout (a sidebar section, a panel) instead of floating at click coords.
|
|
8
|
+
//
|
|
9
|
+
// Stack behavior mirrors ContextMenu so submenus pushed via host.pushList /
|
|
10
|
+
// replaceList work the same way. closeMenu is a no-op — there is no popup to
|
|
11
|
+
// dismiss; commands that depend on closing the menu after execute should
|
|
12
|
+
// instead unmount the container at the call site.
|
|
13
|
+
export function ContainerMenu({ spec, context, dataContext, manageKeys = false, inline, }) {
|
|
14
|
+
const scena = useScena();
|
|
15
|
+
const [stack, setStack] = useState(() => inline ? [{ component: inline.component }] : [{ spec }]);
|
|
16
|
+
// Same StrictMode-safe context injection pattern as ContextMenu: write once
|
|
17
|
+
// synchronously, don't restore on unmount. ActionList's effects fire
|
|
18
|
+
// bottom-up before ours, so an unmount-restore would race them on remount.
|
|
19
|
+
const injectedRef = useRef(false);
|
|
20
|
+
if (context && !injectedRef.current) {
|
|
21
|
+
for (const [k, v] of Object.entries(context)) {
|
|
22
|
+
const path = (k.startsWith('$/') ? k : joinAbs(dataContext, k));
|
|
23
|
+
scena.store.set(path, v);
|
|
24
|
+
}
|
|
25
|
+
injectedRef.current = true;
|
|
26
|
+
}
|
|
27
|
+
const host = useMemo(() => ({
|
|
28
|
+
pushList: (s) => setStack((cur) => [...cur, { spec: s }]),
|
|
29
|
+
replaceList: (s) => setStack((cur) => {
|
|
30
|
+
const next = cur.slice(0, -1);
|
|
31
|
+
next.push({ spec: s });
|
|
32
|
+
return next;
|
|
33
|
+
}),
|
|
34
|
+
openInline: () => undefined,
|
|
35
|
+
openPopover: () => undefined,
|
|
36
|
+
back: () => setStack((cur) => (cur.length > 1 ? cur.slice(0, -1) : cur)),
|
|
37
|
+
closeMenu: () => undefined,
|
|
38
|
+
keepOpen: () => undefined,
|
|
39
|
+
insertAtCursor: () => undefined,
|
|
40
|
+
replaceActiveToken: () => undefined,
|
|
41
|
+
query: '',
|
|
42
|
+
}), []);
|
|
43
|
+
const top = stack[stack.length - 1];
|
|
44
|
+
const onBack = stack.length > 1 ? host.back : undefined;
|
|
45
|
+
if ('component' in top)
|
|
46
|
+
return _jsx(_Fragment, { children: top.component });
|
|
47
|
+
return (_jsx(ActionList, { spec: top.spec, hostCtx: host, onBack: onBack, dataContext: dataContext, manageKeys: manageKeys }));
|
|
48
|
+
}
|
|
49
|
+
function joinAbs(root, rel) {
|
|
50
|
+
if (rel.startsWith('$/'))
|
|
51
|
+
return rel;
|
|
52
|
+
if (!root)
|
|
53
|
+
return rel.startsWith('/') ? `$${rel}` : `$/${rel}`;
|
|
54
|
+
const base = root.endsWith('/') ? root.slice(0, -1) : root;
|
|
55
|
+
const tail = rel.startsWith('/') ? rel : `/${rel}`;
|
|
56
|
+
return `${base}${tail}`;
|
|
57
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import type { BindingPath } from '../../types/component-graph.js';
|
|
3
|
+
import type { ListSpec } from '../../types/host.js';
|
|
4
|
+
export interface ContextMenuProps {
|
|
5
|
+
spec: ListSpec;
|
|
6
|
+
x: number;
|
|
7
|
+
y: number;
|
|
8
|
+
onClose: () => void;
|
|
9
|
+
context?: Record<string, unknown>;
|
|
10
|
+
dataContext?: BindingPath;
|
|
11
|
+
inline?: {
|
|
12
|
+
component: ReactNode;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export declare function ContextMenu({ spec, x, y, onClose, context, dataContext, inline, }: ContextMenuProps): import("react").JSX.Element;
|
|
16
|
+
export declare function useContextMenu(): {
|
|
17
|
+
open: (opts: {
|
|
18
|
+
x: number;
|
|
19
|
+
y: number;
|
|
20
|
+
slot: string;
|
|
21
|
+
context?: Record<string, unknown>;
|
|
22
|
+
dataContext?: BindingPath;
|
|
23
|
+
q?: string;
|
|
24
|
+
}) => void;
|
|
25
|
+
close: () => void;
|
|
26
|
+
rendered: ReactNode | null;
|
|
27
|
+
};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useMemo, useRef, useState } from 'react';
|
|
3
|
+
import { useScena } from '../../react/ScenaProvider.js';
|
|
4
|
+
import { Popup } from '../overlay/Popup.js';
|
|
5
|
+
import { ActionList } from './ActionList.js';
|
|
6
|
+
// Right-click style picker. Wraps Popup + ActionList. Owns the picker stack
|
|
7
|
+
// (push/replace/back), context injection, and host callbacks.
|
|
8
|
+
export function ContextMenu({ spec, x, y, onClose, context, dataContext, inline, }) {
|
|
9
|
+
const scena = useScena();
|
|
10
|
+
const [stack, setStack] = useState(() => (inline ? [{ component: inline.component }] : [{ spec }]));
|
|
11
|
+
const stackRef = useRef(stack);
|
|
12
|
+
stackRef.current = stack;
|
|
13
|
+
// Inject context paths into the store synchronously during render so the
|
|
14
|
+
// child ActionList's useEffect (which fires BEFORE parent useEffects) sees
|
|
15
|
+
// the values when it calls commands.list() or when the run reads them.
|
|
16
|
+
//
|
|
17
|
+
// We deliberately do NOT restore the paths on unmount. The earlier
|
|
18
|
+
// restore-on-cleanup pattern was broken under React 18 StrictMode: dev
|
|
19
|
+
// fires effects as mount → cleanup → remount WITHOUT re-rendering. The
|
|
20
|
+
// cleanup wiped the paths; the remount's ActionList effect (which runs
|
|
21
|
+
// BEFORE the parent ContextMenu effect, bottom-up) then queried
|
|
22
|
+
// commands.list against an empty store. when-gated commands evaluated to
|
|
23
|
+
// false against undefined and the picker cached "no matches" until the
|
|
24
|
+
// next registry change. Skipping restore entirely sidesteps the race.
|
|
25
|
+
// Each new menu open writes its own context, so stale paths from a
|
|
26
|
+
// previously-closed menu are overwritten before being read.
|
|
27
|
+
const injectedRef = useRef(false);
|
|
28
|
+
if (context && !injectedRef.current) {
|
|
29
|
+
for (const [k, v] of Object.entries(context)) {
|
|
30
|
+
const path = (k.startsWith('$/') ? k : joinAbs(dataContext, k));
|
|
31
|
+
scena.store.set(path, v);
|
|
32
|
+
}
|
|
33
|
+
injectedRef.current = true;
|
|
34
|
+
}
|
|
35
|
+
// Build a stable HostCtx scoped to this menu's stack.
|
|
36
|
+
const host = useMemo(() => ({
|
|
37
|
+
pushList: (s) => setStack((cur) => [...cur, { spec: s }]),
|
|
38
|
+
replaceList: (s) => setStack((cur) => {
|
|
39
|
+
const next = cur.slice(0, -1);
|
|
40
|
+
next.push({ spec: s });
|
|
41
|
+
return next;
|
|
42
|
+
}),
|
|
43
|
+
openInline: (componentId, props) => {
|
|
44
|
+
// Build a thin shell that mounts the component via ViewMount-style
|
|
45
|
+
// dispatch. For now we use a synthesized React tree; the real impl
|
|
46
|
+
// would consult scena.components.resolve(componentId).
|
|
47
|
+
setStack((cur) => [
|
|
48
|
+
...cur,
|
|
49
|
+
{ component: _jsx(MissingInline, { name: componentId, props: props }) },
|
|
50
|
+
]);
|
|
51
|
+
},
|
|
52
|
+
openPopover: () => {
|
|
53
|
+
// A detached popover from inside a context menu is uncommon; for the
|
|
54
|
+
// first cut, treat as openInline (push onto stack).
|
|
55
|
+
},
|
|
56
|
+
back: () => setStack((cur) => (cur.length > 1 ? cur.slice(0, -1) : cur)),
|
|
57
|
+
closeMenu: onClose,
|
|
58
|
+
keepOpen: () => undefined, // ContextMenu defaults to closing on dispatch; toggles override via run not running close
|
|
59
|
+
insertAtCursor: () => undefined, // right-click context has no text input
|
|
60
|
+
replaceActiveToken: () => undefined,
|
|
61
|
+
query: '',
|
|
62
|
+
}), [onClose]);
|
|
63
|
+
const top = stack[stack.length - 1];
|
|
64
|
+
const onBack = stack.length > 1 ? host.back : undefined;
|
|
65
|
+
return (_jsx(Popup, { x: x, y: y, onClose: onClose, children: 'component' in top ? (top.component) : (_jsx(ActionList, { spec: top.spec, hostCtx: host, onBack: onBack, dataContext: dataContext })) }));
|
|
66
|
+
}
|
|
67
|
+
function joinAbs(root, rel) {
|
|
68
|
+
if (rel.startsWith('$/'))
|
|
69
|
+
return rel;
|
|
70
|
+
if (!root)
|
|
71
|
+
return rel.startsWith('/') ? `$${rel}` : `$/${rel}`;
|
|
72
|
+
const base = root.endsWith('/') ? root.slice(0, -1) : root;
|
|
73
|
+
const tail = rel.startsWith('/') ? rel : `/${rel}`;
|
|
74
|
+
return `${base}${tail}`;
|
|
75
|
+
}
|
|
76
|
+
function MissingInline({ name, props: _props }) {
|
|
77
|
+
// Placeholder while openInline component-resolution lands. Will be replaced
|
|
78
|
+
// by a ViewMount-style adapter that looks up scena.components.
|
|
79
|
+
return _jsxs("div", { style: { padding: 12 }, children: ["[component: ", name, "]"] });
|
|
80
|
+
}
|
|
81
|
+
// Convenience for the common case — fire a Popup-anchored ActionList at the
|
|
82
|
+
// given coords for a slot tag.
|
|
83
|
+
export function useContextMenu() {
|
|
84
|
+
const [state, setState] = useState(null);
|
|
85
|
+
const open = useCallback((opts) => {
|
|
86
|
+
setState(opts);
|
|
87
|
+
}, []);
|
|
88
|
+
const close = useCallback(() => setState(null), []);
|
|
89
|
+
const rendered = state
|
|
90
|
+
? (_jsx(ContextMenu, { x: state.x, y: state.y, onClose: close, spec: { query: { slot: state.slot, q: state.q } }, context: state.context, dataContext: state.dataContext }))
|
|
91
|
+
: null;
|
|
92
|
+
return { open: open, close, rendered };
|
|
93
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { ActionList } from './ActionList.js';
|
|
2
|
+
export type { ActionListProps, ActionListController } from './ActionList.js';
|
|
3
|
+
export { ContextMenu, useContextMenu } from './ContextMenu.js';
|
|
4
|
+
export type { ContextMenuProps } from './ContextMenu.js';
|
|
5
|
+
export { ContainerMenu } from './ContainerMenu.js';
|
|
6
|
+
export type { ContainerMenuProps } from './ContainerMenu.js';
|
|
7
|
+
export { useChatPicker, sentinelHas } from './useChatPicker.js';
|
|
8
|
+
export type { UseChatPickerParams, UseChatPickerResult } from './useChatPicker.js';
|
|
9
|
+
export { clampCaretIndex, getActiveToken, replaceActiveToken, shortcutMatchesToken, canonicalShortcut, } from './token.js';
|
|
10
|
+
export type { TokenInfo } from './token.js';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// Public surface of the menu/picker family. See doop-roadmap/ideas/scena/15-command-view.md
|
|
2
|
+
// for the architecture; this file is just the export map.
|
|
3
|
+
export { ActionList } from './ActionList.js';
|
|
4
|
+
export { ContextMenu, useContextMenu } from './ContextMenu.js';
|
|
5
|
+
export { ContainerMenu } from './ContainerMenu.js';
|
|
6
|
+
export { useChatPicker, sentinelHas } from './useChatPicker.js';
|
|
7
|
+
export { clampCaretIndex, getActiveToken, replaceActiveToken, shortcutMatchesToken, canonicalShortcut, } from './token.js';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface TokenInfo {
|
|
2
|
+
token: string;
|
|
3
|
+
prefix: string;
|
|
4
|
+
query: string;
|
|
5
|
+
start: number;
|
|
6
|
+
end: number;
|
|
7
|
+
}
|
|
8
|
+
export declare function clampCaretIndex(input: string, caretIndex?: number | null): number;
|
|
9
|
+
export declare function getActiveToken(input: string, prefixes?: string[], caretIndex?: number | null): TokenInfo | null;
|
|
10
|
+
export declare function replaceActiveToken(input: string, caretIndex: number | null | undefined, replacement: string, prefixes?: string[]): string;
|
|
11
|
+
export declare function commitToken(input: string, caretIndex: number | null | undefined, replacement: string, prefixes?: string[]): {
|
|
12
|
+
next: string;
|
|
13
|
+
caret: number;
|
|
14
|
+
};
|
|
15
|
+
export declare function insertShortcut(input: string, caretIndex: number | null | undefined, shortcut: string, prefixes?: string[]): {
|
|
16
|
+
next: string;
|
|
17
|
+
caret: number;
|
|
18
|
+
};
|
|
19
|
+
export declare function shortcutMatchesToken(shortcut: string | string[] | undefined, token: string): boolean;
|
|
20
|
+
export declare function stripSentinel(rawQuery: string, sentinel: string | string[] | undefined): string;
|
|
21
|
+
export declare function tokenMatchesSentinel(token: string, sentinel: string | string[] | undefined): boolean;
|
|
22
|
+
export declare function canonicalShortcut(shortcut: string | string[] | undefined): string | undefined;
|