@elytracms/studio 0.0.92 → 0.0.94
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/convex/analytics-cost.test.ts +817 -0
- package/convex/asset-alt-migration.test.ts +127 -0
- package/convex/asset-replace.test.ts +168 -0
- package/convex/asset-revalidate.test.ts +6 -6
- package/convex/assets.ts +41 -5
- package/convex/auth.ts +5 -2
- package/convex/authority.ts +35 -11
- package/convex/cms.ts +25 -11
- package/convex/contract-parity.test.ts +25 -3
- package/convex/delivery.ts +12 -9
- package/convex/document-revalidate.test.ts +10 -12
- package/convex/guard.ts +9 -7
- package/convex/http-routes.test.ts +18 -8
- package/convex/http.ts +23 -17
- package/convex/ingest.test.ts +326 -0
- package/convex/ingest.ts +281 -0
- package/convex/invites.ts +13 -5
- package/convex/members.ts +10 -4
- package/convex/orphan-hydration.test.ts +116 -0
- package/convex/rail-write-parity.test.ts +11 -8
- package/convex/record-functions.ts +325 -0
- package/convex/records.test.ts +156 -0
- package/convex/redirect-migration.test.ts +205 -0
- package/convex/redirect-revalidate.test.ts +5 -1
- package/convex/redirects.ts +120 -79
- package/convex/role-policy.test.ts +315 -0
- package/convex/roles.ts +83 -0
- package/convex/schema.ts +36 -0
- package/convex/triggers.ts +21 -1
- package/dist/addon.d.ts +71 -0
- package/dist/addon.js +67 -0
- package/dist/addon.js.map +1 -0
- package/dist/components/account/account-settings.js +2 -2
- package/dist/components/account/account-settings.js.map +1 -1
- package/dist/components/account/credential-panel.js +2 -2
- package/dist/components/account/credential-panel.js.map +1 -1
- package/dist/components/account/self-account-schema.d.ts +6 -15
- package/dist/components/account/self-account-schema.js +52 -34
- package/dist/components/account/self-account-schema.js.map +1 -1
- package/dist/components/account/self-account-view-source.d.ts +1 -1
- package/dist/components/account/self-account-view-source.js +6 -4
- package/dist/components/account/self-account-view-source.js.map +1 -1
- package/dist/components/ai/ai-chat-panel-lazy.js +1 -1
- package/dist/components/ai/ai-chat-panel-lazy.js.map +1 -1
- package/dist/components/ai/ai-chat-panel.js +6 -6
- package/dist/components/ai/ai-chat-panel.js.map +1 -1
- package/dist/components/ai/ai-cms-panel.js +2 -2
- package/dist/components/ai/ai-cms-panel.js.map +1 -1
- package/dist/components/ai/ai-panel-toggle.js +4 -4
- package/dist/components/ai/ai-panel-toggle.js.map +1 -1
- package/dist/components/ai/chat-ui.js +2 -2
- package/dist/components/ai/chat-ui.js.map +1 -1
- package/dist/components/ai/proposal-card.d.ts +2 -2
- package/dist/components/ai/proposal-card.js +4 -3
- package/dist/components/ai/proposal-card.js.map +1 -1
- package/dist/components/ai/proposal-issues.js +2 -2
- package/dist/components/ai/proposal-issues.js.map +1 -1
- package/dist/components/ai/translate-document-action.js +2 -2
- package/dist/components/ai/translate-document-action.js.map +1 -1
- package/dist/components/ai/translate-document-dialog.js +10 -8
- package/dist/components/ai/translate-document-dialog.js.map +1 -1
- package/dist/components/ai/translate-skipped.d.ts +19 -0
- package/dist/components/ai/translate-skipped.js +46 -0
- package/dist/components/ai/translate-skipped.js.map +1 -0
- package/dist/components/app-views/default-modules.d.ts +50 -2
- package/dist/components/app-views/default-modules.js +65 -20
- package/dist/components/app-views/default-modules.js.map +1 -1
- package/dist/components/app-views/module.d.ts +53 -3
- package/dist/components/app-views/module.js +14 -7
- package/dist/components/app-views/module.js.map +1 -1
- package/dist/components/app-views/sidebar.js +1 -1
- package/dist/components/app-views/sidebar.js.map +1 -1
- package/dist/components/app-views/studio-config.d.ts +11 -2
- package/dist/components/app-views/studio-config.js.map +1 -1
- package/dist/components/assets/asset-detail.js +20 -10
- package/dist/components/assets/asset-detail.js.map +1 -1
- package/dist/components/assets/asset-grid.js +2 -2
- package/dist/components/assets/asset-grid.js.map +1 -1
- package/dist/components/assets/asset-metadata.d.ts +15 -4
- package/dist/components/assets/asset-metadata.js +15 -2
- package/dist/components/assets/asset-metadata.js.map +1 -1
- package/dist/components/assets/asset-search.js +4 -1
- package/dist/components/assets/asset-search.js.map +1 -1
- package/dist/components/assets/asset-selection.d.ts +0 -8
- package/dist/components/assets/asset-selection.js +4 -23
- package/dist/components/assets/asset-selection.js.map +1 -1
- package/dist/components/assets/asset-thumb.js +6 -5
- package/dist/components/assets/asset-thumb.js.map +1 -1
- package/dist/components/assets/assets-collection-view-source.d.ts +5 -5
- package/dist/components/assets/assets-collection-view-source.js +9 -5
- package/dist/components/assets/assets-collection-view-source.js.map +1 -1
- package/dist/components/assets/assets-module.js +1 -1
- package/dist/components/assets/assets-module.js.map +1 -1
- package/dist/components/assets/assets-store.d.ts +16 -2
- package/dist/components/assets/assets-store.js +28 -6
- package/dist/components/assets/assets-store.js.map +1 -1
- package/dist/components/assets/assets-view-preference.js +1 -1
- package/dist/components/assets/assets-view-preference.js.map +1 -1
- package/dist/components/assets/replace-file.d.ts +22 -0
- package/dist/components/assets/replace-file.js +59 -0
- package/dist/components/assets/replace-file.js.map +1 -0
- package/dist/components/auth-context.d.ts +1 -1
- package/dist/components/auth-context.js +6 -6
- package/dist/components/auth-context.js.map +1 -1
- package/dist/components/builder/blocks-model.d.ts +15 -27
- package/dist/components/builder/blocks-model.js +38 -44
- package/dist/components/builder/blocks-model.js.map +1 -1
- package/dist/components/builder/blocks-panel.d.ts +8 -4
- package/dist/components/builder/blocks-panel.js +10 -9
- package/dist/components/builder/blocks-panel.js.map +1 -1
- package/dist/components/builder/builder-store.d.ts +104 -15
- package/dist/components/builder/builder-store.js +157 -32
- package/dist/components/builder/builder-store.js.map +1 -1
- package/dist/components/builder/canvas-body.d.ts +30 -0
- package/dist/components/builder/canvas-body.js +38 -0
- package/dist/components/builder/canvas-body.js.map +1 -0
- package/dist/components/builder/canvas-breakpoints.js +1 -1
- package/dist/components/builder/canvas-breakpoints.js.map +1 -1
- package/dist/components/builder/canvas-chrome-css.js +7 -0
- package/dist/components/builder/canvas-chrome-css.js.map +1 -1
- package/dist/components/builder/canvas-dnd.d.ts +9 -9
- package/dist/components/builder/canvas-dnd.js +6 -6
- package/dist/components/builder/canvas-dnd.js.map +1 -1
- package/dist/components/builder/canvas-inline-editing.d.ts +1 -1
- package/dist/components/builder/canvas-inline-editing.js.map +1 -1
- package/dist/components/builder/canvas-overlay-geometry.d.ts +1 -1
- package/dist/components/builder/canvas-overlay-geometry.js.map +1 -1
- package/dist/components/builder/canvas-render-context.d.ts +28 -20
- package/dist/components/builder/canvas-render-context.js +46 -47
- package/dist/components/builder/canvas-render-context.js.map +1 -1
- package/dist/components/builder/canvas-runs.d.ts +45 -0
- package/dist/components/builder/canvas-runs.js +51 -0
- package/dist/components/builder/canvas-runs.js.map +1 -0
- package/dist/components/builder/canvas-theme-notice.js +2 -2
- package/dist/components/builder/canvas-theme-notice.js.map +1 -1
- package/dist/components/builder/canvas.js +35 -8
- package/dist/components/builder/canvas.js.map +1 -1
- package/dist/components/builder/clipboard-actions.d.ts +16 -1
- package/dist/components/builder/clipboard-actions.js +48 -11
- package/dist/components/builder/clipboard-actions.js.map +1 -1
- package/dist/components/builder/composition-field-editor-lazy.js +1 -1
- package/dist/components/builder/composition-field-editor-lazy.js.map +1 -1
- package/dist/components/builder/composition-field-editor.d.ts +4 -16
- package/dist/components/builder/composition-field-editor.js +32 -43
- package/dist/components/builder/composition-field-editor.js.map +1 -1
- package/dist/components/builder/delete-key.d.ts +5 -0
- package/dist/components/builder/delete-key.js +4 -0
- package/dist/components/builder/delete-key.js.map +1 -1
- package/dist/components/builder/drag-session.d.ts +1 -1
- package/dist/components/builder/drag-session.js.map +1 -1
- package/dist/components/builder/fixture-thumbnail.js +1 -1
- package/dist/components/builder/fixture-thumbnail.js.map +1 -1
- package/dist/components/builder/insert-context.d.ts +18 -26
- package/dist/components/builder/insert-context.js +47 -94
- package/dist/components/builder/insert-context.js.map +1 -1
- package/dist/components/builder/layout-inspector.js +1 -1
- package/dist/components/builder/layout-inspector.js.map +1 -1
- package/dist/components/builder/node-title.js +1 -1
- package/dist/components/builder/node-title.js.map +1 -1
- package/dist/components/builder/page-tree.d.ts +4 -4
- package/dist/components/builder/page-tree.js +8 -8
- package/dist/components/builder/page-tree.js.map +1 -1
- package/dist/components/builder/placement-refusal.d.ts +18 -0
- package/dist/components/builder/placement-refusal.js +46 -0
- package/dist/components/builder/placement-refusal.js.map +1 -0
- package/dist/components/builder/property-inspector.js +8 -4
- package/dist/components/builder/property-inspector.js.map +1 -1
- package/dist/components/builder/reuse-context.js +68 -30
- package/dist/components/builder/reuse-context.js.map +1 -1
- package/dist/components/builder/reuse.d.ts +79 -1
- package/dist/components/builder/reuse.js +70 -3
- package/dist/components/builder/reuse.js.map +1 -1
- package/dist/components/builder/rich-text-toolbar.js +1 -1
- package/dist/components/builder/rich-text-toolbar.js.map +1 -1
- package/dist/components/builder/run-inspector.d.ts +11 -0
- package/dist/components/builder/run-inspector.js +33 -0
- package/dist/components/builder/run-inspector.js.map +1 -0
- package/dist/components/builder/selection-overlay.js +3 -3
- package/dist/components/builder/selection-overlay.js.map +1 -1
- package/dist/components/builder/spacing-handles.d.ts +2 -2
- package/dist/components/builder/spacing-handles.js +3 -3
- package/dist/components/builder/spacing-handles.js.map +1 -1
- package/dist/components/cms/asset-field.d.ts +38 -1
- package/dist/components/cms/asset-field.js +184 -60
- package/dist/components/cms/asset-field.js.map +1 -1
- package/dist/components/cms/asset-picker-core.d.ts +16 -0
- package/dist/components/cms/asset-picker-core.js +20 -0
- package/dist/components/cms/asset-picker-core.js.map +1 -1
- package/dist/components/cms/asset-picker.js +14 -20
- package/dist/components/cms/asset-picker.js.map +1 -1
- package/dist/components/cms/bulk-actions.d.ts +4 -20
- package/dist/components/cms/bulk-actions.js +5 -26
- package/dist/components/cms/bulk-actions.js.map +1 -1
- package/dist/components/cms/bulk-bar.d.ts +2 -2
- package/dist/components/cms/bulk-bar.js +2 -2
- package/dist/components/cms/bulk-bar.js.map +1 -1
- package/dist/components/cms/cell-edit.d.ts +1 -1
- package/dist/components/cms/cell-edit.js +1 -1
- package/dist/components/cms/cell-edit.js.map +1 -1
- package/dist/components/cms/cell.d.ts +1 -1
- package/dist/components/cms/cell.js +3 -3
- package/dist/components/cms/cell.js.map +1 -1
- package/dist/components/cms/cms-index.js +2 -2
- package/dist/components/cms/cms-index.js.map +1 -1
- package/dist/components/cms/cms-labels.js +2 -2
- package/dist/components/cms/cms-labels.js.map +1 -1
- package/dist/components/cms/cms-store.d.ts +7 -6
- package/dist/components/cms/cms-store.js +23 -43
- package/dist/components/cms/cms-store.js.map +1 -1
- package/dist/components/cms/collection-table.js +7 -7
- package/dist/components/cms/collection-table.js.map +1 -1
- package/dist/components/cms/detail-editor.d.ts +5 -2
- package/dist/components/cms/detail-editor.js +20 -15
- package/dist/components/cms/detail-editor.js.map +1 -1
- package/dist/components/cms/details-pane.js +1 -1
- package/dist/components/cms/details-pane.js.map +1 -1
- package/dist/components/cms/doc-state.js +1 -1
- package/dist/components/cms/doc-state.js.map +1 -1
- package/dist/components/cms/document-diff-view.d.ts +3 -1
- package/dist/components/cms/document-diff-view.js +17 -12
- package/dist/components/cms/document-diff-view.js.map +1 -1
- package/dist/components/cms/document-editor-context.js +1 -1
- package/dist/components/cms/document-editor-context.js.map +1 -1
- package/dist/components/cms/document-staging.js +1 -1
- package/dist/components/cms/document-staging.js.map +1 -1
- package/dist/components/cms/document-url-path.js +2 -2
- package/dist/components/cms/document-url-path.js.map +1 -1
- package/dist/components/cms/duplicate-document.d.ts +1 -1
- package/dist/components/cms/duplicate-document.js.map +1 -1
- package/dist/components/cms/editable-cell.js +3 -3
- package/dist/components/cms/editable-cell.js.map +1 -1
- package/dist/components/cms/field-anchors.d.ts +7 -4
- package/dist/components/cms/field-anchors.js +9 -7
- package/dist/components/cms/field-anchors.js.map +1 -1
- package/dist/components/cms/field-collaboration.d.ts +1 -1
- package/dist/components/cms/field-collaboration.js +1 -1
- package/dist/components/cms/field-collaboration.js.map +1 -1
- package/dist/components/cms/field-connection.d.ts +1 -1
- package/dist/components/cms/field-connection.js +2 -2
- package/dist/components/cms/field-connection.js.map +1 -1
- package/dist/components/cms/field-control-lib.d.ts +15 -1
- package/dist/components/cms/field-control-lib.js +23 -4
- package/dist/components/cms/field-control-lib.js.map +1 -1
- package/dist/components/cms/field-control.js +4 -4
- package/dist/components/cms/field-control.js.map +1 -1
- package/dist/components/cms/field-renderer.d.ts +1 -1
- package/dist/components/cms/field-renderer.js +2 -2
- package/dist/components/cms/field-renderer.js.map +1 -1
- package/dist/components/cms/filter-builder.d.ts +1 -1
- package/dist/components/cms/filter-builder.js +3 -3
- package/dist/components/cms/filter-builder.js.map +1 -1
- package/dist/components/cms/gate-validation.d.ts +21 -30
- package/dist/components/cms/gate-validation.js +11 -32
- package/dist/components/cms/gate-validation.js.map +1 -1
- package/dist/components/cms/import-export.js +2 -2
- package/dist/components/cms/import-export.js.map +1 -1
- package/dist/components/cms/issue-copy.js +1 -1
- package/dist/components/cms/issue-copy.js.map +1 -1
- package/dist/components/cms/issues-pane.js +3 -3
- package/dist/components/cms/issues-pane.js.map +1 -1
- package/dist/components/cms/object-array-field.js +6 -6
- package/dist/components/cms/object-array-field.js.map +1 -1
- package/dist/components/cms/prop-fields.d.ts +7 -2
- package/dist/components/cms/prop-fields.js +14 -7
- package/dist/components/cms/prop-fields.js.map +1 -1
- package/dist/components/cms/reference-confirm.d.ts +1 -1
- package/dist/components/cms/reference-confirm.js +2 -2
- package/dist/components/cms/reference-confirm.js.map +1 -1
- package/dist/components/cms/relation-picker.js +2 -2
- package/dist/components/cms/relation-picker.js.map +1 -1
- package/dist/components/cms/rich-text/block-style.js +1 -1
- package/dist/components/cms/rich-text/block-style.js.map +1 -1
- package/dist/components/cms/rich-text/component-embed.js +4 -4
- package/dist/components/cms/rich-text/component-embed.js.map +1 -1
- package/dist/components/cms/rich-text/editor.js +3 -3
- package/dist/components/cms/rich-text/editor.js.map +1 -1
- package/dist/components/cms/rich-text/embed-picker.d.ts +1 -1
- package/dist/components/cms/rich-text/embed-picker.js +3 -3
- package/dist/components/cms/rich-text/embed-picker.js.map +1 -1
- package/dist/components/cms/rich-text/embed-props.js +3 -3
- package/dist/components/cms/rich-text/embed-props.js.map +1 -1
- package/dist/components/cms/rich-text/extensions.js +1 -1
- package/dist/components/cms/rich-text/extensions.js.map +1 -1
- package/dist/components/cms/rich-text/font-size-control.d.ts +1 -1
- package/dist/components/cms/rich-text/font-size-control.js +2 -2
- package/dist/components/cms/rich-text/font-size-control.js.map +1 -1
- package/dist/components/cms/rich-text/insert.d.ts +20 -9
- package/dist/components/cms/rich-text/insert.js +25 -17
- package/dist/components/cms/rich-text/insert.js.map +1 -1
- package/dist/components/cms/rich-text/lazy.js +1 -1
- package/dist/components/cms/rich-text/lazy.js.map +1 -1
- package/dist/components/cms/rich-text/link-control.js +2 -2
- package/dist/components/cms/rich-text/link-control.js.map +1 -1
- package/dist/components/cms/rich-text/slash-command.js +3 -3
- package/dist/components/cms/rich-text/slash-command.js.map +1 -1
- package/dist/components/cms/rich-text/toolbar.js +1 -1
- package/dist/components/cms/rich-text/toolbar.js.map +1 -1
- package/dist/components/cms/rich-text/unknown-node.js +2 -2
- package/dist/components/cms/rich-text/unknown-node.js.map +1 -1
- package/dist/components/cms/rich-text-preview.js +2 -2
- package/dist/components/cms/rich-text-preview.js.map +1 -1
- package/dist/components/cms/row-actions.d.ts +1 -1
- package/dist/components/cms/row-actions.js.map +1 -1
- package/dist/components/cms/scalar-value-editor.d.ts +1 -1
- package/dist/components/cms/scalar-value-editor.js +2 -2
- package/dist/components/cms/scalar-value-editor.js.map +1 -1
- package/dist/components/cms/use-discard-controller.d.ts +1 -1
- package/dist/components/cms/use-discard-controller.js +1 -1
- package/dist/components/cms/use-discard-controller.js.map +1 -1
- package/dist/components/cms/use-document-draft.d.ts +7 -1
- package/dist/components/cms/use-document-draft.js +16 -2
- package/dist/components/cms/use-document-draft.js.map +1 -1
- package/dist/components/cms/use-document-versions.js +1 -1
- package/dist/components/cms/use-document-versions.js.map +1 -1
- package/dist/components/cms/use-field-focus.d.ts +1 -1
- package/dist/components/cms/use-field-focus.js +2 -2
- package/dist/components/cms/use-field-focus.js.map +1 -1
- package/dist/components/cms/use-live-url.d.ts +1 -1
- package/dist/components/cms/use-live-url.js +3 -3
- package/dist/components/cms/use-live-url.js.map +1 -1
- package/dist/components/cms/use-publish-controller.d.ts +2 -3
- package/dist/components/cms/use-publish-controller.js +8 -20
- package/dist/components/cms/use-publish-controller.js.map +1 -1
- package/dist/components/cms/use-published-perspective.d.ts +1 -1
- package/dist/components/cms/use-published-perspective.js.map +1 -1
- package/dist/components/cms/use-restore-version.d.ts +1 -1
- package/dist/components/cms/use-restore-version.js +1 -1
- package/dist/components/cms/use-restore-version.js.map +1 -1
- package/dist/components/cms/version-compare-lazy.js +1 -1
- package/dist/components/cms/version-compare-lazy.js.map +1 -1
- package/dist/components/cms/version-compare.js +1 -1
- package/dist/components/cms/version-compare.js.map +1 -1
- package/dist/components/cms/versions-pane.js +11 -4
- package/dist/components/cms/versions-pane.js.map +1 -1
- package/dist/components/cms/view-bar.js +3 -3
- package/dist/components/cms/view-bar.js.map +1 -1
- package/dist/components/collection-view/cms-collection-view-source.d.ts +1 -1
- package/dist/components/collection-view/cms-collection-view-source.js +5 -3
- package/dist/components/collection-view/cms-collection-view-source.js.map +1 -1
- package/dist/components/collection-view/collection-view-context.d.ts +1 -1
- package/dist/components/collection-view/collection-view-context.js +1 -1
- package/dist/components/collection-view/collection-view-context.js.map +1 -1
- package/dist/components/collection-view/collection-view-surface.d.ts +13 -0
- package/dist/components/collection-view/collection-view-surface.js +18 -0
- package/dist/components/collection-view/collection-view-surface.js.map +1 -0
- package/dist/components/components/component-detail.js +10 -10
- package/dist/components/components/component-detail.js.map +1 -1
- package/dist/components/components/component-preview.d.ts +2 -2
- package/dist/components/components/component-preview.js +5 -5
- package/dist/components/components/component-preview.js.map +1 -1
- package/dist/components/components/component-status.d.ts +1 -1
- package/dist/components/components/component-status.js +1 -1
- package/dist/components/components/component-status.js.map +1 -1
- package/dist/components/components/components-index.js +2 -2
- package/dist/components/components/components-index.js.map +1 -1
- package/dist/components/components/field-inspector.js +1 -1
- package/dist/components/components/field-inspector.js.map +1 -1
- package/dist/components/components/registry-browser.js +3 -3
- package/dist/components/components/registry-browser.js.map +1 -1
- package/dist/components/components/taxonomy.d.ts +1 -1
- package/dist/components/components/taxonomy.js.map +1 -1
- package/dist/components/components/transfer-dialog.js +4 -3
- package/dist/components/components/transfer-dialog.js.map +1 -1
- package/dist/components/components/transfer.d.ts +10 -10
- package/dist/components/components/transfer.js +13 -12
- package/dist/components/components/transfer.js.map +1 -1
- package/dist/components/current-user-menu.js +1 -1
- package/dist/components/current-user-menu.js.map +1 -1
- package/dist/components/delivery-health-banner.js +2 -2
- package/dist/components/delivery-health-banner.js.map +1 -1
- package/dist/components/deployment-setup-gate.js +3 -3
- package/dist/components/deployment-setup-gate.js.map +1 -1
- package/dist/components/dev-login-badge.js +2 -2
- package/dist/components/dev-login-badge.js.map +1 -1
- package/dist/components/guards.js +1 -1
- package/dist/components/guards.js.map +1 -1
- package/dist/components/inspector/document-panes.d.ts +54 -0
- package/dist/components/inspector/document-panes.js +41 -0
- package/dist/components/inspector/document-panes.js.map +1 -0
- package/dist/components/inspector/inspector-sidebar.js +1 -1
- package/dist/components/inspector/inspector-sidebar.js.map +1 -1
- package/dist/components/nav-issues-registry.d.ts +53 -0
- package/dist/components/nav-issues-registry.js +69 -0
- package/dist/components/nav-issues-registry.js.map +1 -0
- package/dist/components/nav-issues.js +4 -4
- package/dist/components/nav-issues.js.map +1 -1
- package/dist/components/operation-error.d.ts +1 -1
- package/dist/components/operation-error.js +4 -4
- package/dist/components/operation-error.js.map +1 -1
- package/dist/components/playground-badge.js +2 -2
- package/dist/components/playground-badge.js.map +1 -1
- package/dist/components/project-list.js +3 -3
- package/dist/components/project-list.js.map +1 -1
- package/dist/components/project-session.d.ts +2 -2
- package/dist/components/project-session.js +5 -5
- package/dist/components/project-session.js.map +1 -1
- package/dist/components/project-switcher.js +2 -2
- package/dist/components/project-switcher.js.map +1 -1
- package/dist/components/rail-store-status.js +1 -1
- package/dist/components/rail-store-status.js.map +1 -1
- package/dist/components/rail-view-source.d.ts +6 -5
- package/dist/components/rail-view-source.js +4 -3
- package/dist/components/rail-view-source.js.map +1 -1
- package/dist/components/records/record-lens.d.ts +51 -0
- package/dist/components/records/record-lens.js +156 -0
- package/dist/components/records/record-lens.js.map +1 -0
- package/dist/components/records/record-rail.d.ts +86 -0
- package/dist/components/records/record-rail.js +241 -0
- package/dist/components/records/record-rail.js.map +1 -0
- package/dist/components/redirects/redirects-schema.js +1 -1
- package/dist/components/redirects/redirects-schema.js.map +1 -1
- package/dist/components/references/references-panel.js +2 -2
- package/dist/components/references/references-panel.js.map +1 -1
- package/dist/components/section-nav.js +27 -11
- package/dist/components/section-nav.js.map +1 -1
- package/dist/components/section-state.js +2 -2
- package/dist/components/section-state.js.map +1 -1
- package/dist/components/section-surface.js +2 -2
- package/dist/components/section-surface.js.map +1 -1
- package/dist/components/session-expired-overlay.js +3 -3
- package/dist/components/session-expired-overlay.js.map +1 -1
- package/dist/components/settings-form.js +1 -1
- package/dist/components/settings-form.js.map +1 -1
- package/dist/components/sign-in-needed-badge.js +1 -1
- package/dist/components/sign-in-needed-badge.js.map +1 -1
- package/dist/components/sign-in-view.d.ts +2 -2
- package/dist/components/sign-in-view.js +5 -5
- package/dist/components/sign-in-view.js.map +1 -1
- package/dist/components/studio-context.js +3 -3
- package/dist/components/studio-context.js.map +1 -1
- package/dist/components/studio-project-screen.js +30 -3
- package/dist/components/studio-project-screen.js.map +1 -1
- package/dist/components/studio-shell.js +6 -5
- package/dist/components/studio-shell.js.map +1 -1
- package/dist/components/theme-context.d.ts +1 -1
- package/dist/components/theme-context.js +2 -2
- package/dist/components/theme-context.js.map +1 -1
- package/dist/components/use-hydration-phase.d.ts +1 -1
- package/dist/components/use-hydration-phase.js +1 -1
- package/dist/components/use-hydration-phase.js.map +1 -1
- package/dist/components/users/invite-user-bar.d.ts +27 -0
- package/dist/components/users/invite-user-bar.js +25 -9
- package/dist/components/users/invite-user-bar.js.map +1 -1
- package/dist/components/users/role-policy-drift-issues.d.ts +38 -0
- package/dist/components/users/role-policy-drift-issues.js +71 -0
- package/dist/components/users/role-policy-drift-issues.js.map +1 -0
- package/dist/components/users/users-collection-view-source.d.ts +1 -1
- package/dist/components/users/users-collection-view-source.js +4 -2
- package/dist/components/users/users-collection-view-source.js.map +1 -1
- package/dist/components/users/users-module.js +1 -1
- package/dist/components/users/users-module.js.map +1 -1
- package/dist/components/users/users-schema.d.ts +10 -1
- package/dist/components/users/users-schema.js +45 -25
- package/dist/components/users/users-schema.js.map +1 -1
- package/dist/components/users/users-store.js +3 -3
- package/dist/components/users/users-store.js.map +1 -1
- package/dist/components/workspace-projects.d.ts +1 -1
- package/dist/components/workspace-projects.js +1 -1
- package/dist/components/workspace-projects.js.map +1 -1
- package/dist/lib/ai/apply-content.js +56 -116
- package/dist/lib/ai/apply-content.js.map +1 -1
- package/dist/lib/ai/chat-context.d.ts +8 -0
- package/dist/lib/ai/chat-context.js +37 -6
- package/dist/lib/ai/chat-context.js.map +1 -1
- package/dist/lib/ai/chat-state.d.ts +6 -0
- package/dist/lib/ai/chat-state.js.map +1 -1
- package/dist/lib/ai/constraints.d.ts +20 -5
- package/dist/lib/ai/constraints.js +125 -33
- package/dist/lib/ai/constraints.js.map +1 -1
- package/dist/lib/ai/fake-provider.d.ts +16 -3
- package/dist/lib/ai/fake-provider.js +22 -6
- package/dist/lib/ai/fake-provider.js.map +1 -1
- package/dist/lib/ai/index.d.ts +3 -1
- package/dist/lib/ai/index.js +3 -1
- package/dist/lib/ai/index.js.map +1 -1
- package/dist/lib/ai/leash.d.ts +34 -0
- package/dist/lib/ai/leash.js +28 -0
- package/dist/lib/ai/leash.js.map +1 -0
- package/dist/lib/ai/node-handler.d.ts +1 -1
- package/dist/lib/ai/node-handler.js.map +1 -1
- package/dist/lib/ai/rich-text-input.d.ts +8 -8
- package/dist/lib/ai/rich-text-input.js +2 -1
- package/dist/lib/ai/rich-text-input.js.map +1 -1
- package/dist/lib/ai/rich-text-translate.d.ts +27 -4
- package/dist/lib/ai/rich-text-translate.js +28 -10
- package/dist/lib/ai/rich-text-translate.js.map +1 -1
- package/dist/lib/ai/shape.d.ts +13 -13
- package/dist/lib/ai/shape.js +69 -93
- package/dist/lib/ai/shape.js.map +1 -1
- package/dist/lib/ai/tools.d.ts +36 -41
- package/dist/lib/ai/tools.js +83 -87
- package/dist/lib/ai/tools.js.map +1 -1
- package/dist/lib/ai/translate-document.d.ts +32 -4
- package/dist/lib/ai/translate-document.js +145 -47
- package/dist/lib/ai/translate-document.js.map +1 -1
- package/dist/lib/ai/types.d.ts +34 -23
- package/dist/lib/ai/types.js.map +1 -1
- package/dist/lib/ai/update-fields.d.ts +15 -12
- package/dist/lib/ai/update-fields.js +112 -188
- package/dist/lib/ai/update-fields.js.map +1 -1
- package/dist/lib/ai/writer-form.d.ts +116 -0
- package/dist/lib/ai/writer-form.js +429 -0
- package/dist/lib/ai/writer-form.js.map +1 -0
- package/dist/lib/assets/upload.d.ts +31 -2
- package/dist/lib/assets/upload.js +61 -6
- package/dist/lib/assets/upload.js.map +1 -1
- package/dist/lib/clipboard/os-clipboard.d.ts +10 -2
- package/dist/lib/clipboard/os-clipboard.js +8 -4
- package/dist/lib/clipboard/os-clipboard.js.map +1 -1
- package/dist/lib/clipboard/paste-issues.js +1 -1
- package/dist/lib/clipboard/paste-issues.js.map +1 -1
- package/dist/lib/clipboard/paste-target.d.ts +18 -9
- package/dist/lib/clipboard/paste-target.js +10 -9
- package/dist/lib/clipboard/paste-target.js.map +1 -1
- package/dist/lib/cms/field-patch.d.ts +0 -10
- package/dist/lib/cms/field-patch.js +4 -30
- package/dist/lib/cms/field-patch.js.map +1 -1
- package/dist/lib/cms/published-documents.js +1 -1
- package/dist/lib/cms/published-documents.js.map +1 -1
- package/dist/lib/cms-views/view-model.d.ts +9 -9
- package/dist/lib/cms-views/view-model.js +2 -2
- package/dist/lib/cms-views/view-model.js.map +1 -1
- package/dist/lib/collection-view/collection-view-source.d.ts +14 -5
- package/dist/lib/collection-view/collection-view-source.js.map +1 -1
- package/dist/lib/data/collection-content.js +3 -2
- package/dist/lib/data/collection-content.js.map +1 -1
- package/dist/lib/data/content-session-refresh.d.ts +1 -1
- package/dist/lib/data/content-session-refresh.js.map +1 -1
- package/dist/lib/data/playground-unload-guard.d.ts +1 -1
- package/dist/lib/data/playground-unload-guard.js +1 -1
- package/dist/lib/data/playground-unload-guard.js.map +1 -1
- package/dist/lib/dnd/drop-rules.d.ts +25 -31
- package/dist/lib/dnd/drop-rules.js +53 -59
- package/dist/lib/dnd/drop-rules.js.map +1 -1
- package/dist/lib/engine-version.d.ts +1 -1
- package/dist/lib/engine-version.js +1 -1
- package/dist/lib/engine-version.js.map +1 -1
- package/dist/lib/i18n/i18n.js +1 -1
- package/dist/lib/i18n/i18n.js.map +1 -1
- package/dist/lib/i18n/messages.de.js +44 -3
- package/dist/lib/i18n/messages.de.js.map +1 -1
- package/dist/lib/i18n/messages.en.d.ts +38 -2
- package/dist/lib/i18n/messages.en.js +51 -3
- package/dist/lib/i18n/messages.en.js.map +1 -1
- package/dist/lib/operations/project-runtime-config.d.ts +34 -6
- package/dist/lib/operations/project-runtime-config.js +50 -10
- package/dist/lib/operations/project-runtime-config.js.map +1 -1
- package/dist/lib/operations/publish-with-revision.d.ts +33 -1
- package/dist/lib/operations/publish-with-revision.js +32 -0
- package/dist/lib/operations/publish-with-revision.js.map +1 -1
- package/dist/lib/operations/runtime-validator.d.ts +19 -68
- package/dist/lib/operations/runtime-validator.js +12 -138
- package/dist/lib/operations/runtime-validator.js.map +1 -1
- package/dist/lib/operations/studio-operations.d.ts +17 -2
- package/dist/lib/operations/studio-operations.js +18 -11
- package/dist/lib/operations/studio-operations.js.map +1 -1
- package/dist/lib/persistence/convex-adapter.d.ts +16 -3
- package/dist/lib/persistence/convex-adapter.js +83 -47
- package/dist/lib/persistence/convex-adapter.js.map +1 -1
- package/dist/lib/persistence/index.js +14 -1
- package/dist/lib/persistence/index.js.map +1 -1
- package/dist/lib/persona/labels.js +4 -2
- package/dist/lib/persona/labels.js.map +1 -1
- package/dist/lib/query/operations-query.d.ts +1 -1
- package/dist/lib/query/operations-query.js +1 -1
- package/dist/lib/query/operations-query.js.map +1 -1
- package/dist/lib/references/reference-model.js +1 -1
- package/dist/lib/references/reference-model.js.map +1 -1
- package/dist/lib/references/references-store.js +1 -1
- package/dist/lib/references/references-store.js.map +1 -1
- package/dist/lib/workspace/index.d.ts +14 -11
- package/dist/lib/workspace/index.js +20 -24
- package/dist/lib/workspace/index.js.map +1 -1
- package/dist/mount.d.ts +28 -0
- package/dist/mount.js +27 -0
- package/dist/mount.js.map +1 -0
- package/dist/router/root.js +10 -10
- package/dist/router/root.js.map +1 -1
- package/dist/ui/badge.d.ts +1 -1
- package/dist/ui/button.d.ts +2 -2
- package/package.json +15 -11
- package/dist/components/cms/cms-composition-validators.d.ts +0 -16
- package/dist/components/cms/cms-composition-validators.js +0 -133
- package/dist/components/cms/cms-composition-validators.js.map +0 -1
- package/dist/components/cms/publish-clean.d.ts +0 -28
- package/dist/components/cms/publish-clean.js +0 -38
- package/dist/components/cms/publish-clean.js.map +0 -1
- package/dist/components/redirects/redirects-collection-view-source.d.ts +0 -53
- package/dist/components/redirects/redirects-collection-view-source.js +0 -146
- package/dist/components/redirects/redirects-collection-view-source.js.map +0 -1
- package/dist/components/redirects/redirects-module.d.ts +0 -21
- package/dist/components/redirects/redirects-module.js +0 -40
- package/dist/components/redirects/redirects-module.js.map +0 -1
- package/dist/components/redirects/redirects-store.d.ts +0 -68
- package/dist/components/redirects/redirects-store.js +0 -132
- package/dist/components/redirects/redirects-store.js.map +0 -1
- package/dist/lib/assets/asset-placements.d.ts +0 -28
- package/dist/lib/assets/asset-placements.js +0 -83
- package/dist/lib/assets/asset-placements.js.map +0 -1
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { CollectionDef } from '@elytracms/core/cms-core';
|
|
3
|
+
import type { AnyRecordKind, RecordEntryOf, RecordWriteOf } from '@elytracms/core/persistence';
|
|
4
|
+
import type { CollectionViewCapabilities, CollectionViewSource } from '../../lib/collection-view/collection-view-source.js';
|
|
5
|
+
/** The default capability profile of a record rail: create, edit, remove, bulk delete — content, not administration. */
|
|
6
|
+
export declare const RECORD_RAIL_CAPABILITIES: CollectionViewCapabilities;
|
|
7
|
+
/** The one query key of a kind's records in a project. */
|
|
8
|
+
export declare function recordListKey(kind: AnyRecordKind, projectId: string): readonly [string, string, string];
|
|
9
|
+
/**
|
|
10
|
+
* A kind's records in a project, read through the shared operations cache — the ONE query
|
|
11
|
+
* a rail and any reader outside it (the rail foot's URL-map validation reads redirects)
|
|
12
|
+
* share. One key, one cache entry: a save's `cache.patch` updates both at once.
|
|
13
|
+
*/
|
|
14
|
+
export declare function useRecordList<K extends AnyRecordKind>(kind: K, projectId: string): import("@tanstack/react-query").UseQueryResult<RecordEntryOf<K>[]>;
|
|
15
|
+
export interface RecordRailStore<K extends AnyRecordKind = AnyRecordKind> {
|
|
16
|
+
kind: K;
|
|
17
|
+
projectId: string;
|
|
18
|
+
status: 'loading' | 'ready' | 'error';
|
|
19
|
+
errorMessage?: string;
|
|
20
|
+
/** The persisted records, in the kind's order. Unsaved drafts are excluded. */
|
|
21
|
+
records: readonly RecordEntryOf<K>[];
|
|
22
|
+
/** One record: a local unsaved draft if present, else a persisted record. */
|
|
23
|
+
get(id: string): RecordEntryOf<K> | undefined;
|
|
24
|
+
/** Mint a blank in-memory draft and return its id (the "New" navigation target). */
|
|
25
|
+
createDraft(): string;
|
|
26
|
+
/** Persist a create/modify; drops the matching draft on success. */
|
|
27
|
+
save(input: RecordWriteOf<K>): Promise<{
|
|
28
|
+
ok: true;
|
|
29
|
+
record: RecordEntryOf<K>;
|
|
30
|
+
} | {
|
|
31
|
+
ok: false;
|
|
32
|
+
message: string;
|
|
33
|
+
}>;
|
|
34
|
+
/** Delete a persisted record, or silently discard an unsaved draft. */
|
|
35
|
+
remove(id: string): Promise<{
|
|
36
|
+
ok: true;
|
|
37
|
+
} | {
|
|
38
|
+
ok: false;
|
|
39
|
+
message: string;
|
|
40
|
+
}>;
|
|
41
|
+
/** Re-list from the backend. */
|
|
42
|
+
refresh(): Promise<void>;
|
|
43
|
+
}
|
|
44
|
+
/** The record rail store of the nearest {@link RecordRailProvider}. */
|
|
45
|
+
export declare function useRecordRail<K extends AnyRecordKind>(): RecordRailStore<K>;
|
|
46
|
+
export declare function RecordRailProvider<K extends AnyRecordKind>({ kind, projectId, children, }: {
|
|
47
|
+
kind: K;
|
|
48
|
+
projectId: string;
|
|
49
|
+
children: ReactNode;
|
|
50
|
+
}): import("react").JSX.Element;
|
|
51
|
+
/**
|
|
52
|
+
* The columns a blank "New" draft shows: each column's CREATE default (a new redirect
|
|
53
|
+
* reads enabled + permanent — the reason you create one is for it to work), and `''` for
|
|
54
|
+
* a required text column. The save then sends what the editor saw; the adapter still
|
|
55
|
+
* applies the defaults itself for a write that leaves them out (EC-577).
|
|
56
|
+
*/
|
|
57
|
+
export declare function recordDraftColumns(kind: AnyRecordKind): Record<string, unknown>;
|
|
58
|
+
/** How a rail addresses its list and one record's detail (the view's own route segment). */
|
|
59
|
+
export interface RecordRailPaths {
|
|
60
|
+
listPath: string;
|
|
61
|
+
detailPath(id: string): string;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* The {@link CollectionViewSource} of a record rail (must be mounted under its
|
|
65
|
+
* {@link RecordRailProvider}). `schema` is the rail's lens — the kind's default lens or a
|
|
66
|
+
* mount-customized one (EC-311 overrides by name; additive fields persist in `meta`).
|
|
67
|
+
*/
|
|
68
|
+
export declare function useRecordCollectionViewSource<K extends AnyRecordKind>(kind: K, projectId: string, schema: CollectionDef, paths: RecordRailPaths, capabilities?: CollectionViewCapabilities): CollectionViewSource;
|
|
69
|
+
export interface RecordRailModuleProps<K extends AnyRecordKind> {
|
|
70
|
+
kind: K;
|
|
71
|
+
projectId: string;
|
|
72
|
+
/** `null` → the list; an id → that record's detail. */
|
|
73
|
+
recordId: string | null;
|
|
74
|
+
schema: CollectionDef;
|
|
75
|
+
paths: RecordRailPaths;
|
|
76
|
+
capabilities?: CollectionViewCapabilities;
|
|
77
|
+
/** The rail's label, for its loading / failure states. */
|
|
78
|
+
label: string;
|
|
79
|
+
/** Optional copy for the loading / failure states (default: the generic catalog keys). */
|
|
80
|
+
messages?: {
|
|
81
|
+
loading?: () => string;
|
|
82
|
+
loadFailed?: () => string;
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/** The surface of a record rail: the portable list, or one record's detail. */
|
|
86
|
+
export declare function RecordRailModule<K extends AnyRecordKind>(props: RecordRailModuleProps<K>): import("react").JSX.Element;
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useMemo, useRef, useState } from 'react';
|
|
3
|
+
import { createCollectionRegistry, parseLocaleConfig, validateDocument } from '@elytracms/core/cms-core';
|
|
4
|
+
import { createUuidIdFactory } from '@elytracms/core/determinism';
|
|
5
|
+
import { getStudioOperations } from '../../lib/operations/index.js';
|
|
6
|
+
import { listStoreState, useOperationCache, useOperationQuery } from '../../lib/query/index.js';
|
|
7
|
+
import { createRequiredContext } from '../../lib/required-context.js';
|
|
8
|
+
import { localizedLabel, translate } from '../../lib/i18n/index.js';
|
|
9
|
+
import { normalizeOkResult, useRailViewSource } from '../rail-view-source.js';
|
|
10
|
+
import { RailStoreStatus } from '../rail-store-status.js';
|
|
11
|
+
import { CollectionViewSurface } from '../collection-view/collection-view-surface.js';
|
|
12
|
+
import { lensDocumentToRecordWrite, recordToLensDocument } from './record-lens.js';
|
|
13
|
+
/**
|
|
14
|
+
* EC-616 — the generic RECORD RAIL: a record kind (`defineRecordKind`) on the portable
|
|
15
|
+
* list/edit surfaces (AD-19), read and written through the operations chokepoint
|
|
16
|
+
* (`operations.records(kind)`) and the one query cache (AD-17). A rail is `nav.rail(kind)`;
|
|
17
|
+
* the engine's redirects rail is exactly this over `redirectRecordKind`.
|
|
18
|
+
*
|
|
19
|
+
* - **Store** ({@link RecordRailProvider}): the kind's records through ONE query key per
|
|
20
|
+
* kind and project ({@link useRecordList} — the rail foot and the rail share it); the
|
|
21
|
+
* "New" flow keeps an unsaved record as a LOCAL draft until the first explicit save, so
|
|
22
|
+
* an abandoned "New" never writes a stub and never shows in the list.
|
|
23
|
+
* - **Source** ({@link useRecordCollectionViewSource}): the lens projections
|
|
24
|
+
* (`record-lens.ts`), the lens's own validation, the rail's capabilities.
|
|
25
|
+
* - **Surface** ({@link RecordRailModule}): the list, or one record's detail.
|
|
26
|
+
*/
|
|
27
|
+
/** A record rail is not localized — one implicit locale keeps the lens honest. */
|
|
28
|
+
const RAIL_LOCALE = 'en';
|
|
29
|
+
const RAIL_LOCALE_CONFIG = parseLocaleConfig({ default: RAIL_LOCALE, locales: [RAIL_LOCALE] });
|
|
30
|
+
/** The default capability profile of a record rail: create, edit, remove, bulk delete — content, not administration. */
|
|
31
|
+
export const RECORD_RAIL_CAPABILITIES = {
|
|
32
|
+
publish: false,
|
|
33
|
+
versions: false,
|
|
34
|
+
locales: false,
|
|
35
|
+
inlineEdit: false,
|
|
36
|
+
create: true,
|
|
37
|
+
duplicate: false,
|
|
38
|
+
remove: true,
|
|
39
|
+
bulk: true,
|
|
40
|
+
liveUrl: false,
|
|
41
|
+
hierarchy: false,
|
|
42
|
+
importExport: false,
|
|
43
|
+
adminOnly: false,
|
|
44
|
+
};
|
|
45
|
+
/** The one query key of a kind's records in a project. */
|
|
46
|
+
export function recordListKey(kind, projectId) {
|
|
47
|
+
return ['records', kind.kind, projectId];
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* A kind's records in a project, read through the shared operations cache — the ONE query
|
|
51
|
+
* a rail and any reader outside it (the rail foot's URL-map validation reads redirects)
|
|
52
|
+
* share. One key, one cache entry: a save's `cache.patch` updates both at once.
|
|
53
|
+
*/
|
|
54
|
+
export function useRecordList(kind, projectId) {
|
|
55
|
+
return useOperationQuery({
|
|
56
|
+
queryKey: recordListKey(kind, projectId),
|
|
57
|
+
loader: async (operations) => {
|
|
58
|
+
const result = await operations.records(kind).list({ projectId });
|
|
59
|
+
if (!result.ok)
|
|
60
|
+
throw new Error(result.error.message);
|
|
61
|
+
return [...result.value];
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
const { Context: RecordRailContext, useRequired: useRecordRailContext } = createRequiredContext('RecordRail', { article: 'a' });
|
|
66
|
+
/** The record rail store of the nearest {@link RecordRailProvider}. */
|
|
67
|
+
export function useRecordRail() {
|
|
68
|
+
return useRecordRailContext();
|
|
69
|
+
}
|
|
70
|
+
let mintDraftUuid;
|
|
71
|
+
export function RecordRailProvider({ kind, projectId, children, }) {
|
|
72
|
+
const query = useRecordList(kind, projectId);
|
|
73
|
+
const cache = useOperationCache();
|
|
74
|
+
const persisted = query.data ?? EMPTY;
|
|
75
|
+
const { status, errorMessage } = listStoreState(query);
|
|
76
|
+
// In-memory unsaved "New" drafts, keyed by id. Not listed; only get()-able.
|
|
77
|
+
const [drafts, setDrafts] = useState({});
|
|
78
|
+
const draftsRef = useRef(drafts);
|
|
79
|
+
draftsRef.current = drafts;
|
|
80
|
+
const refetch = query.refetch;
|
|
81
|
+
const refresh = useCallback(async () => {
|
|
82
|
+
await refetch();
|
|
83
|
+
}, [refetch]);
|
|
84
|
+
const get = useCallback((id) => drafts[id] ?? persisted.find((record) => record.id === id), [drafts, persisted]);
|
|
85
|
+
const createDraft = useCallback(() => {
|
|
86
|
+
// The draft id becomes the record's real id on its first save: the production id
|
|
87
|
+
// factory (EC-578 — never a clock or a per-session counter, which collide).
|
|
88
|
+
mintDraftUuid ??= createUuidIdFactory();
|
|
89
|
+
const id = `${kind.idPrefix}_${mintDraftUuid()}`;
|
|
90
|
+
const now = new Date().toISOString();
|
|
91
|
+
const draft = { ...recordDraftColumns(kind), id, projectId, createdAt: now, updatedAt: now };
|
|
92
|
+
setDrafts((prev) => ({ ...prev, [id]: draft }));
|
|
93
|
+
return id;
|
|
94
|
+
}, [kind, projectId]);
|
|
95
|
+
const save = useCallback(async (input) => {
|
|
96
|
+
const { operations } = await getStudioOperations();
|
|
97
|
+
const result = await operations.records(kind).save({ projectId, ...input });
|
|
98
|
+
if (!result.ok)
|
|
99
|
+
return { ok: false, message: result.error.message };
|
|
100
|
+
const saved = result.value;
|
|
101
|
+
const savedId = saved.id;
|
|
102
|
+
// The server returns the kind's order on the next read; place the save now and
|
|
103
|
+
// let the patched list keep its order key (re-read on the next fetch).
|
|
104
|
+
cache.patch(recordListKey(kind, projectId), (prev) => {
|
|
105
|
+
const rest = (prev ?? []).filter((record) => record.id !== savedId);
|
|
106
|
+
return sortForRail(kind, [...rest, saved]);
|
|
107
|
+
});
|
|
108
|
+
setDrafts((prev) => {
|
|
109
|
+
if (!(savedId in prev))
|
|
110
|
+
return prev;
|
|
111
|
+
const next = { ...prev };
|
|
112
|
+
delete next[savedId];
|
|
113
|
+
return next;
|
|
114
|
+
});
|
|
115
|
+
return { ok: true, record: saved };
|
|
116
|
+
}, [kind, projectId, cache]);
|
|
117
|
+
const remove = useCallback(async (id) => {
|
|
118
|
+
// An unsaved draft was never persisted — discard it locally, no backend call.
|
|
119
|
+
if (draftsRef.current[id]) {
|
|
120
|
+
setDrafts((prev) => {
|
|
121
|
+
const next = { ...prev };
|
|
122
|
+
delete next[id];
|
|
123
|
+
return next;
|
|
124
|
+
});
|
|
125
|
+
return { ok: true };
|
|
126
|
+
}
|
|
127
|
+
const { operations } = await getStudioOperations();
|
|
128
|
+
const result = await operations.records(kind).remove({ projectId, id });
|
|
129
|
+
if (!result.ok)
|
|
130
|
+
return { ok: false, message: result.error.message };
|
|
131
|
+
cache.patch(recordListKey(kind, projectId), (prev) => (prev ?? []).filter((record) => record.id !== id));
|
|
132
|
+
return { ok: true };
|
|
133
|
+
}, [kind, projectId, cache]);
|
|
134
|
+
const store = useMemo(() => ({
|
|
135
|
+
kind,
|
|
136
|
+
projectId,
|
|
137
|
+
status,
|
|
138
|
+
...(errorMessage !== undefined ? { errorMessage } : {}),
|
|
139
|
+
records: persisted,
|
|
140
|
+
get,
|
|
141
|
+
createDraft,
|
|
142
|
+
save,
|
|
143
|
+
remove,
|
|
144
|
+
refresh,
|
|
145
|
+
}), [kind, projectId, status, errorMessage, persisted, get, createDraft, save, remove, refresh]);
|
|
146
|
+
return _jsx(RecordRailContext.Provider, { value: store, children: children });
|
|
147
|
+
}
|
|
148
|
+
const EMPTY = [];
|
|
149
|
+
/**
|
|
150
|
+
* The columns a blank "New" draft shows: each column's CREATE default (a new redirect
|
|
151
|
+
* reads enabled + permanent — the reason you create one is for it to work), and `''` for
|
|
152
|
+
* a required text column. The save then sends what the editor saw; the adapter still
|
|
153
|
+
* applies the defaults itself for a write that leaves them out (EC-577).
|
|
154
|
+
*/
|
|
155
|
+
export function recordDraftColumns(kind) {
|
|
156
|
+
const columns = {};
|
|
157
|
+
for (const [name, field] of Object.entries(kind.fields)) {
|
|
158
|
+
const parsed = field.safeParse(undefined);
|
|
159
|
+
if (parsed.success && parsed.data !== undefined)
|
|
160
|
+
columns[name] = parsed.data;
|
|
161
|
+
else if (field._zod.def.type === 'string')
|
|
162
|
+
columns[name] = '';
|
|
163
|
+
}
|
|
164
|
+
return columns;
|
|
165
|
+
}
|
|
166
|
+
/** The kind's own list order for a locally patched list (`orderBy`, else creation time). */
|
|
167
|
+
function sortForRail(kind, records) {
|
|
168
|
+
const column = kind.orderBy ?? 'createdAt';
|
|
169
|
+
return [...records].sort((a, b) => {
|
|
170
|
+
const left = String(a[column] ?? '');
|
|
171
|
+
const right = String(b[column] ?? '');
|
|
172
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* The {@link CollectionViewSource} of a record rail (must be mounted under its
|
|
177
|
+
* {@link RecordRailProvider}). `schema` is the rail's lens — the kind's default lens or a
|
|
178
|
+
* mount-customized one (EC-311 overrides by name; additive fields persist in `meta`).
|
|
179
|
+
*/
|
|
180
|
+
export function useRecordCollectionViewSource(kind, projectId, schema, paths, capabilities = RECORD_RAIL_CAPABILITIES) {
|
|
181
|
+
const store = useRecordRail();
|
|
182
|
+
// Run the (possibly customized) lens's validation — required/pattern and custom
|
|
183
|
+
// `validate` fns on an overridden field must actually fire in the editor (EC-311).
|
|
184
|
+
const registry = useMemo(() => createCollectionRegistry([schema]), [schema]);
|
|
185
|
+
const singular = schema.form?.labelSingular;
|
|
186
|
+
const titleField = typeof schema.titleField === 'string' ? schema.titleField : undefined;
|
|
187
|
+
const documents = useCallback(() => store.records.map((record) => recordToLensDocument(kind, record)), [kind, store.records]);
|
|
188
|
+
const get = useCallback((ref) => {
|
|
189
|
+
const record = store.get(ref.id);
|
|
190
|
+
return record ? recordToLensDocument(kind, record) : undefined;
|
|
191
|
+
}, [kind, store]);
|
|
192
|
+
const titleOf = useCallback((ref) => {
|
|
193
|
+
const record = store.get(ref.id);
|
|
194
|
+
const title = titleField ? record?.[titleField] : undefined;
|
|
195
|
+
if (typeof title === 'string' && title.length > 0)
|
|
196
|
+
return title;
|
|
197
|
+
return translate('records.untitled', { label: localizedLabel(singular) ?? kind.kind });
|
|
198
|
+
}, [store, titleField, singular, kind]);
|
|
199
|
+
const validate = useCallback((doc) => validateDocument(registry, doc, { localeConfig: RAIL_LOCALE_CONFIG }), [registry]);
|
|
200
|
+
const create = useCallback(() => ({ collection: kind.kind, id: store.createDraft() }), [kind, store]);
|
|
201
|
+
// EC-616 (multi-session): an edit of a STORED record writes only what the editor changed
|
|
202
|
+
// against the record they opened (`base`, from the edit shell) — the EC-577 rule keeps every
|
|
203
|
+
// other column, so another writer's change made meanwhile survives this save. A record the
|
|
204
|
+
// editor is creating (an unsaved draft) is written whole.
|
|
205
|
+
const save = useCallback((doc, base) => {
|
|
206
|
+
const stored = store.records.some((record) => record.id === doc.id);
|
|
207
|
+
return store.save(lensDocumentToRecordWrite(kind, doc, stored ? base : undefined));
|
|
208
|
+
}, [kind, store]);
|
|
209
|
+
const remove = useCallback((ref) => store.remove(ref.id), [store]);
|
|
210
|
+
return useRailViewSource({
|
|
211
|
+
projectId,
|
|
212
|
+
schema,
|
|
213
|
+
capabilities,
|
|
214
|
+
localeConfig: RAIL_LOCALE_CONFIG,
|
|
215
|
+
documents,
|
|
216
|
+
get,
|
|
217
|
+
titleOf,
|
|
218
|
+
detailPath: paths.detailPath,
|
|
219
|
+
listPath: paths.listPath,
|
|
220
|
+
validate,
|
|
221
|
+
create,
|
|
222
|
+
save,
|
|
223
|
+
remove,
|
|
224
|
+
normalizeSaveResult: normalizeOkResult,
|
|
225
|
+
normalizeRemoveResult: normalizeOkResult,
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
/** The surface of a record rail: the portable list, or one record's detail. */
|
|
229
|
+
export function RecordRailModule(props) {
|
|
230
|
+
return (_jsx(RecordRailProvider, { kind: props.kind, projectId: props.projectId, children: _jsx(RecordRailSurface, { ...props }) }));
|
|
231
|
+
}
|
|
232
|
+
function RecordRailSurface({ kind, projectId, recordId, schema, paths, capabilities, label, messages, }) {
|
|
233
|
+
const store = useRecordRail();
|
|
234
|
+
const source = useRecordCollectionViewSource(kind, projectId, schema, paths, capabilities);
|
|
235
|
+
if (store.status !== 'ready') {
|
|
236
|
+
return (_jsx(RailStoreStatus, { store: store, errorLabel: messages?.loadFailed?.() ?? translate('records.loadFailed', { label }), loadingLabel: messages?.loading?.() ?? translate('records.loading', { label }), errorTestId: `${kind.kind}-rail-error` }));
|
|
237
|
+
}
|
|
238
|
+
// One record's detail opens in the portable edit shell (AD-19); the list otherwise.
|
|
239
|
+
return _jsx(CollectionViewSurface, { source: source, projectId: projectId, documentId: recordId });
|
|
240
|
+
}
|
|
241
|
+
//# sourceMappingURL=record-rail.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"record-rail.js","sourceRoot":"","sources":["../../../src/components/records/record-rail.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useMemo, useRef, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport { createCollectionRegistry, parseLocaleConfig, validateDocument } from '@elytracms/core/cms-core'\nimport type { CmsDocument, CollectionDef, DocumentRef, Locale, LocaleConfig } from '@elytracms/core/cms-core'\nimport type { AnyRecordKind, RecordEntryOf, RecordWriteOf } from '@elytracms/core/persistence'\nimport { createUuidIdFactory } from '@elytracms/core/determinism'\nimport { getStudioOperations } from '../../lib/operations'\nimport { listStoreState, useOperationCache, useOperationQuery } from '../../lib/query'\nimport { createRequiredContext } from '../../lib/required-context'\nimport { localizedLabel, translate } from '../../lib/i18n'\nimport type {\n CollectionViewCapabilities,\n CollectionViewSource,\n} from '../../lib/collection-view/collection-view-source'\nimport { normalizeOkResult, useRailViewSource } from '../rail-view-source'\nimport { RailStoreStatus } from '../rail-store-status'\nimport { CollectionViewSurface } from '../collection-view/collection-view-surface'\nimport { lensDocumentToRecordWrite, recordToLensDocument } from './record-lens'\n\n/**\n * EC-616 — the generic RECORD RAIL: a record kind (`defineRecordKind`) on the portable\n * list/edit surfaces (AD-19), read and written through the operations chokepoint\n * (`operations.records(kind)`) and the one query cache (AD-17). A rail is `nav.rail(kind)`;\n * the engine's redirects rail is exactly this over `redirectRecordKind`.\n *\n * - **Store** ({@link RecordRailProvider}): the kind's records through ONE query key per\n * kind and project ({@link useRecordList} — the rail foot and the rail share it); the\n * \"New\" flow keeps an unsaved record as a LOCAL draft until the first explicit save, so\n * an abandoned \"New\" never writes a stub and never shows in the list.\n * - **Source** ({@link useRecordCollectionViewSource}): the lens projections\n * (`record-lens.ts`), the lens's own validation, the rail's capabilities.\n * - **Surface** ({@link RecordRailModule}): the list, or one record's detail.\n */\n\n/** A record rail is not localized — one implicit locale keeps the lens honest. */\nconst RAIL_LOCALE: Locale = 'en'\nconst RAIL_LOCALE_CONFIG: LocaleConfig = parseLocaleConfig({ default: RAIL_LOCALE, locales: [RAIL_LOCALE] })\n\n/** The default capability profile of a record rail: create, edit, remove, bulk delete — content, not administration. */\nexport const RECORD_RAIL_CAPABILITIES: CollectionViewCapabilities = {\n publish: false,\n versions: false,\n locales: false,\n inlineEdit: false,\n create: true,\n duplicate: false,\n remove: true,\n bulk: true,\n liveUrl: false,\n hierarchy: false,\n importExport: false,\n adminOnly: false,\n}\n\n/** The one query key of a kind's records in a project. */\nexport function recordListKey(kind: AnyRecordKind, projectId: string): readonly [string, string, string] {\n return ['records', kind.kind, projectId]\n}\n\n/**\n * A kind's records in a project, read through the shared operations cache — the ONE query\n * a rail and any reader outside it (the rail foot's URL-map validation reads redirects)\n * share. One key, one cache entry: a save's `cache.patch` updates both at once.\n */\nexport function useRecordList<K extends AnyRecordKind>(kind: K, projectId: string) {\n return useOperationQuery<RecordEntryOf<K>[]>({\n queryKey: recordListKey(kind, projectId),\n loader: async (operations) => {\n const result = await operations.records(kind).list({ projectId })\n if (!result.ok) throw new Error(result.error.message)\n return [...result.value]\n },\n })\n}\n\nexport interface RecordRailStore<K extends AnyRecordKind = AnyRecordKind> {\n kind: K\n projectId: string\n status: 'loading' | 'ready' | 'error'\n errorMessage?: string\n /** The persisted records, in the kind's order. Unsaved drafts are excluded. */\n records: readonly RecordEntryOf<K>[]\n /** One record: a local unsaved draft if present, else a persisted record. */\n get(id: string): RecordEntryOf<K> | undefined\n /** Mint a blank in-memory draft and return its id (the \"New\" navigation target). */\n createDraft(): string\n /** Persist a create/modify; drops the matching draft on success. */\n save(input: RecordWriteOf<K>): Promise<{ ok: true; record: RecordEntryOf<K> } | { ok: false; message: string }>\n /** Delete a persisted record, or silently discard an unsaved draft. */\n remove(id: string): Promise<{ ok: true } | { ok: false; message: string }>\n /** Re-list from the backend. */\n refresh(): Promise<void>\n}\n\nconst { Context: RecordRailContext, useRequired: useRecordRailContext } =\n createRequiredContext<RecordRailStore>('RecordRail', { article: 'a' })\n\n/** The record rail store of the nearest {@link RecordRailProvider}. */\nexport function useRecordRail<K extends AnyRecordKind>(): RecordRailStore<K> {\n return useRecordRailContext() as unknown as RecordRailStore<K>\n}\n\nlet mintDraftUuid: (() => string) | undefined\n\nexport function RecordRailProvider<K extends AnyRecordKind>({\n kind,\n projectId,\n children,\n}: {\n kind: K\n projectId: string\n children: ReactNode\n}) {\n const query = useRecordList(kind, projectId)\n const cache = useOperationCache()\n const persisted = query.data ?? EMPTY\n const { status, errorMessage } = listStoreState(query)\n // In-memory unsaved \"New\" drafts, keyed by id. Not listed; only get()-able.\n const [drafts, setDrafts] = useState<Record<string, RecordEntryOf<K>>>({})\n const draftsRef = useRef(drafts)\n draftsRef.current = drafts\n\n const refetch = query.refetch\n const refresh = useCallback(async () => {\n await refetch()\n }, [refetch])\n\n const get = useCallback(\n (id: string) =>\n drafts[id] ?? (persisted as RecordEntryOf<K>[]).find((record) => (record as { id: string }).id === id),\n [drafts, persisted],\n )\n\n const createDraft = useCallback(() => {\n // The draft id becomes the record's real id on its first save: the production id\n // factory (EC-578 — never a clock or a per-session counter, which collide).\n mintDraftUuid ??= createUuidIdFactory()\n const id = `${kind.idPrefix}_${mintDraftUuid()}`\n const now = new Date().toISOString()\n const draft = { ...recordDraftColumns(kind), id, projectId, createdAt: now, updatedAt: now } as unknown as RecordEntryOf<K>\n setDrafts((prev) => ({ ...prev, [id]: draft }))\n return id\n }, [kind, projectId])\n\n const save = useCallback<RecordRailStore<K>['save']>(\n async (input) => {\n const { operations } = await getStudioOperations()\n const result = await operations.records(kind).save({ projectId, ...input } as never)\n if (!result.ok) return { ok: false, message: result.error.message }\n const saved = result.value as RecordEntryOf<K>\n const savedId = (saved as { id: string }).id\n // The server returns the kind's order on the next read; place the save now and\n // let the patched list keep its order key (re-read on the next fetch).\n cache.patch<RecordEntryOf<K>[]>(recordListKey(kind, projectId), (prev) => {\n const rest = (prev ?? []).filter((record) => (record as { id: string }).id !== savedId)\n return sortForRail(kind, [...rest, saved])\n })\n setDrafts((prev) => {\n if (!(savedId in prev)) return prev\n const next = { ...prev }\n delete next[savedId]\n return next\n })\n return { ok: true, record: saved }\n },\n [kind, projectId, cache],\n )\n\n const remove = useCallback<RecordRailStore<K>['remove']>(\n async (id) => {\n // An unsaved draft was never persisted — discard it locally, no backend call.\n if (draftsRef.current[id]) {\n setDrafts((prev) => {\n const next = { ...prev }\n delete next[id]\n return next\n })\n return { ok: true }\n }\n const { operations } = await getStudioOperations()\n const result = await operations.records(kind).remove({ projectId, id })\n if (!result.ok) return { ok: false, message: result.error.message }\n cache.patch<RecordEntryOf<K>[]>(recordListKey(kind, projectId), (prev) =>\n (prev ?? []).filter((record) => record.id !== id),\n )\n return { ok: true }\n },\n [kind, projectId, cache],\n )\n\n const store = useMemo<RecordRailStore<K>>(\n () => ({\n kind,\n projectId,\n status,\n ...(errorMessage !== undefined ? { errorMessage } : {}),\n records: persisted as RecordEntryOf<K>[],\n get,\n createDraft,\n save,\n remove,\n refresh,\n }),\n [kind, projectId, status, errorMessage, persisted, get, createDraft, save, remove, refresh],\n )\n\n return <RecordRailContext.Provider value={store as unknown as RecordRailStore}>{children}</RecordRailContext.Provider>\n}\n\nconst EMPTY: readonly never[] = []\n\n/**\n * The columns a blank \"New\" draft shows: each column's CREATE default (a new redirect\n * reads enabled + permanent — the reason you create one is for it to work), and `''` for\n * a required text column. The save then sends what the editor saw; the adapter still\n * applies the defaults itself for a write that leaves them out (EC-577).\n */\nexport function recordDraftColumns(kind: AnyRecordKind): Record<string, unknown> {\n const columns: Record<string, unknown> = {}\n for (const [name, field] of Object.entries(kind.fields)) {\n const parsed = (field as { safeParse(value: unknown): { success: boolean; data?: unknown } }).safeParse(undefined)\n if (parsed.success && parsed.data !== undefined) columns[name] = parsed.data\n else if ((field as { _zod: { def: { type: string } } })._zod.def.type === 'string') columns[name] = ''\n }\n return columns\n}\n\n/** The kind's own list order for a locally patched list (`orderBy`, else creation time). */\nfunction sortForRail<R>(kind: AnyRecordKind, records: R[]): R[] {\n const column = kind.orderBy ?? 'createdAt'\n return [...records].sort((a, b) => {\n const left = String((a as Record<string, unknown>)[column] ?? '')\n const right = String((b as Record<string, unknown>)[column] ?? '')\n return left < right ? -1 : left > right ? 1 : 0\n })\n}\n\n/** How a rail addresses its list and one record's detail (the view's own route segment). */\nexport interface RecordRailPaths {\n listPath: string\n detailPath(id: string): string\n}\n\n/**\n * The {@link CollectionViewSource} of a record rail (must be mounted under its\n * {@link RecordRailProvider}). `schema` is the rail's lens — the kind's default lens or a\n * mount-customized one (EC-311 overrides by name; additive fields persist in `meta`).\n */\nexport function useRecordCollectionViewSource<K extends AnyRecordKind>(\n kind: K,\n projectId: string,\n schema: CollectionDef,\n paths: RecordRailPaths,\n capabilities: CollectionViewCapabilities = RECORD_RAIL_CAPABILITIES,\n): CollectionViewSource {\n const store = useRecordRail<K>()\n // Run the (possibly customized) lens's validation — required/pattern and custom\n // `validate` fns on an overridden field must actually fire in the editor (EC-311).\n const registry = useMemo(() => createCollectionRegistry([schema]), [schema])\n const singular = schema.form?.labelSingular\n const titleField = typeof schema.titleField === 'string' ? schema.titleField : undefined\n\n const documents = useCallback(\n () => store.records.map((record) => recordToLensDocument(kind, record)),\n [kind, store.records],\n )\n const get = useCallback(\n (ref: DocumentRef): CmsDocument | undefined => {\n const record = store.get(ref.id)\n return record ? recordToLensDocument(kind, record) : undefined\n },\n [kind, store],\n )\n const titleOf = useCallback(\n (ref: DocumentRef): string => {\n const record = store.get(ref.id) as Record<string, unknown> | undefined\n const title = titleField ? record?.[titleField] : undefined\n if (typeof title === 'string' && title.length > 0) return title\n return translate('records.untitled', { label: localizedLabel(singular) ?? kind.kind })\n },\n [store, titleField, singular, kind],\n )\n const validate = useCallback(\n (doc: CmsDocument) => validateDocument(registry, doc, { localeConfig: RAIL_LOCALE_CONFIG }),\n [registry],\n )\n const create = useCallback((): DocumentRef => ({ collection: kind.kind, id: store.createDraft() }), [kind, store])\n // EC-616 (multi-session): an edit of a STORED record writes only what the editor changed\n // against the record they opened (`base`, from the edit shell) — the EC-577 rule keeps every\n // other column, so another writer's change made meanwhile survives this save. A record the\n // editor is creating (an unsaved draft) is written whole.\n const save = useCallback(\n (doc: CmsDocument, base?: CmsDocument) => {\n const stored = store.records.some((record) => (record as { id: string }).id === doc.id)\n return store.save(lensDocumentToRecordWrite(kind, doc, stored ? base : undefined))\n },\n [kind, store],\n )\n const remove = useCallback((ref: DocumentRef) => store.remove(ref.id), [store])\n\n return useRailViewSource({\n projectId,\n schema,\n capabilities,\n localeConfig: RAIL_LOCALE_CONFIG,\n documents,\n get,\n titleOf,\n detailPath: paths.detailPath,\n listPath: paths.listPath,\n validate,\n create,\n save,\n remove,\n normalizeSaveResult: normalizeOkResult,\n normalizeRemoveResult: normalizeOkResult,\n })\n}\n\nexport interface RecordRailModuleProps<K extends AnyRecordKind> {\n kind: K\n projectId: string\n /** `null` → the list; an id → that record's detail. */\n recordId: string | null\n schema: CollectionDef\n paths: RecordRailPaths\n capabilities?: CollectionViewCapabilities\n /** The rail's label, for its loading / failure states. */\n label: string\n /** Optional copy for the loading / failure states (default: the generic catalog keys). */\n messages?: { loading?: () => string; loadFailed?: () => string }\n}\n\n/** The surface of a record rail: the portable list, or one record's detail. */\nexport function RecordRailModule<K extends AnyRecordKind>(props: RecordRailModuleProps<K>) {\n return (\n <RecordRailProvider kind={props.kind} projectId={props.projectId}>\n <RecordRailSurface {...props} />\n </RecordRailProvider>\n )\n}\n\nfunction RecordRailSurface<K extends AnyRecordKind>({\n kind,\n projectId,\n recordId,\n schema,\n paths,\n capabilities,\n label,\n messages,\n}: RecordRailModuleProps<K>) {\n const store = useRecordRail<K>()\n const source = useRecordCollectionViewSource(kind, projectId, schema, paths, capabilities)\n\n if (store.status !== 'ready') {\n return (\n <RailStoreStatus\n store={store}\n errorLabel={messages?.loadFailed?.() ?? translate('records.loadFailed', { label })}\n loadingLabel={messages?.loading?.() ?? translate('records.loading', { label })}\n errorTestId={`${kind.kind}-rail-error`}\n />\n )\n }\n // One record's detail opens in the portable edit shell (AD-19); the list otherwise.\n return <CollectionViewSurface source={source} projectId={projectId} documentId={recordId} />\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { collectionDefSchema } from '@elytracms/core/cms-core';
|
|
2
|
-
import { msgLabel } from '
|
|
2
|
+
import { msgLabel } from '../../lib/i18n/index.js';
|
|
3
3
|
/**
|
|
4
4
|
* The built-in **redirects** field lens (EC-303). Redirects are a separate
|
|
5
5
|
* editorial rail — NOT a `cmsDocuments` collection — so this `CollectionDef`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"redirects-schema.js","sourceRoot":"","sources":["../../../src/components/redirects/redirects-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAE9D,OAAO,EAAE,QAAQ,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"redirects-schema.js","sourceRoot":"","sources":["../../../src/components/redirects/redirects-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAE9D,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAEzC;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAkB,mBAAmB,CAAC,KAAK,CAAC;IAC7E,EAAE,EAAE,UAAU;IACd,4EAA4E;IAC5E,IAAI,EAAE,UAAU;IAChB,UAAU,EAAE,MAAM;IAClB,qFAAqF;IACrF,mFAAmF;IACnF,yEAAyE;IACzE,2DAA2D;IAC3D,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,sBAAsB,CAAC,EAAE,aAAa,EAAE,QAAQ,CAAC,8BAA8B,CAAC,EAAE;IAC1G,MAAM,EAAE;QACN;YACE,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,MAAM;YACZ,UAAU,EAAE,IAAI;YAChB,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,4BAA4B,CAAC,EAAE,IAAI,EAAE,6CAA6C,EAAE;SAC7G;QACD;YACE,IAAI,EAAE,IAAI;YACV,IAAI,EAAE,MAAM;YACZ,UAAU,EAAE,IAAI;YAChB,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,0BAA0B,CAAC,EAAE,IAAI,EAAE,8CAA8C,EAAE;SAC5G;QACD;YACE,IAAI,EAAE,WAAW;YACjB,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,iCAAiC,CAAC,EAAE,IAAI,EAAE,0CAA0C,EAAE;SAC/G;QACD;YACE,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,SAAS;YACf,UAAU,EAAE,IAAI;YAChB,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,+BAA+B,CAAC,EAAE,IAAI,EAAE,qDAAqD,EAAE;SACxH;KACF;CACF,CAAC,CAAA","sourcesContent":["import { collectionDefSchema } from '@elytracms/core/cms-core'\nimport type { CollectionDef } from '@elytracms/core/cms-core'\nimport { msgLabel } from '../../lib/i18n'\n\n/**\n * The built-in **redirects** field lens (EC-303). Redirects are a separate\n * editorial rail — NOT a `cmsDocuments` collection — so this `CollectionDef`\n * exists only to drive the portable list/edit surfaces over the redirects\n * {@link CollectionViewSource}. The rail's semantics (content-gated, no\n * publish/versions/locales, create + remove) come from the source's\n * capabilities, never the schema `kind`. The title identity is the `from` path.\n */\nexport const redirectsCollectionDef: CollectionDef = collectionDefSchema.parse({\n id: 'redirect',\n // Redirects aren't a CMS `kind`; 'document' just satisfies the lens schema.\n kind: 'document',\n titleField: 'from',\n // EC-241: the built-in rail's labels resolve through the message catalog (msgLabel),\n // so a consumer's translation catalog localizes them — no per-rail EC-311 override\n // needed just to translate. EC-311 field overrides still win for genuine\n // customization (renaming, readOnly, control, validation).\n form: { label: msgLabel('redirects.rail.label'), labelSingular: msgLabel('redirects.rail.labelSingular') },\n fields: [\n {\n name: 'from',\n type: 'text',\n filterable: true,\n form: { label: msgLabel('redirects.field.from.label'), help: 'The source path to forward, e.g. /old-page.' },\n },\n {\n name: 'to',\n type: 'text',\n filterable: true,\n form: { label: msgLabel('redirects.field.to.label'), help: 'The destination path or URL, e.g. /new-page.' },\n },\n {\n name: 'permanent',\n type: 'boolean',\n form: { label: msgLabel('redirects.field.permanent.label'), help: 'On → 301 permanent; off → 302 temporary.' },\n },\n {\n name: 'enabled',\n type: 'boolean',\n filterable: true,\n form: { label: msgLabel('redirects.field.enabled.label'), help: 'Only enabled redirects are served on the live site.' },\n },\n ],\n})\n"]}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { Link, STUDIO_PROJECT_ROUTE, studioPath } from '@elytracms/studio/router';
|
|
3
3
|
import { AlertTriangle, ArrowDownLeft, ArrowUpRight, Link2, RotateCcw } from 'lucide-react';
|
|
4
|
-
import { useReferencePanel } from '
|
|
4
|
+
import { useReferencePanel } from '../../lib/references/index.js';
|
|
5
5
|
import { Badge, Button, Panel, PanelHeader, Row, RowItem, Spacer, Stack, Text } from '@elytracms/studio/ui';
|
|
6
|
-
import { translate } from '
|
|
6
|
+
import { translate } from '../../lib/i18n/index.js';
|
|
7
7
|
/**
|
|
8
8
|
* The shared "References / Used by" panel (EC-172) — one generic component,
|
|
9
9
|
* three hosts (CMS document detail, asset library, component detail). Entries
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"references-panel.js","sourceRoot":"","sources":["../../../src/components/references/references-panel.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,IAAI,EAAE,oBAAoB,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAA;AACjF,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAG3F,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAA;AAEpE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAA;AAC3G,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAA;AAEtD;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAAC,EAC9B,SAAS,EACT,MAAM,EACN,MAAM,EACN,SAAS,GAQV;IACC,MAAM,KAAK,GAAG,iBAAiB,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;IAC9D,MAAM,KAAK,GACT,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAA;IAErF,OAAO,CACL,MAAC,KAAK,IAAC,SAAS,EAAE,SAAS,iBAAc,kBAAkB,aACzD,MAAC,WAAW,eACV,KAAC,KAAK,IAAC,SAAS,EAAC,qBAAqB,GAAG,EACzC,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,MAAM,EAAC,UAAU,YAC9B,SAAS,CAAC,kBAAkB,CAAC,GACzB,EACN,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,KAAC,KAAK,IAAC,OAAO,EAAC,SAAS,YAAE,KAAK,GAAS,CAAC,CAAC,CAAC,IAAI,EACjE,KAAC,MAAM,KAAG,IACE,EACd,KAAC,KAAK,IAAC,GAAG,EAAC,GAAG,EAAC,SAAS,EAAC,KAAK,YAC3B,KAAK,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,CAC5B,KAAC,iBAAiB,KAAG,CACtB,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,CAC7B,KAAC,eAAe,IAAC,OAAO,EAAE,KAAK,CAAC,YAAY,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,GAAI,CACzE,CAAC,CAAC,CAAC,CACF,8BACG,MAAM,CAAC,CAAC,CAAC,CACR,KAAC,gBAAgB,IACf,KAAK,EAAE,SAAS,CAAC,kBAAkB,CAAC,EACpC,IAAI,EAAE,KAAC,YAAY,IAAC,SAAS,EAAC,UAAU,GAAG,EAC3C,IAAI,EAAE,SAAS,CAAC,yBAAyB,CAAC,EAC1C,SAAS,EAAC,UAAU,EACpB,SAAS,EAAE,SAAS,EACpB,OAAO,EAAE,KAAK,CAAC,IAAI,EAAE,QAAQ,IAAI,EAAE,EACnC,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,aAAa,IAAI,IAAI,EACxC,UAAU,EAAE,SAAS,CAAC,0BAA0B,CAAC,GACjD,CACH,CAAC,CAAC,CAAC,IAAI,EACP,MAAM,CAAC,CAAC,CAAC,CACR,KAAC,gBAAgB,IACf,KAAK,EAAE,SAAS,CAAC,mBAAmB,CAAC,EACrC,IAAI,EAAE,KAAC,aAAa,IAAC,SAAS,EAAC,UAAU,GAAG,EAC5C,IAAI,EAAE,SAAS,CAAC,wBAAwB,CAAC,EACzC,SAAS,EAAC,SAAS,EACnB,SAAS,EAAE,SAAS,EACpB,OAAO,EAAE,KAAK,CAAC,IAAI,EAAE,OAAO,IAAI,EAAE,EAClC,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,YAAY,IAAI,IAAI,EACvC,UAAU,EAAE,SAAS,CAAC,yBAAyB,CAAC,GAChD,CACH,CAAC,CAAC,CAAC,IAAI,IACP,CACJ,GACK,IACF,CACT,CAAA;AACH,CAAC;AAED,mEAAmE;AACnE,SAAS,iBAAiB;IACxB,OAAO,CACL,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,OAAO,iBAAa,oBAAoB,YAC1D,SAAS,CAAC,oBAAoB,CAAC,GAC3B,CACR,CAAA;AACH,CAAC;AAED,6DAA6D;AAC7D,SAAS,eAAe,CAAC,EACvB,OAAO,EACP,OAAO,GAIR;IACC,OAAO,CACL,MAAC,KAAK,IAAC,GAAG,EAAC,GAAG,iBAAa,kBAAkB,aAC3C,MAAC,GAAG,IAAC,GAAG,EAAC,GAAG,aACV,KAAC,aAAa,IAAC,SAAS,EAAC,yBAAyB,GAAG,EACrD,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,MAAM,EAAC,UAAU,YAC9B,SAAS,CAAC,uBAAuB,CAAC,GAC9B,IACH,EACN,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,OAAO,YACzB,OAAO,IAAI,SAAS,CAAC,6BAA6B,CAAC,GAC/C,EACP,KAAC,GAAG,cACF,MAAC,MAAM,IAAC,OAAO,EAAC,SAAS,EAAC,IAAI,EAAC,IAAI,EAAC,OAAO,EAAE,OAAO,aAClD,KAAC,SAAS,KAAG,OAAE,SAAS,CAAC,cAAc,CAAC,IACjC,GACL,IACA,CACT,CAAA;AACH,CAAC;AAED,SAAS,gBAAgB,CAAC,EACxB,KAAK,EACL,IAAI,EACJ,IAAI,EACJ,SAAS,EACT,SAAS,EACT,OAAO,EACP,KAAK,EACL,UAAU,GAWX;IACC,OAAO,CACL,MAAC,KAAK,IAAC,GAAG,EAAC,GAAG,iBAAc,cAAc,SAAS,EAAE,aACnD,MAAC,GAAG,IAAC,GAAG,EAAC,GAAG,aACV,eAAM,SAAS,EAAC,uBAAuB,YAAE,IAAI,GAAQ,EACrD,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,MAAM,EAAC,UAAU,EAAC,IAAI,EAAC,OAAO,EAAC,SAAS,EAAC,yBAAyB,YAC/E,KAAK,GACD,EACP,KAAC,KAAK,IAAC,OAAO,EAAC,SAAS,YAAE,OAAO,CAAC,MAAM,GAAS,IAC7C,EACL,KAAK,CAAC,CAAC,CAAC,CACP,MAAC,GAAG,IAAC,GAAG,EAAC,GAAG,iBAAc,cAAc,SAAS,QAAQ,aACvD,KAAC,aAAa,IAAC,SAAS,EAAC,oCAAoC,GAAG,EAChE,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,OAAO,YACzB,SAAS,CAAC,6BAA6B,EAAE,EAAE,KAAK,EAAE,CAAC,GAC/C,IACH,CACP,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CACzB,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,OAAO,iBAAc,cAAc,SAAS,QAAQ,YACtE,UAAU,GACN,CACR,CAAC,CAAC,CAAC,CACF,KAAC,KAAK,IAAC,GAAG,EAAC,GAAG,EAAC,KAAK,EAAE,IAAI,YACvB,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CACtB,KAAC,iBAAiB,IAEhB,KAAK,EAAE,KAAK,EACZ,SAAS,EAAE,SAAS,EACpB,SAAS,EAAE,SAAS,IAHf,KAAK,CAAC,GAAG,CAId,CACH,CAAC,GACI,CACT,IACK,CACT,CAAA;AACH,CAAC;AAED;;;;GAIG;AACH,SAAS,iBAAiB,CAAC,EACzB,KAAK,EACL,SAAS,EACT,SAAS,GAKV;IACC,MAAM,OAAO,GACX,SAAS,KAAK,SAAS;QACrB,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,WAAW,EAAE,IAAI,EAAE,KAAK,CAAC,UAAU,EAAE;QACtD,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,WAAW,EAAE,IAAI,EAAE,KAAK,CAAC,UAAU,EAAE,CAAA;IAC1D,OAAO,CACL,KAAC,OAAO,mBACM,iBAAiB,EAC7B,OAAO,EACL,8BACE,KAAC,KAAK,IAAC,OAAO,EAAC,SAAS,YAAE,KAAK,CAAC,SAAS,GAAS,EAClD,KAAC,aAAa,IAAC,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,GAAI,IAChE,YAGL,MAAC,KAAK,IAAC,GAAG,EAAC,GAAG,EAAC,SAAS,EAAC,SAAS,aAChC,KAAC,GAAG,IAAC,GAAG,EAAC,GAAG,EAAC,SAAS,EAAC,SAAS,YAC9B,KAAC,YAAY,IAAC,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,OAAO,SAAG,GACpF,EACN,MAAC,GAAG,IAAC,GAAG,EAAC,GAAG,EAAC,SAAS,EAAC,SAAS,aAC9B,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,OAAO,EAAC,SAAS,EAAC,UAAU,YAC9C,SAAS,CAAC,2BAA2B,CAAC,GAClC,EACP,KAAC,YAAY,IACX,SAAS,EAAE,SAAS,EACpB,IAAI,EAAE,KAAK,CAAC,UAAU,EACtB,KAAK,EAAE,KAAK,CAAC,aAAa,GAC1B,IACE,IACA,GACA,CACX,CAAA;AACH,CAAC;AAED,iFAAiF;AACjF,SAAS,aAAa,CAAC,EAAE,KAAK,EAAE,SAAS,EAA0C;IACjF,OAAO,CACL,MAAC,GAAG,IAAC,GAAG,EAAC,GAAG,EAAC,SAAS,EAAC,UAAU,iBAAa,oBAAoB,aAC/D,KAAK,CAAC,CAAC,CAAC,KAAC,KAAK,IAAC,OAAO,EAAC,SAAS,YAAE,SAAS,CAAC,uBAAuB,CAAC,GAAS,CAAC,CAAC,CAAC,IAAI,EACpF,SAAS,CAAC,CAAC,CAAC,KAAC,KAAK,IAAC,OAAO,EAAC,SAAS,YAAE,SAAS,CAAC,2BAA2B,CAAC,GAAS,CAAC,CAAC,CAAC,IAAI,IACzF,CACP,CAAA;AACH,CAAC;AAED,qFAAqF;AACrF,SAAS,YAAY,CAAC,EACpB,SAAS,EACT,IAAI,EACJ,KAAK,EACL,OAAO,GAAG,KAAK,GAMhB;IACC,MAAM,SAAS,GAAG,OAAO;QACvB,CAAC,CAAC,yFAAyF;QAC3F,CAAC,CAAC,mFAAmF,CAAA;IACvF,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAClB,OAAO,OAAO,CAAC,CAAC,CAAC,CACf,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,MAAM,EAAC,QAAQ,EAAC,SAAS,EAAC,kBAAkB,YACzD,KAAK,GACD,CACR,CAAC,CAAC,CAAC,CACF,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,OAAO,EAAC,SAAS,EAAC,kBAAkB,YACtD,KAAK,GACD,CACR,CAAA;IACH,CAAC;IACD,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,KAAK,WAAW;YACd,OAAO,CACL,KAAC,IAAI,IACH,EAAE,EAAE,oBAAoB,EACxB,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,WAAW,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,UAAU,CAAC,EAAE,EACzF,MAAM,EAAE,IAAI,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,EACrE,SAAS,EAAE,SAAS,iBACR,oBAAoB,YAE/B,KAAK,GACD,CACR,CAAA;QACH,KAAK,cAAc;YACjB,yEAAyE;YACzE,4DAA4D;YAC5D,OAAO,CACL,KAAC,IAAI,IACH,EAAE,EAAE,oBAAoB,EACxB,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,OAAO,EAAE,EAAE,EACnD,MAAM,EAAE,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,EAC9D,SAAS,EAAE,SAAS,iBACR,wBAAwB,YAEnC,KAAK,GACD,CACR,CAAA;QACH,KAAK,WAAW;YACd,OAAO,CACL,KAAC,IAAI,IACH,EAAE,EAAE,oBAAoB,EACxB,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,EACrE,SAAS,EAAE,SAAS,iBACR,0BAA0B,YAErC,KAAK,GACD,CACR,CAAA;QACH,KAAK,OAAO;YACV,OAAO,CACL,KAAC,IAAI,IACH,EAAE,EAAE,oBAAoB,EACxB,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,EAAE,EAClD,MAAM,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE,EAC/B,SAAS,EAAE,SAAS,iBACR,sBAAsB,YAEjC,KAAK,GACD,CACR,CAAA;IACL,CAAC;AACH,CAAC","sourcesContent":["import { Link, STUDIO_PROJECT_ROUTE, studioPath } from '@elytracms/studio/router'\nimport { AlertTriangle, ArrowDownLeft, ArrowUpRight, Link2, RotateCcw } from 'lucide-react'\nimport type { ReactNode } from 'react'\nimport type { ReferenceSource, ReferenceTarget } from '@elytracms/core/persistence'\nimport { useReferencePanel } from '@elytracms/studio/lib/references'\nimport type { ReferenceDeepLink, ReferenceEntryView } from '@elytracms/studio/lib/references'\nimport { Badge, Button, Panel, PanelHeader, Row, RowItem, Spacer, Stack, Text } from '@elytracms/studio/ui'\nimport { translate } from '@elytracms/studio/lib/i18n'\n\n/**\n * The shared \"References / Used by\" panel (EC-172) — one generic component,\n * three hosts (CMS document detail, asset library, component detail). Entries\n * are served entirely by `@elytracms/core/operations` reference queries over the\n * EC-155 index; every entry deep-links to the exact field control or graph\n * node its index path identifies, and draft-only entries are visibly\n * distinguished from published (live) ones.\n */\nexport function ReferencesPanel({\n projectId,\n source,\n target,\n className,\n}: {\n projectId: string\n /** The holding entity — enables the outbound \"References\" section. */\n source?: ReferenceSource\n /** The referenced entity — enables the inbound \"Used by\" section. */\n target?: ReferenceTarget\n className?: string\n}) {\n const panel = useReferencePanel({ projectId, source, target })\n const total =\n panel.data === null ? null : panel.data.outbound.length + panel.data.inbound.length\n\n return (\n <Panel className={className} data-testid=\"references-panel\">\n <PanelHeader>\n <Link2 className=\"size-4 text-primary\" />\n <Text size=\"sm\" weight=\"semibold\">\n {translate('references.title')}\n </Text>\n {total !== null ? <Badge variant=\"outline\">{total}</Badge> : null}\n <Spacer />\n </PanelHeader>\n <Stack gap=\"4\" className=\"p-4\">\n {panel.status === 'loading' ? (\n <ReferencesLoading />\n ) : panel.status === 'error' ? (\n <ReferencesError message={panel.errorMessage} onRetry={panel.refresh} />\n ) : (\n <>\n {source ? (\n <ReferenceSection\n title={translate('references.title')}\n icon={<ArrowUpRight className=\"size-3.5\" />}\n hint={translate('references.hintOutbound')}\n direction=\"outbound\"\n projectId={projectId}\n entries={panel.data?.outbound ?? []}\n error={panel.data?.outboundError ?? null}\n emptyLabel={translate('references.emptyOutbound')}\n />\n ) : null}\n {target ? (\n <ReferenceSection\n title={translate('references.usedBy')}\n icon={<ArrowDownLeft className=\"size-3.5\" />}\n hint={translate('references.hintInbound')}\n direction=\"inbound\"\n projectId={projectId}\n entries={panel.data?.inbound ?? []}\n error={panel.data?.inboundError ?? null}\n emptyLabel={translate('references.emptyInbound')}\n />\n ) : null}\n </>\n )}\n </Stack>\n </Panel>\n )\n}\n\n/** Named loading state, consistent with existing studio panels. */\nfunction ReferencesLoading() {\n return (\n <Text size=\"sm\" tone=\"muted\" data-testid=\"references-loading\">\n {translate('references.loading')}\n </Text>\n )\n}\n\n/** Named error state with retry — never a silent failure. */\nfunction ReferencesError({\n message,\n onRetry,\n}: {\n message: string | null\n onRetry: () => void\n}) {\n return (\n <Stack gap=\"2\" data-testid=\"references-error\">\n <Row gap=\"2\">\n <AlertTriangle className=\"size-4 text-destructive\" />\n <Text size=\"sm\" weight=\"semibold\">\n {translate('references.loadFailed')}\n </Text>\n </Row>\n <Text size=\"xs\" tone=\"muted\">\n {message ?? translate('references.indexUnreachable')}\n </Text>\n <Row>\n <Button variant=\"outline\" size=\"sm\" onClick={onRetry}>\n <RotateCcw /> {translate('common.retry')}\n </Button>\n </Row>\n </Stack>\n )\n}\n\nfunction ReferenceSection({\n title,\n icon,\n hint,\n direction,\n projectId,\n entries,\n error,\n emptyLabel,\n}: {\n title: string\n icon: ReactNode\n hint: string\n direction: 'outbound' | 'inbound'\n projectId: string\n entries: readonly ReferenceEntryView[]\n /** EC-205: this direction's read failed — shown inline, the panel stays usable. */\n error?: string | null\n emptyLabel: string\n}) {\n return (\n <Stack gap=\"2\" data-testid={`references-${direction}`}>\n <Row gap=\"2\">\n <span className=\"text-muted-foreground\">{icon}</span>\n <Text size=\"xs\" weight=\"semibold\" tone=\"muted\" className=\"uppercase tracking-wide\">\n {title}\n </Text>\n <Badge variant=\"outline\">{entries.length}</Badge>\n </Row>\n {error ? (\n <Row gap=\"2\" data-testid={`references-${direction}-error`}>\n <AlertTriangle className=\"size-3.5 shrink-0 text-destructive\" />\n <Text size=\"xs\" tone=\"muted\">\n {translate('references.sectionLoadError', { error })}\n </Text>\n </Row>\n ) : entries.length === 0 ? (\n <Text size=\"sm\" tone=\"muted\" data-testid={`references-${direction}-empty`}>\n {emptyLabel}\n </Text>\n ) : (\n <Stack gap=\"2\" title={hint}>\n {entries.map((entry) => (\n <ReferenceEntryRow\n key={entry.key}\n entry={entry}\n direction={direction}\n projectId={projectId}\n />\n ))}\n </Stack>\n )}\n </Stack>\n )\n}\n\n/**\n * One entry row. Inbound rows lead with the *source* (where it is used) and\n * deep-link to the exact location; outbound rows lead with the *target* and\n * keep the exact-field link on the location line.\n */\nfunction ReferenceEntryRow({\n entry,\n direction,\n projectId,\n}: {\n entry: ReferenceEntryView\n direction: 'outbound' | 'inbound'\n projectId: string\n}) {\n const primary =\n direction === 'inbound'\n ? { label: entry.sourceLabel, link: entry.sourceLink }\n : { label: entry.targetLabel, link: entry.targetLink }\n return (\n <RowItem\n data-testid=\"reference-entry\"\n actions={\n <>\n <Badge variant=\"outline\">{entry.kindLabel}</Badge>\n <VariantBadges draft={entry.draft} published={entry.published} />\n </>\n }\n >\n <Stack gap=\"1\" className=\"min-w-0\">\n <Row gap=\"2\" className=\"min-w-0\">\n <DeepLinkText projectId={projectId} link={primary.link} label={primary.label} primary />\n </Row>\n <Row gap=\"1\" className=\"min-w-0\">\n <Text size=\"xs\" tone=\"muted\" className=\"shrink-0\">\n {translate('references.locationPrefix')}\n </Text>\n <DeepLinkText\n projectId={projectId}\n link={entry.sourceLink}\n label={entry.locationLabel}\n />\n </Row>\n </Stack>\n </RowItem>\n )\n}\n\n/** Draft/published visibility (the Sanity hidden-draft-reference case, AD-6). */\nfunction VariantBadges({ draft, published }: { draft: boolean; published: boolean }) {\n return (\n <Row gap=\"1\" className=\"shrink-0\" data-testid=\"reference-variants\">\n {draft ? <Badge variant=\"warning\">{translate('references.badgeDraft')}</Badge> : null}\n {published ? <Badge variant=\"success\">{translate('references.badgePublished')}</Badge> : null}\n </Row>\n )\n}\n\n/** Render a deep-link descriptor as a router link (plain text when unresolvable). */\nfunction DeepLinkText({\n projectId,\n link,\n label,\n primary = false,\n}: {\n projectId: string\n link: ReferenceDeepLink | null\n label: string\n primary?: boolean\n}) {\n const className = primary\n ? 'min-w-0 truncate text-sm font-medium text-foreground hover:text-primary hover:underline'\n : 'min-w-0 truncate text-xs text-muted-foreground hover:text-primary hover:underline'\n if (link === null) {\n return primary ? (\n <Text size=\"sm\" weight=\"medium\" className=\"min-w-0 truncate\">\n {label}\n </Text>\n ) : (\n <Text size=\"xs\" tone=\"muted\" className=\"min-w-0 truncate\">\n {label}\n </Text>\n )\n }\n switch (link.kind) {\n case 'cms-field':\n return (\n <Link\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId, _splat: studioPath.cmsDocument(link.collectionId, link.documentId) }}\n search={link.fieldPath !== undefined ? { field: link.fieldPath } : {}}\n className={className}\n data-testid=\"reference-link-cms\"\n >\n {label}\n </Link>\n )\n case 'builder-node':\n // The builder redirect (EC-187) only reads `?page=`; the page-graph-only\n // `node` has no home in the document editor and is dropped.\n return (\n <Link\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId, _splat: studioPath.builder() }}\n search={link.pageId !== undefined ? { page: link.pageId } : {}}\n className={className}\n data-testid=\"reference-link-builder\"\n >\n {label}\n </Link>\n )\n case 'component':\n return (\n <Link\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId, _splat: studioPath.component(link.componentId) }}\n className={className}\n data-testid=\"reference-link-component\"\n >\n {label}\n </Link>\n )\n case 'asset':\n return (\n <Link\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId, _splat: studioPath.assets() }}\n search={{ asset: link.assetId }}\n className={className}\n data-testid=\"reference-link-asset\"\n >\n {label}\n </Link>\n )\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"references-panel.js","sourceRoot":"","sources":["../../../src/components/references/references-panel.tsx"],"names":[],"mappings":"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","sourcesContent":["import { Link, STUDIO_PROJECT_ROUTE, studioPath } from '@elytracms/studio/router'\nimport { AlertTriangle, ArrowDownLeft, ArrowUpRight, Link2, RotateCcw } from 'lucide-react'\nimport type { ReactNode } from 'react'\nimport type { ReferenceSource, ReferenceTarget } from '@elytracms/core/persistence'\nimport { useReferencePanel } from '../../lib/references'\nimport type { ReferenceDeepLink, ReferenceEntryView } from '../../lib/references'\nimport { Badge, Button, Panel, PanelHeader, Row, RowItem, Spacer, Stack, Text } from '@elytracms/studio/ui'\nimport { translate } from '../../lib/i18n'\n\n/**\n * The shared \"References / Used by\" panel (EC-172) — one generic component,\n * three hosts (CMS document detail, asset library, component detail). Entries\n * are served entirely by `@elytracms/core/operations` reference queries over the\n * EC-155 index; every entry deep-links to the exact field control or graph\n * node its index path identifies, and draft-only entries are visibly\n * distinguished from published (live) ones.\n */\nexport function ReferencesPanel({\n projectId,\n source,\n target,\n className,\n}: {\n projectId: string\n /** The holding entity — enables the outbound \"References\" section. */\n source?: ReferenceSource\n /** The referenced entity — enables the inbound \"Used by\" section. */\n target?: ReferenceTarget\n className?: string\n}) {\n const panel = useReferencePanel({ projectId, source, target })\n const total =\n panel.data === null ? null : panel.data.outbound.length + panel.data.inbound.length\n\n return (\n <Panel className={className} data-testid=\"references-panel\">\n <PanelHeader>\n <Link2 className=\"size-4 text-primary\" />\n <Text size=\"sm\" weight=\"semibold\">\n {translate('references.title')}\n </Text>\n {total !== null ? <Badge variant=\"outline\">{total}</Badge> : null}\n <Spacer />\n </PanelHeader>\n <Stack gap=\"4\" className=\"p-4\">\n {panel.status === 'loading' ? (\n <ReferencesLoading />\n ) : panel.status === 'error' ? (\n <ReferencesError message={panel.errorMessage} onRetry={panel.refresh} />\n ) : (\n <>\n {source ? (\n <ReferenceSection\n title={translate('references.title')}\n icon={<ArrowUpRight className=\"size-3.5\" />}\n hint={translate('references.hintOutbound')}\n direction=\"outbound\"\n projectId={projectId}\n entries={panel.data?.outbound ?? []}\n error={panel.data?.outboundError ?? null}\n emptyLabel={translate('references.emptyOutbound')}\n />\n ) : null}\n {target ? (\n <ReferenceSection\n title={translate('references.usedBy')}\n icon={<ArrowDownLeft className=\"size-3.5\" />}\n hint={translate('references.hintInbound')}\n direction=\"inbound\"\n projectId={projectId}\n entries={panel.data?.inbound ?? []}\n error={panel.data?.inboundError ?? null}\n emptyLabel={translate('references.emptyInbound')}\n />\n ) : null}\n </>\n )}\n </Stack>\n </Panel>\n )\n}\n\n/** Named loading state, consistent with existing studio panels. */\nfunction ReferencesLoading() {\n return (\n <Text size=\"sm\" tone=\"muted\" data-testid=\"references-loading\">\n {translate('references.loading')}\n </Text>\n )\n}\n\n/** Named error state with retry — never a silent failure. */\nfunction ReferencesError({\n message,\n onRetry,\n}: {\n message: string | null\n onRetry: () => void\n}) {\n return (\n <Stack gap=\"2\" data-testid=\"references-error\">\n <Row gap=\"2\">\n <AlertTriangle className=\"size-4 text-destructive\" />\n <Text size=\"sm\" weight=\"semibold\">\n {translate('references.loadFailed')}\n </Text>\n </Row>\n <Text size=\"xs\" tone=\"muted\">\n {message ?? translate('references.indexUnreachable')}\n </Text>\n <Row>\n <Button variant=\"outline\" size=\"sm\" onClick={onRetry}>\n <RotateCcw /> {translate('common.retry')}\n </Button>\n </Row>\n </Stack>\n )\n}\n\nfunction ReferenceSection({\n title,\n icon,\n hint,\n direction,\n projectId,\n entries,\n error,\n emptyLabel,\n}: {\n title: string\n icon: ReactNode\n hint: string\n direction: 'outbound' | 'inbound'\n projectId: string\n entries: readonly ReferenceEntryView[]\n /** EC-205: this direction's read failed — shown inline, the panel stays usable. */\n error?: string | null\n emptyLabel: string\n}) {\n return (\n <Stack gap=\"2\" data-testid={`references-${direction}`}>\n <Row gap=\"2\">\n <span className=\"text-muted-foreground\">{icon}</span>\n <Text size=\"xs\" weight=\"semibold\" tone=\"muted\" className=\"uppercase tracking-wide\">\n {title}\n </Text>\n <Badge variant=\"outline\">{entries.length}</Badge>\n </Row>\n {error ? (\n <Row gap=\"2\" data-testid={`references-${direction}-error`}>\n <AlertTriangle className=\"size-3.5 shrink-0 text-destructive\" />\n <Text size=\"xs\" tone=\"muted\">\n {translate('references.sectionLoadError', { error })}\n </Text>\n </Row>\n ) : entries.length === 0 ? (\n <Text size=\"sm\" tone=\"muted\" data-testid={`references-${direction}-empty`}>\n {emptyLabel}\n </Text>\n ) : (\n <Stack gap=\"2\" title={hint}>\n {entries.map((entry) => (\n <ReferenceEntryRow\n key={entry.key}\n entry={entry}\n direction={direction}\n projectId={projectId}\n />\n ))}\n </Stack>\n )}\n </Stack>\n )\n}\n\n/**\n * One entry row. Inbound rows lead with the *source* (where it is used) and\n * deep-link to the exact location; outbound rows lead with the *target* and\n * keep the exact-field link on the location line.\n */\nfunction ReferenceEntryRow({\n entry,\n direction,\n projectId,\n}: {\n entry: ReferenceEntryView\n direction: 'outbound' | 'inbound'\n projectId: string\n}) {\n const primary =\n direction === 'inbound'\n ? { label: entry.sourceLabel, link: entry.sourceLink }\n : { label: entry.targetLabel, link: entry.targetLink }\n return (\n <RowItem\n data-testid=\"reference-entry\"\n actions={\n <>\n <Badge variant=\"outline\">{entry.kindLabel}</Badge>\n <VariantBadges draft={entry.draft} published={entry.published} />\n </>\n }\n >\n <Stack gap=\"1\" className=\"min-w-0\">\n <Row gap=\"2\" className=\"min-w-0\">\n <DeepLinkText projectId={projectId} link={primary.link} label={primary.label} primary />\n </Row>\n <Row gap=\"1\" className=\"min-w-0\">\n <Text size=\"xs\" tone=\"muted\" className=\"shrink-0\">\n {translate('references.locationPrefix')}\n </Text>\n <DeepLinkText\n projectId={projectId}\n link={entry.sourceLink}\n label={entry.locationLabel}\n />\n </Row>\n </Stack>\n </RowItem>\n )\n}\n\n/** Draft/published visibility (the Sanity hidden-draft-reference case, AD-6). */\nfunction VariantBadges({ draft, published }: { draft: boolean; published: boolean }) {\n return (\n <Row gap=\"1\" className=\"shrink-0\" data-testid=\"reference-variants\">\n {draft ? <Badge variant=\"warning\">{translate('references.badgeDraft')}</Badge> : null}\n {published ? <Badge variant=\"success\">{translate('references.badgePublished')}</Badge> : null}\n </Row>\n )\n}\n\n/** Render a deep-link descriptor as a router link (plain text when unresolvable). */\nfunction DeepLinkText({\n projectId,\n link,\n label,\n primary = false,\n}: {\n projectId: string\n link: ReferenceDeepLink | null\n label: string\n primary?: boolean\n}) {\n const className = primary\n ? 'min-w-0 truncate text-sm font-medium text-foreground hover:text-primary hover:underline'\n : 'min-w-0 truncate text-xs text-muted-foreground hover:text-primary hover:underline'\n if (link === null) {\n return primary ? (\n <Text size=\"sm\" weight=\"medium\" className=\"min-w-0 truncate\">\n {label}\n </Text>\n ) : (\n <Text size=\"xs\" tone=\"muted\" className=\"min-w-0 truncate\">\n {label}\n </Text>\n )\n }\n switch (link.kind) {\n case 'cms-field':\n return (\n <Link\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId, _splat: studioPath.cmsDocument(link.collectionId, link.documentId) }}\n search={link.fieldPath !== undefined ? { field: link.fieldPath } : {}}\n className={className}\n data-testid=\"reference-link-cms\"\n >\n {label}\n </Link>\n )\n case 'builder-node':\n // The builder redirect (EC-187) only reads `?page=`; the page-graph-only\n // `node` has no home in the document editor and is dropped.\n return (\n <Link\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId, _splat: studioPath.builder() }}\n search={link.pageId !== undefined ? { page: link.pageId } : {}}\n className={className}\n data-testid=\"reference-link-builder\"\n >\n {label}\n </Link>\n )\n case 'component':\n return (\n <Link\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId, _splat: studioPath.component(link.componentId) }}\n className={className}\n data-testid=\"reference-link-component\"\n >\n {label}\n </Link>\n )\n case 'asset':\n return (\n <Link\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId, _splat: studioPath.assets() }}\n search={{ asset: link.assetId }}\n className={className}\n data-testid=\"reference-link-asset\"\n >\n {label}\n </Link>\n )\n }\n}\n"]}
|
|
@@ -2,18 +2,20 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
import { Fragment, useCallback, useEffect, useMemo, useState, useSyncExternalStore } from 'react';
|
|
3
3
|
import { Link, STUDIO_PROJECT_ROUTE, useLocation } from '@elytracms/studio/router';
|
|
4
4
|
import { ChevronDown, PanelLeftClose, PanelLeftOpen } from 'lucide-react';
|
|
5
|
-
import { cn } from '
|
|
6
|
-
import { readCollapsedViews, storeCollapsedViews } from '
|
|
7
|
-
import { getStudioRegistry } from '
|
|
8
|
-
import { projectConfigIssues, resolveDeploymentCollections, resolveDeploymentReuse, resolveDeploymentRoutes, } from '
|
|
9
|
-
import { routeValidationIssues } from '
|
|
10
|
-
import { contentSessionOrphans, serverContentSessionOrphans, subscribeContentSession, } from '
|
|
11
|
-
import { translate } from '
|
|
5
|
+
import { cn } from '../lib/utils.js';
|
|
6
|
+
import { readCollapsedViews, storeCollapsedViews } from '../lib/rail.js';
|
|
7
|
+
import { getStudioRegistry } from '../lib/registry/index.js';
|
|
8
|
+
import { projectConfigIssues, resolveDeploymentCollections, resolveDeploymentReuse, resolveDeploymentRoutes, } from '../lib/workspace/index.js';
|
|
9
|
+
import { routeValidationIssues } from '../lib/routes/route-issues.js';
|
|
10
|
+
import { contentSessionOrphans, serverContentSessionOrphans, subscribeContentSession, } from '../lib/data/index.js';
|
|
11
|
+
import { translate } from '../lib/i18n/index.js';
|
|
12
12
|
import { Text } from '@elytracms/studio/ui';
|
|
13
13
|
import { NavIssues, customizationItems, orphanedCollectionItems, routeIssueItems } from './nav-issues.js';
|
|
14
14
|
import { activeViewIdFromPath, isNavViewVisible, useStudioNav } from './app-views/sidebar.js';
|
|
15
15
|
import { resolveReuse } from './builder/reuse.js';
|
|
16
|
-
import {
|
|
16
|
+
import { redirectRecordKind } from '@elytracms/core/persistence';
|
|
17
|
+
import { useRecordList } from './records/record-rail.js';
|
|
18
|
+
import { useContributedNavIssues, useNavIssues } from './nav-issues-registry.js';
|
|
17
19
|
import { useFieldRenderers } from './cms/field-renderer.js';
|
|
18
20
|
import { useProjectRole } from './auth-context.js';
|
|
19
21
|
/**
|
|
@@ -29,19 +31,24 @@ import { useProjectRole } from './auth-context.js';
|
|
|
29
31
|
export function SectionNav({ project, collapsed, onToggleCollapsed }) {
|
|
30
32
|
const { pathname } = useLocation();
|
|
31
33
|
const activeViewId = activeViewIdFromPath(pathname);
|
|
32
|
-
const { nodes, issues: navIssues } = useStudioNav(project);
|
|
34
|
+
const { nodes, issues: navIssues, registry } = useStudioNav(project);
|
|
33
35
|
const { issues: rendererIssues } = useFieldRenderers();
|
|
34
36
|
const configIssues = useProjectConfigIssues();
|
|
35
37
|
const reuseIssues = useReuseIssues();
|
|
36
38
|
const projectId = project.id;
|
|
37
39
|
const routeIssues = useRouteIssues(projectId);
|
|
38
40
|
const orphanIssues = useOrphanedCollectionIssues();
|
|
41
|
+
// EC-616: findings contributed by views and addons (`useNavIssues`), and the views that
|
|
42
|
+
// declare always-on findings (`useIssues`) — mounted below, whether or not they are open.
|
|
43
|
+
const contributedIssues = useContributedNavIssues();
|
|
44
|
+
const issueViews = useMemo(() => registry.views.filter((view) => view.useIssues), [registry]);
|
|
39
45
|
// Config issues lead: "this project declares no collections" explains every
|
|
40
46
|
// other emptiness on the screen, so it is the first thing to read.
|
|
41
47
|
const issues = [
|
|
42
48
|
...customizationItems([...configIssues, ...navIssues, ...rendererIssues, ...reuseIssues]),
|
|
43
49
|
...routeIssues,
|
|
44
50
|
...orphanIssues,
|
|
51
|
+
...contributedIssues,
|
|
45
52
|
];
|
|
46
53
|
// EC-240 (AD-18): per-nav-item, default-open permission gating. The signed-in
|
|
47
54
|
// user's role drives an OPTIONAL `visible` predicate on any view; absent →
|
|
@@ -93,7 +100,7 @@ export function SectionNav({ project, collapsed, onToggleCollapsed }) {
|
|
|
93
100
|
return (_jsxs(Fragment, { children: [_jsx(NavViewLink, { projectId: projectId, view: node, activeViewId: activeViewId, collapsed: collapsed, hasActiveChild: hasActiveChild, collapsible: hasChildren && !collapsed, expanded: !viewCollapsed, onToggleExpanded: () => toggleViewCollapsed(node.id) }), collapsed || viewCollapsed
|
|
94
101
|
? null
|
|
95
102
|
: node.children?.filter(isVisible).map((child) => (_jsx(NavViewLink, { projectId: projectId, view: child, activeViewId: activeViewId, collapsed: collapsed, nested: true }, child.id)))] }, node.id));
|
|
96
|
-
}), _jsxs("div", { className: "mt-auto flex flex-col gap-0.5", children: [issues.length > 0 ? _jsx(NavIssues, { issues: issues, collapsed: collapsed }) : null, _jsx("button", { type: "button", onClick: onToggleCollapsed, "aria-label": collapsed ? translate('shell.sidebar.expand') : translate('shell.sidebar.collapse'), "aria-pressed": collapsed, title: collapsed ? translate('shell.sidebar.expand') : translate('shell.sidebar.collapse'), className: cn('flex h-9 items-center gap-2.5 rounded-md text-sm text-muted-foreground transition-colors hover:bg-muted hover:text-foreground', collapsed ? 'justify-center px-0' : 'px-3'), children: collapsed ? (_jsx(PanelLeftOpen, { className: "size-4 shrink-0" })) : (_jsxs(_Fragment, { children: [_jsx(PanelLeftClose, { className: "size-4 shrink-0" }), _jsx("span", { className: "min-w-0 flex-1 truncate text-left", children: translate('shell.sidebar.collapseLabel') })] })) })] })] }));
|
|
103
|
+
}), issueViews.map((view) => (_jsx(ViewIssuesProbe, { view: view, project: project }, view.id))), _jsxs("div", { className: "mt-auto flex flex-col gap-0.5", children: [issues.length > 0 ? _jsx(NavIssues, { issues: issues, collapsed: collapsed }) : null, _jsx("button", { type: "button", onClick: onToggleCollapsed, "aria-label": collapsed ? translate('shell.sidebar.expand') : translate('shell.sidebar.collapse'), "aria-pressed": collapsed, title: collapsed ? translate('shell.sidebar.expand') : translate('shell.sidebar.collapse'), className: cn('flex h-9 items-center gap-2.5 rounded-md text-sm text-muted-foreground transition-colors hover:bg-muted hover:text-foreground', collapsed ? 'justify-center px-0' : 'px-3'), children: collapsed ? (_jsx(PanelLeftOpen, { className: "size-4 shrink-0" })) : (_jsxs(_Fragment, { children: [_jsx(PanelLeftClose, { className: "size-4 shrink-0" }), _jsx("span", { className: "min-w-0 flex-1 truncate text-left", children: translate('shell.sidebar.collapseLabel') })] })) })] })] }));
|
|
97
104
|
}
|
|
98
105
|
/**
|
|
99
106
|
* EC-479 — what the project has not declared but the studio cannot work without.
|
|
@@ -150,7 +157,7 @@ function useReuseIssues() {
|
|
|
150
157
|
* {@link routeIssueItems} — severity included since EC-493.
|
|
151
158
|
*/
|
|
152
159
|
function useRouteIssues(projectId) {
|
|
153
|
-
const redirects =
|
|
160
|
+
const redirects = useRecordList(redirectRecordKind, projectId).data;
|
|
154
161
|
return useMemo(() => routeIssueItems(routeValidationIssues({ routes: resolveDeploymentRoutes(), collections: resolveDeploymentCollections() }, redirects ?? [])), [redirects]);
|
|
155
162
|
}
|
|
156
163
|
/**
|
|
@@ -174,6 +181,15 @@ function useOrphanedCollectionIssues() {
|
|
|
174
181
|
const orphans = useSyncExternalStore(subscribeContentSession, contentSessionOrphans, serverContentSessionOrphans);
|
|
175
182
|
return useMemo(() => orphanedCollectionItems(orphans), [orphans]);
|
|
176
183
|
}
|
|
184
|
+
/**
|
|
185
|
+
* EC-616 — runs one view's always-on findings hook (`useIssues`) and publishes what it
|
|
186
|
+
* returns to the foot. One component per view, so each hook keeps its own hook order.
|
|
187
|
+
*/
|
|
188
|
+
function ViewIssuesProbe({ view, project }) {
|
|
189
|
+
const issues = view.useIssues({ project, projectId: project.id });
|
|
190
|
+
useNavIssues(`view:`, issues);
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
177
193
|
/** One rail view (top-level or nested) — a Link to its home route, active by view id. */
|
|
178
194
|
function NavViewLink({ projectId, view, activeViewId, collapsed, nested = false, hasActiveChild = false, collapsible = false, expanded = true, onToggleExpanded, }) {
|
|
179
195
|
const isActive = view.id === activeViewId;
|