@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,145 @@
|
|
|
1
|
+
// Token parsing for chat-style inputs. A token is a contiguous non-whitespace
|
|
2
|
+
// run starting with one of the configured prefixes ('/' or '@' by default).
|
|
3
|
+
// The "active" token is the one straddling the caret, or none.
|
|
4
|
+
//
|
|
5
|
+
// Ported from web's chat slash token.ts and generalized to accept any prefix.
|
|
6
|
+
export function clampCaretIndex(input, caretIndex) {
|
|
7
|
+
if (typeof caretIndex !== 'number' || Number.isNaN(caretIndex))
|
|
8
|
+
return input.length;
|
|
9
|
+
return Math.max(0, Math.min(input.length, Math.floor(caretIndex)));
|
|
10
|
+
}
|
|
11
|
+
// Returns the active token straddling the caret, when its prefix matches one
|
|
12
|
+
// of `prefixes`. Returns null otherwise. Whitespace-bounded; identical caret
|
|
13
|
+
// rules to web (caret inside or immediately after the token both count).
|
|
14
|
+
export function getActiveToken(input, prefixes = ['/', '@'], caretIndex) {
|
|
15
|
+
if (!input)
|
|
16
|
+
return null;
|
|
17
|
+
const caret = clampCaretIndex(input, caretIndex);
|
|
18
|
+
let probe = -1;
|
|
19
|
+
if (caret < input.length && !/\s/.test(input[caret] ?? '')) {
|
|
20
|
+
probe = caret;
|
|
21
|
+
}
|
|
22
|
+
else if (caret > 0 && !/\s/.test(input[caret - 1] ?? '')) {
|
|
23
|
+
probe = caret - 1;
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
let start = probe;
|
|
29
|
+
while (start > 0 && !/\s/.test(input[start - 1] ?? ''))
|
|
30
|
+
start -= 1;
|
|
31
|
+
let end = probe + 1;
|
|
32
|
+
while (end < input.length && !/\s/.test(input[end] ?? ''))
|
|
33
|
+
end += 1;
|
|
34
|
+
const token = input.slice(start, end);
|
|
35
|
+
const prefix = prefixes.find((p) => token.startsWith(p));
|
|
36
|
+
if (!prefix)
|
|
37
|
+
return null;
|
|
38
|
+
return {
|
|
39
|
+
token,
|
|
40
|
+
prefix,
|
|
41
|
+
query: token.slice(prefix.length),
|
|
42
|
+
start,
|
|
43
|
+
end,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
// Rewrite the active token with `replacement` while preserving the rest of
|
|
47
|
+
// the input. `replacement` may be empty to delete the token. Trailing
|
|
48
|
+
// whitespace is collapsed.
|
|
49
|
+
export function replaceActiveToken(input, caretIndex, replacement, prefixes = ['/', '@']) {
|
|
50
|
+
const info = getActiveToken(input, prefixes, caretIndex);
|
|
51
|
+
if (!info)
|
|
52
|
+
return input;
|
|
53
|
+
const before = input.slice(0, info.start);
|
|
54
|
+
const after = input.slice(info.end);
|
|
55
|
+
return `${before}${replacement}${after}`.replace(/[ \t]+$/g, '');
|
|
56
|
+
}
|
|
57
|
+
// Replace the active token with `replacement` and report the caret position
|
|
58
|
+
// after it, so the host can sync caret state + focus. Unlike replaceActiveToken
|
|
59
|
+
// this preserves an intentional trailing space in `replacement` (only the
|
|
60
|
+
// empty-replacement / deletion case trims), and returns the caret so a mention
|
|
61
|
+
// insertion like '@/path ' lands the caret past the space — which dismisses the
|
|
62
|
+
// token and closes the picker. No active token → input unchanged, caret clamped.
|
|
63
|
+
// commitToken('msg @foo', 8, '@/a/b ') → { next: 'msg @/a/b ', caret: 10 }
|
|
64
|
+
// commitToken('say /model', 10, '') → { next: 'say', caret: 3 }
|
|
65
|
+
export function commitToken(input, caretIndex, replacement, prefixes = ['/', '@']) {
|
|
66
|
+
const info = getActiveToken(input, prefixes, caretIndex);
|
|
67
|
+
if (!info)
|
|
68
|
+
return { next: input, caret: clampCaretIndex(input, caretIndex) };
|
|
69
|
+
const before = input.slice(0, info.start);
|
|
70
|
+
const after = input.slice(info.end);
|
|
71
|
+
let next = `${before}${replacement}${after}`;
|
|
72
|
+
if (replacement === '')
|
|
73
|
+
next = next.replace(/[ \t]+$/g, '');
|
|
74
|
+
return { next, caret: Math.min(before.length + replacement.length, next.length) };
|
|
75
|
+
}
|
|
76
|
+
// Write a command's shortcut into the input for a composer-button open:
|
|
77
|
+
// replace the active token if the caret is on one, else append a new token
|
|
78
|
+
// (with a separating space when needed). Returns the new string and the caret
|
|
79
|
+
// position just after the inserted shortcut — the host syncs its caret to this
|
|
80
|
+
// so token detection lands on the freshly-written token.
|
|
81
|
+
// insertShortcut('', 0, '/mode') → { next: '/mode', caret: 5 }
|
|
82
|
+
// insertShortcut('/mod', 4, '/model') → { next: '/model', caret: 6 }
|
|
83
|
+
// insertShortcut('hi', 2, '/mode') → { next: 'hi /mode', caret: 8 }
|
|
84
|
+
export function insertShortcut(input, caretIndex, shortcut, prefixes = ['/', '@']) {
|
|
85
|
+
const info = getActiveToken(input, prefixes, caretIndex);
|
|
86
|
+
if (info) {
|
|
87
|
+
const next = `${input.slice(0, info.start)}${shortcut}${input.slice(info.end)}`;
|
|
88
|
+
return { next, caret: info.start + shortcut.length };
|
|
89
|
+
}
|
|
90
|
+
const sep = input.length > 0 && !/\s$/.test(input) ? ' ' : '';
|
|
91
|
+
const next = `${input}${sep}${shortcut}`;
|
|
92
|
+
return { next, caret: next.length };
|
|
93
|
+
}
|
|
94
|
+
// Match a typed token against a command's `shortcut`. shortcut may be a
|
|
95
|
+
// string ('/agent') or array (['/agent', '/persona']). Returns true if the
|
|
96
|
+
// typed token starts with any registered shortcut — keeps the menu open while
|
|
97
|
+
// the user types more after the sentinel.
|
|
98
|
+
export function shortcutMatchesToken(shortcut, token) {
|
|
99
|
+
if (!shortcut)
|
|
100
|
+
return false;
|
|
101
|
+
const shortcuts = Array.isArray(shortcut) ? shortcut : [shortcut];
|
|
102
|
+
const lower = token.toLowerCase();
|
|
103
|
+
return shortcuts.some((s) => lower.startsWith(s.toLowerCase()));
|
|
104
|
+
}
|
|
105
|
+
// Strip a submenu's sentinel (and a following '=' or whitespace) off the raw
|
|
106
|
+
// token query, leaving the inline-filter text. The sentinel is in shortcut
|
|
107
|
+
// form ('/model'); the raw query excludes the prefix ('model=gemi') so the
|
|
108
|
+
// prefix is dropped before matching. With an array, the first matching
|
|
109
|
+
// sentinel wins. Returns the raw query unchanged when no sentinel is given or
|
|
110
|
+
// none matches.
|
|
111
|
+
// stripSentinel('model=gemi', '/model') === 'gemi'
|
|
112
|
+
// stripSentinel('model', '/model') === ''
|
|
113
|
+
// stripSentinel('rout', '/model') === 'rout'
|
|
114
|
+
// stripSentinel('persona x', ['/agent','/persona']) === 'x'
|
|
115
|
+
export function stripSentinel(rawQuery, sentinel) {
|
|
116
|
+
if (!sentinel)
|
|
117
|
+
return rawQuery;
|
|
118
|
+
const sentinels = Array.isArray(sentinel) ? sentinel : [sentinel];
|
|
119
|
+
const lower = rawQuery.toLowerCase();
|
|
120
|
+
for (const s of sentinels) {
|
|
121
|
+
const bare = s.replace(/^[/@]/, '');
|
|
122
|
+
if (lower.startsWith(bare.toLowerCase())) {
|
|
123
|
+
return rawQuery.slice(bare.length).replace(/^[=\s]+/, '');
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return rawQuery;
|
|
127
|
+
}
|
|
128
|
+
// True when the token still starts with any of the frame's sentinels — the
|
|
129
|
+
// picker keeps a pushed submenu open while this holds, and collapses to the
|
|
130
|
+
// root command list once it doesn't.
|
|
131
|
+
export function tokenMatchesSentinel(token, sentinel) {
|
|
132
|
+
if (!sentinel)
|
|
133
|
+
return true;
|
|
134
|
+
const sentinels = Array.isArray(sentinel) ? sentinel : [sentinel];
|
|
135
|
+
const lower = token.toLowerCase();
|
|
136
|
+
return sentinels.some((s) => lower.startsWith(s.toLowerCase()));
|
|
137
|
+
}
|
|
138
|
+
// Returns the canonical shortcut for a command — first entry when array,
|
|
139
|
+
// the string itself when single, undefined when unset. Used by composer
|
|
140
|
+
// buttons to rewrite the input to match the slash-typed form.
|
|
141
|
+
export function canonicalShortcut(shortcut) {
|
|
142
|
+
if (!shortcut)
|
|
143
|
+
return undefined;
|
|
144
|
+
return Array.isArray(shortcut) ? shortcut[0] : shortcut;
|
|
145
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type Dispatch, type KeyboardEvent as ReactKeyboardEvent, type ReactNode, type SetStateAction } from 'react';
|
|
2
|
+
import type { PickerAction, HostCtx } from '../../types/host.js';
|
|
3
|
+
import type { BindingPath } from '../../types/component-graph.js';
|
|
4
|
+
export interface UseChatPickerParams {
|
|
5
|
+
input: string;
|
|
6
|
+
setInput: Dispatch<SetStateAction<string>>;
|
|
7
|
+
caretIndex?: number | null;
|
|
8
|
+
panelDataContext?: BindingPath;
|
|
9
|
+
slashSlot?: string;
|
|
10
|
+
mentionSlot?: string;
|
|
11
|
+
mentionProvider?: (host: HostCtx) => PickerAction[] | Promise<PickerAction[]>;
|
|
12
|
+
prefixes?: string[];
|
|
13
|
+
onCaretChange?: (caret: number) => void;
|
|
14
|
+
}
|
|
15
|
+
export interface UseChatPickerResult {
|
|
16
|
+
menuVisible: boolean;
|
|
17
|
+
activeSentinel: string | string[] | undefined;
|
|
18
|
+
pickerNode: ReactNode | null;
|
|
19
|
+
handleMenuKeyDown(e: ReactKeyboardEvent<HTMLTextAreaElement | HTMLInputElement>): boolean;
|
|
20
|
+
openCommand(commandId: string): number | null;
|
|
21
|
+
closeMenu(): void;
|
|
22
|
+
}
|
|
23
|
+
export declare function useChatPicker(params: UseChatPickerParams): UseChatPickerResult;
|
|
24
|
+
export declare function sentinelHas(sentinel: string | string[] | undefined, target: string): boolean;
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect, useMemo, useRef, useState, } from 'react';
|
|
3
|
+
import { useScena } from '../../react/ScenaProvider.js';
|
|
4
|
+
import { canonicalShortcut, commitToken, getActiveToken, insertShortcut, stripSentinel, tokenMatchesSentinel, } from './token.js';
|
|
5
|
+
import { ActionList } from './ActionList.js';
|
|
6
|
+
// Per-instance host hook for chat-style inputs. Owns:
|
|
7
|
+
// - Token detection on each keystroke
|
|
8
|
+
// - Picker stack (push/replace/back/openInline)
|
|
9
|
+
// - HostCtx factory bound to this instance
|
|
10
|
+
// - Composer button → typing convergence via openCommand(id)
|
|
11
|
+
//
|
|
12
|
+
// Each chat panel owns one of these. Ten panels = ten hooks, no shared state.
|
|
13
|
+
// Reference-stable default so consumers that don't pass `prefixes` don't
|
|
14
|
+
// invalidate every memo that depends on it (tokenInfo → host → ActionList
|
|
15
|
+
// effect → setResolved → re-render). The destructure-default was a fresh
|
|
16
|
+
// array every call and caused "Maximum update depth exceeded".
|
|
17
|
+
const DEFAULT_PREFIXES = ['/', '@'];
|
|
18
|
+
export function useChatPicker(params) {
|
|
19
|
+
const { input, setInput, caretIndex, panelDataContext, slashSlot = 'chat:input/', mentionSlot = 'chat:input@', mentionProvider, prefixes = DEFAULT_PREFIXES, onCaretChange, } = params;
|
|
20
|
+
const scena = useScena();
|
|
21
|
+
// Picker stack — top is what renders. Reset by token changes.
|
|
22
|
+
const [stack, setStack] = useState([]);
|
|
23
|
+
const stackRef = useRef(stack);
|
|
24
|
+
stackRef.current = stack;
|
|
25
|
+
// Imperative handle on the visible ActionList. handleMenuKeyDown routes
|
|
26
|
+
// navigation keys through it so only the focused panel's picker reacts —
|
|
27
|
+
// ActionList's document-level listener is disabled here via manageKeys.
|
|
28
|
+
const actionListRef = useRef(null);
|
|
29
|
+
// Token detection on each render.
|
|
30
|
+
const tokenInfo = useMemo(() => getActiveToken(input, prefixes, caretIndex), [input, prefixes, caretIndex]);
|
|
31
|
+
const menuVisible = tokenInfo !== null;
|
|
32
|
+
// Root frame for the current token's prefix.
|
|
33
|
+
const rootSpecFor = useCallback((query) => tokenInfo?.prefix === '@'
|
|
34
|
+
? mentionProvider
|
|
35
|
+
? { provider: mentionProvider, footerHints: true, rich: true }
|
|
36
|
+
: { query: { slot: mentionSlot, q: query }, footerHints: true, rich: true }
|
|
37
|
+
: { query: { slot: slashSlot, q: query }, footerHints: true, rich: true }, [tokenInfo?.prefix, mentionProvider, mentionSlot, slashSlot]);
|
|
38
|
+
// When the active token changes, decide what the root frame should be.
|
|
39
|
+
// - '/' token → query slot (slashSlot)
|
|
40
|
+
// - '@' token → mentionProvider (if any), else mention slot
|
|
41
|
+
// - no token → empty stack
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
if (!tokenInfo) {
|
|
44
|
+
if (stackRef.current.length > 0)
|
|
45
|
+
setStack([]);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const top = stackRef.current[stackRef.current.length - 1];
|
|
49
|
+
const isRoot = stackRef.current.length <= 1;
|
|
50
|
+
if (isRoot) {
|
|
51
|
+
setStack([{ spec: rootSpecFor(tokenInfo.query) }]);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
// A submenu (pushed by a command) is open. If it declared a sentinel and
|
|
55
|
+
// the token no longer starts with it (user edited '/model' → '/rout'),
|
|
56
|
+
// collapse all the way back to the root command list.
|
|
57
|
+
const topSpec = top && 'spec' in top ? top.spec : undefined;
|
|
58
|
+
if (topSpec?.sentinel && !tokenMatchesSentinel(tokenInfo.token, topSpec.sentinel)) {
|
|
59
|
+
setStack([{ spec: rootSpecFor(tokenInfo.query) }]);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (topSpec?.query) {
|
|
63
|
+
// Re-resolve query-mode submenu against the new q.
|
|
64
|
+
setStack((cur) => {
|
|
65
|
+
const next = cur.slice(0, -1);
|
|
66
|
+
next.push({ spec: { ...topSpec, query: { ...topSpec.query, q: tokenInfo.query } } });
|
|
67
|
+
return next;
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
// provider/items frames: left as-is; their provider re-runs on its own
|
|
71
|
+
// when host.query (the post-sentinel filter text) changes.
|
|
72
|
+
}, [tokenInfo, rootSpecFor]);
|
|
73
|
+
// Sentinel of the visible submenu (if it declared one) and the post-sentinel
|
|
74
|
+
// filter text. For '/model=gemi' with sentinel '/model', filterQuery='gemi'
|
|
75
|
+
// — providers read host.query to filter inline.
|
|
76
|
+
const activeSentinel = useMemo(() => {
|
|
77
|
+
const t = stack[stack.length - 1];
|
|
78
|
+
return t && 'spec' in t ? t.spec.sentinel : undefined;
|
|
79
|
+
}, [stack]);
|
|
80
|
+
const filterQuery = useMemo(() => stripSentinel(tokenInfo?.query ?? '', activeSentinel), [tokenInfo, activeSentinel]);
|
|
81
|
+
// Host callbacks — bound to this hook's state. Re-created when setInput
|
|
82
|
+
// changes so insertAtCursor / replaceActiveToken always see the latest
|
|
83
|
+
// dispatcher.
|
|
84
|
+
const host = useMemo(() => ({
|
|
85
|
+
pushList: (s) => setStack((cur) => [...cur, { spec: s }]),
|
|
86
|
+
replaceList: (s) => setStack((cur) => {
|
|
87
|
+
const next = cur.slice(0, -1);
|
|
88
|
+
next.push({ spec: s });
|
|
89
|
+
return next;
|
|
90
|
+
}),
|
|
91
|
+
openInline: (componentId) => setStack((cur) => [
|
|
92
|
+
...cur,
|
|
93
|
+
{ component: _jsx(MissingInline, { name: componentId }) },
|
|
94
|
+
]),
|
|
95
|
+
openPopover: () => undefined, // chat-style hosts don't open detached popovers from within
|
|
96
|
+
back: () => setStack((cur) => (cur.length > 1 ? cur.slice(0, -1) : cur)),
|
|
97
|
+
closeMenu: () => {
|
|
98
|
+
setStack([]);
|
|
99
|
+
const r = commitToken(input, caretIndex, '', prefixes);
|
|
100
|
+
setInput(r.next);
|
|
101
|
+
onCaretChange?.(r.caret);
|
|
102
|
+
},
|
|
103
|
+
keepOpen: () => undefined,
|
|
104
|
+
insertAtCursor: (text) => {
|
|
105
|
+
const r = commitToken(input, caretIndex, text, prefixes);
|
|
106
|
+
setInput(r.next);
|
|
107
|
+
onCaretChange?.(r.caret);
|
|
108
|
+
},
|
|
109
|
+
replaceActiveToken: (replacement) => {
|
|
110
|
+
const r = commitToken(input, caretIndex, replacement, prefixes);
|
|
111
|
+
setInput(r.next);
|
|
112
|
+
onCaretChange?.(r.caret);
|
|
113
|
+
},
|
|
114
|
+
query: filterQuery,
|
|
115
|
+
}), [input, caretIndex, prefixes, setInput, filterQuery, onCaretChange]);
|
|
116
|
+
const top = stack[stack.length - 1];
|
|
117
|
+
// Hoisted out of the conditional so `onBack` keeps a stable identity. Inline,
|
|
118
|
+
// it minted a new arrow every render and defeated the useCallback below that
|
|
119
|
+
// depends on it.
|
|
120
|
+
const popStack = useCallback(() => setStack((cur) => cur.slice(0, -1)), []);
|
|
121
|
+
const onBack = stack.length > 1 ? popStack : undefined;
|
|
122
|
+
// Keyboard hook the host wires to its textarea onKeyDown. Returns true when
|
|
123
|
+
// handled so the host knows whether to call its own default behavior.
|
|
124
|
+
// Navigation keys (ArrowDown/Up/Enter/Tab) are routed through the visible
|
|
125
|
+
// ActionList's imperative controller — this scopes them to THIS panel's
|
|
126
|
+
// picker even when another chat panel also has a picker open.
|
|
127
|
+
const handleMenuKeyDown = useCallback((e) => {
|
|
128
|
+
if (!menuVisible)
|
|
129
|
+
return false;
|
|
130
|
+
if (e.key === 'Escape') {
|
|
131
|
+
e.preventDefault();
|
|
132
|
+
host.closeMenu();
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
if (e.key === 'ArrowLeft' && onBack) {
|
|
136
|
+
const target = e.target;
|
|
137
|
+
if (target.selectionStart === target.selectionEnd) {
|
|
138
|
+
if (tokenInfo && target.selectionStart === tokenInfo.start + tokenInfo.prefix.length) {
|
|
139
|
+
e.preventDefault();
|
|
140
|
+
onBack();
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if (e.key === 'ArrowDown' ||
|
|
146
|
+
e.key === 'ArrowUp' ||
|
|
147
|
+
e.key === 'Enter' ||
|
|
148
|
+
e.key === 'Tab') {
|
|
149
|
+
return actionListRef.current?.handleKey(e) ?? false;
|
|
150
|
+
}
|
|
151
|
+
return false;
|
|
152
|
+
}, [menuVisible, host, onBack, tokenInfo]);
|
|
153
|
+
// openCommand: composer buttons converge with typing. Rewrites the input
|
|
154
|
+
// to the command's canonical shortcut, then triggers picker via the token
|
|
155
|
+
// detection cycle.
|
|
156
|
+
// Opens a command's picker from a composer button. Writes the command's
|
|
157
|
+
// shortcut into the input (replacing the active token, or appending one when
|
|
158
|
+
// the input has none — the button click should work on an empty composer),
|
|
159
|
+
// seeds the root frame, runs the command, and returns the caret position
|
|
160
|
+
// just after the inserted token so the host can sync its caret state + focus
|
|
161
|
+
// the input (otherwise keyboard nav is dead and token detection misfires).
|
|
162
|
+
const openCommand = useCallback((commandId) => {
|
|
163
|
+
const cmd = scena.commands.get(commandId);
|
|
164
|
+
if (!cmd)
|
|
165
|
+
return null;
|
|
166
|
+
const sc = canonicalShortcut(cmd.shortcut);
|
|
167
|
+
let newCaret = null;
|
|
168
|
+
if (sc) {
|
|
169
|
+
const { next, caret } = insertShortcut(input, caretIndex, sc, prefixes);
|
|
170
|
+
newCaret = caret;
|
|
171
|
+
setInput(next);
|
|
172
|
+
// Seed the root command frame first so a submenu the command pushes lands
|
|
173
|
+
// at depth 2 — the token effect only replaces the root frame at depth ≤ 1
|
|
174
|
+
// and would otherwise clobber a freshly-pushed submenu.
|
|
175
|
+
setStack([{ spec: { query: { slot: slashSlot, q: sc.replace(/^[/@]/, '') }, footerHints: true, rich: true } }]);
|
|
176
|
+
}
|
|
177
|
+
// Immediate dispatch so the command's `run` opens its submenu through host
|
|
178
|
+
// (or toggles + closes for toggle commands).
|
|
179
|
+
void scena.commands.execute(commandId, undefined, {
|
|
180
|
+
source: 'menu',
|
|
181
|
+
dataContext: panelDataContext,
|
|
182
|
+
host,
|
|
183
|
+
});
|
|
184
|
+
return newCaret;
|
|
185
|
+
}, [scena, panelDataContext, input, caretIndex, prefixes, host, setInput, slashSlot]);
|
|
186
|
+
const closeMenu = useCallback(() => host.closeMenu(), [host]);
|
|
187
|
+
const pickerNode = top
|
|
188
|
+
? 'component' in top
|
|
189
|
+
? top.component
|
|
190
|
+
: (_jsx(ActionList, { ref: actionListRef, spec: top.spec, hostCtx: host, onBack: onBack, dataContext: panelDataContext, manageKeys: false }))
|
|
191
|
+
: null;
|
|
192
|
+
return {
|
|
193
|
+
menuVisible,
|
|
194
|
+
activeSentinel,
|
|
195
|
+
pickerNode,
|
|
196
|
+
handleMenuKeyDown,
|
|
197
|
+
openCommand,
|
|
198
|
+
closeMenu,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
// True when `sentinel` (a frame's single/alias sentinel) contains `target`.
|
|
202
|
+
// Helper for hosts checking "is my submenu the one currently open?".
|
|
203
|
+
export function sentinelHas(sentinel, target) {
|
|
204
|
+
if (!sentinel)
|
|
205
|
+
return false;
|
|
206
|
+
return Array.isArray(sentinel) ? sentinel.includes(target) : sentinel === target;
|
|
207
|
+
}
|
|
208
|
+
function MissingInline({ name }) {
|
|
209
|
+
return _jsxs("div", { style: { padding: 12 }, children: ["[component: ", name, "]"] });
|
|
210
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
.oo-breadcrumb {
|
|
2
|
+
display: flex;
|
|
3
|
+
align-items: center;
|
|
4
|
+
gap: 4px;
|
|
5
|
+
font-size: var(--oo-font-size-sm);
|
|
6
|
+
color: var(--oo-color-fg);
|
|
7
|
+
}
|
|
8
|
+
.oo-breadcrumb__sep {
|
|
9
|
+
color: var(--oo-color-muted);
|
|
10
|
+
margin: 0 4px;
|
|
11
|
+
}
|
|
12
|
+
.oo-breadcrumb__link {
|
|
13
|
+
background: none;
|
|
14
|
+
border: none;
|
|
15
|
+
padding: 0;
|
|
16
|
+
font: inherit;
|
|
17
|
+
color: var(--oo-color-accent);
|
|
18
|
+
cursor: pointer;
|
|
19
|
+
text-decoration: none;
|
|
20
|
+
}
|
|
21
|
+
.oo-breadcrumb__link:hover {
|
|
22
|
+
text-decoration: underline;
|
|
23
|
+
}
|
|
24
|
+
.oo-breadcrumb__item {
|
|
25
|
+
color: var(--oo-color-muted);
|
|
26
|
+
}
|
|
27
|
+
.oo-breadcrumb__item[data-current='true'] {
|
|
28
|
+
color: var(--oo-color-fg);
|
|
29
|
+
font-weight: 500;
|
|
30
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type CSSProperties, type ReactNode } from 'react';
|
|
2
|
+
import './Breadcrumb.css';
|
|
3
|
+
export interface BreadcrumbItem {
|
|
4
|
+
label: ReactNode;
|
|
5
|
+
href?: string;
|
|
6
|
+
onClick?: () => void;
|
|
7
|
+
}
|
|
8
|
+
export interface BreadcrumbProps {
|
|
9
|
+
items: BreadcrumbItem[];
|
|
10
|
+
separator?: ReactNode;
|
|
11
|
+
style?: CSSProperties;
|
|
12
|
+
className?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function Breadcrumb({ items, separator, style, className }: BreadcrumbProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Fragment } from 'react';
|
|
3
|
+
import './Breadcrumb.css';
|
|
4
|
+
// Trail of items separated by `/`. Last item renders as plain text (current
|
|
5
|
+
// location); earlier items render as links (href) or buttons (onClick) or
|
|
6
|
+
// muted plain text when neither is set.
|
|
7
|
+
export function Breadcrumb({ items, separator = '/', style, className }) {
|
|
8
|
+
return (_jsx("nav", { className: ['oo-breadcrumb', className].filter(Boolean).join(' '), "aria-label": "Breadcrumb", style: style, children: items.map((item, i) => {
|
|
9
|
+
const isLast = i === items.length - 1;
|
|
10
|
+
return (_jsxs(Fragment, { children: [i > 0 ? _jsx("span", { className: "oo-breadcrumb__sep", "aria-hidden": true, children: separator }) : null, item.href ? (_jsx("a", { className: "oo-breadcrumb__link", href: item.href, "aria-current": isLast ? 'page' : undefined, children: item.label })) : item.onClick && !isLast ? (_jsx("button", { type: "button", className: "oo-breadcrumb__link", onClick: item.onClick, children: item.label })) : (_jsx("span", { className: "oo-breadcrumb__item", "data-current": isLast ? 'true' : undefined, children: item.label }))] }, i));
|
|
11
|
+
}) }));
|
|
12
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
.oo-link {
|
|
2
|
+
color: var(--oo-color-accent);
|
|
3
|
+
text-decoration: none;
|
|
4
|
+
cursor: pointer;
|
|
5
|
+
}
|
|
6
|
+
.oo-link:hover {
|
|
7
|
+
text-decoration: underline;
|
|
8
|
+
}
|
|
9
|
+
.oo-link[data-disabled='true'] {
|
|
10
|
+
color: var(--oo-color-muted);
|
|
11
|
+
cursor: not-allowed;
|
|
12
|
+
text-decoration: none;
|
|
13
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { CSSProperties, ReactNode } from 'react';
|
|
2
|
+
import './Link.css';
|
|
3
|
+
export type LinkKind = 'tel' | 'email' | 'web';
|
|
4
|
+
export declare function resolveLinkHref(raw: string): {
|
|
5
|
+
href: string;
|
|
6
|
+
kind: LinkKind;
|
|
7
|
+
};
|
|
8
|
+
export interface LinkProps {
|
|
9
|
+
href?: string;
|
|
10
|
+
label?: ReactNode;
|
|
11
|
+
children?: ReactNode;
|
|
12
|
+
external?: boolean;
|
|
13
|
+
disabled?: boolean;
|
|
14
|
+
onClick?: (e: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => void;
|
|
15
|
+
weight?: number;
|
|
16
|
+
style?: CSSProperties;
|
|
17
|
+
}
|
|
18
|
+
export declare function Link({ href, label, children, external, disabled, onClick, weight, style }: LinkProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { weightStyle } from '../_utils.js';
|
|
3
|
+
import './Link.css';
|
|
4
|
+
const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
5
|
+
// Phone-ish: optional leading +, then digits with spaces/dots/dashes/parens.
|
|
6
|
+
const TEL_RE = /^\+?\d[\d\s().-]*$/;
|
|
7
|
+
// Detect the target kind and normalize to a spec-compliant href:
|
|
8
|
+
// a phone number (or tel: / tel://) → tel:<number> (dials)
|
|
9
|
+
// an email address (or mailto:/email://) → mailto:<addr> (composes)
|
|
10
|
+
// any other already-schemed value → kept as-is
|
|
11
|
+
// everything else → https://<value>
|
|
12
|
+
export function resolveLinkHref(raw) {
|
|
13
|
+
const v = raw.trim();
|
|
14
|
+
// Accept the non-standard tel:// / email:// forms but emit the real scheme.
|
|
15
|
+
if (/^tel:\/\//i.test(v))
|
|
16
|
+
return { href: `tel:${v.slice(6)}`, kind: 'tel' };
|
|
17
|
+
if (/^email:\/\//i.test(v))
|
|
18
|
+
return { href: `mailto:${v.slice(8)}`, kind: 'email' };
|
|
19
|
+
if (/^mailto:/i.test(v))
|
|
20
|
+
return { href: v, kind: 'email' };
|
|
21
|
+
if (/^tel:/i.test(v))
|
|
22
|
+
return { href: v, kind: 'tel' };
|
|
23
|
+
// Already carries a real scheme (http://, https://, ftp://, …) — keep it.
|
|
24
|
+
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(v))
|
|
25
|
+
return { href: v, kind: 'web' };
|
|
26
|
+
if (EMAIL_RE.test(v))
|
|
27
|
+
return { href: `mailto:${v}`, kind: 'email' };
|
|
28
|
+
if (TEL_RE.test(v))
|
|
29
|
+
return { href: `tel:${v}`, kind: 'tel' };
|
|
30
|
+
return { href: `https://${v}`, kind: 'web' };
|
|
31
|
+
}
|
|
32
|
+
export function Link({ href, label, children, external, disabled, onClick, weight, style }) {
|
|
33
|
+
const resolved = href ? resolveLinkHref(href) : null;
|
|
34
|
+
const content = children ?? label ?? href ?? '';
|
|
35
|
+
const isExternal = external ?? resolved?.kind === 'web';
|
|
36
|
+
return (_jsx("a", { className: "oo-link", href: disabled ? undefined : resolved?.href, target: isExternal ? '_blank' : undefined, rel: isExternal ? 'noopener noreferrer' : undefined, "data-kind": resolved?.kind, "data-disabled": disabled || undefined, style: { ...weightStyle(weight), ...style }, onClick: (e) => {
|
|
37
|
+
if (disabled) {
|
|
38
|
+
e.preventDefault();
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
onClick?.(e);
|
|
42
|
+
}, children: content }));
|
|
43
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
.oo-tabs {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
flex: 1;
|
|
5
|
+
min-height: 0;
|
|
6
|
+
}
|
|
7
|
+
.oo-tabs__strip {
|
|
8
|
+
display: flex;
|
|
9
|
+
gap: 2px;
|
|
10
|
+
border-bottom: 1px solid var(--oo-color-border);
|
|
11
|
+
}
|
|
12
|
+
.oo-tabs__tab {
|
|
13
|
+
padding: var(--oo-spacing-xs) var(--oo-spacing-md);
|
|
14
|
+
border: none;
|
|
15
|
+
border-bottom: 2px solid transparent;
|
|
16
|
+
background: transparent;
|
|
17
|
+
color: var(--oo-color-muted);
|
|
18
|
+
cursor: pointer;
|
|
19
|
+
font-size: var(--oo-font-size-sm);
|
|
20
|
+
transition: color 0.327s, border-color 0.327s;
|
|
21
|
+
}
|
|
22
|
+
.oo-tabs__tab:hover {
|
|
23
|
+
background: none;
|
|
24
|
+
color: inherit;
|
|
25
|
+
}
|
|
26
|
+
.oo-tabs__tab[aria-selected='true'] {
|
|
27
|
+
color: inherit;
|
|
28
|
+
border-bottom-color: var(--oo-color-accent);
|
|
29
|
+
}
|
|
30
|
+
.oo-tabs__tab[disabled] {
|
|
31
|
+
cursor: not-allowed;
|
|
32
|
+
opacity: 0.5;
|
|
33
|
+
}
|
|
34
|
+
.oo-tabs__panel {
|
|
35
|
+
flex: 1;
|
|
36
|
+
min-height: 0;
|
|
37
|
+
padding: var(--oo-spacing-sm);
|
|
38
|
+
overflow: auto;
|
|
39
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type CSSProperties, type ReactNode } from 'react';
|
|
2
|
+
import './Tabs.css';
|
|
3
|
+
export interface TabsTab {
|
|
4
|
+
key?: string;
|
|
5
|
+
title?: string;
|
|
6
|
+
child?: ReactNode;
|
|
7
|
+
label?: string;
|
|
8
|
+
content?: ReactNode;
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface TabsProps {
|
|
12
|
+
tabs?: TabsTab[];
|
|
13
|
+
weight?: number;
|
|
14
|
+
value?: string;
|
|
15
|
+
defaultValue?: string;
|
|
16
|
+
style?: CSSProperties;
|
|
17
|
+
}
|
|
18
|
+
export declare function Tabs({ tabs, value, defaultValue, weight, style }: TabsProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from 'react';
|
|
3
|
+
import { useWriteBack } from '../../react/mount-context.js';
|
|
4
|
+
import { weightStyle } from '../_utils.js';
|
|
5
|
+
import './Tabs.css';
|
|
6
|
+
function tabTitle(t) {
|
|
7
|
+
return t.title ?? t.label ?? '';
|
|
8
|
+
}
|
|
9
|
+
function tabChild(t) {
|
|
10
|
+
return t.child ?? t.content;
|
|
11
|
+
}
|
|
12
|
+
export function Tabs({ tabs = [], value, defaultValue, weight, style }) {
|
|
13
|
+
const writeValue = useWriteBack('value');
|
|
14
|
+
// Stable per-tab key: explicit `key`, else the index. Selection tracks this,
|
|
15
|
+
// NOT the (possibly translated) title — so a language switch keeps the tab.
|
|
16
|
+
const keys = tabs.map((t, i) => t.key ?? String(i));
|
|
17
|
+
const [internal, setInternal] = useState(value ?? defaultValue ?? keys[0]);
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
if (value === undefined)
|
|
20
|
+
return;
|
|
21
|
+
setInternal((current) => (current === value ? current : value));
|
|
22
|
+
}, [value]);
|
|
23
|
+
function select(key) {
|
|
24
|
+
setInternal(key);
|
|
25
|
+
writeValue(key);
|
|
26
|
+
}
|
|
27
|
+
const activeIdx = [keys.indexOf(internal ?? ''), keys.indexOf(defaultValue ?? ''), 0].find((i) => i >= 0) ?? 0;
|
|
28
|
+
const active = tabs[activeIdx];
|
|
29
|
+
// Default `flex: 1` fills the surface; weightStyle wins when set.
|
|
30
|
+
const inlineStyle = {
|
|
31
|
+
// display: 'flex',
|
|
32
|
+
// flexDirection: 'column',
|
|
33
|
+
// minHeight: 0,
|
|
34
|
+
// flex: 1,
|
|
35
|
+
...weightStyle(weight),
|
|
36
|
+
...style,
|
|
37
|
+
};
|
|
38
|
+
return (_jsxs("div", { className: "oo-tabs", style: inlineStyle, children: [_jsx("div", { className: "oo-tabs__strip", role: "tablist", children: tabs.map((t, i) => {
|
|
39
|
+
const title = tabTitle(t);
|
|
40
|
+
return (_jsx("button", { className: "oo-tabs__tab", role: "tab", "aria-selected": t === active, disabled: t.disabled, onClick: () => select(keys[i]), style: {
|
|
41
|
+
// padding: 'var(--oo-spacing-xs) var(--oo-spacing-md)',
|
|
42
|
+
// fontSize: 'var(--oo-font-size-sm)',
|
|
43
|
+
// border: 'none',
|
|
44
|
+
// borderBottom:
|
|
45
|
+
// t === active
|
|
46
|
+
// ? '2px solid var(--oo-color-accent)'
|
|
47
|
+
// : '2px solid transparent',
|
|
48
|
+
// background: 'transparent',
|
|
49
|
+
// color: t === active ? 'inherit' : 'var(--oo-color-muted)',
|
|
50
|
+
// cursor: t.disabled ? 'not-allowed' : 'pointer',
|
|
51
|
+
// opacity: t.disabled ? 0.5 : 1,
|
|
52
|
+
// font: 'inherit',
|
|
53
|
+
}, children: title }, keys[i]));
|
|
54
|
+
}) }), _jsx("div", { role: "tabpanel", style: { flex: 1, minHeight: 0, overflow: 'auto' }, children: active ? tabChild(active) : null })] }));
|
|
55
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/* Generic chrome strip: layout toolbars, panel headers, titlebar slots.
|
|
2
|
+
* Direction / alignment / gap come from data-attrs and inline props so the
|
|
3
|
+
* same class works for horizontal control rows AND vertical side rails. */
|
|
4
|
+
.oo-toolbar {
|
|
5
|
+
display: flex;
|
|
6
|
+
align-items: center;
|
|
7
|
+
gap: var(--oo-spacing-sm);
|
|
8
|
+
padding: var(--oo-spacing-xs) var(--oo-spacing-sm);
|
|
9
|
+
border-bottom: 1px solid var(--oo-color-border);
|
|
10
|
+
background: var(--oo-color-surface);
|
|
11
|
+
color: var(--oo-color-fg);
|
|
12
|
+
font-size: var(--oo-font-size-sm);
|
|
13
|
+
flex: 0 0 auto;
|
|
14
|
+
min-height: var(--oo-toolbar-height, 32px);
|
|
15
|
+
user-select: none;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.oo-toolbar[data-direction='column'] {
|
|
19
|
+
flex-direction: column;
|
|
20
|
+
align-items: stretch;
|
|
21
|
+
border-bottom: none;
|
|
22
|
+
border-right: 1px solid var(--oo-color-border);
|
|
23
|
+
min-height: 0;
|
|
24
|
+
min-width: var(--oo-toolbar-height, 32px);
|
|
25
|
+
}
|