@jini-ai/admin 0.4.1 → 0.4.3
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/CHANGELOG.md +20 -0
- package/README.md +7 -0
- package/dist/agent-plugins/react/components/AgentPluginFileTree.js +1 -1
- package/dist/agent-plugins/react/components/AgentPluginListPanel.d.ts +1 -1
- package/dist/agent-plugins/react/hooks/AgentPluginInspector.hooks.d.ts +0 -1
- package/dist/agent-plugins/react/hooks/AgentPluginInspector.hooks.js +7 -6
- package/dist/agent-plugins/react/hooks/AgentPluginPanel.hooks.d.ts +1 -1
- package/dist/agent-plugins/react/hooks/AgentPluginPanel.hooks.js +3 -2
- package/dist/agent-plugins/react/hooks/AgentPluginsPage.hooks.d.ts +1 -1
- package/dist/agent-plugins/react/hooks/AgentPluginsPage.hooks.js +1 -1
- package/dist/agent-plugins/react/hooks/MarketplaceTab.hooks.d.ts +1 -1
- package/dist/agent-plugins/react/index.js +1 -1
- package/dist/agent-plugins/react/pages/AgentPluginsPage.d.ts +1 -1
- package/dist/agent-plugins/react/tabs/DownloadedTab.d.ts +1 -1
- package/dist/agent-plugins/react/tabs/InstalledTab.d.ts +1 -1
- package/dist/agent-plugins/react/tabs/MarketplaceTab.d.ts +1 -1
- package/dist/comments/adapters/http.d.ts +29 -0
- package/dist/comments/adapters/http.js +50 -0
- package/dist/comments/adapters/memory.d.ts +17 -0
- package/dist/comments/adapters/memory.js +61 -0
- package/dist/comments/comments.module.d.ts +73 -0
- package/dist/comments/comments.module.js +16 -0
- package/dist/comments/conformance/comments-api.conformance.d.ts +9 -0
- package/dist/comments/conformance/comments-api.conformance.js +63 -0
- package/dist/comments/controllers/comment-queue.controller.d.ts +48 -0
- package/dist/comments/controllers/comment-queue.controller.js +164 -0
- package/dist/comments/controllers/comment-settings.controller.d.ts +32 -0
- package/dist/comments/controllers/comment-settings.controller.js +77 -0
- package/dist/comments/controllers/comments.controller.d.ts +16 -0
- package/dist/comments/controllers/comments.controller.js +16 -0
- package/dist/comments/index.d.ts +13 -0
- package/dist/comments/index.js +8 -0
- package/dist/comments/messages.en.d.ts +54 -0
- package/dist/comments/messages.en.js +56 -0
- package/dist/comments/models.d.ts +22 -0
- package/dist/comments/models.js +2 -0
- package/dist/comments/ports.d.ts +14 -0
- package/dist/comments/ports.js +5 -0
- package/dist/comments/react/components/QueueSection.d.ts +30 -0
- package/dist/comments/react/components/QueueSection.js +57 -0
- package/dist/comments/react/components/SettingsSection.d.ts +14 -0
- package/dist/comments/react/components/SettingsSection.js +23 -0
- package/dist/comments/react/hooks/CommentsPage.hooks.d.ts +9 -0
- package/dist/comments/react/hooks/CommentsPage.hooks.js +10 -0
- package/dist/comments/react/hooks/CommentsPorts.hooks.d.ts +13 -0
- package/dist/comments/react/hooks/CommentsPorts.hooks.js +15 -0
- package/dist/comments/react/hooks/QueueSection.hooks.d.ts +62 -0
- package/dist/comments/react/hooks/QueueSection.hooks.js +57 -0
- package/dist/comments/react/hooks/SettingsSection.hooks.d.ts +12 -0
- package/dist/comments/react/hooks/SettingsSection.hooks.js +21 -0
- package/dist/comments/react/hooks/use-comment-queue.hooks.d.ts +34 -0
- package/dist/comments/react/hooks/use-comment-queue.hooks.js +47 -0
- package/dist/comments/react/hooks/use-comment-settings.hooks.d.ts +22 -0
- package/dist/comments/react/hooks/use-comment-settings.hooks.js +30 -0
- package/dist/comments/react/hooks/use-comments.hooks.d.ts +15 -0
- package/dist/comments/react/hooks/use-comments.hooks.js +20 -0
- package/dist/comments/react/index.d.ts +316 -0
- package/dist/comments/react/index.js +35 -0
- package/dist/comments/react/pages/CommentsPage.d.ts +12 -0
- package/dist/comments/react/pages/CommentsPage.js +15 -0
- package/dist/comments/rules.d.ts +140 -0
- package/dist/comments/rules.js +199 -0
- package/dist/core/index.d.ts +3 -1
- package/dist/core/index.js +1 -0
- package/dist/core/ledger-slice.d.ts +90 -0
- package/dist/core/ledger-slice.js +81 -0
- package/dist/core/ports/comments.d.ts +2 -0
- package/dist/core/ports/forms.d.ts +19 -7
- package/dist/core/ports/forms.js +4 -4
- package/dist/core/ports/index.d.ts +1 -1
- package/dist/core/ports/seo.d.ts +33 -6
- package/dist/core/react/bind-react.d.ts +46 -0
- package/dist/core/react/bind-react.js +28 -0
- package/dist/core/react/index.d.ts +3 -0
- package/dist/core/react/index.js +3 -0
- package/dist/core/react/use-controller.d.ts +16 -0
- package/dist/core/react/use-controller.js +18 -0
- package/dist/forms/adapters/http.d.ts +26 -0
- package/dist/forms/adapters/http.js +43 -0
- package/dist/forms/adapters/memory.d.ts +14 -0
- package/dist/forms/adapters/memory.js +77 -0
- package/dist/forms/conformance/forms-api.conformance.d.ts +17 -0
- package/dist/forms/conformance/forms-api.conformance.js +71 -0
- package/dist/forms/controllers/draft-store.d.ts +18 -0
- package/dist/forms/controllers/draft-store.js +17 -0
- package/dist/forms/controllers/field-attributes-dialog.controller.d.ts +37 -0
- package/dist/forms/controllers/field-attributes-dialog.controller.js +27 -0
- package/dist/forms/controllers/form-editor.controller.d.ts +36 -0
- package/dist/forms/controllers/form-editor.controller.js +22 -0
- package/dist/forms/controllers/form-fields-editor.controller.d.ts +20 -0
- package/dist/forms/controllers/form-fields-editor.controller.js +10 -0
- package/dist/forms/controllers/form-submission-detail.controller.d.ts +20 -0
- package/dist/forms/controllers/form-submission-detail.controller.js +10 -0
- package/dist/forms/controllers/form-submissions.controller.d.ts +25 -0
- package/dist/forms/controllers/form-submissions.controller.js +10 -0
- package/dist/forms/controllers/forms-list.controller.d.ts +22 -0
- package/dist/forms/controllers/forms-list.controller.js +10 -0
- package/dist/forms/forms.module.d.ts +137 -0
- package/dist/forms/forms.module.js +17 -0
- package/dist/forms/html-rules.d.ts +20 -0
- package/dist/forms/html-rules.js +24 -0
- package/dist/forms/index.d.ts +14 -0
- package/dist/forms/index.js +12 -0
- package/dist/forms/messages.en.d.ts +100 -0
- package/dist/forms/messages.en.js +104 -0
- package/dist/forms/models.d.ts +29 -0
- package/dist/forms/models.js +8 -0
- package/dist/forms/ports.d.ts +26 -0
- package/dist/forms/ports.js +6 -0
- package/dist/forms/react/components/FieldAttributesDialog.d.ts +14 -0
- package/dist/forms/react/components/FieldAttributesDialog.js +47 -0
- package/dist/forms/react/components/FormEditorFieldsBody.d.ts +28 -0
- package/dist/forms/react/components/FormEditorFieldsBody.js +24 -0
- package/dist/forms/react/components/FormEditorHeaderText.d.ts +7 -0
- package/dist/forms/react/components/FormEditorHeaderText.js +8 -0
- package/dist/forms/react/components/FormEditorMainPanel.d.ts +12 -0
- package/dist/forms/react/components/FormEditorMainPanel.js +13 -0
- package/dist/forms/react/components/FormEditorTabStrip.d.ts +13 -0
- package/dist/forms/react/components/FormEditorTabStrip.js +14 -0
- package/dist/forms/react/components/FormFieldsEditor.d.ts +13 -0
- package/dist/forms/react/components/FormFieldsEditor.js +39 -0
- package/dist/forms/react/components/FormSubmissionDate.d.ts +8 -0
- package/dist/forms/react/components/FormSubmissionDate.js +5 -0
- package/dist/forms/react/components/FormSubmissionDetail.d.ts +13 -0
- package/dist/forms/react/components/FormSubmissionDetail.js +29 -0
- package/dist/forms/react/components/FormSubmissions.d.ts +12 -0
- package/dist/forms/react/components/FormSubmissions.js +46 -0
- package/dist/forms/react/hooks/FormsPorts.hooks.d.ts +41 -0
- package/dist/forms/react/hooks/FormsPorts.hooks.js +16 -0
- package/dist/forms/react/hooks/forms-view.hooks.d.ts +122 -0
- package/dist/forms/react/hooks/forms-view.hooks.js +79 -0
- package/dist/forms/react/hooks/use-field-attributes-dialog.hooks.d.ts +31 -0
- package/dist/forms/react/hooks/use-field-attributes-dialog.hooks.js +22 -0
- package/dist/forms/react/hooks/use-form-editor.hooks.d.ts +118 -0
- package/dist/forms/react/hooks/use-form-editor.hooks.js +208 -0
- package/dist/forms/react/hooks/use-form-fields-editor.hooks.d.ts +34 -0
- package/dist/forms/react/hooks/use-form-fields-editor.hooks.js +36 -0
- package/dist/forms/react/hooks/use-form-submission-date.hooks.d.ts +12 -0
- package/dist/forms/react/hooks/use-form-submission-date.hooks.js +9 -0
- package/dist/forms/react/hooks/use-form-submission-detail.hooks.d.ts +68 -0
- package/dist/forms/react/hooks/use-form-submission-detail.hooks.js +81 -0
- package/dist/forms/react/hooks/use-form-submissions.hooks.d.ts +67 -0
- package/dist/forms/react/hooks/use-form-submissions.hooks.js +110 -0
- package/dist/forms/react/hooks/use-forms-list.hooks.d.ts +78 -0
- package/dist/forms/react/hooks/use-forms-list.hooks.js +103 -0
- package/dist/forms/react/index.d.ts +449 -0
- package/dist/forms/react/index.js +35 -0
- package/dist/forms/react/pages/FormEditor.d.ts +19 -0
- package/dist/forms/react/pages/FormEditor.js +40 -0
- package/dist/forms/react/pages/FormsList.d.ts +39 -0
- package/dist/forms/react/pages/FormsList.js +55 -0
- package/dist/forms/rules.d.ts +291 -0
- package/dist/forms/rules.js +338 -0
- package/dist/media/react/hooks/EditMediaPanel.hooks.js +1 -1
- package/dist/media/react/hooks/LibraryTab.hooks.d.ts +1 -3
- package/dist/media/react/hooks/LibraryTab.hooks.js +8 -17
- package/dist/media/react/hooks/MediaCard.hooks.js +6 -20
- package/dist/media/react/hooks/MediaGrid.hooks.d.ts +2 -2
- package/dist/media/react/hooks/MediaPage.hooks.d.ts +1 -1
- package/dist/media/react/hooks/MediaPage.hooks.js +1 -1
- package/dist/media/react/hooks/MediaPicker.hooks.js +1 -1
- package/dist/media/react/hooks/ProvidersTab.hooks.js +5 -1
- package/dist/media/react/hooks/UploadButton.hooks.d.ts +9 -2
- package/dist/media/react/hooks/UploadButton.hooks.js +12 -10
- package/dist/media/react/index.js +1 -1
- package/dist/media/react/tabs/LibraryTab.d.ts +1 -1
- package/dist/media/react/tabs/ProvidersTab.d.ts +1 -1
- package/dist/playground/react/components/Canvas.d.ts +1 -1
- package/dist/playground/react/hooks/Canvas.hooks.d.ts +1 -1
- package/dist/playground/react/hooks/Canvas.hooks.js +1 -1
- package/dist/playground/react/hooks/PlaygroundPage.hooks.d.ts +1 -1
- package/dist/playground/react/index.js +1 -1
- package/dist/playground/react/pages/PlaygroundPage.d.ts +1 -1
- package/dist/playground/react/tabs/CanvasTab.d.ts +1 -1
- package/dist/react/bind-react.d.ts +1 -45
- package/dist/react/bind-react.js +2 -27
- package/dist/react/components/ConfirmDialog/ConfirmDialog.d.ts +22 -5
- package/dist/react/components/ConfirmDialog/ConfirmDialog.hooks.d.ts +2 -2
- package/dist/react/components/ConfirmDialog/ConfirmDialog.hooks.js +17 -55
- package/dist/react/components/ConfirmDialog/ConfirmDialog.js +33 -70
- package/dist/react/components/RowMenu/RowMenu.hooks.d.ts +23 -16
- package/dist/react/components/RowMenu/RowMenu.hooks.js +74 -54
- package/dist/react/entities/EntityField.d.ts +1 -1
- package/dist/react/entities/EntityField.js +4 -2
- package/dist/react/entities/types.d.ts +9 -0
- package/dist/react/entities/types.js +9 -0
- package/dist/react/use-controller.d.ts +1 -15
- package/dist/react/use-controller.js +2 -17
- package/dist/redirects/adapters/http.d.ts +20 -0
- package/dist/redirects/adapters/http.js +31 -0
- package/dist/redirects/adapters/memory.d.ts +13 -0
- package/dist/redirects/adapters/memory.js +83 -0
- package/dist/redirects/conformance/redirects-api.conformance.d.ts +12 -0
- package/dist/redirects/conformance/redirects-api.conformance.js +66 -0
- package/dist/redirects/controllers/hit-count.controller.d.ts +14 -0
- package/dist/redirects/controllers/hit-count.controller.js +10 -0
- package/dist/redirects/controllers/import-redirects.controller.d.ts +28 -0
- package/dist/redirects/controllers/import-redirects.controller.js +29 -0
- package/dist/redirects/controllers/redirects.controller.d.ts +37 -0
- package/dist/redirects/controllers/redirects.controller.js +38 -0
- package/dist/redirects/index.d.ts +11 -0
- package/dist/redirects/index.js +10 -0
- package/dist/redirects/messages.en.d.ts +56 -0
- package/dist/redirects/messages.en.js +55 -0
- package/dist/redirects/models.d.ts +14 -0
- package/dist/redirects/models.js +2 -0
- package/dist/redirects/ports.d.ts +9 -0
- package/dist/redirects/ports.js +4 -0
- package/dist/redirects/react/components/HitCountCell.d.ts +19 -0
- package/dist/redirects/react/components/HitCountCell.js +19 -0
- package/dist/redirects/react/components/ImportRedirectsForm.d.ts +17 -0
- package/dist/redirects/react/components/ImportRedirectsForm.js +12 -0
- package/dist/redirects/react/hooks/RedirectsOptions.hooks.d.ts +27 -0
- package/dist/redirects/react/hooks/RedirectsOptions.hooks.js +15 -0
- package/dist/redirects/react/hooks/RedirectsPage.hooks.d.ts +50 -0
- package/dist/redirects/react/hooks/RedirectsPage.hooks.js +30 -0
- package/dist/redirects/react/hooks/RedirectsPorts.hooks.d.ts +10 -0
- package/dist/redirects/react/hooks/RedirectsPorts.hooks.js +11 -0
- package/dist/redirects/react/hooks/use-hit-count-cell.hooks.d.ts +19 -0
- package/dist/redirects/react/hooks/use-hit-count-cell.hooks.js +15 -0
- package/dist/redirects/react/hooks/use-import-redirects-form.hooks.d.ts +21 -0
- package/dist/redirects/react/hooks/use-import-redirects-form.hooks.js +25 -0
- package/dist/redirects/react/hooks/use-redirects.hooks.d.ts +34 -0
- package/dist/redirects/react/hooks/use-redirects.hooks.js +63 -0
- package/dist/redirects/react/hooks/wired.hooks.d.ts +55 -0
- package/dist/redirects/react/hooks/wired.hooks.js +31 -0
- package/dist/redirects/react/index.d.ts +316 -0
- package/dist/redirects/react/index.js +28 -0
- package/dist/redirects/react/pages/RedirectsPage.d.ts +15 -0
- package/dist/redirects/react/pages/RedirectsPage.js +20 -0
- package/dist/redirects/redirects.module.d.ts +75 -0
- package/dist/redirects/redirects.module.js +13 -0
- package/dist/redirects/rules.d.ts +131 -0
- package/dist/redirects/rules.js +134 -0
- package/dist/security/react/hooks/AccessTokensTab.hooks.d.ts +1 -1
- package/dist/security/react/hooks/AccessTokensTab.hooks.js +3 -3
- package/dist/security/react/hooks/RootKeyTab.hooks.d.ts +1 -1
- package/dist/security/react/hooks/RootKeyTab.hooks.js +3 -4
- package/dist/security/react/hooks/SecurityPage.hooks.d.ts +1 -1
- package/dist/security/react/hooks/SecurityPorts.hooks.d.ts +4 -0
- package/dist/security/react/hooks/SecurityPorts.hooks.js +5 -0
- package/dist/security/react/index.js +1 -1
- package/dist/security/react/pages/SecurityPage.d.ts +1 -1
- package/dist/security/react/tabs/AccessTokensTab.d.ts +1 -1
- package/dist/security/react/tabs/RootKeyTab.d.ts +1 -1
- package/dist/seo/adapters/http.d.ts +31 -0
- package/dist/seo/adapters/http.js +37 -0
- package/dist/seo/adapters/memory.d.ts +28 -0
- package/dist/seo/adapters/memory.js +129 -0
- package/dist/seo/conformance/seo-api.conformance.d.ts +8 -0
- package/dist/seo/conformance/seo-api.conformance.js +44 -0
- package/dist/seo/controllers/entry-picker.controller.d.ts +21 -0
- package/dist/seo/controllers/entry-picker.controller.js +17 -0
- package/dist/seo/controllers/entry-section.controller.d.ts +20 -0
- package/dist/seo/controllers/entry-section.controller.js +7 -0
- package/dist/seo/controllers/media-ref.controller.d.ts +26 -0
- package/dist/seo/controllers/media-ref.controller.js +14 -0
- package/dist/seo/controllers/seo-entry.controller.d.ts +31 -0
- package/dist/seo/controllers/seo-entry.controller.js +52 -0
- package/dist/seo/controllers/seo.controller.d.ts +29 -0
- package/dist/seo/controllers/seo.controller.js +50 -0
- package/dist/seo/controllers/sitemap.controller.d.ts +25 -0
- package/dist/seo/controllers/sitemap.controller.js +27 -0
- package/dist/seo/index.d.ts +14 -0
- package/dist/seo/index.js +12 -0
- package/dist/seo/messages.en.d.ts +78 -0
- package/dist/seo/messages.en.js +78 -0
- package/dist/seo/models.d.ts +179 -0
- package/dist/seo/models.js +8 -0
- package/dist/seo/ports.d.ts +11 -0
- package/dist/seo/ports.js +4 -0
- package/dist/seo/react/components/MediaPickerBridge.d.ts +4 -0
- package/dist/seo/react/components/MediaPickerBridge.js +8 -0
- package/dist/seo/react/components/MediaRefField.d.ts +36 -0
- package/dist/seo/react/components/MediaRefField.js +12 -0
- package/dist/seo/react/components/SeoIcons.d.ts +13 -0
- package/dist/seo/react/components/SeoIcons.js +32 -0
- package/dist/seo/react/components/SitemapModal.d.ts +48 -0
- package/dist/seo/react/components/SitemapModal.js +70 -0
- package/dist/seo/react/hooks/MediaPickerBridge.hooks.d.ts +6 -0
- package/dist/seo/react/hooks/MediaPickerBridge.hooks.js +27 -0
- package/dist/seo/react/hooks/MediaRefField.hooks.d.ts +22 -0
- package/dist/seo/react/hooks/MediaRefField.hooks.js +30 -0
- package/dist/seo/react/hooks/SeoModuleTabs.d.ts +14 -0
- package/dist/seo/react/hooks/SeoModuleTabs.js +18 -0
- package/dist/seo/react/hooks/SeoPage.hooks.d.ts +69 -0
- package/dist/seo/react/hooks/SeoPage.hooks.js +37 -0
- package/dist/seo/react/hooks/SeoPorts.hooks.d.ts +19 -0
- package/dist/seo/react/hooks/SeoPorts.hooks.js +22 -0
- package/dist/seo/react/hooks/SitemapModal.hooks.d.ts +26 -0
- package/dist/seo/react/hooks/SitemapModal.hooks.js +20 -0
- package/dist/seo/react/hooks/use-entry-picker.hooks.d.ts +11 -0
- package/dist/seo/react/hooks/use-entry-picker.hooks.js +20 -0
- package/dist/seo/react/hooks/use-seo-entry-panel.hooks.d.ts +14 -0
- package/dist/seo/react/hooks/use-seo-entry-panel.hooks.js +26 -0
- package/dist/seo/react/hooks/use-seo-entry-section.hooks.d.ts +4 -0
- package/dist/seo/react/hooks/use-seo-entry-section.hooks.js +7 -0
- package/dist/seo/react/hooks/use-seo.hooks.d.ts +11 -0
- package/dist/seo/react/hooks/use-seo.hooks.js +29 -0
- package/dist/seo/react/hooks/use-sitemap-modal.hooks.d.ts +10 -0
- package/dist/seo/react/hooks/use-sitemap-modal.hooks.js +18 -0
- package/dist/seo/react/index.d.ts +366 -0
- package/dist/seo/react/index.js +30 -0
- package/dist/seo/react/options.d.ts +19 -0
- package/dist/seo/react/options.js +2 -0
- package/dist/seo/react/pages/SeoPage.d.ts +161 -0
- package/dist/seo/react/pages/SeoPage.js +215 -0
- package/dist/seo/rules.d.ts +136 -0
- package/dist/seo/rules.js +212 -0
- package/dist/seo/seo.module.d.ts +114 -0
- package/dist/seo/seo.module.js +14 -0
- package/dist/source-control/react/hooks/ProvidersTab.hooks.d.ts +1 -1
- package/dist/source-control/react/hooks/ProvidersTab.hooks.js +4 -4
- package/dist/source-control/react/hooks/SourceControlPage.hooks.d.ts +1 -1
- package/dist/source-control/react/index.js +1 -1
- package/dist/source-control/react/pages/SourceControlPage.d.ts +1 -1
- package/dist/source-control/react/tabs/ProvidersTab.d.ts +1 -1
- package/dist/widgets/adapters/http.d.ts +19 -0
- package/dist/widgets/adapters/http.js +33 -0
- package/dist/widgets/adapters/memory.d.ts +22 -0
- package/dist/widgets/adapters/memory.js +93 -0
- package/dist/widgets/conformance/widgets-api.conformance.d.ts +6 -0
- package/dist/widgets/conformance/widgets-api.conformance.js +55 -0
- package/dist/widgets/controllers/instance-editor.controller.d.ts +49 -0
- package/dist/widgets/controllers/instance-editor.controller.js +87 -0
- package/dist/widgets/controllers/library.controller.d.ts +33 -0
- package/dist/widgets/controllers/library.controller.js +53 -0
- package/dist/widgets/controllers/region-editor.controller.d.ts +42 -0
- package/dist/widgets/controllers/region-editor.controller.js +69 -0
- package/dist/widgets/controllers/regions.controller.d.ts +27 -0
- package/dist/widgets/controllers/regions.controller.js +44 -0
- package/dist/widgets/index.d.ts +11 -0
- package/dist/widgets/index.js +9 -0
- package/dist/widgets/messages.en.d.ts +78 -0
- package/dist/widgets/messages.en.js +83 -0
- package/dist/widgets/models.d.ts +62 -0
- package/dist/widgets/models.js +2 -0
- package/dist/widgets/ports.d.ts +79 -0
- package/dist/widgets/ports.js +5 -0
- package/dist/widgets/react/components/WidgetInstanceNotices.d.ts +14 -0
- package/dist/widgets/react/components/WidgetInstanceNotices.js +25 -0
- package/dist/widgets/react/components/WidgetRegionEditorHeaderActions.d.ts +22 -0
- package/dist/widgets/react/components/WidgetRegionEditorHeaderActions.js +18 -0
- package/dist/widgets/react/components/WidgetsLibraryNotices.d.ts +13 -0
- package/dist/widgets/react/components/WidgetsLibraryNotices.js +10 -0
- package/dist/widgets/react/hooks/WidgetsPorts.hooks.d.ts +270 -0
- package/dist/widgets/react/hooks/WidgetsPorts.hooks.js +17 -0
- package/dist/widgets/react/hooks/navigation.hooks.d.ts +9 -0
- package/dist/widgets/react/hooks/navigation.hooks.js +6 -0
- package/dist/widgets/react/hooks/page-views.hooks.d.ts +87 -0
- package/dist/widgets/react/hooks/page-views.hooks.js +30 -0
- package/dist/widgets/react/hooks/use-widget-instance-editor.hooks.d.ts +56 -0
- package/dist/widgets/react/hooks/use-widget-instance-editor.hooks.js +38 -0
- package/dist/widgets/react/hooks/use-widget-region-editor.hooks.d.ts +32 -0
- package/dist/widgets/react/hooks/use-widget-region-editor.hooks.js +22 -0
- package/dist/widgets/react/hooks/use-widget-regions.hooks.d.ts +23 -0
- package/dist/widgets/react/hooks/use-widget-regions.hooks.js +22 -0
- package/dist/widgets/react/hooks/use-widgets-library.hooks.d.ts +37 -0
- package/dist/widgets/react/hooks/use-widgets-library.hooks.js +23 -0
- package/dist/widgets/react/index.d.ts +391 -0
- package/dist/widgets/react/index.js +31 -0
- package/dist/widgets/react/pages/WidgetInstanceEditor.d.ts +23 -0
- package/dist/widgets/react/pages/WidgetInstanceEditor.js +37 -0
- package/dist/widgets/react/pages/WidgetRegionEditor.d.ts +22 -0
- package/dist/widgets/react/pages/WidgetRegionEditor.js +25 -0
- package/dist/widgets/react/pages/WidgetRegions.d.ts +21 -0
- package/dist/widgets/react/pages/WidgetRegions.js +31 -0
- package/dist/widgets/react/pages/WidgetsLibrary.d.ts +24 -0
- package/dist/widgets/react/pages/WidgetsLibrary.js +61 -0
- package/dist/widgets/rules.d.ts +94 -0
- package/dist/widgets/rules.js +83 -0
- package/dist/widgets/widgets.module.d.ts +122 -0
- package/dist/widgets/widgets.module.js +21 -0
- package/package.json +206 -9
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
import { AdminApiError as ApiError, describeApiError as describeCoreApiError } from "../core/transport/errors.js";
|
|
2
|
+
const describeApiError = (e, fallback) => describeCoreApiError({ e, fallback });
|
|
3
|
+
/**
|
|
4
|
+
* @file Pure logic for the `forms` feature (`FormEditor.tsx` — see `FormsList.tsx`'s own
|
|
5
|
+
* `rules.ts`/hook for the list screen's extraction) — everything that computes a value rather than
|
|
6
|
+
* rendering one. Follows the convention `features/posts/rules.ts` establishes: no React, no
|
|
7
|
+
* hooks, importable and directly testable.
|
|
8
|
+
*
|
|
9
|
+
* One deliberate exception to "pure", same as `collections/rules.ts`'s `nextRowId`:
|
|
10
|
+
* {@link nextAttrRowId} is a module-level counter `attrRowsFromField`/`addAttrRow` advance. Only
|
|
11
|
+
* `FieldAttributesDialog` (via `use-field-attributes-dialog.hooks.ts`) consumes it today, so it
|
|
12
|
+
* would be equally correct sitting in that hook file — kept here instead for the same reason
|
|
13
|
+
* `collections/rules.ts` keeps its row-id counter alongside its other draft-list transforms: one
|
|
14
|
+
* place for "how draft rows get local keys" per feature, not split by which hook happens to use it
|
|
15
|
+
* first.
|
|
16
|
+
*
|
|
17
|
+
* `KEYS` (fetch-query migration, 2026-08-12): forms and form-submissions are two independent
|
|
18
|
+
* resources (separate ports, no method overlap — see `core/ports/forms.ts`/`form-submissions-
|
|
19
|
+
* port.hooks.ts`'s own doc comments), so they get two entirely separate top-level key namespaces
|
|
20
|
+
* rather than sharing one grandparent. Within each, `list`/`detail(id)` (and `submissionsList(formId)`/
|
|
21
|
+
* `submissionDetail(formId, id)`) are SIBLINGS, not parent/child — `@jini-ai/ui/fetch-query`'s
|
|
22
|
+
* `QueryKey` doc warns that invalidation matches by prefix, and `collections/rules.ts`'s own `KEYS`
|
|
23
|
+
* doc records the regression that bit the predecessor when a detail key nested under its list: every
|
|
24
|
+
* save silently fired 3 extra background requests because invalidating the list also invalidated the
|
|
25
|
+
* editor's own currently-open read. Fixed second-array-element ("list" vs "detail") rather than
|
|
26
|
+
* nesting keeps `KEYS.list`'s invalidation from ever touching an open `KEYS.form(id)`/
|
|
27
|
+
* `KEYS.submissionDetail(...)` read, regardless of what `id` happens to be.
|
|
28
|
+
*/
|
|
29
|
+
/**
|
|
30
|
+
* This screen's name on `lib/content-refresh-bus.ts` — see `taxonomy/rules.ts`'s `TAXONOMY_RESOURCE`
|
|
31
|
+
* for why this is a plain colocated constant rather than a shared registry. `forms_create_definition`/
|
|
32
|
+
* `forms_update_definition`/`forms_set_definition_status` (`apps/website/src/features/forms/agent-
|
|
33
|
+
* tools.ts`) are agent-callable, so `FormsList.tsx` needs the same "an assistant write shows up
|
|
34
|
+
* without a reload" fix `use-taxonomy.hooks.ts` shipped first.
|
|
35
|
+
*/
|
|
36
|
+
export const FORMS_LIST_RESOURCE = "forms";
|
|
37
|
+
/** The list has no sort control: recently edited forms belong first. Copy before sorting so
|
|
38
|
+
* the query cache and injected repository keep their own order; ties retain response order. */
|
|
39
|
+
export function newestUpdatedForms({ forms }, _optional = {}) {
|
|
40
|
+
return [...forms].sort((a, b) => (Date.parse(b.updatedAt) || 0) - (Date.parse(a.updatedAt) || 0));
|
|
41
|
+
}
|
|
42
|
+
/** One form event's compact local date and full tooltip. Shared by definitions and submissions;
|
|
43
|
+
* malformed legacy values stay visible as a dash rather than throwing during table rendering. */
|
|
44
|
+
export function formDateDisplay({ iso, locale }, { timeZone } = {}) {
|
|
45
|
+
const date = new Date(iso);
|
|
46
|
+
if (Number.isNaN(date.getTime()))
|
|
47
|
+
return { text: "—", full: "—", dateTime: undefined };
|
|
48
|
+
return {
|
|
49
|
+
text: new Intl.DateTimeFormat(locale, { dateStyle: "short", timeStyle: "short", timeZone }).format(date),
|
|
50
|
+
full: new Intl.DateTimeFormat(locale, {
|
|
51
|
+
year: "numeric", month: "short", day: "numeric", hour: "numeric", minute: "2-digit", timeZone,
|
|
52
|
+
}).format(date),
|
|
53
|
+
dateTime: date.toISOString(),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/** Form-specific date copy. The shared admin timestamp helper intentionally slices ISO text
|
|
57
|
+
* and its relative helper is English-only; these two events need the operator's locale and
|
|
58
|
+
* browser time zone. Absolute dates remain accurate while the list stays open without a timer.
|
|
59
|
+
*
|
|
60
|
+
* Owner 2026-10-05: line 1 created, line 2 updated, each a compact short date + time with no event
|
|
61
|
+
* word; one line when updated shows the same date and minute as created. The comparison is on the
|
|
62
|
+
* displayed short text, so edits within the same minute collapse and the rule follows the locale's
|
|
63
|
+
* own precision. Intl picks the date order — never a hand-built "M/D/YY".
|
|
64
|
+
*
|
|
65
|
+
* @complexity O(1) time and space — two dates, fixed-size formatting.
|
|
66
|
+
*/
|
|
67
|
+
export function formDatesLines({ form, locale, t }, options = {}) {
|
|
68
|
+
const created = formDateDisplay({ iso: form.createdAt, locale }, options);
|
|
69
|
+
const updated = formDateDisplay({ iso: form.updatedAt, locale }, options);
|
|
70
|
+
if (created.text === updated.text) {
|
|
71
|
+
const label = t("Created {created} · Updated {updated}")
|
|
72
|
+
.replace("{created}", created.full)
|
|
73
|
+
.replace("{updated}", updated.full);
|
|
74
|
+
return [{ kind: "both", text: created.text, dateTime: created.dateTime, label }];
|
|
75
|
+
}
|
|
76
|
+
return [
|
|
77
|
+
{ kind: "created", text: created.text, dateTime: created.dateTime, label: t("Created {date}").replace("{date}", created.full) },
|
|
78
|
+
{ kind: "updated", text: updated.text, dateTime: updated.dateTime, label: t("Updated {date}").replace("{date}", updated.full) },
|
|
79
|
+
];
|
|
80
|
+
}
|
|
81
|
+
export const KEYS = {
|
|
82
|
+
list: ["forms", "list"],
|
|
83
|
+
form: (id) => ["forms", "detail", id],
|
|
84
|
+
submissionsList: (formId) => ["form-submissions", "list", formId],
|
|
85
|
+
submissionDetail: (formId, submissionId) => [
|
|
86
|
+
"form-submissions",
|
|
87
|
+
"detail",
|
|
88
|
+
formId,
|
|
89
|
+
submissionId,
|
|
90
|
+
],
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* `useFormEditor`'s error banner, extracted out of that hook (`refactor/fetch-query` complexity
|
|
94
|
+
* pass, 2026-08-12 — same reason `collections/rules.ts`'s `visibleEntryEditorError` was extracted:
|
|
95
|
+
* the hook's own precedence chain over four sources pushed it past the complexity ceiling).
|
|
96
|
+
*
|
|
97
|
+
* Precedence: an active write's own failure (update/create/status, in that order — mirrors
|
|
98
|
+
* `redirects/rules.ts`'s `firstWriteError` array-order precedence, since `update`/`status` can both
|
|
99
|
+
* apply to the SAME loaded form) always wins. The list-load failure only surfaces before the form
|
|
100
|
+
* has ever loaded — once `hasForm` is true, a later BACKGROUND refresh failure must not blank an
|
|
101
|
+
* editor the operator is actively using (same "a later failure must not erase what already
|
|
102
|
+
* rendered" guard `FormEditor.tsx`'s own file header already documents at the render layer; this is
|
|
103
|
+
* its data-layer half).
|
|
104
|
+
*
|
|
105
|
+
* @complexity Time/space: O(1) — four fixed checks, no iteration.
|
|
106
|
+
*/
|
|
107
|
+
export function visibleFormEditorError(params, _optional = {}) {
|
|
108
|
+
// Two distinct fallback strings, matching the pre-migration `handleSave`/`handleStatusToggle`
|
|
109
|
+
// catch blocks verbatim ("save failed" vs "status update failed") — not one shared string.
|
|
110
|
+
if (params.updateError)
|
|
111
|
+
return describeApiError(params.updateError, params.saveFallback);
|
|
112
|
+
if (params.createError)
|
|
113
|
+
return describeApiError(params.createError, params.saveFallback);
|
|
114
|
+
if (params.statusError)
|
|
115
|
+
return describeApiError(params.statusError, params.statusUpdateFallback);
|
|
116
|
+
if (params.hasForm)
|
|
117
|
+
return null;
|
|
118
|
+
return params.listError ? describeApiError(params.listError, params.loadFormFallback) : null;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* `useFormsList`'s error banner: the row-toggle write's own failure outranks a background
|
|
122
|
+
* list-refresh failure, same precedence `redirects/rules.ts`'s `visibleRedirectsError` documents.
|
|
123
|
+
* Two sources only (not the three-plus that pushed `visibleFormEditorError`/`visibleTaxonomyError`
|
|
124
|
+
* out to `rules.ts`), kept here anyway rather than inlined so the hook body doesn't grow a nested
|
|
125
|
+
* ternary chain (`@jini-ai/ui/fetch-query`'s `resolveFetchQueryStatus` doc explains why a flat
|
|
126
|
+
* extraction beats a nested ternary of the same branch count for the complexity gate).
|
|
127
|
+
*
|
|
128
|
+
* @complexity Time/space: O(1) — two fixed checks, no iteration.
|
|
129
|
+
*/
|
|
130
|
+
export function formsListError(params, _optional = {}) {
|
|
131
|
+
// The delete's own failure outranks both the toggle's and a background list-refresh failure —
|
|
132
|
+
// same precedence tier `use-posts.hooks.ts`'s `removePost` gives its own delete, and a 404
|
|
133
|
+
// "already gone" resolves to `message: null` here so it never reaches this banner at all (see
|
|
134
|
+
// `describeTrashError`'s own doc for why).
|
|
135
|
+
const deleteMessage = describeTrashError({ error: params.deleteError, fallback: params.deleteFallback, versionChangedMessage: params.versionChangedMessage }).message;
|
|
136
|
+
if (deleteMessage)
|
|
137
|
+
return deleteMessage;
|
|
138
|
+
if (params.toggleError)
|
|
139
|
+
return describeApiError(params.toggleError, params.statusUpdateFallback);
|
|
140
|
+
if (params.hasForms)
|
|
141
|
+
return null;
|
|
142
|
+
return params.listError ? describeApiError(params.listError, params.loadFormsFallback) : null;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Classifies a failed `api.trash` call — shared by `useFormsList`'s `removeForm` (forms) and
|
|
146
|
+
* `useFormSubmissionDetail`'s `confirmDelete` (submissions), since both routes now go through the
|
|
147
|
+
* same generic `POST /trash/items` (`routes/trash/items.ts`) and its 404/409 contract:
|
|
148
|
+
*
|
|
149
|
+
* - 404 (`NOT_FOUND`/`FORMS_SUBMISSION_NOT_FOUND`/…): the row is already gone — another tab,
|
|
150
|
+
* another operator, or a prior click that actually succeeded before a flaky response read as a
|
|
151
|
+
* failure. `alreadyGone: true`, `message: null` — the caller's job is a quiet list refresh, not
|
|
152
|
+
* an error banner blaming the operator for something that already happened.
|
|
153
|
+
* - 409 `TRASH_VERSION_CHANGED`: the row changed under the operator since this screen last read
|
|
154
|
+
* it. Specific reload-and-retry copy, not the generic fallback.
|
|
155
|
+
* - Anything else (network failure, an unrelated `ApiError`, …): falls through to
|
|
156
|
+
* `describeApiError`'s generic fallback message.
|
|
157
|
+
*
|
|
158
|
+
* @complexity O(1) — one `instanceof` check plus two fixed comparisons.
|
|
159
|
+
*/
|
|
160
|
+
export function describeTrashError({ error: e, fallback, versionChangedMessage }, _optional = {}) {
|
|
161
|
+
if (!e)
|
|
162
|
+
return { alreadyGone: false, message: null };
|
|
163
|
+
if (e instanceof ApiError || ("status" in e && typeof e.status === "number")) {
|
|
164
|
+
// Existing host transports may retain their own ApiError constructor; status/code are wire fields.
|
|
165
|
+
if (e.status === 404)
|
|
166
|
+
return { alreadyGone: true, message: null };
|
|
167
|
+
if ("code" in e && e.code === "TRASH_VERSION_CHANGED") {
|
|
168
|
+
return { alreadyGone: false, message: versionChangedMessage };
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return { alreadyGone: false, message: describeApiError(e, fallback) };
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* The row-action menu for one form. `RowMenu` has no per-item `disabled` — the in-flight guard lives
|
|
175
|
+
* in `use-forms-list.hooks.ts`'s own `toggleStatus` (a no-op while `rowSavingId` is already set), the
|
|
176
|
+
* same shape `use-redirects.hooks.ts`'s `onToggleStatus` uses for its identical `if (saving) return;`
|
|
177
|
+
* guard. (CORRECTED 2026-09-05: this comment previously said the guard "stays in the caller's
|
|
178
|
+
* `onToggleStatus` closure (`FormsList.tsx`'s own `if (rowSavingId) return;`)" and claimed that was
|
|
179
|
+
* the same shape `Redirects.tsx` uses — false; `Redirects.tsx` never had such a guard inline, only its
|
|
180
|
+
* hook does. Flagged by the 2026-09-05 Gemini admin-tooling audit as a standing no-logic-in-`.tsx`
|
|
181
|
+
* violation; moving the guard into the hook also fixed the comment's own false precedent claim.)
|
|
182
|
+
*
|
|
183
|
+
* @complexity Time/space: O(1) — three fixed entries, no iteration.
|
|
184
|
+
*/
|
|
185
|
+
export function formRowMenuItems({ form, handlers, t }, _optional = {}) {
|
|
186
|
+
return [
|
|
187
|
+
// Slug, not id — the admin URL reads `/admin/forms/<slug>` (ui-fixes-backlog.md #8); the GET
|
|
188
|
+
// route still resolves an id too, so this is not a behavior change for any existing bookmark.
|
|
189
|
+
{ key: "edit", label: t("Edit"), onSelect: () => handlers.onEdit(form) },
|
|
190
|
+
{
|
|
191
|
+
key: "toggle-status",
|
|
192
|
+
label: form.status === "active" ? t("Disable") : t("Enable"),
|
|
193
|
+
tone: form.status === "active" ? "warning" : "default",
|
|
194
|
+
onSelect: () => handlers.onToggleStatus(form),
|
|
195
|
+
},
|
|
196
|
+
// Opens `FormsList.tsx`'s `ConfirmDialog` — no network call from this selection itself, same
|
|
197
|
+
// "a click alone can never delete" contract `use-form-submission-detail.hooks.ts`'s
|
|
198
|
+
// `requestDelete` already documents for the sibling submission-delete flow.
|
|
199
|
+
{ key: "delete", label: t("Delete"), tone: "danger", onSelect: () => handlers.onDelete(form) },
|
|
200
|
+
];
|
|
201
|
+
}
|
|
202
|
+
export const FIELD_TYPES = ["text", "email", "textarea", "checkbox"];
|
|
203
|
+
/** The two tab-panel views on an existing form's editor (`formId !== "new"`). */
|
|
204
|
+
export const FORM_TABS = [
|
|
205
|
+
{ id: "fields", label: "Fields" },
|
|
206
|
+
{ id: "submissions", label: "Submissions" },
|
|
207
|
+
];
|
|
208
|
+
/**
|
|
209
|
+
* Roving-tabindex arrow-key step for the Fields/Submissions tablist — ArrowLeft/ArrowRight cycle
|
|
210
|
+
* between the two tabs, Home/End jump to the first/last.
|
|
211
|
+
*
|
|
212
|
+
* @complexity O(1) — `FORM_TABS` is a fixed 2-item array.
|
|
213
|
+
*/
|
|
214
|
+
export function nextTabIndex({ key, currentIndex }, _optional = {}) {
|
|
215
|
+
if (key === "ArrowRight")
|
|
216
|
+
return (currentIndex + 1) % FORM_TABS.length;
|
|
217
|
+
if (key === "ArrowLeft")
|
|
218
|
+
return (currentIndex - 1 + FORM_TABS.length) % FORM_TABS.length;
|
|
219
|
+
if (key === "Home")
|
|
220
|
+
return 0;
|
|
221
|
+
if (key === "End")
|
|
222
|
+
return FORM_TABS.length - 1;
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
export function blankField(_required = {}, _optional = {}) {
|
|
226
|
+
return { id: "", label: "", type: "text", required: false };
|
|
227
|
+
}
|
|
228
|
+
/** Shared "what do we call this field in a title/label" fallback — a blank draft field has neither
|
|
229
|
+
* a label nor an id yet, so both the kebab's `aria-label` and the modal's own `<h2>` need the same
|
|
230
|
+
* `label -> id -> "Field N"` chain rather than risking the two drifting apart. */
|
|
231
|
+
export function fieldDisplayName({ field, index }, _optional = {}) {
|
|
232
|
+
return field.label || field.id || `Field ${index + 1}`;
|
|
233
|
+
}
|
|
234
|
+
/** @complexity O(n) in `fields.length`. */
|
|
235
|
+
export function updateFormField({ fields, index, patch }, _optional = {}) {
|
|
236
|
+
return fields.map((f, i) => (i === index ? { ...f, ...patch } : f));
|
|
237
|
+
}
|
|
238
|
+
/** @complexity O(n) in `fields.length`. */
|
|
239
|
+
export function addFormField({ fields }, _optional = {}) {
|
|
240
|
+
return [...fields, blankField()];
|
|
241
|
+
}
|
|
242
|
+
/** @complexity O(n) in `fields.length`. */
|
|
243
|
+
export function removeFormField({ fields, index }, _optional = {}) {
|
|
244
|
+
return fields.filter((_, i) => i !== index);
|
|
245
|
+
}
|
|
246
|
+
/** The already-persisted field ids on a form — `FormFieldsEditor` disables the id input and the
|
|
247
|
+
* Remove button for these (a saved field's id cannot be changed or removed once created). `null`
|
|
248
|
+
* (form not loaded yet, or the "new form" case) has no existing ids. */
|
|
249
|
+
export function existingFieldIdsOf({ form }, _optional = {}) {
|
|
250
|
+
return form ? form.fields.map((f) => f.id) : [];
|
|
251
|
+
}
|
|
252
|
+
// ---------------------------------------------------------------------------
|
|
253
|
+
// Field attributes modal (per-field CSS classes + HTML attributes)
|
|
254
|
+
// ---------------------------------------------------------------------------
|
|
255
|
+
/** Mirrors `forms.ts`'s `ATTRIBUTE_NAME_PATTERN` exactly — see `FormEditor.tsx`'s own header
|
|
256
|
+
* comment for why this is a fast-reject-only duplicate, not the authoritative check. Keep in sync
|
|
257
|
+
* by hand if the server allowlist changes. */
|
|
258
|
+
export const ATTRIBUTE_NAME_PATTERN = /^(aria-[a-z0-9-]+|data-[a-z0-9-]+|placeholder|autocomplete|inputmode|pattern|title|min|max|step|minlength|spellcheck|readonly)$/;
|
|
259
|
+
/** Same bounds as `forms.ts`'s `MAX_CLASS_NAME_LENGTH`/`MAX_ATTRIBUTES_PER_FIELD` — client-side
|
|
260
|
+
* early-reject only, not enforcement (see `ATTRIBUTE_NAME_PATTERN` above). */
|
|
261
|
+
export const MAX_CLASS_NAME_LENGTH = 300;
|
|
262
|
+
export const MAX_ATTRIBUTES_PER_FIELD = 12;
|
|
263
|
+
/** A handful of the allowlisted names as real, pickable suggestions (the modal's own "here's what
|
|
264
|
+
* you can do" surface — an operator has no other way to discover the allowlist) rather than every
|
|
265
|
+
* one: `aria-*`/`data-*` are open namespaces, so `aria-label`/`data-testid` stand in for the whole
|
|
266
|
+
* prefix family instead of listing every field-specific `aria-*` name that doesn't exist yet. */
|
|
267
|
+
export const ATTRIBUTE_NAME_SUGGESTIONS = [
|
|
268
|
+
"aria-label",
|
|
269
|
+
"aria-describedby",
|
|
270
|
+
"data-testid",
|
|
271
|
+
"placeholder",
|
|
272
|
+
"autocomplete",
|
|
273
|
+
"inputmode",
|
|
274
|
+
"pattern",
|
|
275
|
+
"title",
|
|
276
|
+
"min",
|
|
277
|
+
"max",
|
|
278
|
+
"step",
|
|
279
|
+
"minlength",
|
|
280
|
+
"spellcheck",
|
|
281
|
+
"readonly",
|
|
282
|
+
];
|
|
283
|
+
/** Local-only row key for the modal's attribute list, so React can key a row before it has a
|
|
284
|
+
* stable identity — same `_rowId`/module-counter pattern `collections/rules.ts`'s
|
|
285
|
+
* `EditFieldsDialog` support uses for its own draft field rows. */
|
|
286
|
+
let nextAttrRowId = 0;
|
|
287
|
+
export function attrRowsFromField({ field }, _optional = {}) {
|
|
288
|
+
return Object.entries(field.attributes ?? {}).map(([name, value]) => ({ _rowId: nextAttrRowId++, name, value }));
|
|
289
|
+
}
|
|
290
|
+
/** @complexity O(n) in `rows.length`. */
|
|
291
|
+
export function updateAttrRow({ rows, rowId, patch }, _optional = {}) {
|
|
292
|
+
return rows.map((r) => (r._rowId === rowId ? { ...r, ...patch } : r));
|
|
293
|
+
}
|
|
294
|
+
/** @complexity O(n) in `rows.length`. */
|
|
295
|
+
export function removeAttrRow({ rows, rowId }, _optional = {}) {
|
|
296
|
+
return rows.filter((r) => r._rowId !== rowId);
|
|
297
|
+
}
|
|
298
|
+
/** @complexity O(n) in `rows.length`. */
|
|
299
|
+
export function addAttrRow({ rows }, _optional = {}) {
|
|
300
|
+
return [...rows, { _rowId: nextAttrRowId++, name: "", value: "" }];
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Validates + shapes `FieldAttributesDialog`'s draft into the `Partial<AdminFormField>` patch
|
|
304
|
+
* `onSave` applies: a class-name length cap, then every named attribute row against the allowlist
|
|
305
|
+
* (blank names are silently skipped — an empty trailing row is not an error), then a total-count
|
|
306
|
+
* cap on the surviving attributes. The security-relevant check is server-side (`forms.ts`'s
|
|
307
|
+
* `validateFieldDescriptors`); this is the fast, pre-save rejection only — see this feature's own
|
|
308
|
+
* `ATTRIBUTE_NAME_PATTERN` doc.
|
|
309
|
+
*
|
|
310
|
+
* @complexity O(n) in `rows.length`, short-circuiting on the first disallowed name.
|
|
311
|
+
*/
|
|
312
|
+
export function buildFieldAttributesPatch(draft, _optional = {}) {
|
|
313
|
+
const trimmedClassName = draft.className.trim();
|
|
314
|
+
if (trimmedClassName.length > MAX_CLASS_NAME_LENGTH) {
|
|
315
|
+
return { ok: false, error: `CSS classes must be at most ${MAX_CLASS_NAME_LENGTH} characters.` };
|
|
316
|
+
}
|
|
317
|
+
const attributes = {};
|
|
318
|
+
for (const row of draft.rows) {
|
|
319
|
+
const name = row.name.trim();
|
|
320
|
+
if (!name)
|
|
321
|
+
continue;
|
|
322
|
+
if (!ATTRIBUTE_NAME_PATTERN.test(name)) {
|
|
323
|
+
return { ok: false, error: `Attribute "${name}" isn't allowed. Use aria-*, data-*, or one of the suggested names.` };
|
|
324
|
+
}
|
|
325
|
+
attributes[name] = row.value;
|
|
326
|
+
}
|
|
327
|
+
if (Object.keys(attributes).length > MAX_ATTRIBUTES_PER_FIELD) {
|
|
328
|
+
return { ok: false, error: `At most ${MAX_ATTRIBUTES_PER_FIELD} attributes are allowed per field.` };
|
|
329
|
+
}
|
|
330
|
+
return {
|
|
331
|
+
ok: true,
|
|
332
|
+
patch: {
|
|
333
|
+
className: trimmedClassName || undefined,
|
|
334
|
+
attributes: Object.keys(attributes).length > 0 ? attributes : undefined,
|
|
335
|
+
},
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
//# sourceMappingURL=rules.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useEffect, useLayoutEffect, useRef, useState } from 'react';
|
|
2
|
-
import { useController } from '../../../react/use-controller.js';
|
|
2
|
+
import { useController } from '../../../core/react/use-controller.js';
|
|
3
3
|
import { createEditMediaController } from '../../controllers/edit-media.controller.js';
|
|
4
4
|
import { canReplaceMedia, mediaEmbedSnippet, parseOptionalPixelSize, safeMediaUrl } from '../../rules.js';
|
|
5
5
|
import { parseMediaHtmlAttributes, describeMediaHtmlAttributeError } from '../../html-attributes.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { TabViewProps } from '../../../react/bind-react.js';
|
|
1
|
+
import type { TabViewProps } from '../../../core/react/bind-react.js';
|
|
2
2
|
import { createLibraryController } from '../../controllers/library.controller.js';
|
|
3
3
|
import type { MediaAsset } from '../../models.js';
|
|
4
4
|
export declare function useLibraryTab({ params, permissions }: TabViewProps, _optional?: Record<string, never>): {
|
|
@@ -67,8 +67,6 @@ export declare function useLibraryTab({ params, permissions }: TabViewProps, _op
|
|
|
67
67
|
onOrder: ({ value }: {
|
|
68
68
|
value: string;
|
|
69
69
|
}) => void;
|
|
70
|
-
onOrderCreated: () => void;
|
|
71
|
-
onOrderAlphabetical: () => void;
|
|
72
70
|
search: string;
|
|
73
71
|
loading: boolean;
|
|
74
72
|
busy: boolean;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useEffect, useLayoutEffect, useRef, useState } from 'react';
|
|
2
|
-
import { useController } from '../../../react/use-controller.js';
|
|
2
|
+
import { useController } from '../../../core/react/use-controller.js';
|
|
3
3
|
import { createLibraryController } from '../../controllers/library.controller.js';
|
|
4
4
|
import { useSharedMediaLibrary } from './MediaPage.hooks.js';
|
|
5
5
|
import { useMediaPorts } from './MediaPorts.hooks.js';
|
|
@@ -51,7 +51,7 @@ export function useLibraryTab({ params, permissions = [] }, _optional = {}) {
|
|
|
51
51
|
// Lightbox — an index into the list, not the item itself, so arrow-key/navigation
|
|
52
52
|
// buttons move the number without re-deriving "what's next" from an item reference.
|
|
53
53
|
const [lightboxIndex, setLightboxIndex] = useState(null);
|
|
54
|
-
const items = snapshot?.items ?? [];
|
|
54
|
+
const items = snapshot?.items ?? [], pendingIds = snapshot?.pendingIds ?? [];
|
|
55
55
|
const canUpload = permissions.includes('media.upload');
|
|
56
56
|
const canEdit = permissions.includes('media.update');
|
|
57
57
|
const canTrash = permissions.includes('media.trash');
|
|
@@ -59,7 +59,7 @@ export function useLibraryTab({ params, permissions = [] }, _optional = {}) {
|
|
|
59
59
|
const canPurge = permissions.includes('media.delete.force');
|
|
60
60
|
const actions = items.map((item, index) => ({
|
|
61
61
|
item,
|
|
62
|
-
busy:
|
|
62
|
+
busy: pendingIds.includes(item.id),
|
|
63
63
|
...(canEdit ? { onEdit: () => setEditing(item) } : {}),
|
|
64
64
|
onPreview: () => setLightboxIndex(index),
|
|
65
65
|
...(canTrash
|
|
@@ -123,16 +123,6 @@ export function useLibraryTab({ params, permissions = [] }, _optional = {}) {
|
|
|
123
123
|
onOrder: ({ value }) => {
|
|
124
124
|
void controller?.setQuery({ query: { ...controller.getSnapshot().query, orderBy: value === 'alphabetical' ? 'alphabetical' : 'created' } });
|
|
125
125
|
},
|
|
126
|
-
onOrderCreated: () => {
|
|
127
|
-
void controller?.setQuery({
|
|
128
|
-
query: { ...controller.getSnapshot().query, orderBy: 'created' },
|
|
129
|
-
});
|
|
130
|
-
},
|
|
131
|
-
onOrderAlphabetical: () => {
|
|
132
|
-
void controller?.setQuery({
|
|
133
|
-
query: { ...controller.getSnapshot().query, orderBy: 'alphabetical' },
|
|
134
|
-
});
|
|
135
|
-
},
|
|
136
126
|
search: snapshot?.query.search ?? '',
|
|
137
127
|
loading: !snapshot || (snapshot.items === null && snapshot.loading && !snapshot.error),
|
|
138
128
|
busy: snapshot?.busy ?? false,
|
|
@@ -147,13 +137,14 @@ export function useLibraryTab({ params, permissions = [] }, _optional = {}) {
|
|
|
147
137
|
},
|
|
148
138
|
cancelPurge: () => controller?.cancelPurge(),
|
|
149
139
|
confirmPurge: () => {
|
|
150
|
-
|
|
140
|
+
const item = controller?.getSnapshot().pendingPurge;
|
|
141
|
+
// The controller refuses to purge without a pending item, so there is nothing to announce.
|
|
142
|
+
if (!canPurge || !controller || !item)
|
|
151
143
|
return;
|
|
152
|
-
const
|
|
153
|
-
const index = items.findIndex(row => row.id === item?.id);
|
|
144
|
+
const index = items.findIndex(row => row.id === item.id);
|
|
154
145
|
return controller.confirmPurge({ confirmed: true }).then(saved => {
|
|
155
146
|
if (saved) {
|
|
156
|
-
setSuccess(
|
|
147
|
+
setSuccess(`Deleted permanently ${item.title}`);
|
|
157
148
|
setFocusAfterPurge(Math.max(0, index));
|
|
158
149
|
}
|
|
159
150
|
});
|
|
@@ -1,24 +1,11 @@
|
|
|
1
1
|
import { useState } from 'react';
|
|
2
2
|
import { safeMediaUrl, formatByteSize, formatUploadDate, mediaRowHandles } from '../../rules.js';
|
|
3
3
|
export function useMediaCard(props, _optional = {}) {
|
|
4
|
-
/**
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
* render-and-fall-back chain (`<img>` → onError → `<video>` → onError → placeholder) rather than a
|
|
10
|
-
* HEAD probe per card. Chosen over the HEAD approach because: (1) the byte route's documented
|
|
11
|
-
* contract covers GET — sniffing/Range were specified for that, not for HEAD, so building on HEAD
|
|
12
|
-
* would be assuming a behavior nobody confirmed; (2) a HEAD-first design means every card blocks on
|
|
13
|
-
* a round trip before any pixel paints, working against the fixed-aspect-box + lazy-loading this
|
|
14
|
-
* grid needs anyway, whereas the optimistic path costs nothing extra for the common case (a real
|
|
15
|
-
* image just loads) and only "wastes" a request for the video/unsupported minority — and even then,
|
|
16
|
-
* a browser's image decoder typically fails off the header bytes rather than pulling the whole
|
|
17
|
-
* file; (3) it composes for free with the server's security defusal: a sniffed HTML/SVG comes back
|
|
18
|
-
* as `application/octet-stream` with `Content-Disposition: attachment`, which is not a valid image
|
|
19
|
-
* OR video MIME, so it fails both probes and lands on the placeholder with zero special-casing —
|
|
20
|
-
* this file never needs to detect "is this the defused case" itself.
|
|
21
|
-
*/
|
|
4
|
+
/** Preview optimistically: image -> video -> placeholder. The list supplies no reliable media
|
|
5
|
+
* kind, and the byte route guarantees GET rather than HEAD, so probing headers would assume an
|
|
6
|
+
* unsupported contract and delay every card's first paint. Images load directly; failed decoders
|
|
7
|
+
* usually stop at header bytes. Defused HTML/SVG served as octet-stream attachments fails both
|
|
8
|
+
* decoders and reaches the placeholder without content-specific handling. */
|
|
22
9
|
const src = safeMediaUrl({ url: props.api.originalUrl({ id: props.item.id, version: props.item.version }) });
|
|
23
10
|
const [probe, setProbe] = useState({ src, stage: 'image' });
|
|
24
11
|
// New bytes can change media type under the same id. Retry the fallback from image
|
|
@@ -45,8 +32,7 @@ export function useMediaCard(props, _optional = {}) {
|
|
|
45
32
|
menuItems: [
|
|
46
33
|
...(props.onEdit ? [{ id: 'edit', label: 'Edit metadata', onPress: props.onEdit, disabled: props.busy === true }] : []),
|
|
47
34
|
...(props.onTrash && props.item.status !== 'trashed' ? [{ id: 'trash', label: 'Trash', onPress: props.onTrash, disabled: props.busy === true }] : []),
|
|
48
|
-
// Restore
|
|
49
|
-
// the card itself keeps the legacy look and active cards' menus stay legacy-identical.
|
|
35
|
+
// Restore belongs only in a trashed card's menu; active cards have nothing to restore.
|
|
50
36
|
...(props.onRestore && props.item.status === 'trashed' ? [{ id: 'restore', label: 'Restore', onPress: props.onRestore, disabled: props.busy === true }] : []),
|
|
51
37
|
...(props.onPurge && props.item.status === 'trashed' ? [{ id: 'purge', label: 'Delete permanently', attrs: { 'data-jini-variant': 'danger' }, onPress: props.onPurge, disabled: props.busy === true }] : []),
|
|
52
38
|
],
|
|
@@ -11,13 +11,13 @@ export declare function useMediaGrid(props: MediaGridProps, _optional?: Record<s
|
|
|
11
11
|
actions: {
|
|
12
12
|
handleBase: string;
|
|
13
13
|
busy?: boolean;
|
|
14
|
-
|
|
14
|
+
onPurge?: () => void;
|
|
15
15
|
locale?: string;
|
|
16
|
+
item: import("../../models.js").MediaAsset;
|
|
16
17
|
onEdit?: () => void;
|
|
17
18
|
onPreview?: () => void;
|
|
18
19
|
onTrash?: () => void;
|
|
19
20
|
onRestore?: () => void;
|
|
20
|
-
onPurge?: () => void;
|
|
21
21
|
onChoose?: () => void;
|
|
22
22
|
}[];
|
|
23
23
|
gridRef?: Ref<HTMLDivElement>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
|
-
import type { ModulePageProps } from '../../../react/bind-react.js';
|
|
2
|
+
import type { ModulePageProps } from '../../../core/react/bind-react.js';
|
|
3
3
|
import { createLibraryController } from '../../controllers/library.controller.js';
|
|
4
4
|
import type { LibraryState } from '../../controllers/library.controller.js';
|
|
5
5
|
export declare const MediaLibraryContext: import("react").Context<{
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createElement, createContext, useContext, useEffect, useState } from 'react';
|
|
2
|
-
import { useController } from '../../../react/use-controller.js';
|
|
2
|
+
import { useController } from '../../../core/react/use-controller.js';
|
|
3
3
|
import { createLibraryController } from '../../controllers/library.controller.js';
|
|
4
4
|
import { useMediaPorts } from './MediaPorts.hooks.js';
|
|
5
5
|
import { AllIcon, ImageIcon, VideoIcon, CloudIcon } from '../components/MediaIcons.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useController } from '../../../react/use-controller.js';
|
|
1
|
+
import { useController } from '../../../core/react/use-controller.js';
|
|
2
2
|
import { createLibraryController } from '../../controllers/library.controller.js';
|
|
3
3
|
import { acceptsMedia } from '../../rules.js';
|
|
4
4
|
export function useMediaPicker(props, _optional = {}) {
|
|
@@ -48,7 +48,11 @@ export function useProvidersTab(_required = {}, _optional = {}) {
|
|
|
48
48
|
}
|
|
49
49
|
// Older hosts retain the one-button editor; writes remain sequential and failures
|
|
50
50
|
// retain local edits. Atomic whole-set hosts should implement saveChanges instead.
|
|
51
|
-
|
|
51
|
+
// Submitted ids the last read never returned (catalog-only providers getting their
|
|
52
|
+
// first key or settings) start from an empty stored state, or their writes are lost.
|
|
53
|
+
const known = new Set(lastRead.map(item => item.id));
|
|
54
|
+
const unread = Object.keys(providers).filter(id => !known.has(id)).map(id => ({ id, configured: false }));
|
|
55
|
+
for (const item of [...lastRead, ...unread]) {
|
|
52
56
|
const entry = providers[item.id];
|
|
53
57
|
if (!entry) {
|
|
54
58
|
if (item.configured || item.baseUrl || item.model)
|
|
@@ -14,10 +14,17 @@ export interface UploadButtonProps {
|
|
|
14
14
|
/** Reads a browser File at the React boundary; headless controllers only receive bytes. */
|
|
15
15
|
export declare function readMediaFile({ file }: {
|
|
16
16
|
file: File;
|
|
17
|
-
}, { signal }?: {
|
|
17
|
+
}, { signal, createReader }?: {
|
|
18
18
|
signal?: AbortSignal;
|
|
19
|
+
createReader?: () => FileReader;
|
|
19
20
|
}): Promise<UploadInput>;
|
|
20
|
-
export declare function useUploadButton(props: UploadButtonProps,
|
|
21
|
+
export declare function useUploadButton(props: UploadButtonProps, { readFile }?: {
|
|
22
|
+
readFile?: (required: {
|
|
23
|
+
file: File;
|
|
24
|
+
}, optional?: {
|
|
25
|
+
signal?: AbortSignal;
|
|
26
|
+
}) => Promise<UploadInput>;
|
|
27
|
+
}): {
|
|
21
28
|
alt: string;
|
|
22
29
|
withAlt: boolean;
|
|
23
30
|
multiple: boolean;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { useEffect, useRef, useState } from 'react';
|
|
2
2
|
/** Reads a browser File at the React boundary; headless controllers only receive bytes. */
|
|
3
|
-
export function readMediaFile({ file }, { signal } = {}) {
|
|
3
|
+
export function readMediaFile({ file }, { signal, createReader = () => new FileReader() } = {}) {
|
|
4
4
|
return new Promise((resolve, reject) => {
|
|
5
5
|
signal?.throwIfAborted();
|
|
6
|
-
const reader =
|
|
6
|
+
const reader = createReader();
|
|
7
7
|
const abort = () => {
|
|
8
8
|
reader.abort();
|
|
9
9
|
reject(new Error('File read aborted'));
|
|
@@ -26,13 +26,15 @@ export function readMediaFile({ file }, { signal } = {}) {
|
|
|
26
26
|
reader.readAsDataURL(file);
|
|
27
27
|
});
|
|
28
28
|
}
|
|
29
|
-
export function useUploadButton(props,
|
|
29
|
+
export function useUploadButton(props, { readFile = readMediaFile } = {}) {
|
|
30
30
|
// Advisory picker formats mirror the host's standard ceiling, including video. The
|
|
31
31
|
// injected server still validates/sniffs bytes; a file picker is never a security boundary.
|
|
32
32
|
const inputRef = useRef(null);
|
|
33
33
|
const [selectedFiles, setSelectedFiles] = useState([]);
|
|
34
34
|
const [alt, setAlt] = useState(''), [busy, setBusy] = useState(false), [error, setError] = useState(null), [success, setSuccess] = useState(null);
|
|
35
|
-
|
|
35
|
+
// The mount effect swaps in a fresh controller (StrictMode replays it), so handlers,
|
|
36
|
+
// which only run after mount, always read a live signal.
|
|
37
|
+
const inFlight = useRef(false), lifetime = useRef(new AbortController());
|
|
36
38
|
useEffect(() => {
|
|
37
39
|
const abort = new AbortController();
|
|
38
40
|
lifetime.current = abort;
|
|
@@ -48,7 +50,7 @@ export function useUploadButton(props, _optional = {}) {
|
|
|
48
50
|
async function uploadFiles(files) {
|
|
49
51
|
if (!files.length || inFlight.current || props.disabled)
|
|
50
52
|
return;
|
|
51
|
-
const signal = lifetime.current
|
|
53
|
+
const signal = lifetime.current.signal;
|
|
52
54
|
inFlight.current = true;
|
|
53
55
|
setBusy(true);
|
|
54
56
|
setError(null);
|
|
@@ -57,12 +59,12 @@ export function useUploadButton(props, _optional = {}) {
|
|
|
57
59
|
let remaining = [...files];
|
|
58
60
|
try {
|
|
59
61
|
for (const file of files) {
|
|
60
|
-
const input = await
|
|
61
|
-
if (signal
|
|
62
|
+
const input = await readFile({ file }, { signal });
|
|
63
|
+
if (signal.aborted)
|
|
62
64
|
return;
|
|
63
65
|
if (!await props.upload({ input, ...(alt.trim() ? { alt: alt.trim() } : {}) }))
|
|
64
66
|
throw new Error(`Upload failed for ${file.name}`);
|
|
65
|
-
if (signal
|
|
67
|
+
if (signal.aborted)
|
|
66
68
|
return;
|
|
67
69
|
uploaded.push(file.name);
|
|
68
70
|
remaining = remaining.slice(1);
|
|
@@ -72,12 +74,12 @@ export function useUploadButton(props, _optional = {}) {
|
|
|
72
74
|
inputRef.current.value = '';
|
|
73
75
|
}
|
|
74
76
|
catch (error) {
|
|
75
|
-
if (!signal
|
|
77
|
+
if (!signal.aborted)
|
|
76
78
|
setError(error instanceof Error ? error.message : 'Upload failed');
|
|
77
79
|
}
|
|
78
80
|
finally {
|
|
79
81
|
inFlight.current = false;
|
|
80
|
-
if (!signal
|
|
82
|
+
if (!signal.aborted) {
|
|
81
83
|
// Keep only failed/unattempted files: retry must not duplicate successes in a batch.
|
|
82
84
|
setSelectedFiles(remaining);
|
|
83
85
|
setBusy(false);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createElement } from 'react';
|
|
2
|
-
import { bindReact } from '../../react/bind-react.js';
|
|
2
|
+
import { bindReact } from '../../core/react/bind-react.js';
|
|
3
3
|
import { mediaModule } from '../media.module.js';
|
|
4
4
|
import { MediaPortsContext } from './hooks/MediaPorts.hooks.js';
|
|
5
5
|
export { useMediaPorts } from './hooks/MediaPorts.hooks.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { TabViewProps } from '../../../react/bind-react.js';
|
|
1
|
+
import type { TabViewProps } from '../../../core/react/bind-react.js';
|
|
2
2
|
export declare function LibraryTab(props: TabViewProps, _optional?: Record<string, never>): import("react").JSX.Element;
|
|
3
3
|
export default LibraryTab;
|
|
4
4
|
//# sourceMappingURL=LibraryTab.d.ts.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { TabViewProps } from '../../../react/bind-react.js';
|
|
1
|
+
import type { TabViewProps } from '../../../core/react/bind-react.js';
|
|
2
2
|
export declare function ProvidersTab(_props: TabViewProps, _optional?: Record<string, never>): import("react").JSX.Element;
|
|
3
3
|
export default ProvidersTab;
|
|
4
4
|
//# sourceMappingURL=ProvidersTab.d.ts.map
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import type { TabViewProps } from '../../../react/bind-react.js';
|
|
1
|
+
import type { TabViewProps } from '../../../core/react/bind-react.js';
|
|
2
2
|
export declare function Canvas(props: TabViewProps, _optional?: {}): import("react").JSX.Element;
|
|
3
3
|
//# sourceMappingURL=Canvas.d.ts.map
|