@cosmicdrift/kumiko-renderer 0.373.0 → 0.377.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/app/action-form-support.d.ts +58 -0
- package/dist/app/action-form-support.js +96 -0
- package/dist/app/client-actions.d.ts +46 -0
- package/dist/app/client-actions.js +60 -0
- package/dist/app/column-renderers.js +1 -1
- package/dist/app/config-edit-shim.js +1 -1
- package/dist/app/content-preview.js +1 -6
- package/dist/app/custom-screens.js +1 -1
- package/dist/app/dashboard-body.d.ts +1 -1
- package/dist/app/entity-list-expanded-row.d.ts +1 -1
- package/dist/app/entity-list-expanded-row.js +4 -0
- package/dist/app/form-schema.d.ts +1 -1
- package/dist/app/form-schema.js +2 -2
- package/dist/app/header-actions-bar.d.ts +19 -0
- package/dist/app/header-actions-bar.js +30 -0
- package/dist/app/kumiko-screen.d.ts +1 -1
- package/dist/app/kumiko-screen.js +134 -66
- package/dist/app/list-empty-content.d.ts +2 -2
- package/dist/app/list-empty-content.js +3 -2
- package/dist/app/list-facets.d.ts +1 -1
- package/dist/app/nav.js +2 -2
- package/dist/app/query-state-banners.d.ts +1 -1
- package/dist/app/reference-link.d.ts +1 -1
- package/dist/app/row-actions.d.ts +10 -2
- package/dist/app/row-actions.js +85 -6
- package/dist/app/secret-mint-body.d.ts +1 -1
- package/dist/app/secrets-edit-body.d.ts +1 -1
- package/dist/app/use-record-actions.d.ts +12 -0
- package/dist/app/use-record-actions.js +30 -0
- package/dist/app/write-failed-error.d.ts +1 -1
- package/dist/components/availability-options-select.d.ts +1 -1
- package/dist/components/embedded-list-field.d.ts +1 -1
- package/dist/components/embedded-list-field.js +1 -1
- package/dist/components/file-list-field.d.ts +1 -1
- package/dist/components/grid-cell-for-field.d.ts +4 -2
- package/dist/components/grid-cell-for-field.js +2 -2
- package/dist/components/multi-select-checkboxes.d.ts +1 -1
- package/dist/components/multi-select-checkboxes.js +1 -1
- package/dist/components/query-options-select.d.ts +1 -1
- package/dist/components/related-list-section.d.ts +1 -1
- package/dist/components/related-list-section.js +16 -3
- package/dist/components/render-edit-logic.d.ts +1 -1
- package/dist/components/render-edit-types.d.ts +2 -2
- package/dist/components/render-edit.d.ts +1 -1
- package/dist/components/render-edit.js +5 -3
- package/dist/components/render-field.d.ts +5 -2
- package/dist/components/render-field.js +103 -9
- package/dist/components/render-list.d.ts +1 -1
- package/dist/components/render-list.js +20 -4
- package/dist/components/write-form-section.d.ts +1 -1
- package/dist/components/write-form-section.js +1 -1
- package/dist/context/dispatcher-context.d.ts +1 -1
- package/dist/format-when.js +1 -1
- package/dist/headless/apex/css.d.ts +3 -0
- package/dist/headless/apex/css.js +298 -0
- package/dist/headless/apex/index.d.ts +236 -0
- package/dist/headless/apex/index.js +377 -0
- package/dist/headless/apex/lightbox.d.ts +4 -0
- package/dist/headless/apex/lightbox.js +81 -0
- package/dist/headless/contracts/asset.d.ts +15 -0
- package/dist/headless/contracts/asset.js +17 -0
- package/dist/headless/contracts/index.d.ts +3 -0
- package/dist/headless/contracts/index.js +1 -0
- package/dist/headless/contracts/locale.d.ts +7 -0
- package/dist/headless/contracts/locale.js +11 -0
- package/dist/headless/contracts/primitives.d.ts +93 -0
- package/dist/headless/contracts/primitives.js +21 -0
- package/dist/headless/dispatcher/index.d.ts +2 -0
- package/dist/headless/dispatcher/index.js +1 -0
- package/dist/headless/dispatcher/types.d.ts +86 -0
- package/dist/headless/dispatcher/types.js +34 -0
- package/dist/headless/form/form-controller.d.ts +2 -0
- package/dist/headless/form/form-controller.js +409 -0
- package/dist/headless/form/index.d.ts +3 -0
- package/dist/headless/form/index.js +2 -0
- package/dist/headless/form/types.d.ts +76 -0
- package/dist/headless/form/types.js +1 -0
- package/dist/headless/form/zod-bridge.d.ts +10 -0
- package/dist/headless/form/zod-bridge.js +103 -0
- package/dist/headless/format/document-values.d.ts +12 -0
- package/dist/headless/format/document-values.js +70 -0
- package/dist/headless/format/escape.d.ts +5 -0
- package/dist/headless/format/escape.js +75 -0
- package/dist/headless/format/html-template.d.ts +9 -0
- package/dist/headless/format/html-template.js +39 -0
- package/dist/headless/format/index.d.ts +12 -0
- package/dist/headless/format/index.js +237 -0
- package/dist/headless/format/money.d.ts +3 -0
- package/dist/headless/format/money.js +39 -0
- package/dist/headless/index.d.ts +14 -0
- package/dist/headless/index.js +9 -0
- package/dist/headless/locale-routing/index.d.ts +41 -0
- package/dist/headless/locale-routing/index.js +145 -0
- package/dist/headless/nav/index.d.ts +2 -0
- package/dist/headless/nav/index.js +1 -0
- package/dist/headless/nav/resolve.d.ts +2 -0
- package/dist/headless/nav/resolve.js +71 -0
- package/dist/headless/nav/types.d.ts +26 -0
- package/dist/headless/nav/types.js +1 -0
- package/dist/headless/store/create-store.d.ts +2 -0
- package/dist/headless/store/create-store.js +43 -0
- package/dist/headless/store/equality.d.ts +1 -0
- package/dist/headless/store/equality.js +34 -0
- package/dist/headless/store/index.d.ts +3 -0
- package/dist/headless/store/index.js +2 -0
- package/dist/headless/store/types.d.ts +7 -0
- package/dist/headless/store/types.js +10 -0
- package/dist/headless/view-model/edit.d.ts +18 -0
- package/dist/headless/view-model/edit.js +501 -0
- package/dist/headless/view-model/embedded-list.d.ts +27 -0
- package/dist/headless/view-model/embedded-list.js +63 -0
- package/dist/headless/view-model/index.d.ts +7 -0
- package/dist/headless/view-model/index.js +3 -0
- package/dist/headless/view-model/list-file-display.d.ts +3 -0
- package/dist/headless/view-model/list-file-display.js +27 -0
- package/dist/headless/view-model/list.d.ts +16 -0
- package/dist/headless/view-model/list.js +229 -0
- package/dist/headless/view-model/types.d.ts +382 -0
- package/dist/headless/view-model/types.js +1 -0
- package/dist/hooks/use-ai-text.d.ts +1 -1
- package/dist/hooks/use-form.d.ts +1 -1
- package/dist/hooks/use-form.js +1 -1
- package/dist/hooks/use-mutation.d.ts +1 -1
- package/dist/hooks/use-query.d.ts +1 -1
- package/dist/hooks/use-store.d.ts +1 -1
- package/dist/hooks/use-store.js +7 -7
- package/dist/hooks/use-stream-handler.d.ts +1 -1
- package/dist/i18n-defaults.js +7 -0
- package/dist/i18n.d.ts +1 -1
- package/dist/i18n.js +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +7 -4
- package/dist/live/csrf.d.ts +2 -0
- package/dist/live/csrf.js +60 -0
- package/dist/live/dispatcher-live.d.ts +7 -0
- package/dist/live/dispatcher-live.js +286 -0
- package/dist/live/envelopes.d.ts +101 -0
- package/dist/live/envelopes.js +35 -0
- package/dist/live/error-mapping.d.ts +16 -0
- package/dist/live/error-mapping.js +82 -0
- package/dist/live/index.d.ts +7 -0
- package/dist/live/index.js +5 -0
- package/dist/live/locale.d.ts +2 -0
- package/dist/live/locale.js +25 -0
- package/dist/live/sse-stream.d.ts +14 -0
- package/dist/live/sse-stream.js +128 -0
- package/dist/primitives.d.ts +53 -36
- package/dist/primitives.js +1 -1
- package/dist/session/session-ended.d.ts +1 -1
- package/dist/tokens.d.ts +3 -4
- package/dist/tokens.js +4 -5
- package/dist/web/app/app-schema-boot.d.ts +19 -0
- package/dist/web/app/app-schema-boot.js +124 -0
- package/dist/web/app/browser-locale.d.ts +22 -0
- package/dist/web/app/browser-locale.js +78 -0
- package/dist/web/app/client-plugin.d.ts +106 -0
- package/dist/web/app/client-plugin.js +12 -0
- package/dist/web/app/create-app.d.ts +104 -0
- package/dist/web/app/create-app.js +498 -0
- package/dist/web/app/create-public-surface.d.ts +36 -0
- package/dist/web/app/create-public-surface.js +40 -0
- package/dist/web/app/dashboard-body.d.ts +3 -0
- package/dist/web/app/dashboard-body.js +865 -0
- package/dist/web/app/dashboard-refresh.d.ts +6 -0
- package/dist/web/app/dashboard-refresh.js +15 -0
- package/dist/web/app/document-lang-sync.d.ts +4 -0
- package/dist/web/app/document-lang-sync.js +27 -0
- package/dist/web/app/draft-storage.d.ts +11 -0
- package/dist/web/app/draft-storage.js +53 -0
- package/dist/web/app/nav-providers-context.d.ts +13 -0
- package/dist/web/app/nav-providers-context.js +18 -0
- package/dist/web/app/nav.d.ts +40 -0
- package/dist/web/app/nav.js +231 -0
- package/dist/web/app/plain-content-editor.d.ts +3 -0
- package/dist/web/app/plain-content-editor.js +30 -0
- package/dist/web/app/relative-time.d.ts +10 -0
- package/dist/web/app/relative-time.js +56 -0
- package/dist/web/app/resolvers-context.d.ts +12 -0
- package/dist/web/app/resolvers-context.js +10 -0
- package/dist/web/app/rich-content-editor.d.ts +3 -0
- package/dist/web/app/rich-content-editor.js +15 -0
- package/dist/web/app/root-error-fallback.d.ts +2 -0
- package/dist/web/app/root-error-fallback.js +21 -0
- package/dist/web/app/screen-error-fallback.d.ts +6 -0
- package/dist/web/app/screen-error-fallback.js +17 -0
- package/dist/web/app/tiptap-editor.d.ts +3 -0
- package/dist/web/app/tiptap-editor.js +120 -0
- package/dist/web/app/toast-action-notifier.d.ts +5 -0
- package/dist/web/app/toast-action-notifier.js +17 -0
- package/dist/web/components/config-cascade.d.ts +13 -0
- package/dist/web/components/config-cascade.js +155 -0
- package/dist/web/components/config-source-badge.d.ts +6 -0
- package/dist/web/components/config-source-badge.js +55 -0
- package/dist/web/default-color-scheme.d.ts +2 -0
- package/dist/web/default-color-scheme.js +3 -0
- package/dist/web/icons.d.ts +94 -0
- package/dist/web/icons.js +100 -0
- package/dist/web/index.d.ts +78 -0
- package/dist/web/index.js +53 -0
- package/dist/web/layout/app-layout.d.ts +21 -0
- package/dist/web/layout/app-layout.js +8 -0
- package/dist/web/layout/avatar.d.ts +12 -0
- package/dist/web/layout/avatar.js +63 -0
- package/dist/web/layout/default-app-shell.d.ts +41 -0
- package/dist/web/layout/default-app-shell.js +16 -0
- package/dist/web/layout/editor-panel.d.ts +10 -0
- package/dist/web/layout/editor-panel.js +29 -0
- package/dist/web/layout/fill-classes.d.ts +9 -0
- package/dist/web/layout/fill-classes.js +15 -0
- package/dist/web/layout/filter-app-schema-navs.d.ts +7 -0
- package/dist/web/layout/filter-app-schema-navs.js +68 -0
- package/dist/web/layout/header-action-group.d.ts +6 -0
- package/dist/web/layout/header-action-group.js +8 -0
- package/dist/web/layout/header-overflow-menu.d.ts +4 -0
- package/dist/web/layout/header-overflow-menu.js +8 -0
- package/dist/web/layout/language-menu-items.d.ts +8 -0
- package/dist/web/layout/language-menu-items.js +16 -0
- package/dist/web/layout/language-switcher.d.ts +25 -0
- package/dist/web/layout/language-switcher.js +46 -0
- package/dist/web/layout/nav-tree.d.ts +19 -0
- package/dist/web/layout/nav-tree.js +536 -0
- package/dist/web/layout/page-header-slot.d.ts +25 -0
- package/dist/web/layout/page-header-slot.js +50 -0
- package/dist/web/layout/profile-menu.d.ts +13 -0
- package/dist/web/layout/profile-menu.js +6 -0
- package/dist/web/layout/shell-breadcrumb.d.ts +11 -0
- package/dist/web/layout/shell-breadcrumb.js +34 -0
- package/dist/web/layout/shell-header.d.ts +12 -0
- package/dist/web/layout/shell-header.js +159 -0
- package/dist/web/layout/sidebar-brand.d.ts +15 -0
- package/dist/web/layout/sidebar-brand.js +9 -0
- package/dist/web/layout/sidebar-panel.d.ts +41 -0
- package/dist/web/layout/sidebar-panel.js +107 -0
- package/dist/web/layout/sidebar-user.d.ts +8 -0
- package/dist/web/layout/sidebar-user.js +17 -0
- package/dist/web/layout/sidebar.d.ts +17 -0
- package/dist/web/layout/sidebar.js +11 -0
- package/dist/web/layout/target-resolver-stub.d.ts +13 -0
- package/dist/web/layout/target-resolver-stub.js +61 -0
- package/dist/web/layout/target-url.d.ts +12 -0
- package/dist/web/layout/target-url.js +77 -0
- package/dist/web/layout/theme-label.d.ts +15 -0
- package/dist/web/layout/theme-label.js +35 -0
- package/dist/web/layout/theme-menu-item.d.ts +12 -0
- package/dist/web/layout/theme-menu-item.js +16 -0
- package/dist/web/layout/theme-toggle.d.ts +17 -0
- package/dist/web/layout/theme-toggle.js +22 -0
- package/dist/web/layout/topbar.d.ts +8 -0
- package/dist/web/layout/topbar.js +4 -0
- package/dist/web/layout/workspace-shell.d.ts +63 -0
- package/dist/web/layout/workspace-shell.js +220 -0
- package/dist/web/layout/workspace-switcher.d.ts +15 -0
- package/dist/web/layout/workspace-switcher.js +36 -0
- package/dist/web/lib/accept-attr.d.ts +1 -0
- package/dist/web/lib/accept-attr.js +6 -0
- package/dist/web/lib/clamp.d.ts +1 -0
- package/dist/web/lib/clamp.js +3 -0
- package/dist/web/lib/cn.d.ts +6 -0
- package/dist/web/lib/cn.js +9 -0
- package/dist/web/lib/download.d.ts +2 -0
- package/dist/web/lib/download.js +24 -0
- package/dist/web/lib/file-ref-download.d.ts +15 -0
- package/dist/web/lib/file-ref-download.js +62 -0
- package/dist/web/lib/file-upload-form-data.d.ts +7 -0
- package/dist/web/lib/file-upload-form-data.js +11 -0
- package/dist/web/lib/resize-image.d.ts +1 -0
- package/dist/web/lib/resize-image.js +66 -0
- package/dist/web/lib/upload-file-ref.d.ts +9 -0
- package/dist/web/lib/upload-file-ref.js +29 -0
- package/dist/web/primitives/action-menu.d.ts +34 -0
- package/dist/web/primitives/action-menu.js +30 -0
- package/dist/web/primitives/calendar-popover.d.ts +13 -0
- package/dist/web/primitives/calendar-popover.js +76 -0
- package/dist/web/primitives/combobox.d.ts +56 -0
- package/dist/web/primitives/combobox.js +127 -0
- package/dist/web/primitives/date-field.d.ts +18 -0
- package/dist/web/primitives/date-field.js +74 -0
- package/dist/web/primitives/date-input.d.ts +1 -0
- package/dist/web/primitives/date-input.js +5 -0
- package/dist/web/primitives/date-parse.d.ts +11 -0
- package/dist/web/primitives/date-parse.js +159 -0
- package/dist/web/primitives/dialog.d.ts +3 -0
- package/dist/web/primitives/dialog.js +42 -0
- package/dist/web/primitives/drawer.d.ts +3 -0
- package/dist/web/primitives/drawer.js +17 -0
- package/dist/web/primitives/dropdown-menu.d.ts +15 -0
- package/dist/web/primitives/dropdown-menu.js +56 -0
- package/dist/web/primitives/embedded-list-input.d.ts +9 -0
- package/dist/web/primitives/embedded-list-input.js +382 -0
- package/dist/web/primitives/file-thumbnail.d.ts +25 -0
- package/dist/web/primitives/file-thumbnail.js +90 -0
- package/dist/web/primitives/file-upload.d.ts +21 -0
- package/dist/web/primitives/file-upload.js +56 -0
- package/dist/web/primitives/index.d.ts +34 -0
- package/dist/web/primitives/index.js +2021 -0
- package/dist/web/primitives/json-view.d.ts +7 -0
- package/dist/web/primitives/json-view.js +84 -0
- package/dist/web/primitives/layout.d.ts +27 -0
- package/dist/web/primitives/layout.js +30 -0
- package/dist/web/primitives/lightbox.d.ts +3 -0
- package/dist/web/primitives/lightbox.js +36 -0
- package/dist/web/primitives/locale-number.d.ts +2 -0
- package/dist/web/primitives/locale-number.js +54 -0
- package/dist/web/primitives/located-timestamp-input.d.ts +22 -0
- package/dist/web/primitives/located-timestamp-input.js +22 -0
- package/dist/web/primitives/metric.d.ts +5 -0
- package/dist/web/primitives/metric.js +18 -0
- package/dist/web/primitives/modal-shell.d.ts +17 -0
- package/dist/web/primitives/modal-shell.js +9 -0
- package/dist/web/primitives/modal.d.ts +3 -0
- package/dist/web/primitives/modal.js +13 -0
- package/dist/web/primitives/money-input.d.ts +18 -0
- package/dist/web/primitives/money-input.js +122 -0
- package/dist/web/primitives/narrow-pinned-footer.d.ts +12 -0
- package/dist/web/primitives/narrow-pinned-footer.js +153 -0
- package/dist/web/primitives/number-input.d.ts +18 -0
- package/dist/web/primitives/number-input.js +53 -0
- package/dist/web/primitives/option-groups.d.ts +11 -0
- package/dist/web/primitives/option-groups.js +27 -0
- package/dist/web/primitives/page-header.d.ts +3 -0
- package/dist/web/primitives/page-header.js +52 -0
- package/dist/web/primitives/promo-panel.d.ts +3 -0
- package/dist/web/primitives/promo-panel.js +11 -0
- package/dist/web/primitives/segmented-select.d.ts +21 -0
- package/dist/web/primitives/segmented-select.js +62 -0
- package/dist/web/primitives/share-actions.d.ts +4 -0
- package/dist/web/primitives/share-actions.js +56 -0
- package/dist/web/primitives/status-badge.d.ts +3 -0
- package/dist/web/primitives/status-badge.js +5 -0
- package/dist/web/primitives/sticky-action-bar.d.ts +5 -0
- package/dist/web/primitives/sticky-action-bar.js +15 -0
- package/dist/web/primitives/tabs.d.ts +3 -0
- package/dist/web/primitives/tabs.js +107 -0
- package/dist/web/primitives/timestamp-input.d.ts +27 -0
- package/dist/web/primitives/timestamp-input.js +87 -0
- package/dist/web/primitives/toast.d.ts +19 -0
- package/dist/web/primitives/toast.js +63 -0
- package/dist/web/primitives/tz-input.d.ts +11 -0
- package/dist/web/primitives/tz-input.js +8 -0
- package/dist/web/primitives/tz-options.d.ts +4 -0
- package/dist/web/primitives/tz-options.js +17 -0
- package/dist/web/primitives/use-keyboard-inset.d.ts +1 -0
- package/dist/web/primitives/use-keyboard-inset.js +28 -0
- package/dist/web/primitives/use-narrow-viewport.d.ts +4 -0
- package/dist/web/primitives/use-narrow-viewport.js +46 -0
- package/dist/web/react-css-custom-properties.d.ts +6 -0
- package/dist/web/react-css-custom-properties.js +1 -0
- package/dist/web/sse/live-events.d.ts +15 -0
- package/dist/web/sse/live-events.js +148 -0
- package/dist/web/theme-plugin.d.ts +41 -0
- package/dist/web/theme-plugin.js +123 -0
- package/dist/web/tokens.d.ts +24 -0
- package/dist/web/tokens.js +203 -0
- package/dist/web/ui/avatar.d.ts +11 -0
- package/dist/web/ui/avatar.js +25 -0
- package/dist/web/ui/badge.d.ts +9 -0
- package/dist/web/ui/badge.js +26 -0
- package/dist/web/ui/breadcrumb.d.ts +11 -0
- package/dist/web/ui/breadcrumb.js +29 -0
- package/dist/web/ui/button.d.ts +10 -0
- package/dist/web/ui/button.js +37 -0
- package/dist/web/ui/checkbox.d.ts +4 -0
- package/dist/web/ui/checkbox.js +11 -0
- package/dist/web/ui/input.d.ts +3 -0
- package/dist/web/ui/input.js +8 -0
- package/dist/web/ui/label.d.ts +4 -0
- package/dist/web/ui/label.js +10 -0
- package/dist/web/ui/resizable.d.ts +7 -0
- package/dist/web/ui/resizable.js +16 -0
- package/dist/web/ui/separator.d.ts +4 -0
- package/dist/web/ui/separator.js +10 -0
- package/dist/web/ui/sheet.d.ts +14 -0
- package/dist/web/ui/sheet.js +42 -0
- package/dist/web/ui/sidebar.d.ts +73 -0
- package/dist/web/ui/sidebar.js +211 -0
- package/dist/web/ui/skeleton.d.ts +2 -0
- package/dist/web/ui/skeleton.js +7 -0
- package/dist/web/ui/switch.d.ts +6 -0
- package/dist/web/ui/switch.js +10 -0
- package/dist/web/ui/table.d.ts +10 -0
- package/dist/web/ui/table.js +30 -0
- package/dist/web/ui/tabs.d.ts +11 -0
- package/dist/web/ui/tabs.js +31 -0
- package/dist/web/ui/textarea.d.ts +3 -0
- package/dist/web/ui/textarea.js +8 -0
- package/dist/web/ui/tooltip.d.ts +7 -0
- package/dist/web/ui/tooltip.js +19 -0
- package/dist/web/ui/use-mobile.d.ts +1 -0
- package/dist/web/ui/use-mobile.js +16 -0
- package/dist/web/version/update-checker.d.ts +9 -0
- package/dist/web/version/update-checker.js +88 -0
- package/dist/web/widgets/ai-text-field.d.ts +36 -0
- package/dist/web/widgets/ai-text-field.js +137 -0
- package/dist/web/widgets/chart-window.d.ts +58 -0
- package/dist/web/widgets/chart-window.js +190 -0
- package/dist/web/widgets/charts.d.ts +208 -0
- package/dist/web/widgets/charts.js +685 -0
- package/dist/web/widgets/collapsible-section.d.ts +10 -0
- package/dist/web/widgets/collapsible-section.js +17 -0
- package/dist/web/widgets/dashboard-list.d.ts +21 -0
- package/dist/web/widgets/dashboard-list.js +45 -0
- package/dist/web/widgets/detail-list.d.ts +15 -0
- package/dist/web/widgets/detail-list.js +8 -0
- package/dist/web/widgets/drawer.d.ts +65 -0
- package/dist/web/widgets/drawer.js +142 -0
- package/dist/web/widgets/feed-list.d.ts +13 -0
- package/dist/web/widgets/feed-list.js +9 -0
- package/dist/web/widgets/floating-panel.d.ts +30 -0
- package/dist/web/widgets/floating-panel.js +242 -0
- package/dist/web/widgets/form-fields.d.ts +93 -0
- package/dist/web/widgets/form-fields.js +50 -0
- package/dist/web/widgets/index.d.ts +27 -0
- package/dist/web/widgets/index.js +30 -0
- package/dist/web/widgets/infinity-list.d.ts +46 -0
- package/dist/web/widgets/infinity-list.js +211 -0
- package/dist/web/widgets/mode-switch.d.ts +21 -0
- package/dist/web/widgets/mode-switch.js +25 -0
- package/dist/web/widgets/photo-slots.d.ts +29 -0
- package/dist/web/widgets/photo-slots.js +52 -0
- package/dist/web/widgets/plan-card.d.ts +36 -0
- package/dist/web/widgets/plan-card.js +13 -0
- package/dist/web/widgets/progress-bar.d.ts +19 -0
- package/dist/web/widgets/progress-bar.js +24 -0
- package/dist/web/widgets/progress-list.d.ts +19 -0
- package/dist/web/widgets/progress-list.js +11 -0
- package/dist/web/widgets/public-shell.d.ts +22 -0
- package/dist/web/widgets/public-shell.js +15 -0
- package/dist/web/widgets/query-table.d.ts +29 -0
- package/dist/web/widgets/query-table.js +30 -0
- package/dist/web/widgets/result-panel.d.ts +69 -0
- package/dist/web/widgets/result-panel.js +30 -0
- package/dist/web/widgets/section-card.d.ts +12 -0
- package/dist/web/widgets/section-card.js +9 -0
- package/dist/web/widgets/sheet-parts.d.ts +9 -0
- package/dist/web/widgets/sheet-parts.js +11 -0
- package/dist/web/widgets/side-by-side-table.d.ts +31 -0
- package/dist/web/widgets/side-by-side-table.js +11 -0
- package/dist/web/widgets/stat.d.ts +54 -0
- package/dist/web/widgets/stat.js +65 -0
- package/dist/web/widgets/states.d.ts +23 -0
- package/dist/web/widgets/states.js +20 -0
- package/dist/web/widgets/status-badge.d.ts +15 -0
- package/dist/web/widgets/status-badge.js +24 -0
- package/dist/web/widgets/step-bar.d.ts +35 -0
- package/dist/web/widgets/step-bar.js +60 -0
- package/dist/web/widgets/upload-zone.d.ts +24 -0
- package/dist/web/widgets/upload-zone.js +108 -0
- package/dist/web/widgets/use-draft.d.ts +20 -0
- package/dist/web/widgets/use-draft.js +21 -0
- package/dist/web/widgets/use-pointer-drag.d.ts +21 -0
- package/dist/web/widgets/use-pointer-drag.js +53 -0
- package/package.json +105 -7
- package/src/changes.json +77 -0
- package/src/web/fonts/OFL.txt +93 -0
- package/src/web/fonts/ibm-plex-mono-latin-400.woff2 +0 -0
- package/src/web/fonts/ibm-plex-mono-latin-500.woff2 +0 -0
- package/src/web/fonts/ibm-plex-mono-latin-ext-400.woff2 +0 -0
- package/src/web/fonts/ibm-plex-mono-latin-ext-500.woff2 +0 -0
- package/src/web/fonts/ibm-plex-sans-latin-400.woff2 +0 -0
- package/src/web/fonts/ibm-plex-sans-latin-500.woff2 +0 -0
- package/src/web/fonts/ibm-plex-sans-latin-600.woff2 +0 -0
- package/src/web/fonts/ibm-plex-sans-latin-ext-400.woff2 +0 -0
- package/src/web/fonts/ibm-plex-sans-latin-ext-500.woff2 +0 -0
- package/src/web/fonts/ibm-plex-sans-latin-ext-600.woff2 +0 -0
- package/src/web/styles.css +401 -0
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { DashboardRefresh } from "@cosmicdrift/kumiko-framework/ui-types";
|
|
2
|
+
/** Polling interval of a dashboard in ms; undefined means no polling. A record
|
|
3
|
+
* field that is missing, not a positive number or not loaded yet disables it
|
|
4
|
+
* until a valid value arrives. The result never drops below
|
|
5
|
+
* DASHBOARD_MIN_REFRESH_SECONDS, whatever the record says. */
|
|
6
|
+
export declare function resolveRefreshIntervalMs(refresh: DashboardRefresh | undefined, record: Readonly<Record<string, unknown>> | null): number | undefined;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { DASHBOARD_MIN_REFRESH_SECONDS } from "@cosmicdrift/kumiko-types/screen";
|
|
2
|
+
// setInterval treats anything above a signed 32-bit int as 0 ms and would fire in a tight loop.
|
|
3
|
+
const MAX_TIMER_MS = 2_147_483_647;
|
|
4
|
+
/** Polling interval of a dashboard in ms; undefined means no polling. A record
|
|
5
|
+
* field that is missing, not a positive number or not loaded yet disables it
|
|
6
|
+
* until a valid value arrives. The result never drops below
|
|
7
|
+
* DASHBOARD_MIN_REFRESH_SECONDS, whatever the record says. */
|
|
8
|
+
export function resolveRefreshIntervalMs(refresh, record) {
|
|
9
|
+
if (refresh === undefined)
|
|
10
|
+
return undefined;
|
|
11
|
+
const seconds = "seconds" in refresh ? refresh.seconds : record?.[refresh.field];
|
|
12
|
+
if (typeof seconds !== "number" || !Number.isFinite(seconds) || seconds <= 0)
|
|
13
|
+
return undefined;
|
|
14
|
+
return Math.min(Math.max(seconds, DASHBOARD_MIN_REFRESH_SECONDS) * 1000, MAX_TIMER_MS);
|
|
15
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// Keeps document.documentElement.lang (+ dataset.kumikoLocale) in sync with
|
|
2
|
+
// the active locale. Screen readers and the browser's built-in translate
|
|
3
|
+
// offer read lang; dispatcher-live reads dataset.kumikoLocale for X-Locale
|
|
4
|
+
// (see dispatcher-live/src/locale.ts) so a static <html lang> cannot spoof
|
|
5
|
+
// the active UI language.
|
|
6
|
+
//
|
|
7
|
+
// Lives here, not in @cosmicdrift/kumiko-renderer: that package is DOM-free
|
|
8
|
+
// by design (Renderer-Boundaries Guard enforces it — web/native both
|
|
9
|
+
// consume it). createApp/createPublicSurface are the two places every web
|
|
10
|
+
// app already goes through, so mounting this here covers every app with
|
|
11
|
+
// zero app-side wiring, same as createBrowserLocaleResolver.
|
|
12
|
+
import { useEffect } from "react";
|
|
13
|
+
function syncDocumentLocale(locale) {
|
|
14
|
+
document.documentElement.lang = locale;
|
|
15
|
+
// Dedicated marker for dispatcher-live — static <html lang> must not
|
|
16
|
+
// look like an active UI locale (kumiko-framework#2334).
|
|
17
|
+
document.documentElement.dataset["kumikoLocale"] = locale;
|
|
18
|
+
}
|
|
19
|
+
export function DocumentLangSync({ resolver }) {
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
syncDocumentLocale(resolver.locale());
|
|
22
|
+
return resolver.subscribe(() => {
|
|
23
|
+
syncDocumentLocale(resolver.locale());
|
|
24
|
+
});
|
|
25
|
+
}, [resolver]);
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { DraftStorage } from "../../index.ts";
|
|
2
|
+
export type CreateBrowserDraftStorageOptions = {
|
|
3
|
+
/** sessionStorage-key prefix, one entry per screenId gets appended.
|
|
4
|
+
* Default: `"kumiko:draft-id:"`. */
|
|
5
|
+
readonly storagePrefix?: string;
|
|
6
|
+
};
|
|
7
|
+
/** Default DraftStorage when createKumikoApp boots without one. Guards every
|
|
8
|
+
* sessionStorage call — Safari private mode and storage-disabled browsers
|
|
9
|
+
* throw on access, and a lost draftId just falls back to the `list` resume
|
|
10
|
+
* path (same UX as a genuinely cleared storage), not a broken form. */
|
|
11
|
+
export declare function createBrowserDraftStorage(options?: CreateBrowserDraftStorageOptions): DraftStorage;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// Browser-backed DraftStorage default for createKumikoApp (issue #1913).
|
|
2
|
+
//
|
|
3
|
+
// Persists a create-mode draftId per screen in `sessionStorage` — same-tab
|
|
4
|
+
// scope only (deliberately, unlike browser-locale.ts's localStorage): the
|
|
5
|
+
// draft row itself already survives a new tab / cleared storage via
|
|
6
|
+
// RenderEdit's `form-draft:query:list` mount-time fallback, so this is only
|
|
7
|
+
// about not losing the draftId on an accidental same-tab reload.
|
|
8
|
+
function keyFor(prefix, screenId) {
|
|
9
|
+
return `${prefix}${screenId}`;
|
|
10
|
+
}
|
|
11
|
+
/** Default DraftStorage when createKumikoApp boots without one. Guards every
|
|
12
|
+
* sessionStorage call — Safari private mode and storage-disabled browsers
|
|
13
|
+
* throw on access, and a lost draftId just falls back to the `list` resume
|
|
14
|
+
* path (same UX as a genuinely cleared storage), not a broken form. */
|
|
15
|
+
export function createBrowserDraftStorage(options = {}) {
|
|
16
|
+
const prefix = options.storagePrefix ?? "kumiko:draft-id:";
|
|
17
|
+
return {
|
|
18
|
+
getDraftId: (screenId) => {
|
|
19
|
+
if (typeof sessionStorage === "undefined")
|
|
20
|
+
return null;
|
|
21
|
+
try {
|
|
22
|
+
return sessionStorage.getItem(keyFor(prefix, screenId));
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
setDraftId: (screenId, draftId) => {
|
|
29
|
+
// skip: no sessionStorage in this environment (SSR, disabled) — the
|
|
30
|
+
// draftId just stays in-memory-only for this render.
|
|
31
|
+
if (typeof sessionStorage === "undefined")
|
|
32
|
+
return;
|
|
33
|
+
try {
|
|
34
|
+
sessionStorage.setItem(keyFor(prefix, screenId), draftId);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// Persistence failure isn't fatal — the draft stays usable in the
|
|
38
|
+
// current tab, only a reload would lose the draftId.
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
clearDraftId: (screenId) => {
|
|
42
|
+
// skip: no sessionStorage in this environment — nothing to clear.
|
|
43
|
+
if (typeof sessionStorage === "undefined")
|
|
44
|
+
return;
|
|
45
|
+
try {
|
|
46
|
+
sessionStorage.removeItem(keyFor(prefix, screenId));
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// skip: nothing to clean up if storage is unreachable.
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { TreeChildrenSubscribe } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
|
+
import { type ReactNode } from "react";
|
|
3
|
+
export type NavProvidersProviderProps = {
|
|
4
|
+
readonly value: ReadonlyMap<string, TreeChildrenSubscribe>;
|
|
5
|
+
readonly entities?: ReadonlyMap<string, readonly string[]>;
|
|
6
|
+
readonly children: ReactNode;
|
|
7
|
+
};
|
|
8
|
+
export declare function NavProvidersProvider({ value, entities, children, }: NavProvidersProviderProps): ReactNode;
|
|
9
|
+
/** Provider-Map keyed auf Nav-QN. Empty-Map-Default → kein Crash ohne
|
|
10
|
+
* registrierte Provider. */
|
|
11
|
+
export declare function useNavProviders(): ReadonlyMap<string, TreeChildrenSubscribe>;
|
|
12
|
+
/** SSE-Entity-Liste pro Provider-QN für Live-Refresh. Empty-Map-Default. */
|
|
13
|
+
export declare function useNavEntities(): ReadonlyMap<string, readonly string[]>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useContext } from "react";
|
|
3
|
+
const EMPTY_PROVIDERS = new Map();
|
|
4
|
+
const EMPTY_ENTITIES = new Map();
|
|
5
|
+
const NavProvidersContext = createContext(EMPTY_PROVIDERS);
|
|
6
|
+
const NavEntitiesContext = createContext(EMPTY_ENTITIES);
|
|
7
|
+
export function NavProvidersProvider({ value, entities, children, }) {
|
|
8
|
+
return (_jsx(NavProvidersContext.Provider, { value: value, children: _jsx(NavEntitiesContext.Provider, { value: entities ?? EMPTY_ENTITIES, children: children }) }));
|
|
9
|
+
}
|
|
10
|
+
/** Provider-Map keyed auf Nav-QN. Empty-Map-Default → kein Crash ohne
|
|
11
|
+
* registrierte Provider. */
|
|
12
|
+
export function useNavProviders() {
|
|
13
|
+
return useContext(NavProvidersContext);
|
|
14
|
+
}
|
|
15
|
+
/** SSE-Entity-Liste pro Provider-QN für Live-Refresh. Empty-Map-Default. */
|
|
16
|
+
export function useNavEntities() {
|
|
17
|
+
return useContext(NavEntitiesContext);
|
|
18
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { type AnchorHTMLAttributes, type ReactNode } from "react";
|
|
2
|
+
import { type FeatureSchema, type NavApi, type NavTarget } from "../../index.ts";
|
|
3
|
+
/** React-Hook der eine NavApi aus der Browser-History baut. Sollte
|
|
4
|
+
* einmal im App-Root aufgerufen und als value an den shared
|
|
5
|
+
* `<NavProvider>` durchgereicht werden — createKumikoApp tut das.
|
|
6
|
+
*
|
|
7
|
+
* hasWorkspaces aus dem Schema (schema.workspaces non-empty) entscheidet,
|
|
8
|
+
* ob das erste URL-Segment als Workspace-id parsed wird. Pure Pass-
|
|
9
|
+
* through an parsePath; formatPath checkt selbst auf target.workspaceId.
|
|
10
|
+
*
|
|
11
|
+
* basePath mounted die App unter einem URL-Prefix (z.B. "/admin"). Read-
|
|
12
|
+
* Pfad strippt vor parsePath, Write-Pfad prepend'd vor pushState/href.
|
|
13
|
+
* URLs außerhalb des basePath liefern route=undefined, ohne Auto-Redirect.
|
|
14
|
+
*
|
|
15
|
+
* Achtung — Ambiguität bei route=undefined mit basePath:
|
|
16
|
+
* Sowohl die App-Root (URL === basePath, z.B. "/admin") als auch URLs
|
|
17
|
+
* außerhalb des basePath (z.B. "/marketing") liefern route=undefined. Eine
|
|
18
|
+
* App die zwischen "render Default-Screen" und "render Not-Found"
|
|
19
|
+
* unterscheiden muss, muss zusätzlich `window.location.pathname` prüfen:
|
|
20
|
+
*
|
|
21
|
+
* if (window.location.pathname.startsWith("/admin")) {
|
|
22
|
+
* // in-app, aber an der Root → Default-Screen
|
|
23
|
+
* } else {
|
|
24
|
+
* // out-of-app → Not-Found / Marketing-Layer
|
|
25
|
+
* }
|
|
26
|
+
*
|
|
27
|
+
* Im non-basePath-Modus ergibt sich diese Ambiguität nicht — out-of-app
|
|
28
|
+
* ist dort kein Konzept. */
|
|
29
|
+
export declare function useBrowserNavApi(options?: {
|
|
30
|
+
readonly hasWorkspaces?: boolean;
|
|
31
|
+
readonly basePath?: string;
|
|
32
|
+
/** Feature list ObjectTarget resolution needs (resolveTarget). Omit when
|
|
33
|
+
* the caller never passes an ObjectTarget — a ScreenTarget resolves
|
|
34
|
+
* without looking at the feature list at all. */
|
|
35
|
+
readonly features?: readonly FeatureSchema[];
|
|
36
|
+
}): NavApi;
|
|
37
|
+
export type KumikoLinkProps = Omit<AnchorHTMLAttributes<HTMLAnchorElement>, "href"> & {
|
|
38
|
+
readonly to: NavTarget;
|
|
39
|
+
};
|
|
40
|
+
export declare function KumikoLink({ to, onClick, children, ...rest }: KumikoLinkProps): ReactNode;
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
// Browser-Impl der Navigation: HTML5-History + popstate subscribe.
|
|
3
|
+
// Types + Context + useNav leben im shared `@cosmicdrift/kumiko-renderer`; dieser
|
|
4
|
+
// File liefert nur die Web-spezifische NavApi-Instanz und die
|
|
5
|
+
// `<KumikoLink>` Anchor-Komponente.
|
|
6
|
+
//
|
|
7
|
+
// Bewusst KEIN createStore: Source-of-truth ist `window.location.pathname`
|
|
8
|
+
// (extern, kann via replaceState außerhalb unserer Kontrolle wechseln).
|
|
9
|
+
// Ein Store wäre entweder eine zweite Wahrheit (Drift-Risiko) oder ein
|
|
10
|
+
// reiner Tick-Counter (Anti-Pattern — createStore ist State-Holder, kein
|
|
11
|
+
// Event-Bus). Hand-rolled Listener-Set ist hier idiomatisch: zwei
|
|
12
|
+
// Notify-Trigger (eigenes pushPath, popstate-Event), nicht generalisierbar.
|
|
13
|
+
import { useCallback, useMemo, useSyncExternalStore, } from "react";
|
|
14
|
+
import { formatPath, parsePath, resolveTarget, useNav, } from "../../index.js";
|
|
15
|
+
// basePath erlaubt es, die App unter einem URL-Prefix zu mounten
|
|
16
|
+
// (z.B. `/admin`). Read-Pfad strippt den Prefix vor dem parsePath,
|
|
17
|
+
// Write-Pfad prepend'd ihn vor dem pushState/replaceState/href.
|
|
18
|
+
//
|
|
19
|
+
// Wenn die URL nicht im basePath liegt (z.B. /marketing/foo bei
|
|
20
|
+
// basePath="/admin"), liefert stripBasePath `undefined` — useBrowserNavApi
|
|
21
|
+
// gibt dann route=undefined zurück, die App rendert ihren "outside"-State
|
|
22
|
+
// (Not-Found, Marketing-Layer, Server-Routing-Pickup, …). Es gibt KEIN
|
|
23
|
+
// Auto-Redirect zur App-Root — die Host-App entscheidet selbst.
|
|
24
|
+
function normalizeBasePath(raw) {
|
|
25
|
+
if (raw === undefined || raw === "" || raw === "/")
|
|
26
|
+
return "";
|
|
27
|
+
const withLeading = raw.startsWith("/") ? raw : `/${raw}`;
|
|
28
|
+
return withLeading.endsWith("/") ? withLeading.slice(0, -1) : withLeading;
|
|
29
|
+
}
|
|
30
|
+
// Returns the in-app path (basePath-relativ) wenn `path` im basePath liegt,
|
|
31
|
+
// sonst `undefined`. Strict-segment-Boundary: "/administrator" matcht
|
|
32
|
+
// nicht "/admin" — sonst würde der Prefix-Check auf String-Ebene falsche
|
|
33
|
+
// Treffer liefern.
|
|
34
|
+
function stripBasePath(path, basePath) {
|
|
35
|
+
if (basePath === "")
|
|
36
|
+
return path;
|
|
37
|
+
if (path === basePath)
|
|
38
|
+
return "/";
|
|
39
|
+
if (path.startsWith(`${basePath}/`))
|
|
40
|
+
return path.slice(basePath.length);
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
function prependBasePath(path, basePath) {
|
|
44
|
+
if (basePath === "")
|
|
45
|
+
return path;
|
|
46
|
+
// formatPath liefert immer absoluten in-app-Pfad: "/" oder "/screen-id"
|
|
47
|
+
// oder "/screen-id/entity-id". "/" → einfach basePath, sonst concat.
|
|
48
|
+
if (path === "/" || path === "")
|
|
49
|
+
return basePath;
|
|
50
|
+
return `${basePath}${path}`;
|
|
51
|
+
}
|
|
52
|
+
// pushState feuert keinen popstate — wir halten einen eigenen
|
|
53
|
+
// Listener-Set, den navigate() notifiziert. popstate (Back/Forward)
|
|
54
|
+
// läuft über einen window-Listener, den wir einmalig verdrahten.
|
|
55
|
+
const listeners = new Set();
|
|
56
|
+
let popstateWired = false;
|
|
57
|
+
// Stable identity for the omitted-features case — `?? []` would create a
|
|
58
|
+
// fresh array every render, busting the useMemo below on every render for
|
|
59
|
+
// every existing useBrowserNavApi() call site (none of which pass features).
|
|
60
|
+
const NO_FEATURES = [];
|
|
61
|
+
function ensurePopstateWired() {
|
|
62
|
+
if (popstateWired)
|
|
63
|
+
return;
|
|
64
|
+
if (typeof window === "undefined")
|
|
65
|
+
return;
|
|
66
|
+
window.addEventListener("popstate", () => {
|
|
67
|
+
for (const l of listeners)
|
|
68
|
+
l();
|
|
69
|
+
});
|
|
70
|
+
popstateWired = true;
|
|
71
|
+
}
|
|
72
|
+
function subscribe(listener) {
|
|
73
|
+
ensurePopstateWired();
|
|
74
|
+
listeners.add(listener);
|
|
75
|
+
return () => {
|
|
76
|
+
listeners.delete(listener);
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
function readPath() {
|
|
80
|
+
return typeof window !== "undefined" ? window.location.pathname : "/";
|
|
81
|
+
}
|
|
82
|
+
// Read der aktuellen ?key=value-Pairs als Plain-Record. URLSearchParams-
|
|
83
|
+
// Iterator gibt String/String — wir kollabieren auf "letzter Wert
|
|
84
|
+
// gewinnt" wenn ein Key mehrfach im Query auftaucht (`?a=1&a=2` → "2").
|
|
85
|
+
// Multi-Value-Lists sind kein Use-Case in Kumiko-Filter-State; explizit
|
|
86
|
+
// dokumentieren statt unspezifizierten Behavior zu liefern.
|
|
87
|
+
function readSearch() {
|
|
88
|
+
return typeof window !== "undefined" ? window.location.search : "";
|
|
89
|
+
}
|
|
90
|
+
function parseSearchParams(search) {
|
|
91
|
+
const out = {};
|
|
92
|
+
if (search === "")
|
|
93
|
+
return out;
|
|
94
|
+
const params = new URLSearchParams(search);
|
|
95
|
+
for (const [k, v] of params.entries())
|
|
96
|
+
out[k] = v;
|
|
97
|
+
return out;
|
|
98
|
+
}
|
|
99
|
+
// Mergt updates in den aktuellen ?-String. `null` löscht, sonst
|
|
100
|
+
// überschreibt. Verwendet replaceState (KEIN push) — Sort/Filter-Toggles
|
|
101
|
+
// flutten sonst die Back-Navigation und User clicked sich durch
|
|
102
|
+
// dutzende Zwischen-States um zur vorherigen Seite zu kommen.
|
|
103
|
+
function applySearchParamUpdates(updates) {
|
|
104
|
+
if (typeof window === "undefined")
|
|
105
|
+
return;
|
|
106
|
+
const params = new URLSearchParams(window.location.search);
|
|
107
|
+
for (const [key, value] of Object.entries(updates)) {
|
|
108
|
+
if (value === null) {
|
|
109
|
+
params.delete(key);
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
params.set(key, value);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
const next = params.toString();
|
|
116
|
+
const nextSearch = next === "" ? "" : `?${next}`;
|
|
117
|
+
if (nextSearch === window.location.search)
|
|
118
|
+
return;
|
|
119
|
+
const url = `${window.location.pathname}${nextSearch}${window.location.hash}`;
|
|
120
|
+
window.history.replaceState(null, "", url);
|
|
121
|
+
for (const l of listeners)
|
|
122
|
+
l();
|
|
123
|
+
}
|
|
124
|
+
// undefined = the caller passed no search params; "" = an explicitly empty query.
|
|
125
|
+
function formatSearch(options) {
|
|
126
|
+
if (options?.searchParams === undefined)
|
|
127
|
+
return undefined;
|
|
128
|
+
const query = new URLSearchParams(options.searchParams).toString();
|
|
129
|
+
return query === "" ? "" : `?${query}`;
|
|
130
|
+
}
|
|
131
|
+
function pushPath(path, search) {
|
|
132
|
+
if (typeof window === "undefined")
|
|
133
|
+
return;
|
|
134
|
+
// Repeated navigate() calls to the same URL must not flood the history.
|
|
135
|
+
// Without explicit search params the current query counts as unchanged.
|
|
136
|
+
const unchanged = window.location.pathname === path &&
|
|
137
|
+
(search === undefined || window.location.search === search);
|
|
138
|
+
if (unchanged)
|
|
139
|
+
return;
|
|
140
|
+
window.history.pushState(null, "", `${path}${search ?? ""}`);
|
|
141
|
+
for (const l of listeners)
|
|
142
|
+
l();
|
|
143
|
+
}
|
|
144
|
+
function replacePath(path, search) {
|
|
145
|
+
if (typeof window === "undefined")
|
|
146
|
+
return;
|
|
147
|
+
// No path-change short-circuit here: callers explicitly chose replace
|
|
148
|
+
// to avoid creating a history entry, even when the URL is identical.
|
|
149
|
+
// (pushPath skips no-op pushes; replacePath honors the call so the
|
|
150
|
+
// entry-stack semantics stay predictable.)
|
|
151
|
+
window.history.replaceState(null, "", `${path}${search ?? ""}`);
|
|
152
|
+
for (const l of listeners)
|
|
153
|
+
l();
|
|
154
|
+
}
|
|
155
|
+
/** React-Hook der eine NavApi aus der Browser-History baut. Sollte
|
|
156
|
+
* einmal im App-Root aufgerufen und als value an den shared
|
|
157
|
+
* `<NavProvider>` durchgereicht werden — createKumikoApp tut das.
|
|
158
|
+
*
|
|
159
|
+
* hasWorkspaces aus dem Schema (schema.workspaces non-empty) entscheidet,
|
|
160
|
+
* ob das erste URL-Segment als Workspace-id parsed wird. Pure Pass-
|
|
161
|
+
* through an parsePath; formatPath checkt selbst auf target.workspaceId.
|
|
162
|
+
*
|
|
163
|
+
* basePath mounted die App unter einem URL-Prefix (z.B. "/admin"). Read-
|
|
164
|
+
* Pfad strippt vor parsePath, Write-Pfad prepend'd vor pushState/href.
|
|
165
|
+
* URLs außerhalb des basePath liefern route=undefined, ohne Auto-Redirect.
|
|
166
|
+
*
|
|
167
|
+
* Achtung — Ambiguität bei route=undefined mit basePath:
|
|
168
|
+
* Sowohl die App-Root (URL === basePath, z.B. "/admin") als auch URLs
|
|
169
|
+
* außerhalb des basePath (z.B. "/marketing") liefern route=undefined. Eine
|
|
170
|
+
* App die zwischen "render Default-Screen" und "render Not-Found"
|
|
171
|
+
* unterscheiden muss, muss zusätzlich `window.location.pathname` prüfen:
|
|
172
|
+
*
|
|
173
|
+
* if (window.location.pathname.startsWith("/admin")) {
|
|
174
|
+
* // in-app, aber an der Root → Default-Screen
|
|
175
|
+
* } else {
|
|
176
|
+
* // out-of-app → Not-Found / Marketing-Layer
|
|
177
|
+
* }
|
|
178
|
+
*
|
|
179
|
+
* Im non-basePath-Modus ergibt sich diese Ambiguität nicht — out-of-app
|
|
180
|
+
* ist dort kein Konzept. */
|
|
181
|
+
export function useBrowserNavApi(options) {
|
|
182
|
+
const path = useSyncExternalStore(subscribe, readPath, () => "/");
|
|
183
|
+
// Search wird über denselben Listener-Set notifiziert (popstate +
|
|
184
|
+
// unsere replaceState-Calls), also reichen wir denselben subscribe
|
|
185
|
+
// durch. Beide Snapshots werden zusammen recomputed — kein Drift
|
|
186
|
+
// zwischen Pfad und Query.
|
|
187
|
+
const search = useSyncExternalStore(subscribe, readSearch, () => "");
|
|
188
|
+
const hasWorkspaces = options?.hasWorkspaces === true;
|
|
189
|
+
const basePath = useMemo(() => normalizeBasePath(options?.basePath), [options?.basePath]);
|
|
190
|
+
const searchParams = useMemo(() => parseSearchParams(search), [search]);
|
|
191
|
+
const inAppPath = useMemo(() => stripBasePath(path, basePath), [path, basePath]);
|
|
192
|
+
const features = options?.features ?? NO_FEATURES;
|
|
193
|
+
return useMemo(() => {
|
|
194
|
+
const route = inAppPath === undefined ? undefined : parsePath(inAppPath, hasWorkspaces);
|
|
195
|
+
// NavTarget-Contract: workspaceId weglassen = aktueller Workspace
|
|
196
|
+
// bleibt. formatPath kennt die aktuelle Route nicht — ohne Injection
|
|
197
|
+
// landet `/screen-id` in parsePath(hasWorkspaces) als workspaceId,
|
|
198
|
+
// WorkspaceShell sieht einen unbekannten Workspace und revertet
|
|
199
|
+
// sofort auf den Default-Screen ("Klick tut nichts"-Prod-Bug).
|
|
200
|
+
const inCurrentWorkspace = (target) => hasWorkspaces && target.workspaceId === undefined && route?.workspaceId !== undefined
|
|
201
|
+
? { ...target, workspaceId: route.workspaceId }
|
|
202
|
+
: target;
|
|
203
|
+
// resolveTarget runs first so an ObjectTarget never reaches formatPath —
|
|
204
|
+
// everything downstream (workspace-injection, path-building) only ever
|
|
205
|
+
// sees the ScreenTarget shape it already knew about.
|
|
206
|
+
const toPath = (target) => prependBasePath(formatPath(inCurrentWorkspace(resolveTarget(features, target))), basePath);
|
|
207
|
+
return {
|
|
208
|
+
route,
|
|
209
|
+
navigate: (target, navigateOptions) => pushPath(toPath(target), formatSearch(navigateOptions)),
|
|
210
|
+
replace: (target, navigateOptions) => replacePath(toPath(target), formatSearch(navigateOptions)),
|
|
211
|
+
hrefFor: (target, navigateOptions) => `${toPath(target)}${formatSearch(navigateOptions) ?? ""}`,
|
|
212
|
+
searchParams,
|
|
213
|
+
setSearchParams: applySearchParamUpdates,
|
|
214
|
+
};
|
|
215
|
+
}, [inAppPath, hasWorkspaces, basePath, searchParams, features]);
|
|
216
|
+
}
|
|
217
|
+
export function KumikoLink({ to, onClick, children, ...rest }) {
|
|
218
|
+
const nav = useNav();
|
|
219
|
+
const handleClick = useCallback((e) => {
|
|
220
|
+
onClick?.(e);
|
|
221
|
+
if (e.defaultPrevented)
|
|
222
|
+
return;
|
|
223
|
+
// Standard-Browser-Verhalten für Cmd/Ctrl/Shift/Alt + Middle-Click
|
|
224
|
+
// erhalten — nur der plain-left-click landet bei navigate().
|
|
225
|
+
if (e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey)
|
|
226
|
+
return;
|
|
227
|
+
e.preventDefault();
|
|
228
|
+
nav.navigate(to);
|
|
229
|
+
}, [nav, to, onClick]);
|
|
230
|
+
return (_jsx("a", { href: nav.hrefFor(to), onClick: handleClick, ...rest, children: children }));
|
|
231
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
import { TextareaContentEditor, VariableChips } from "../../index.js";
|
|
4
|
+
export function PlainContentEditor({ id, value, onChange, variables, readOnly, }) {
|
|
5
|
+
const [caret, setCaret] = useState(null);
|
|
6
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: keyed on value only — must fire once per committed insert, not on every caret write
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
if (caret === null)
|
|
9
|
+
return;
|
|
10
|
+
const el = document.getElementById(id);
|
|
11
|
+
if (el instanceof HTMLTextAreaElement) {
|
|
12
|
+
el.focus();
|
|
13
|
+
el.setSelectionRange(caret, caret);
|
|
14
|
+
}
|
|
15
|
+
setCaret(null);
|
|
16
|
+
}, [value]);
|
|
17
|
+
const insertAtCaret = (name) => {
|
|
18
|
+
const el = document.getElementById(id);
|
|
19
|
+
const placeholder = `{{${name}}}`;
|
|
20
|
+
if (!(el instanceof HTMLTextAreaElement)) {
|
|
21
|
+
onChange(value + placeholder);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const start = el.selectionStart ?? value.length;
|
|
25
|
+
const end = el.selectionEnd ?? value.length;
|
|
26
|
+
setCaret(start + placeholder.length);
|
|
27
|
+
onChange(value.slice(0, start) + placeholder + value.slice(end));
|
|
28
|
+
};
|
|
29
|
+
return (_jsxs("div", { className: "rounded-md border border-input bg-transparent", children: [_jsx(TextareaContentEditor, { id: id, value: value, onChange: onChange, variables: variables, readOnly: readOnly }), variables.length > 0 && (_jsx("div", { className: "flex flex-wrap gap-1 border-t border-input p-1", children: _jsx(VariableChips, { variables: variables, onInsert: insertAtCaret, disabled: readOnly }) }))] }));
|
|
30
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** "5 minutes ago" / "in 30 seconds" in the given locale. The unit grows with the
|
|
2
|
+
* distance (seconds below a minute, minutes below an hour, hours below a day,
|
|
3
|
+
* days beyond); each step is chosen on the already rounded value so 59.6 minutes
|
|
4
|
+
* reads "1 hour" instead of "60 minutes". `numeric: "auto"` yields "now" and "yesterday". */
|
|
5
|
+
export declare function formatRelativeTime(atMs: number, nowMs: number, locale: string): string;
|
|
6
|
+
/** Interval at which a relative text at `distanceMs` from now needs a redraw. */
|
|
7
|
+
export declare function relativeTickIntervalMs(distanceMs: number): number;
|
|
8
|
+
/** Returns a formatter for relative times that redraws its component on a timer, but
|
|
9
|
+
* only while a render actually called it: no relative output on screen, no timer. */
|
|
10
|
+
export declare function useRelativeTimeFormatter(locale: string): (atMs: number) => string;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from "react";
|
|
2
|
+
import { resolveSafeLocale } from "../primitives/locale-number.js";
|
|
3
|
+
const SECONDS_PER_MINUTE = 60;
|
|
4
|
+
const MINUTES_PER_HOUR = 60;
|
|
5
|
+
const HOURS_PER_DAY = 24;
|
|
6
|
+
/** "5 minutes ago" / "in 30 seconds" in the given locale. The unit grows with the
|
|
7
|
+
* distance (seconds below a minute, minutes below an hour, hours below a day,
|
|
8
|
+
* days beyond); each step is chosen on the already rounded value so 59.6 minutes
|
|
9
|
+
* reads "1 hour" instead of "60 minutes". `numeric: "auto"` yields "now" and "yesterday". */
|
|
10
|
+
export function formatRelativeTime(atMs, nowMs, locale) {
|
|
11
|
+
const formatter = new Intl.RelativeTimeFormat(resolveSafeLocale(locale), { numeric: "auto" });
|
|
12
|
+
const seconds = Math.round((atMs - nowMs) / 1000);
|
|
13
|
+
if (Math.abs(seconds) < SECONDS_PER_MINUTE)
|
|
14
|
+
return formatter.format(seconds, "second");
|
|
15
|
+
const minutes = Math.round(seconds / SECONDS_PER_MINUTE);
|
|
16
|
+
if (Math.abs(minutes) < MINUTES_PER_HOUR)
|
|
17
|
+
return formatter.format(minutes, "minute");
|
|
18
|
+
const hours = Math.round(minutes / MINUTES_PER_HOUR);
|
|
19
|
+
if (Math.abs(hours) < HOURS_PER_DAY)
|
|
20
|
+
return formatter.format(hours, "hour");
|
|
21
|
+
return formatter.format(Math.round(hours / HOURS_PER_DAY), "day");
|
|
22
|
+
}
|
|
23
|
+
const SECOND_TICK_MS = 1000;
|
|
24
|
+
// Above a minute the text changes at most once per minute; half a minute keeps it
|
|
25
|
+
// at most 30 s late without waking the page every second for a "5 minutes ago".
|
|
26
|
+
const COARSE_TICK_MS = 30_000;
|
|
27
|
+
const SECOND_RESOLUTION_BELOW_MS = SECONDS_PER_MINUTE * 1000;
|
|
28
|
+
/** Interval at which a relative text at `distanceMs` from now needs a redraw. */
|
|
29
|
+
export function relativeTickIntervalMs(distanceMs) {
|
|
30
|
+
return distanceMs < SECOND_RESOLUTION_BELOW_MS ? SECOND_TICK_MS : COARSE_TICK_MS;
|
|
31
|
+
}
|
|
32
|
+
/** Returns a formatter for relative times that redraws its component on a timer, but
|
|
33
|
+
* only while a render actually called it: no relative output on screen, no timer. */
|
|
34
|
+
export function useRelativeTimeFormatter(locale) {
|
|
35
|
+
const [, setTick] = useState(0);
|
|
36
|
+
const [intervalMs, setIntervalMs] = useState(null);
|
|
37
|
+
const nearestDistanceMs = useRef(Number.POSITIVE_INFINITY);
|
|
38
|
+
nearestDistanceMs.current = Number.POSITIVE_INFINITY;
|
|
39
|
+
// After every render: the formatter calls of this render decide whether and how fast to tick.
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
const distance = nearestDistanceMs.current;
|
|
42
|
+
setIntervalMs(distance === Number.POSITIVE_INFINITY ? null : relativeTickIntervalMs(distance));
|
|
43
|
+
});
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
// skip: no relative output rendered, nothing to keep current
|
|
46
|
+
if (intervalMs === null)
|
|
47
|
+
return;
|
|
48
|
+
const timer = setInterval(() => setTick((tick) => tick + 1), intervalMs);
|
|
49
|
+
return () => clearInterval(timer);
|
|
50
|
+
}, [intervalMs]);
|
|
51
|
+
return (atMs) => {
|
|
52
|
+
const nowMs = Date.now();
|
|
53
|
+
nearestDistanceMs.current = Math.min(nearestDistanceMs.current, Math.abs(atMs - nowMs));
|
|
54
|
+
return formatRelativeTime(atMs, nowMs, locale);
|
|
55
|
+
};
|
|
56
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { TargetRef } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
|
+
import type { ComponentType, ReactNode } from "react";
|
|
3
|
+
export type ResolverComponent = ComponentType<{
|
|
4
|
+
readonly target: TargetRef;
|
|
5
|
+
readonly onClose: () => void;
|
|
6
|
+
}>;
|
|
7
|
+
export type ResolversProviderProps = {
|
|
8
|
+
readonly resolvers: ReadonlyMap<string, ResolverComponent>;
|
|
9
|
+
readonly children: ReactNode;
|
|
10
|
+
};
|
|
11
|
+
export declare function ResolversProvider({ resolvers, children }: ResolversProviderProps): ReactNode;
|
|
12
|
+
export declare function useResolvers(): ReadonlyMap<string, ResolverComponent>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useContext } from "react";
|
|
3
|
+
const ResolversContext = createContext(null);
|
|
4
|
+
export function ResolversProvider({ resolvers, children }) {
|
|
5
|
+
return _jsx(ResolversContext.Provider, { value: resolvers, children: children });
|
|
6
|
+
}
|
|
7
|
+
export function useResolvers() {
|
|
8
|
+
const ctx = useContext(ResolversContext);
|
|
9
|
+
return ctx ?? new Map();
|
|
10
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
// @runtime client
|
|
3
|
+
//
|
|
4
|
+
// "rich" contentFormat editor: WYSIWYG on tiptap. Public contract is the
|
|
5
|
+
// same four props every ContentEditorComponent gets — no tiptap type
|
|
6
|
+
// crosses this boundary. tiptap itself is dynamic-imported (TiptapEditor
|
|
7
|
+
// lives in ./tiptap-editor) so an app that never mounts a "rich" collection
|
|
8
|
+
// never pays for it; the fallback while the chunk loads is the same plain
|
|
9
|
+
// textarea #1794 uses for a missing editor, never a blank panel.
|
|
10
|
+
import { lazy, Suspense } from "react";
|
|
11
|
+
import { TextareaContentEditor } from "../../index.js";
|
|
12
|
+
const TiptapEditor = lazy(() => import("./tiptap-editor.js"));
|
|
13
|
+
export function RichContentEditor(props) {
|
|
14
|
+
return (_jsx(Suspense, { fallback: _jsx(TextareaContentEditor, { ...props }), children: _jsx(TiptapEditor, { ...props }) }));
|
|
15
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { ROOT_ERROR_FALLBACK_TEST_ID } from "@cosmicdrift/kumiko-types/error-boundary";
|
|
3
|
+
// Renders outside every provider, so no i18n/theme/primitives: plain DOM + inline text.
|
|
4
|
+
const ROOT_ERROR_TEXTS = {
|
|
5
|
+
de: { title: "Etwas ist schiefgelaufen.", reload: "Seite neu laden" },
|
|
6
|
+
en: { title: "Something went wrong.", reload: "Reload page" },
|
|
7
|
+
es: { title: "Algo salió mal.", reload: "Recargar la página" },
|
|
8
|
+
};
|
|
9
|
+
function rootErrorTexts() {
|
|
10
|
+
const language = typeof navigator === "undefined" ? "en" : navigator.language;
|
|
11
|
+
const lower = language.toLowerCase();
|
|
12
|
+
if (lower.startsWith("de"))
|
|
13
|
+
return ROOT_ERROR_TEXTS.de;
|
|
14
|
+
if (lower.startsWith("es"))
|
|
15
|
+
return ROOT_ERROR_TEXTS.es;
|
|
16
|
+
return ROOT_ERROR_TEXTS.en;
|
|
17
|
+
}
|
|
18
|
+
export function RootErrorFallback() {
|
|
19
|
+
const texts = rootErrorTexts();
|
|
20
|
+
return (_jsxs("div", { role: "alert", "data-testid": ROOT_ERROR_FALLBACK_TEST_ID, children: [_jsx("p", { children: texts.title }), _jsx("button", { type: "button", onClick: () => window.location.reload(), children: texts.reload })] }));
|
|
21
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { SCREEN_ERROR_FALLBACK_TEST_ID } from "@cosmicdrift/kumiko-types/error-boundary";
|
|
3
|
+
import { usePrimitives, useTranslation } from "../../index.js";
|
|
4
|
+
// Opt-in: a bundle that leaves NODE_ENV unset must not leak error messages to end users.
|
|
5
|
+
function showsErrorDetails() {
|
|
6
|
+
if (typeof process === "undefined")
|
|
7
|
+
return false;
|
|
8
|
+
const nodeEnv = process.env.NODE_ENV;
|
|
9
|
+
return nodeEnv !== undefined && nodeEnv !== "" && nodeEnv !== "production";
|
|
10
|
+
}
|
|
11
|
+
export function ScreenErrorFallback({ screen, error, onReset, }) {
|
|
12
|
+
const { Banner, Button } = usePrimitives();
|
|
13
|
+
const t = useTranslation();
|
|
14
|
+
return (_jsx(Banner, { variant: "error", padded: true, testId: SCREEN_ERROR_FALLBACK_TEST_ID, title: t("kumiko.screen-error.title"), actions: _jsx(Button, { variant: "secondary", onClick: onReset, testId: `${SCREEN_ERROR_FALLBACK_TEST_ID}-reset`, children: t("kumiko.actions.reload") }), children: showsErrorDetails()
|
|
15
|
+
? t("kumiko.screen-error.detail", { screen, message: error.message })
|
|
16
|
+
: t("kumiko.screen-error.screen", { screen }) }));
|
|
17
|
+
}
|