@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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transfer-dialog.js","sourceRoot":"","sources":["../../../src/components/components/transfer-dialog.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useMemo, useState } from 'react'\nimport { FilePlus2, FileText, Loader2, Search } from 'lucide-react'\nimport type { ReactNode } from 'react'\nimport type { ComponentManifest } from '@elytracms/core/component-registry'\nimport type { CmsDocument, DocumentPatch } from '@elytracms/core/cms-core'\nimport type { ComponentNode } from '@elytracms/core/project-graph'\nimport { STUDIO_PROJECT_ROUTE, studioPath, useNavigate } from '@elytracms/studio/router'\nimport {\n Button,\n Code,\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n Input,\n Row,\n Stack,\n Text,\n} from '@elytracms/studio/ui'\nimport { cn } from '@elytracms/studio/lib/utils'\nimport { localizedLabel, translate } from '@elytracms/studio/lib/i18n'\nimport { CmsSessionGate } from '../project-session'\nimport { CmsProvider, useCms } from '../cms/cms-store'\nimport { stageDocument } from '../cms/document-staging'\nimport { resolveCmsInitialCollections } from '../app-views/default-modules'\nimport {\n appendToBlockDocument,\n buildBlockDocument,\n canAppendTo,\n collectionsWithBlocks,\n isComponentAllowed,\n} from './transfer'\nimport type { CollectionBlocksTarget } from './transfer'\n\n/** Existing documents offered per target, beyond which the rest are hidden (filter to find them). */\nconst MAX_EXISTING_TARGETS = 8\n\n/**\n * EC-320 (+ 0.0.44 follow-up) — \"Use in document…\". Transfers the current\n * component combination (the live preview {@link ComponentNode}, edited props\n * and all) into a document's blocks field, then opens that document. Each\n * eligible collection+field target offers a NEW draft AND its existing\n * documents to append into.\n *\n * The dialog mounts a CMS session (so it reuses the store's create/save machinery,\n * collision-free ids and persistence) only while open. On confirm it builds the\n * draft or the append (pure {@link buildBlockDocument} / {@link appendToBlockDocument}),\n * saves it and AWAITS the persisted write landing (belt and braces alongside the\n * STAGE for the next CMS mount, see `document-staging` — both matter: staging\n * covers the very next mount regardless of mode, the await guards any later\n * reload), then navigates to the document — focused on its blocks field so it\n * opens on the canvas.\n */\nexport function TransferToDocumentDialog({\n projectId,\n manifest,\n node,\n open,\n onOpenChange,\n}: {\n projectId: string\n manifest: ComponentManifest\n node: ComponentNode\n open: boolean\n onOpenChange: (open: boolean) => void\n}) {\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent className=\"max-w-md\">\n <DialogHeader>\n <DialogTitle>\n {translate('components.transfer.title', { name: localizedLabel(manifest.title, manifest.id) })}\n </DialogTitle>\n <DialogDescription>{translate('components.transfer.description')}</DialogDescription>\n </DialogHeader>\n {open ? (\n <CmsSessionGate>\n {(cms) => {\n // Delegate to the shared, tested resolver (also used by the CMS/Assets\n // tabs) so this dialog's targets stay in sync with what those tabs show,\n // rather than reimplementing the ladder inline. (EC-479 removed the\n // reason the old inline version was subtly wrong: `CmsProvider` had a\n // `?? sampleFilterableCollections` fixture fallback that an inline `[]`\n // \"suppressed\" — there is no fixture fallback to suppress now, so the\n // distinction between `[]` and `undefined` no longer carries meaning.)\n const collections = resolveCmsInitialCollections(cms)\n return (\n <CmsProvider\n projectId={projectId}\n initialCollections={collections}\n initialDocuments={cms?.documents}\n initialPublishedKeys={cms?.publishedKeys}\n initialRevisions={cms?.revisions}\n // EC-461: hydrated seed (both modes now) supersedes staged docs.\n seedReflectsWrites={cms !== null}\n >\n <TransferPicker projectId={projectId} node={node} onClose={() => onOpenChange(false)} />\n </CmsProvider>\n )\n }}\n </CmsSessionGate>\n ) : null}\n </DialogContent>\n </Dialog>\n )\n}\n\n/** A pending transfer's in-flight target key: `<collectionId>/new` or `<collectionId>/<docId>`. */\nfunction newTargetKey(target: CollectionBlocksTarget): string {\n return `${target.collection.id}/new`\n}\nfunction existingTargetKey(target: CollectionBlocksTarget, doc: CmsDocument): string {\n return `${target.collection.id}/${doc.id}`\n}\n\n/**\n * Exported for the fresh-Convex-project regression test (bug fix,\n * transfer-dialog.test.tsx) — `TransferToDocumentDialog` is otherwise its only\n * caller.\n */\nexport function TransferPicker({\n projectId,\n node,\n onClose,\n}: {\n projectId: string\n node: ComponentNode\n onClose: () => void\n}) {\n const cms = useCms()\n const navigate = useNavigate()\n const [query, setQuery] = useState('')\n // The in-flight target's key while its save is awaited (disables every row\n // + shows a spinner on the clicked one) — `null` when idle.\n const [pendingKey, setPendingKey] = useState<string | null>(null)\n\n // Read the LIVE store schema (`cms.collections`) rather than re-deriving it\n // from the same `resolveCmsInitialCollections` result passed to `CmsProvider`\n // above — `cms.collections` is what the provider actually resolved to, so the\n // targets list can never drift from what the CMS/Assets tabs show for this\n // same session. Allow-gated (0.0.44 follow-up): a target\n // whose field's `allow` vocabulary rejects this component is never offered,\n // new-document or existing-document alike — the validator would flag it.\n const targets = useMemo(\n () => collectionsWithBlocks(cms.collections).filter((t) => isComponentAllowed(t.field, node.componentId)),\n [cms.collections, node.componentId],\n )\n\n /**\n * Persist (awaited) + stage + navigate — shared by both transfer paths. EC-595 A2: a\n * NEW document is one insert-if-absent create; an EXISTING one gets the transfer's\n * patch (one `insert` of the embed node at the end of its field) — never a\n * whole-document write, so a concurrent edit of that document is never overwritten.\n */\n async function persistAndOpen(\n doc: CmsDocument,\n target: CollectionBlocksTarget,\n patches?: readonly DocumentPatch[],\n ) {\n if (patches) await cms.commitPatches({ collection: doc.collection, id: doc.id }, patches)\n else cms.insertDocument(doc)\n // AWAIT the queued write landing, so a reload of the destination editor — not just\n // its very next mount — finds the document.\n await cms.flushPersistence({ collection: doc.collection, id: doc.id })\n // Cross-mount handoff for the very next `CmsProvider` mount, in EVERY mode (see\n // document-staging.ts): the session's view of the written document.\n stageDocument(projectId, cms.getDocument({ collection: doc.collection, id: doc.id }) ?? doc)\n onClose()\n void navigate({\n to: STUDIO_PROJECT_ROUTE,\n params: { projectId, _splat: studioPath.cmsDocument(target.collection.id, doc.id) },\n search: { field: `values/${target.field.name}` },\n })\n }\n\n async function transferNew(target: CollectionBlocksTarget) {\n const key = newTargetKey(target)\n setPendingKey(key)\n try {\n const doc = buildBlockDocument({\n collection: target.collection,\n blocksField: target.field,\n node,\n newId: cms.newDocumentId,\n locales: cms.localeConfig,\n })\n await persistAndOpen(doc, target)\n } finally {\n setPendingKey(null)\n }\n }\n\n async function transferInto(target: CollectionBlocksTarget, existing: CmsDocument) {\n const key = existingTargetKey(target, existing)\n setPendingKey(key)\n try {\n // The FORCED FLUSH before the transfer reads the document (the ruling).\n await cms.flushPersistence({ collection: existing.collection, id: existing.id })\n const current = cms.getDocument({ collection: existing.collection, id: existing.id }) ?? existing\n const { document: doc, patches } = appendToBlockDocument({\n collection: target.collection,\n document: current,\n blocksField: target.field,\n node,\n locale: cms.activeLocale,\n newId: cms.newDocumentId,\n })\n await persistAndOpen(doc, target, patches)\n } finally {\n setPendingKey(null)\n }\n }\n\n if (targets.length === 0) {\n return (\n <Text size=\"sm\" tone=\"muted\" data-testid=\"transfer-no-targets\">\n {translate('components.transfer.noTargets')}\n </Text>\n )\n }\n\n const trimmedQuery = query.trim().toLowerCase()\n const hasAnyExistingDocs = targets.some((t) => cms.documentsOf(t.collection.id).length > 0)\n\n return (\n <>\n {hasAnyExistingDocs ? (\n <div className=\"relative\">\n <Search className=\"pointer-events-none absolute left-2.5 top-1/2 size-3.5 -translate-y-1/2 text-muted-foreground\" />\n <Input\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n placeholder={translate('common.searchPlaceholder')}\n className=\"pl-8\"\n />\n </div>\n ) : null}\n <Stack gap=\"4\" className=\"max-h-[50vh] overflow-y-auto\">\n {targets.map((target) => {\n const existing = cms\n .documentsOf(target.collection.id)\n .filter((doc) => canAppendTo({ collection: target.collection, document: doc, blocksField: target.field, locale: cms.activeLocale }))\n .filter(\n (doc) =>\n trimmedQuery === '' ||\n cms.titleOf({ collection: target.collection.id, id: doc.id }).toLowerCase().includes(trimmedQuery),\n )\n .slice(0, MAX_EXISTING_TARGETS)\n\n return (\n <Stack key={target.collection.id} gap=\"2\">\n <Stack gap=\"px\">\n <Text size=\"sm\" weight=\"medium\" className=\"truncate\">\n {localizedLabel(target.collection.form?.label, target.collection.id)}\n </Text>\n <Row gap=\"1\" align=\"center\">\n <Text size=\"xs\" tone=\"muted\">\n {translate('components.transfer.intoField')}\n </Text>\n <Code tone=\"muted\">{target.field.name}</Code>\n </Row>\n </Stack>\n <Stack gap=\"1\">\n <TransferTargetRow\n testId=\"transfer-target\"\n icon={<FilePlus2 className=\"size-4\" />}\n label={translate('components.transfer.newDocument', {\n collection: localizedLabel(target.collection.form?.labelSingular, target.collection.id),\n })}\n pending={pendingKey === newTargetKey(target)}\n disabled={pendingKey !== null}\n onClick={() => void transferNew(target)}\n />\n {existing.length > 0 ? (\n <Text size=\"xs\" tone=\"muted\" className=\"px-1 pt-1\">\n {translate('components.transfer.orExisting')}\n </Text>\n ) : null}\n {existing.map((doc) => (\n <TransferTargetRow\n key={doc.id}\n testId=\"transfer-target-existing\"\n icon={<FileText className=\"size-4\" />}\n label={cms.titleOf({ collection: target.collection.id, id: doc.id })}\n pending={pendingKey === existingTargetKey(target, doc)}\n disabled={pendingKey !== null}\n onClick={() => void transferInto(target, doc)}\n />\n ))}\n </Stack>\n </Stack>\n )\n })}\n </Stack>\n <DialogFooter>\n <Button type=\"button\" variant=\"ghost\" onClick={onClose} disabled={pendingKey !== null}>\n {translate('common.cancel')}\n </Button>\n </DialogFooter>\n </>\n )\n}\n\n/** One selectable transfer target row — a new draft, or an existing document to append into. */\nfunction TransferTargetRow({\n testId,\n icon,\n label,\n pending,\n disabled,\n onClick,\n}: {\n testId: string\n icon: ReactNode\n label: string\n pending: boolean\n disabled: boolean\n onClick: () => void\n}) {\n return (\n <button\n type=\"button\"\n data-testid={testId}\n disabled={disabled}\n onClick={onClick}\n className={cn(\n 'flex items-center gap-3 rounded-md border border-border p-3 text-left transition-colors hover:border-primary/40 hover:bg-muted',\n disabled && 'cursor-not-allowed opacity-60',\n )}\n >\n <span className=\"grid size-8 shrink-0 place-items-center rounded-md bg-muted text-muted-foreground\">\n {pending ? <Loader2 className=\"size-4 animate-spin\" /> : icon}\n </span>\n <Text size=\"sm\" weight=\"medium\" className=\"min-w-0 flex-1 truncate\">\n {label}\n </Text>\n </button>\n )\n}\n"]}
|
|
1
|
+
{"version":3,"file":"transfer-dialog.js","sourceRoot":"","sources":["../../../src/components/components/transfer-dialog.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useMemo, useState } from 'react'\nimport { FilePlus2, FileText, Loader2, Search } from 'lucide-react'\nimport type { ReactNode } from 'react'\nimport type { ComponentManifest } from '@elytracms/core/component-registry'\nimport type { CmsDocument, DocumentPatch } from '@elytracms/core/cms-core'\nimport type { ComponentNode } from '@elytracms/core/project-graph'\nimport { STUDIO_PROJECT_ROUTE, studioPath, useNavigate } from '@elytracms/studio/router'\nimport {\n Button,\n Code,\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n Input,\n Row,\n Stack,\n Text,\n} from '@elytracms/studio/ui'\nimport { cn } from '../../lib/utils'\nimport { getStudioRegistry } from '../../lib/registry'\nimport { localizedLabel, translate } from '../../lib/i18n'\nimport { CmsSessionGate } from '../project-session'\nimport { CmsProvider, useCms } from '../cms/cms-store'\nimport { stageDocument } from '../cms/document-staging'\nimport { resolveCmsInitialCollections } from '../app-views/default-modules'\nimport {\n appendToBlockDocument,\n buildBlockDocument,\n canAppendTo,\n collectionsWithBlocks,\n isComponentAllowed,\n} from './transfer'\nimport type { CollectionBlocksTarget } from './transfer'\n\n/** Existing documents offered per target, beyond which the rest are hidden (filter to find them). */\nconst MAX_EXISTING_TARGETS = 8\n\n/**\n * EC-320 (+ 0.0.44 follow-up) — \"Use in document…\". Transfers the current\n * component combination (the live preview {@link ComponentNode}, edited props\n * and all) into a document's blocks field, then opens that document. Each\n * eligible collection+field target offers a NEW draft AND its existing\n * documents to append into.\n *\n * The dialog mounts a CMS session (so it reuses the store's create/save machinery,\n * collision-free ids and persistence) only while open. On confirm it builds the\n * draft or the append (pure {@link buildBlockDocument} / {@link appendToBlockDocument}),\n * saves it and AWAITS the persisted write landing (belt and braces alongside the\n * STAGE for the next CMS mount, see `document-staging` — both matter: staging\n * covers the very next mount regardless of mode, the await guards any later\n * reload), then navigates to the document — focused on its blocks field so it\n * opens on the canvas.\n */\nexport function TransferToDocumentDialog({\n projectId,\n manifest,\n node,\n open,\n onOpenChange,\n}: {\n projectId: string\n manifest: ComponentManifest\n node: ComponentNode\n open: boolean\n onOpenChange: (open: boolean) => void\n}) {\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent className=\"max-w-md\">\n <DialogHeader>\n <DialogTitle>\n {translate('components.transfer.title', { name: localizedLabel(manifest.title, manifest.id) })}\n </DialogTitle>\n <DialogDescription>{translate('components.transfer.description')}</DialogDescription>\n </DialogHeader>\n {open ? (\n <CmsSessionGate>\n {(cms) => {\n // Delegate to the shared, tested resolver (also used by the CMS/Assets\n // tabs) so this dialog's targets stay in sync with what those tabs show,\n // rather than reimplementing the ladder inline. (EC-479 removed the\n // reason the old inline version was subtly wrong: `CmsProvider` had a\n // `?? sampleFilterableCollections` fixture fallback that an inline `[]`\n // \"suppressed\" — there is no fixture fallback to suppress now, so the\n // distinction between `[]` and `undefined` no longer carries meaning.)\n const collections = resolveCmsInitialCollections(cms)\n return (\n <CmsProvider\n projectId={projectId}\n initialCollections={collections}\n initialDocuments={cms?.documents}\n initialPublishedKeys={cms?.publishedKeys}\n initialRevisions={cms?.revisions}\n // EC-461: hydrated seed (both modes now) supersedes staged docs.\n seedReflectsWrites={cms !== null}\n >\n <TransferPicker projectId={projectId} node={node} onClose={() => onOpenChange(false)} />\n </CmsProvider>\n )\n }}\n </CmsSessionGate>\n ) : null}\n </DialogContent>\n </Dialog>\n )\n}\n\n/** A pending transfer's in-flight target key: `<collectionId>/new` or `<collectionId>/<docId>`. */\nfunction newTargetKey(target: CollectionBlocksTarget): string {\n return `${target.collection.id}/new`\n}\nfunction existingTargetKey(target: CollectionBlocksTarget, doc: CmsDocument): string {\n return `${target.collection.id}/${doc.id}`\n}\n\n/**\n * Exported for the fresh-Convex-project regression test (bug fix,\n * transfer-dialog.test.tsx) — `TransferToDocumentDialog` is otherwise its only\n * caller.\n */\nexport function TransferPicker({\n projectId,\n node,\n onClose,\n}: {\n projectId: string\n node: ComponentNode\n onClose: () => void\n}) {\n const cms = useCms()\n const navigate = useNavigate()\n const [query, setQuery] = useState('')\n // The in-flight target's key while its save is awaited (disables every row\n // + shows a spinner on the clicked one) — `null` when idle.\n const [pendingKey, setPendingKey] = useState<string | null>(null)\n\n // Read the LIVE store schema (`cms.collections`) rather than re-deriving it\n // from the same `resolveCmsInitialCollections` result passed to `CmsProvider`\n // above — `cms.collections` is what the provider actually resolved to, so the\n // targets list can never drift from what the CMS/Assets tabs show for this\n // same session. Allow-gated (0.0.44 follow-up): a target\n // whose field's `allow` vocabulary rejects this component is never offered,\n // new-document or existing-document alike — the validator would flag it.\n const targets = useMemo(\n () =>\n collectionsWithBlocks(cms.collections).filter((t) =>\n isComponentAllowed(t.field, node.componentId, getStudioRegistry().registry.lookup),\n ),\n [cms.collections, node.componentId],\n )\n\n /**\n * Persist (awaited) + stage + navigate — shared by both transfer paths. EC-595 A2: a\n * NEW document is one insert-if-absent create; an EXISTING one gets the transfer's\n * patch (one `insert` of the embed node at the end of its field) — never a\n * whole-document write, so a concurrent edit of that document is never overwritten.\n */\n async function persistAndOpen(\n doc: CmsDocument,\n target: CollectionBlocksTarget,\n patches?: readonly DocumentPatch[],\n ) {\n if (patches) await cms.commitPatches({ collection: doc.collection, id: doc.id }, patches)\n else cms.insertDocument(doc)\n // AWAIT the queued write landing, so a reload of the destination editor — not just\n // its very next mount — finds the document.\n await cms.flushPersistence({ collection: doc.collection, id: doc.id })\n // Cross-mount handoff for the very next `CmsProvider` mount, in EVERY mode (see\n // document-staging.ts): the session's view of the written document.\n stageDocument(projectId, cms.getDocument({ collection: doc.collection, id: doc.id }) ?? doc)\n onClose()\n void navigate({\n to: STUDIO_PROJECT_ROUTE,\n params: { projectId, _splat: studioPath.cmsDocument(target.collection.id, doc.id) },\n search: { field: `values/${target.field.name}` },\n })\n }\n\n async function transferNew(target: CollectionBlocksTarget) {\n const key = newTargetKey(target)\n setPendingKey(key)\n try {\n const doc = buildBlockDocument({\n collection: target.collection,\n blocksField: target.field,\n node,\n newId: cms.newDocumentId,\n locales: cms.localeConfig,\n })\n await persistAndOpen(doc, target)\n } finally {\n setPendingKey(null)\n }\n }\n\n async function transferInto(target: CollectionBlocksTarget, existing: CmsDocument) {\n const key = existingTargetKey(target, existing)\n setPendingKey(key)\n try {\n // The FORCED FLUSH before the transfer reads the document (the ruling).\n await cms.flushPersistence({ collection: existing.collection, id: existing.id })\n const current = cms.getDocument({ collection: existing.collection, id: existing.id }) ?? existing\n const { document: doc, patches } = appendToBlockDocument({\n collection: target.collection,\n document: current,\n blocksField: target.field,\n node,\n locale: cms.activeLocale,\n newId: cms.newDocumentId,\n })\n await persistAndOpen(doc, target, patches)\n } finally {\n setPendingKey(null)\n }\n }\n\n if (targets.length === 0) {\n return (\n <Text size=\"sm\" tone=\"muted\" data-testid=\"transfer-no-targets\">\n {translate('components.transfer.noTargets')}\n </Text>\n )\n }\n\n const trimmedQuery = query.trim().toLowerCase()\n const hasAnyExistingDocs = targets.some((t) => cms.documentsOf(t.collection.id).length > 0)\n\n return (\n <>\n {hasAnyExistingDocs ? (\n <div className=\"relative\">\n <Search className=\"pointer-events-none absolute left-2.5 top-1/2 size-3.5 -translate-y-1/2 text-muted-foreground\" />\n <Input\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n placeholder={translate('common.searchPlaceholder')}\n className=\"pl-8\"\n />\n </div>\n ) : null}\n <Stack gap=\"4\" className=\"max-h-[50vh] overflow-y-auto\">\n {targets.map((target) => {\n const existing = cms\n .documentsOf(target.collection.id)\n .filter((doc) => canAppendTo({ collection: target.collection, document: doc, blocksField: target.field, locale: cms.activeLocale }))\n .filter(\n (doc) =>\n trimmedQuery === '' ||\n cms.titleOf({ collection: target.collection.id, id: doc.id }).toLowerCase().includes(trimmedQuery),\n )\n .slice(0, MAX_EXISTING_TARGETS)\n\n return (\n <Stack key={target.collection.id} gap=\"2\">\n <Stack gap=\"px\">\n <Text size=\"sm\" weight=\"medium\" className=\"truncate\">\n {localizedLabel(target.collection.form?.label, target.collection.id)}\n </Text>\n <Row gap=\"1\" align=\"center\">\n <Text size=\"xs\" tone=\"muted\">\n {translate('components.transfer.intoField')}\n </Text>\n <Code tone=\"muted\">{target.field.name}</Code>\n </Row>\n </Stack>\n <Stack gap=\"1\">\n <TransferTargetRow\n testId=\"transfer-target\"\n icon={<FilePlus2 className=\"size-4\" />}\n label={translate('components.transfer.newDocument', {\n collection: localizedLabel(target.collection.form?.labelSingular, target.collection.id),\n })}\n pending={pendingKey === newTargetKey(target)}\n disabled={pendingKey !== null}\n onClick={() => void transferNew(target)}\n />\n {existing.length > 0 ? (\n <Text size=\"xs\" tone=\"muted\" className=\"px-1 pt-1\">\n {translate('components.transfer.orExisting')}\n </Text>\n ) : null}\n {existing.map((doc) => (\n <TransferTargetRow\n key={doc.id}\n testId=\"transfer-target-existing\"\n icon={<FileText className=\"size-4\" />}\n label={cms.titleOf({ collection: target.collection.id, id: doc.id })}\n pending={pendingKey === existingTargetKey(target, doc)}\n disabled={pendingKey !== null}\n onClick={() => void transferInto(target, doc)}\n />\n ))}\n </Stack>\n </Stack>\n )\n })}\n </Stack>\n <DialogFooter>\n <Button type=\"button\" variant=\"ghost\" onClick={onClose} disabled={pendingKey !== null}>\n {translate('common.cancel')}\n </Button>\n </DialogFooter>\n </>\n )\n}\n\n/** One selectable transfer target row — a new draft, or an existing document to append into. */\nfunction TransferTargetRow({\n testId,\n icon,\n label,\n pending,\n disabled,\n onClick,\n}: {\n testId: string\n icon: ReactNode\n label: string\n pending: boolean\n disabled: boolean\n onClick: () => void\n}) {\n return (\n <button\n type=\"button\"\n data-testid={testId}\n disabled={disabled}\n onClick={onClick}\n className={cn(\n 'flex items-center gap-3 rounded-md border border-border p-3 text-left transition-colors hover:border-primary/40 hover:bg-muted',\n disabled && 'cursor-not-allowed opacity-60',\n )}\n >\n <span className=\"grid size-8 shrink-0 place-items-center rounded-md bg-muted text-muted-foreground\">\n {pending ? <Loader2 className=\"size-4 animate-spin\" /> : icon}\n </span>\n <Text size=\"sm\" weight=\"medium\" className=\"min-w-0 flex-1 truncate\">\n {label}\n </Text>\n </button>\n )\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { BlocksFieldDef, CmsDocument, CollectionDef, DocumentPatch, Locale, LocaleConfig } from '@elytracms/core/cms-core';
|
|
2
|
-
import type { ComponentNode } from '@elytracms/core/project-graph';
|
|
2
|
+
import type { ComponentLookup, ComponentNode } from '@elytracms/core/project-graph';
|
|
3
3
|
import type { IdFactory } from '@elytracms/core/persistence';
|
|
4
4
|
/**
|
|
5
5
|
* EC-320 (+ 0.0.44 follow-up) — the "design guide → real content" bridge. A
|
|
@@ -27,16 +27,16 @@ export interface CollectionBlocksTarget {
|
|
|
27
27
|
*/
|
|
28
28
|
export declare function collectionsWithBlocks(collections: readonly CollectionDef[]): CollectionBlocksTarget[];
|
|
29
29
|
/**
|
|
30
|
-
* Is `componentId` permitted as a top-level block of `field`?
|
|
31
|
-
*
|
|
32
|
-
* `allow
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* targets the component isn't allowed into (new AND existing
|
|
37
|
-
* alike).
|
|
30
|
+
* Is `componentId` permitted as a top-level block of `field`? EC-584: a READER of the
|
|
31
|
+
* one core placement judge over the field's effective vocabulary (`fieldAllowList` —
|
|
32
|
+
* its `allow`, else the blocks default), the verdict `validateCompositionValue`
|
|
33
|
+
* reaches at the gate — so a target this predicate accepts can never fail its
|
|
34
|
+
* `forbidden-component` / `server-only-component` check. With `components` (the
|
|
35
|
+
* project's manifest lookup) canvas eligibility is judged too. The transfer dialog
|
|
36
|
+
* uses this to hide targets the component isn't allowed into (new AND existing
|
|
37
|
+
* document targets alike); the value-level sinks stay on the gate.
|
|
38
38
|
*/
|
|
39
|
-
export declare function isComponentAllowed(field: BlocksFieldDef, componentId: string): boolean;
|
|
39
|
+
export declare function isComponentAllowed(field: BlocksFieldDef, componentId: string, components?: ComponentLookup): boolean;
|
|
40
40
|
export interface BuildBlockDocumentInput {
|
|
41
41
|
collection: CollectionDef;
|
|
42
42
|
/** The blocks field the node lands in (from {@link collectionsWithBlocks}). */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { fieldPath, isBlocksField, isFieldLocalized, isSequenceValue, itemLimit, seedDefaults, writeField, } from '@elytracms/core/cms-core';
|
|
2
|
-
import { blockRootsOf, editBlockRoots } from '@elytracms/core/project-graph';
|
|
1
|
+
import { fieldAllowList, fieldPath, isBlocksField, isFieldLocalized, isSequenceValue, itemLimit, seedDefaults, writeField, } from '@elytracms/core/cms-core';
|
|
2
|
+
import { ANY_COMPONENT, blockRootsOf, editBlockRoots, placementRefusal } from '@elytracms/core/project-graph';
|
|
3
3
|
import { embedNodeFrom } from './component-preview.js';
|
|
4
4
|
/**
|
|
5
5
|
* Collections that can receive a transferred combination — those declaring a
|
|
@@ -19,17 +19,18 @@ export function collectionsWithBlocks(collections) {
|
|
|
19
19
|
return targets;
|
|
20
20
|
}
|
|
21
21
|
/**
|
|
22
|
-
* Is `componentId` permitted as a top-level block of `field`?
|
|
23
|
-
*
|
|
24
|
-
* `allow
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* targets the component isn't allowed into (new AND existing
|
|
29
|
-
* alike).
|
|
22
|
+
* Is `componentId` permitted as a top-level block of `field`? EC-584: a READER of the
|
|
23
|
+
* one core placement judge over the field's effective vocabulary (`fieldAllowList` —
|
|
24
|
+
* its `allow`, else the blocks default), the verdict `validateCompositionValue`
|
|
25
|
+
* reaches at the gate — so a target this predicate accepts can never fail its
|
|
26
|
+
* `forbidden-component` / `server-only-component` check. With `components` (the
|
|
27
|
+
* project's manifest lookup) canvas eligibility is judged too. The transfer dialog
|
|
28
|
+
* uses this to hide targets the component isn't allowed into (new AND existing
|
|
29
|
+
* document targets alike); the value-level sinks stay on the gate.
|
|
30
30
|
*/
|
|
31
|
-
export function isComponentAllowed(field, componentId) {
|
|
32
|
-
return
|
|
31
|
+
export function isComponentAllowed(field, componentId, components) {
|
|
32
|
+
return (placementRefusal({ componentId, view: components?.get(componentId), allow: fieldAllowList(field) ?? ANY_COMPONENT }) ===
|
|
33
|
+
null);
|
|
33
34
|
}
|
|
34
35
|
/**
|
|
35
36
|
* Re-embed `node` under a fresh id from the injected factory (EC-578 / EC-595 A2): UUIDs
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transfer.js","sourceRoot":"","sources":["../../../src/components/components/transfer.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n fieldPath,\n isBlocksField,\n isFieldLocalized,\n isSequenceValue,\n itemLimit,\n seedDefaults,\n writeField,\n} from '@elytracms/core/cms-core'\nimport type {\n BlocksFieldDef,\n CmsDocument,\n CollectionDef,\n DocumentPatch,\n Locale,\n LocaleConfig,\n} from '@elytracms/core/cms-core'\nimport { blockRootsOf, editBlockRoots } from '@elytracms/core/project-graph'\nimport type { ComponentNode } from '@elytracms/core/project-graph'\nimport type { IdFactory } from '@elytracms/core/persistence'\nimport { embedNodeFrom } from './component-preview'\n\n/**\n * EC-320 (+ 0.0.44 follow-up) — the \"design guide → real content\" bridge. A\n * component combination the user has arranged in the Components view (a\n * manifest + edited prop values, captured as a {@link ComponentNode}) is\n * transferred into a document's blocks/composition field — either a NEW DRAFT\n * document created for it ({@link buildBlockDocument}) or an EXISTING\n * document's field, appended alongside whatever it already holds\n * ({@link appendToBlockDocument}). Pure so id-minting/embedding/appending are\n * unit-testable; the React dialog owns the picker, persistence, and\n * navigation.\n */\n\nexport interface CollectionBlocksTarget {\n collection: CollectionDef\n /** The (first) blocks field the node lands in. */\n field: BlocksFieldDef\n}\n\n/**\n * Collections that can receive a transferred combination — those declaring a\n * `blocks` (composition) field. The first blocks field wins (a collection with a\n * single canvas is the norm; a second is an unusual edge we don't disambiguate).\n * Callers additionally gate each target through {@link isComponentAllowed} —\n * this only reports which collections HAVE a blocks field, not which ones\n * accept a given component.\n */\nexport function collectionsWithBlocks(\n collections: readonly CollectionDef[],\n): CollectionBlocksTarget[] {\n const targets: CollectionBlocksTarget[] = []\n for (const collection of collections) {\n const field = collection.fields.find(isBlocksField)\n if (field) targets.push({ collection, field })\n }\n return targets\n}\n\n/**\n * Is `componentId` permitted as a top-level block of `field`? Mirrors\n * `validateCompositionValue`'s (`@elytracms/core/project-graph`) top-level\n * `allow` rule EXACTLY (EC-544): an absent list permits any registered\n * component; a list is the closed vocabulary (an empty one permits nothing).\n * A target this predicate accepts can never fail that validator's\n * `forbidden-component` check on save — the transfer dialog uses this to hide\n * targets the component isn't allowed into (new AND existing document targets\n * alike).\n */\nexport function isComponentAllowed(field: BlocksFieldDef, componentId: string): boolean {\n return !field.allow || field.allow.includes(componentId)\n}\n\n/**\n * Re-embed `node` under a fresh id from the injected factory (EC-578 / EC-595 A2): UUIDs\n * in the studio, so an embed appended into a document can never collide with an id\n * already in the tree — or with one another session appends at the same time (the old\n * `node-<n>` counter seeded past the tree's ids minted the same id in both).\n */\nfunction embedFreshNode(node: ComponentNode, newId: IdFactory): ComponentNode {\n return embedNodeFrom(node, newId())\n}\n\nexport interface BuildBlockDocumentInput {\n collection: CollectionDef\n /** The blocks field the node lands in (from {@link collectionsWithBlocks}). */\n blocksField: BlocksFieldDef\n /** The component node — a manifest id + the edited static props (+ slots). */\n node: ComponentNode\n /** THE id factory (EC-578) — mints the new document's id and the embed's node id. */\n newId: IdFactory\n /** The project's locale config — a new document is written in its default locale (EC-580). */\n locales: LocaleConfig\n}\n\n/**\n * Build the draft document that embeds `node` in the collection's blocks field.\n * Reuses the CMS create machinery: the injected id factory (EC-578) for the id\n * and {@link seedDefaults} so declared field defaults are present (as a\n * normal `+ New` would be). The field stores the converged v2 sequence holding\n * the one tagged root, written through the core write path (`editBlockRoots`,\n * EC-576) — cardinality is a constraint (`validation.max`), not a shape (EC-500 D2,\n * EC-581). The\n * transferred node REPLACES whatever the field's declared default seeded, as it\n * always has.\n *\n * EC-580: both writes go through core's `writeField` in the project's DEFAULT\n * locale — a new document is created in its default bucket, so a localized blocks\n * field holds the node under `localized[default]`, never on the shared bag.\n */\nexport function buildBlockDocument({\n collection,\n blocksField,\n node,\n newId,\n locales,\n}: BuildBlockDocumentInput): CmsDocument {\n const seeded: CmsDocument = { collection: collection.id, id: newId(), ...seedDefaults(collection, locales) }\n return writeField(\n collection,\n seeded,\n blocksField.name,\n locales.default,\n editBlockRoots(undefined, () => [embedFreshNode(node, newId)]),\n )\n}\n\n/** The blocks field's current value on `document`, respecting locale (EC-018). */\nfunction readBlocksValue(\n collection: CollectionDef,\n document: CmsDocument,\n blocksField: BlocksFieldDef,\n locale: Locale,\n): unknown {\n return isFieldLocalized(collection, blocksField.name)\n ? document.localized?.[locale]?.[blocksField.name]\n : document.values[blocksField.name]\n}\n\nexport interface CanAppendToInput {\n collection: CollectionDef\n document: CmsDocument\n blocksField: BlocksFieldDef\n /** The locale being edited (EC-176 ambient active locale) for a localized field. */\n locale: Locale\n}\n\n/**\n * Can `node` be appended to `document`'s blocks field without destroying\n * existing content? Always true without a declared item limit (an ordered list\n * has room for one more); a field limited by `validation.max` (EC-581: `max: 1` is\n * the single-block field `multiple: false` used to spell) only while it is below\n * the limit — at it, there is no room without overwriting an existing block, so\n * the transfer dialog excludes that document from its offered targets rather than\n * risk data loss.\n */\nexport function canAppendTo({ collection, document, blocksField, locale }: CanAppendToInput): boolean {\n const limit = itemLimit(blocksField)\n if (limit === undefined) return true\n return blockRootsOf(readBlocksValue(collection, document, blocksField, locale)).length < limit\n}\n\nexport interface AppendToBlockDocumentInput {\n collection: CollectionDef\n /** The document to append into (the transfer dialog's \"insert into existing document\"). */\n document: CmsDocument\n blocksField: BlocksFieldDef\n /** The component node to append — not yet re-ided (fresh id minted here). */\n node: ComponentNode\n /** The locale being edited (EC-176 ambient active locale) for a localized field. */\n locale: Locale\n /** THE id factory (EC-578) — mints the embed's node id. */\n newId: IdFactory\n}\n\n/** The appended document AND the patch that says so (EC-595: the writer emits its patch at the source). */\nexport interface AppendToBlockDocumentResult {\n document: CmsDocument\n /**\n * ONE `insert` of the new embed node at the end of the field's top-level sequence,\n * anchored after the last existing component root — what is persisted\n * (`documents.patch`), so a concurrent edit elsewhere in the document (or a\n * concurrent append) is never overwritten by this one.\n */\n patches: DocumentPatch[]\n}\n\n/**\n * Append `node` into an EXISTING document's blocks field (EC-320 follow-up,\n * 0.0.44 — \"insert into existing document\"), leaving everything else about\n * the document untouched: other fields, other locale bundles, and the\n * field's own existing content, which the embed always lands AFTER.\n *\n * Writes through the core write path (`editBlockRoots`,\n * `@elytracms/core/project-graph`, EC-576): the new root is appended after\n * the existing roots, every typography item keeps its place and order, and\n * the value written is the v2 sequence — undefined-safe for a never-set\n * field. Locale-aware writes go through core's `writeField` (the same\n * locale-write every editor uses, EC-580) so a localized\n * blocks field's OTHER locale bundles are never touched, only the edited\n * locale's.\n *\n * Node-id collision guard: `node` is re-embedded under a fresh id minted past\n * every existing root's ids ({@link embedFreshNode}), never a fixed `node-1`\n * — a document whose blocks already hold that id would otherwise collide.\n *\n * Throws if `blocksField` already holds its declared item limit (`validation.max`,\n * EC-581) — a defensive invariant guard, not a user-facing\n * validation path: the caller must pre-filter candidates with\n * {@link canAppendTo} so this is never reached in that state.\n */\nexport function appendToBlockDocument({\n collection,\n document,\n blocksField,\n node,\n locale,\n newId,\n}: AppendToBlockDocumentInput): AppendToBlockDocumentResult {\n const current = readBlocksValue(collection, document, blocksField, locale)\n const embedded = embedFreshNode(node, newId)\n let lastRoot: string | undefined\n const nextValue = editBlockRoots(current, (existingRoots) => {\n const limit = itemLimit(blocksField)\n if (limit !== undefined && existingRoots.length >= limit) {\n throw new Error(\n `Cannot append to blocks field \"${blocksField.name}\": it already holds its limit of ${limit} (caller must gate with canAppendTo).`,\n )\n }\n lastRoot = existingRoots.at(-1)?.id\n return [...existingRoots, embedded]\n })\n const written = writeField(collection, document, blocksField.name, locale, nextValue)\n // The stored node is the canonical one `writeField` wrote (tagged, canonical props).\n const storedValue = readBlocksValue(collection, written, blocksField, locale)\n const storedNode = isSequenceValue(storedValue)\n ? (storedValue.content.find(\n (entry): entry is ComponentNode =>\n typeof entry === 'object' && entry !== null && (entry as ComponentNode).id === embedded.id,\n ) ?? embedded)\n : embedded\n const parent = fieldPath(blocksField.name, isFieldLocalized(collection, blocksField.name) ? locale : undefined)\n const index = isSequenceValue(current) ? current.content.length : 0\n return {\n document: written,\n patches: [\n {\n op: 'insert',\n at: { parent, index, ...(lastRoot !== undefined ? { after: lastRoot } : {}) },\n node: storedNode,\n },\n ],\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"transfer.js","sourceRoot":"","sources":["../../../src/components/components/transfer.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n fieldAllowList,\n fieldPath,\n isBlocksField,\n isFieldLocalized,\n isSequenceValue,\n itemLimit,\n seedDefaults,\n writeField,\n} from '@elytracms/core/cms-core'\nimport type {\n BlocksFieldDef,\n CmsDocument,\n CollectionDef,\n DocumentPatch,\n Locale,\n LocaleConfig,\n} from '@elytracms/core/cms-core'\nimport { ANY_COMPONENT, blockRootsOf, editBlockRoots, placementRefusal } from '@elytracms/core/project-graph'\nimport type { ComponentLookup, ComponentNode } from '@elytracms/core/project-graph'\nimport type { IdFactory } from '@elytracms/core/persistence'\nimport { embedNodeFrom } from './component-preview'\n\n/**\n * EC-320 (+ 0.0.44 follow-up) — the \"design guide → real content\" bridge. A\n * component combination the user has arranged in the Components view (a\n * manifest + edited prop values, captured as a {@link ComponentNode}) is\n * transferred into a document's blocks/composition field — either a NEW DRAFT\n * document created for it ({@link buildBlockDocument}) or an EXISTING\n * document's field, appended alongside whatever it already holds\n * ({@link appendToBlockDocument}). Pure so id-minting/embedding/appending are\n * unit-testable; the React dialog owns the picker, persistence, and\n * navigation.\n */\n\nexport interface CollectionBlocksTarget {\n collection: CollectionDef\n /** The (first) blocks field the node lands in. */\n field: BlocksFieldDef\n}\n\n/**\n * Collections that can receive a transferred combination — those declaring a\n * `blocks` (composition) field. The first blocks field wins (a collection with a\n * single canvas is the norm; a second is an unusual edge we don't disambiguate).\n * Callers additionally gate each target through {@link isComponentAllowed} —\n * this only reports which collections HAVE a blocks field, not which ones\n * accept a given component.\n */\nexport function collectionsWithBlocks(\n collections: readonly CollectionDef[],\n): CollectionBlocksTarget[] {\n const targets: CollectionBlocksTarget[] = []\n for (const collection of collections) {\n const field = collection.fields.find(isBlocksField)\n if (field) targets.push({ collection, field })\n }\n return targets\n}\n\n/**\n * Is `componentId` permitted as a top-level block of `field`? EC-584: a READER of the\n * one core placement judge over the field's effective vocabulary (`fieldAllowList` —\n * its `allow`, else the blocks default), the verdict `validateCompositionValue`\n * reaches at the gate — so a target this predicate accepts can never fail its\n * `forbidden-component` / `server-only-component` check. With `components` (the\n * project's manifest lookup) canvas eligibility is judged too. The transfer dialog\n * uses this to hide targets the component isn't allowed into (new AND existing\n * document targets alike); the value-level sinks stay on the gate.\n */\nexport function isComponentAllowed(field: BlocksFieldDef, componentId: string, components?: ComponentLookup): boolean {\n return (\n placementRefusal({ componentId, view: components?.get(componentId), allow: fieldAllowList(field) ?? ANY_COMPONENT }) ===\n null\n )\n}\n\n/**\n * Re-embed `node` under a fresh id from the injected factory (EC-578 / EC-595 A2): UUIDs\n * in the studio, so an embed appended into a document can never collide with an id\n * already in the tree — or with one another session appends at the same time (the old\n * `node-<n>` counter seeded past the tree's ids minted the same id in both).\n */\nfunction embedFreshNode(node: ComponentNode, newId: IdFactory): ComponentNode {\n return embedNodeFrom(node, newId())\n}\n\nexport interface BuildBlockDocumentInput {\n collection: CollectionDef\n /** The blocks field the node lands in (from {@link collectionsWithBlocks}). */\n blocksField: BlocksFieldDef\n /** The component node — a manifest id + the edited static props (+ slots). */\n node: ComponentNode\n /** THE id factory (EC-578) — mints the new document's id and the embed's node id. */\n newId: IdFactory\n /** The project's locale config — a new document is written in its default locale (EC-580). */\n locales: LocaleConfig\n}\n\n/**\n * Build the draft document that embeds `node` in the collection's blocks field.\n * Reuses the CMS create machinery: the injected id factory (EC-578) for the id\n * and {@link seedDefaults} so declared field defaults are present (as a\n * normal `+ New` would be). The field stores the converged v2 sequence holding\n * the one tagged root, written through the core write path (`editBlockRoots`,\n * EC-576) — cardinality is a constraint (`validation.max`), not a shape (EC-500 D2,\n * EC-581). The\n * transferred node REPLACES whatever the field's declared default seeded, as it\n * always has.\n *\n * EC-580: both writes go through core's `writeField` in the project's DEFAULT\n * locale — a new document is created in its default bucket, so a localized blocks\n * field holds the node under `localized[default]`, never on the shared bag.\n */\nexport function buildBlockDocument({\n collection,\n blocksField,\n node,\n newId,\n locales,\n}: BuildBlockDocumentInput): CmsDocument {\n const seeded: CmsDocument = { collection: collection.id, id: newId(), ...seedDefaults(collection, locales) }\n return writeField(\n collection,\n seeded,\n blocksField.name,\n locales.default,\n editBlockRoots(undefined, () => [embedFreshNode(node, newId)]),\n )\n}\n\n/** The blocks field's current value on `document`, respecting locale (EC-018). */\nfunction readBlocksValue(\n collection: CollectionDef,\n document: CmsDocument,\n blocksField: BlocksFieldDef,\n locale: Locale,\n): unknown {\n return isFieldLocalized(collection, blocksField.name)\n ? document.localized?.[locale]?.[blocksField.name]\n : document.values[blocksField.name]\n}\n\nexport interface CanAppendToInput {\n collection: CollectionDef\n document: CmsDocument\n blocksField: BlocksFieldDef\n /** The locale being edited (EC-176 ambient active locale) for a localized field. */\n locale: Locale\n}\n\n/**\n * Can `node` be appended to `document`'s blocks field without destroying\n * existing content? Always true without a declared item limit (an ordered list\n * has room for one more); a field limited by `validation.max` (EC-581: `max: 1` is\n * the single-block field `multiple: false` used to spell) only while it is below\n * the limit — at it, there is no room without overwriting an existing block, so\n * the transfer dialog excludes that document from its offered targets rather than\n * risk data loss.\n */\nexport function canAppendTo({ collection, document, blocksField, locale }: CanAppendToInput): boolean {\n const limit = itemLimit(blocksField)\n if (limit === undefined) return true\n return blockRootsOf(readBlocksValue(collection, document, blocksField, locale)).length < limit\n}\n\nexport interface AppendToBlockDocumentInput {\n collection: CollectionDef\n /** The document to append into (the transfer dialog's \"insert into existing document\"). */\n document: CmsDocument\n blocksField: BlocksFieldDef\n /** The component node to append — not yet re-ided (fresh id minted here). */\n node: ComponentNode\n /** The locale being edited (EC-176 ambient active locale) for a localized field. */\n locale: Locale\n /** THE id factory (EC-578) — mints the embed's node id. */\n newId: IdFactory\n}\n\n/** The appended document AND the patch that says so (EC-595: the writer emits its patch at the source). */\nexport interface AppendToBlockDocumentResult {\n document: CmsDocument\n /**\n * ONE `insert` of the new embed node at the end of the field's top-level sequence,\n * anchored after the last existing component root — what is persisted\n * (`documents.patch`), so a concurrent edit elsewhere in the document (or a\n * concurrent append) is never overwritten by this one.\n */\n patches: DocumentPatch[]\n}\n\n/**\n * Append `node` into an EXISTING document's blocks field (EC-320 follow-up,\n * 0.0.44 — \"insert into existing document\"), leaving everything else about\n * the document untouched: other fields, other locale bundles, and the\n * field's own existing content, which the embed always lands AFTER.\n *\n * Writes through the core write path (`editBlockRoots`,\n * `@elytracms/core/project-graph`, EC-576): the new root is appended after\n * the existing roots, every typography item keeps its place and order, and\n * the value written is the v2 sequence — undefined-safe for a never-set\n * field. Locale-aware writes go through core's `writeField` (the same\n * locale-write every editor uses, EC-580) so a localized\n * blocks field's OTHER locale bundles are never touched, only the edited\n * locale's.\n *\n * Node-id collision guard: `node` is re-embedded under a fresh id minted past\n * every existing root's ids ({@link embedFreshNode}), never a fixed `node-1`\n * — a document whose blocks already hold that id would otherwise collide.\n *\n * Throws if `blocksField` already holds its declared item limit (`validation.max`,\n * EC-581) — a defensive invariant guard, not a user-facing\n * validation path: the caller must pre-filter candidates with\n * {@link canAppendTo} so this is never reached in that state.\n */\nexport function appendToBlockDocument({\n collection,\n document,\n blocksField,\n node,\n locale,\n newId,\n}: AppendToBlockDocumentInput): AppendToBlockDocumentResult {\n const current = readBlocksValue(collection, document, blocksField, locale)\n const embedded = embedFreshNode(node, newId)\n let lastRoot: string | undefined\n const nextValue = editBlockRoots(current, (existingRoots) => {\n const limit = itemLimit(blocksField)\n if (limit !== undefined && existingRoots.length >= limit) {\n throw new Error(\n `Cannot append to blocks field \"${blocksField.name}\": it already holds its limit of ${limit} (caller must gate with canAppendTo).`,\n )\n }\n lastRoot = existingRoots.at(-1)?.id\n return [...existingRoots, embedded]\n })\n const written = writeField(collection, document, blocksField.name, locale, nextValue)\n // The stored node is the canonical one `writeField` wrote (tagged, canonical props).\n const storedValue = readBlocksValue(collection, written, blocksField, locale)\n const storedNode = isSequenceValue(storedValue)\n ? (storedValue.content.find(\n (entry): entry is ComponentNode =>\n typeof entry === 'object' && entry !== null && (entry as ComponentNode).id === embedded.id,\n ) ?? embedded)\n : embedded\n const parent = fieldPath(blocksField.name, isFieldLocalized(collection, blocksField.name) ? locale : undefined)\n const index = isSequenceValue(current) ? current.content.length : 0\n return {\n document: written,\n patches: [\n {\n op: 'insert',\n at: { parent, index, ...(lastRoot !== undefined ? { after: lastRoot } : {}) },\n node: storedNode,\n },\n ],\n }\n}\n"]}
|
|
@@ -3,7 +3,7 @@ import { LogOut, Moon, Settings, Sun } from 'lucide-react';
|
|
|
3
3
|
import { roleGrantsCapability } from '@elytracms/core/persistence';
|
|
4
4
|
import { Avatar, Badge, Button, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, Row, Stack, Text, } from '@elytracms/studio/ui';
|
|
5
5
|
import { STUDIO_PROJECT_ROUTE, studioPath, useNavigate } from '@elytracms/studio/router';
|
|
6
|
-
import { useT } from '
|
|
6
|
+
import { useT } from '../lib/i18n/index.js';
|
|
7
7
|
import { useAuth, useProjectRole } from './auth-context.js';
|
|
8
8
|
import { useTheme } from './theme-context.js';
|
|
9
9
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"current-user-menu.js","sourceRoot":"","sources":["../../src/components/current-user-menu.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,cAAc,CAAA;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAA;AAElE,OAAO,EACL,MAAM,EACN,KAAK,EACL,MAAM,EACN,YAAY,EACZ,mBAAmB,EACnB,gBAAgB,EAChB,qBAAqB,EACrB,mBAAmB,EACnB,GAAG,EACH,KAAK,EACL,IAAI,GACL,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAE,oBAAoB,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AACxF,OAAO,EAAE,IAAI,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"current-user-menu.js","sourceRoot":"","sources":["../../src/components/current-user-menu.tsx"],"names":[],"mappings":"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","sourcesContent":["import { LogOut, Moon, Settings, Sun } from 'lucide-react'\nimport { roleGrantsCapability } from '@elytracms/core/persistence'\nimport type { ProjectRole } from '@elytracms/core/persistence'\nimport {\n Avatar,\n Badge,\n Button,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n Row,\n Stack,\n Text,\n} from '@elytracms/studio/ui'\nimport { STUDIO_PROJECT_ROUTE, studioPath, useNavigate } from '@elytracms/studio/router'\nimport { useT } from '../lib/i18n'\nimport { useAuth, useProjectRole } from './auth-context'\nimport { useTheme } from './theme-context'\n\n/**\n * Current-user affordance in the shell header (EC-150): who is signed in,\n * their role on the active project (badge), theme switch, and sign-out. Role\n * display is informational — actual enforcement happens at the operations\n * chokepoint and server-side, never in the UI.\n */\nexport function CurrentUserMenu({ projectId }: { projectId: string | null }) {\n const auth = useAuth()\n const role = useProjectRole(projectId)\n const { theme, setTheme } = useTheme()\n const navigate = useNavigate()\n const t = useT()\n const user = auth.session?.user\n if (!user) return null\n\n // Show one identity line — the name if set, otherwise the email (never both).\n const displayName = user.name ?? user.email\n const isDark = theme === 'dark'\n\n return (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button variant=\"ghost\" size=\"icon-sm\" className=\"rounded-full\" aria-label={t('userMenu.accountMenu')}>\n <Avatar label={displayName} size=\"sm\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"min-w-60\">\n {/* Identity header — normal case, single line, with the project role. */}\n <Row gap=\"2\" className=\"px-2 py-2\">\n <Avatar label={displayName} size=\"md\" />\n <Stack gap=\"1\" className=\"min-w-0 flex-1\">\n <Text size=\"sm\" weight=\"medium\" className=\"truncate leading-tight\">\n {displayName}\n </Text>\n {role ? (\n <Row>\n <RoleBadge role={role} />\n </Row>\n ) : null}\n </Stack>\n </Row>\n <DropdownMenuSeparator />\n {/* Theme switch — preventDefault keeps the menu open so the change is visible. */}\n <DropdownMenuItem\n onSelect={(event) => {\n event.preventDefault()\n setTheme(isDark ? 'light' : 'dark')\n }}\n >\n {isDark ? <Sun /> : <Moon />}\n {isDark ? t('userMenu.lightMode') : t('userMenu.darkMode')}\n </DropdownMenuItem>\n {/* Account settings (EC-240 H-tail item 4): the member's OWN record in the\n portable edit shell — self-permitted identity + password. Needs a project\n scope to address the users route; disabled on the project-less shell. */}\n <DropdownMenuItem\n disabled={!projectId}\n onSelect={() => {\n if (projectId) {\n void navigate({\n to: STUDIO_PROJECT_ROUTE,\n params: { projectId, _splat: studioPath.user(user.id) },\n })\n }\n }}\n >\n <Settings />\n {t('userMenu.accountSettings')}\n </DropdownMenuItem>\n <DropdownMenuSeparator />\n <DropdownMenuItem variant=\"destructive\" onSelect={() => void auth.signOut()}>\n <LogOut />\n {t('userMenu.signOut')}\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n )\n}\n\nexport function RoleBadge({ role }: { role: ProjectRole }) {\n // Variant by capability tier (EC-240c), not role literals — a custom role set\n // styles by what it grants: administer → primary, write → info, else outline.\n const variant = roleGrantsCapability(role, 'project.administer')\n ? 'primary'\n : roleGrantsCapability(role, 'content.write')\n ? 'info'\n : 'outline'\n return <Badge variant={variant}>{role}</Badge>\n}\n"]}
|
|
@@ -2,10 +2,10 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { useState } from 'react';
|
|
3
3
|
import { Rocket, X } from 'lucide-react';
|
|
4
4
|
import { Button, Row, Spacer, Text } from '@elytracms/studio/ui';
|
|
5
|
-
import { translate } from '
|
|
5
|
+
import { translate } from '../lib/i18n/index.js';
|
|
6
6
|
import { resolveBackendConfig } from '../lib/persistence/backend-mode.js';
|
|
7
7
|
import { missingInstantPublishVars } from '../lib/delivery/instant-publish.js';
|
|
8
|
-
import { useOperationQuery } from '
|
|
8
|
+
import { useOperationQuery } from '../lib/query/index.js';
|
|
9
9
|
/**
|
|
10
10
|
* EC-268: a boot-up health check for instant publish (EC-146). When the studio is
|
|
11
11
|
* connected to a Convex deployment, it reads the delivery config through the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"delivery-health-banner.js","sourceRoot":"","sources":["../../src/components/delivery-health-banner.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAChC,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,cAAc,CAAA;AACxC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAA;AAChE,OAAO,EAAE,SAAS,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"delivery-health-banner.js","sourceRoot":"","sources":["../../src/components/delivery-health-banner.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAChC,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,cAAc,CAAA;AACxC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAA;AAChE,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AACvC,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAA;AACtE,OAAO,EAAE,yBAAyB,EAAE,MAAM,iCAAiC,CAAA;AAC3E,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAGhD;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,oBAAoB,CAAC,EAAE,SAAS,EAAyB;IACvE,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IACjD,MAAM,OAAO,GAAG,oBAAoB,EAAE,CAAC,IAAI,KAAK,QAAQ,CAAA;IAExD,MAAM,KAAK,GAAG,iBAAiB,CAAuB;QACpD,QAAQ,EAAE,CAAC,UAAU,EAAE,QAAQ,EAAE,SAAS,CAAC;QAC3C,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE;YAC3B,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC,CAAA;YAC9D,IAAI,CAAC,MAAM,CAAC,EAAE;gBAAE,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;YACrD,OAAO,MAAM,CAAC,KAAK,CAAA;QACrB,CAAC;QACD,OAAO;KACR,CAAC,CAAA;IACF,0EAA0E;IAC1E,gFAAgF;IAChF,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,yBAAyB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;IAEvE,IAAI,SAAS,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IAElD,OAAO,CACL,MAAC,GAAG,IACF,GAAG,EAAC,GAAG,EACP,SAAS,EAAC,oFAAoF,iBAClF,wBAAwB,aAEpC,KAAC,MAAM,IAAC,SAAS,EAAC,mBAAmB,wBAAe,EACpD,MAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,SAAS,EAAC,cAAc,oEACI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,6JAGvD,EACP,KAAC,MAAM,KAAG,EACV,KAAC,MAAM,IACL,OAAO,EAAC,OAAO,EACf,IAAI,EAAC,SAAS,EACd,OAAO,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,gBACrB,SAAS,CAAC,8BAA8B,CAAC,EACrD,KAAK,EAAE,SAAS,CAAC,8BAA8B,CAAC,YAEhD,KAAC,CAAC,0BAAe,GACV,IACL,CACP,CAAA;AACH,CAAC","sourcesContent":["import { useState } from 'react'\nimport { Rocket, X } from 'lucide-react'\nimport { Button, Row, Spacer, Text } from '@elytracms/studio/ui'\nimport { translate } from '../lib/i18n'\nimport { resolveBackendConfig } from '../lib/persistence/backend-mode'\nimport { missingInstantPublishVars } from '../lib/delivery/instant-publish'\nimport { useOperationQuery } from '../lib/query'\nimport type { DeliveryConfigStatus } from '@elytracms/core/persistence'\n\n/**\n * EC-268: a boot-up health check for instant publish (EC-146). When the studio is\n * connected to a Convex deployment, it reads the delivery config through the\n * operations seam (EC-329 — `operations.delivery.config`, cached by\n * `useOperationQuery` like every other studio read, AD-17) and, if any of the\n * delivery env vars are unset, shows a dismissible warning. Without this the\n * pipeline fails SILENTLY — a publish reports success but `scheduleRevalidate`\n * returns before scheduling (no `CANVAS_REVALIDATE_ENDPOINT`/`_SECRET` pair) and\n * the live site never updates. In playground mode (no backend) instant publish does\n * not apply — no host reads the in-browser adapter, so there is no emitter — and the\n * banner never renders (the query is disabled, so it never has data). A failed\n * read degrades silently — it must never block the studio — by treating the\n * query's error branch the same as \"nothing missing\", matching the former raw\n * client call's swallowed `.catch()`.\n */\nexport function DeliveryHealthBanner({ projectId }: { projectId: string }) {\n const [dismissed, setDismissed] = useState(false)\n const enabled = resolveBackendConfig().kind === 'convex'\n\n const query = useOperationQuery<DeliveryConfigStatus>({\n queryKey: ['delivery', 'config', projectId],\n loader: async (operations) => {\n const result = await operations.delivery.config({ projectId })\n if (!result.ok) throw new Error(result.error.message)\n return result.value\n },\n enabled,\n })\n // Loading/error/playground (disabled) all read as \"nothing missing\" — the\n // banner only ever surfaces a CONFIRMED gap, never a transient/unreachable one.\n const missing = query.data ? missingInstantPublishVars(query.data) : []\n\n if (dismissed || missing.length === 0) return null\n\n return (\n <Row\n gap=\"2\"\n className=\"shrink-0 border-b border-warning/30 bg-warning/10 px-4 py-1.5 text-warning sm:px-6\"\n data-testid=\"delivery-health-banner\"\n >\n <Rocket className=\"size-3.5 shrink-0\" aria-hidden />\n <Text size=\"xs\" className=\"text-warning\">\n Instant publish isn’t fully configured — {missing.join(', ')} not set on this Convex\n deployment. Publishes succeed, but the live site won’t update until you set them (see\n CONNECT.md) and re-push functions.\n </Text>\n <Spacer />\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={() => setDismissed(true)}\n aria-label={translate('shell.deliveryHealth.dismiss')}\n title={translate('shell.deliveryHealth.dismiss')}\n >\n <X aria-hidden />\n </Button>\n </Row>\n )\n}\n"]}
|
|
@@ -2,9 +2,9 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
|
|
|
2
2
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
3
3
|
import { Loader2 } from 'lucide-react';
|
|
4
4
|
import { Button, Card, CardContent, CardHeader, CardTitle, Code, Stack, Text } from '@elytracms/studio/ui';
|
|
5
|
-
import { resolveBackendConfig, selectPersistenceAdapter, studioDeploymentReadiness, } from '
|
|
6
|
-
import { getWorkspaceState, resolveDeploymentAi, resolveDeploymentCollections, resolveDeploymentUrl, } from '
|
|
7
|
-
import { translate, useHydrated, useT } from '
|
|
5
|
+
import { resolveBackendConfig, selectPersistenceAdapter, studioDeploymentReadiness, } from '../lib/persistence/index.js';
|
|
6
|
+
import { getWorkspaceState, resolveDeploymentAi, resolveDeploymentCollections, resolveDeploymentUrl, } from '../lib/workspace/index.js';
|
|
7
|
+
import { translate, useHydrated, useT } from '../lib/i18n/index.js';
|
|
8
8
|
import { probeDeployedFunctionsVersion } from '@elytracms/core/export-sync/deployment-ready';
|
|
9
9
|
/**
|
|
10
10
|
* Deployment-setup boot gate (EC-258) — the single readiness surface that
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deployment-setup-gate.js","sourceRoot":"","sources":["../../src/components/deployment-setup-gate.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useEffect, useMemo, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport { Loader2 } from 'lucide-react'\nimport { Button, Card, CardContent, CardHeader, CardTitle, Code, Stack, Text } from '@elytracms/studio/ui'\nimport {\n resolveBackendConfig,\n selectPersistenceAdapter,\n studioDeploymentReadiness,\n} from '@elytracms/studio/lib/persistence'\nimport {\n getWorkspaceState,\n resolveDeploymentAi,\n resolveDeploymentCollections,\n resolveDeploymentUrl,\n} from '@elytracms/studio/lib/workspace'\nimport { translate, useHydrated, useT } from '@elytracms/studio/lib/i18n'\nimport { probeDeployedFunctionsVersion } from '@elytracms/core/export-sync/deployment-ready'\nimport type {\n DeploymentReadinessReport,\n FunctionsVersionProbe,\n ReadinessCheck,\n} from '@elytracms/core/export-sync/deployment-ready'\n\n/**\n * Deployment-setup boot gate (EC-258) — the single readiness surface that\n * supersedes the separate `BackendGate` (missing `VITE_CONVEX_URL` / unreachable)\n * and `WorkspaceGate` (invalid config) screens by composing the shared\n * `checkDeploymentReady` contract. It renders ONE deterministic \"Deployment setup\"\n * screen listing each failing precondition with its copy-pasteable fix, instead of\n * letting the app boot into a misleading downstream error (most painfully the\n * Konditorei JWT-key footgun). All checks passing → children render untouched.\n *\n * Runs before auth and any adapter is constructed (it is the thing that keeps\n * `selectAuthAdapter`/`selectPersistenceAdapter` from throwing on a misconfigured\n * backend). Convex mode probes reachability once on the client (`adapter.status()`)\n * with a retry; playground and unconfigured resolve synchronously. SSR renders the\n * deterministic \"checking\" state. Auth-signing-keys is reported as `unknown` at boot\n * (the JWKS endpoint is CORS-blocked from the browser — EC-257's honest sign-in error\n * is the browser's actionable surface; `elytra doctor` probes it from node), and the\n * per-user bucket stays `unknown` (this gate runs pre-auth), so neither ever blocks boot.\n */\nexport function DeploymentSetupGate({ children }: { children: ReactNode }) {\n const config = useMemo(() => resolveBackendConfig(), [])\n // EC-399: a configured (and not disabled) `defineStudio({ ai })` block means the\n // AI route exists — its provider-key expectation joins the deployment-env checks.\n const aiRouteConfigured = useMemo(() => {\n const ai = resolveDeploymentAi()\n return Boolean(ai && ai.enabled !== false)\n }, [])\n\n const staticReport = useCallback(\n (): DeploymentReadinessReport =>\n studioDeploymentReadiness({\n backend: config,\n workspace: getWorkspaceState(),\n collections: resolveDeploymentCollections(),\n aiRouteConfigured,\n }),\n [config, aiRouteConfigured],\n )\n\n const [state, setState] = useState<GateState>(() =>\n config.kind === 'convex' ? { phase: 'checking' } : { phase: 'resolved', report: staticReport() },\n )\n\n const check = useCallback(() => {\n if (config.kind !== 'convex') {\n setState({ phase: 'resolved', report: staticReport() })\n return\n }\n const effectiveUrl = resolveDeploymentUrl(config.url)\n const resolve = (\n reachable: { available: boolean; detail?: string },\n functionsVersion: FunctionsVersionProbe,\n ) =>\n setState({\n phase: 'resolved',\n report: studioDeploymentReadiness({\n backend: config,\n effectiveUrl,\n workspace: getWorkspaceState(),\n collections: resolveDeploymentCollections(),\n reachable,\n functionsVersion,\n aiRouteConfigured,\n }),\n })\n setState({ phase: 'checking' })\n // EC-552: the version handshake probes in parallel with the reachability check —\n // same deployment URL, one extra public query. The probe never rejects (network\n // failures resolve to an honest `unknown`), so awaiting it in both paths is safe.\n const versionProbe = probeDeployedFunctionsVersion(effectiveUrl)\n void selectPersistenceAdapter()\n .then((adapter) => adapter.status())\n .then(async (status) =>\n resolve(\n { available: status.available, ...(status.detail ? { detail: status.detail } : {}) },\n await versionProbe,\n ),\n )\n .catch(async (error: unknown) =>\n resolve(\n { available: false, detail: error instanceof Error ? error.message : String(error) },\n await versionProbe,\n ),\n )\n }, [config, staticReport, aiRouteConfigured])\n\n useEffect(() => {\n if (config.kind === 'convex') check()\n }, [config.kind, check])\n\n if (state.phase === 'checking') return <DeploymentChecking />\n if (blockingFailures(state.report).length > 0) {\n return (\n <DeploymentSetupScreen\n report={state.report}\n onRetry={config.kind === 'convex' ? check : undefined}\n />\n )\n }\n return <>{children}</>\n}\n\ntype GateState = { phase: 'checking' } | { phase: 'resolved'; report: DeploymentReadinessReport }\n\n/**\n * The checks that BLOCK boot: a failed deployment-, deployment-env-, or app-level\n * precondition. The per-user bucket (\"not a member\") is not a boot blocker — it is\n * surfaced post-auth (\"ask an admin\"), and at boot it is `unknown` (pre-auth) anyway.\n * Deployment-env checks are `unknown` in the browser today (nothing gathered — EC-399),\n * so they never block in practice; `warn` findings never block anywhere.\n */\nfunction blockingFailures(report: DeploymentReadinessReport): ReadinessCheck[] {\n return report.checks.filter(\n (c) =>\n c.status === 'fail' &&\n (c.bucket === 'deployment' || c.bucket === 'deployment-env' || c.bucket === 'app'),\n )\n}\n\n/**\n * Deterministic boot state while the backend probe is in flight — and, in a\n * CONNECTED build, the body of the prerendered shell itself.\n *\n * **EC-525.** EC-523 removed the last localized string from the shell and proved it\n * against the built artefact — but against the *playground* build, which is the demo's\n * default and the one nobody deploys. Every real studio is connected, so\n * `config.kind === 'convex'` seeds `{ phase: 'checking' }` and *this* is what a\n * production cold load actually bakes: `Checking deployment…`, in the build machine's\n * locale, shown to every visitor whatever theirs is. The class was not closed until\n * both builds satisfied it.\n *\n * Same shape as `SessionLoading` (`router/root.tsx`), for the same reasons, which are\n * written out there: a build-time prerender has no request and therefore no locale, so\n * the shell shows a spinner and an EMPTY `role=\"status\"` region, and `useHydrated`\n * fills it in the visitor's real language once hydration lands. No `aria-label` — that\n * bakes the wrong-language string somewhere a screen-reader user cannot see it flip —\n * and no `aria-busy`, which would suppress the announcement this shape exists to make.\n *\n * Exported so the connected shell's pre-hydration markup is pinnable with\n * `renderToStaticMarkup` — same reason `SessionLoading` is. The built artefact is the\n * real proof; the pin is what fails when someone puts a string back.\n */\nexport function DeploymentChecking() {\n const hydrated = useHydrated()\n const t = useT()\n return (\n <BootScreenFrame>\n <div role=\"status\">\n <Loader2 className=\"size-6 animate-spin text-muted-foreground\" aria-hidden=\"true\" />\n {hydrated ? <span className=\"sr-only\">{t('shell.deploymentSetup.checking')}</span> : null}\n </div>\n </BootScreenFrame>\n )\n}\n\n/**\n * The unified \"Deployment setup\" screen (EC-258): every failing check + its exact\n * fix. Pure/presentational so it is fixture-reproducible and static-render testable.\n */\nexport function DeploymentSetupScreen({\n report,\n onRetry,\n}: {\n report: DeploymentReadinessReport\n onRetry?: () => void\n}) {\n const failures = report.checks.filter((c) => c.status === 'fail')\n const envChecks = report.checks.filter((c) => c.bucket === 'deployment-env')\n return (\n <BootScreenFrame>\n <Card className=\"max-w-2xl border-destructive/40\">\n <CardHeader>\n <CardTitle>{translate('shell.deploymentSetup.title')}</CardTitle>\n </CardHeader>\n <CardContent>\n <Stack gap=\"3\">\n <Text size=\"sm\" tone=\"muted\">\n {translate('shell.deploymentSetup.description')}\n </Text>\n <Stack gap=\"2\" role=\"list\" aria-label={translate('shell.deploymentSetup.issuesLabel')}>\n {failures.map((check) => (\n <CheckRow key={check.id} check={check} />\n ))}\n </Stack>\n {envChecks.length > 0 ? <ExpectedEnvSection checks={envChecks} /> : null}\n {onRetry ? (\n <Button variant=\"outline\" size=\"sm\" onClick={onRetry} className=\"self-start\">\n {translate('shell.deploymentSetup.retry')}\n </Button>\n ) : null}\n </Stack>\n </CardContent>\n </Card>\n </BootScreenFrame>\n )\n}\n\n/**\n * The expected-env facts (EC-399) — the SAME contract `elytra doctor` checks, so the\n * studio and CLI tell one story. Presence only: each row is a var name + set/missing/\n * unknown status text; values never exist in the report. In the browser the presence\n * sources aren't readable pre-auth, so rows typically read `unknown` and point at\n * `elytra doctor` — which is exactly the honest answer.\n */\nfunction ExpectedEnvSection({ checks }: { checks: ReadinessCheck[] }) {\n return (\n <Stack gap=\"2\">\n <Text size=\"sm\" className=\"font-medium\">\n {translate('shell.deploymentSetup.envSection')}\n </Text>\n <Text size=\"xs\" tone=\"muted\">\n {translate('shell.deploymentSetup.envSectionHint')}\n </Text>\n <Stack gap=\"1\" role=\"list\" aria-label={translate('shell.deploymentSetup.envSection')}>\n {checks.map((check) => (\n <EnvCheckRow key={check.id} check={check} />\n ))}\n </Stack>\n </Stack>\n )\n}\n\nconst ENV_STATUS_GLYPH: Record<ReadinessCheck['status'], string> = {\n ok: '✓',\n warn: '!',\n fail: '✗',\n unknown: '?',\n}\n\nfunction EnvCheckRow({ check }: { check: ReadinessCheck }) {\n const failing = check.status === 'fail'\n return (\n <div\n role=\"listitem\"\n className={`rounded-md border p-2 ${failing ? 'border-destructive/30 bg-destructive/5' : 'border-border bg-muted/30'}`}\n >\n <Text size=\"xs\" mono className={failing ? 'text-destructive' : undefined}>\n {ENV_STATUS_GLYPH[check.status]} [{check.id}]\n </Text>\n <Text size=\"xs\" tone=\"muted\">\n {check.detail}\n </Text>\n {check.fix && (check.status === 'fail' || check.status === 'warn') ? (\n <Text size=\"xs\" tone=\"muted\" className=\"mt-1\">\n {translate('shell.deploymentSetup.fixLabel')}: <Code className=\"break-all\">{check.fix}</Code>\n </Text>\n ) : null}\n </div>\n )\n}\n\nfunction CheckRow({ check }: { check: ReadinessCheck }) {\n return (\n <div role=\"listitem\" className=\"rounded-md border border-destructive/30 bg-destructive/5 p-2\">\n <Text size=\"xs\" mono className=\"text-destructive\">\n [{check.id}]\n </Text>\n <Text size=\"sm\">{check.detail}</Text>\n {check.fix ? (\n <Text size=\"xs\" tone=\"muted\" className=\"mt-1\">\n {translate('shell.deploymentSetup.fixLabel')}: <Code className=\"break-all\">{check.fix}</Code>\n </Text>\n ) : null}\n </div>\n )\n}\n\nfunction BootScreenFrame({ children }: { children: ReactNode }) {\n return <div className=\"grid min-h-dvh place-items-center bg-background p-6\">{children}</div>\n}\n"]}
|
|
1
|
+
{"version":3,"file":"deployment-setup-gate.js","sourceRoot":"","sources":["../../src/components/deployment-setup-gate.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useEffect, useMemo, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport { Loader2 } from 'lucide-react'\nimport { Button, Card, CardContent, CardHeader, CardTitle, Code, Stack, Text } from '@elytracms/studio/ui'\nimport {\n resolveBackendConfig,\n selectPersistenceAdapter,\n studioDeploymentReadiness,\n} from '../lib/persistence'\nimport {\n getWorkspaceState,\n resolveDeploymentAi,\n resolveDeploymentCollections,\n resolveDeploymentUrl,\n} from '../lib/workspace'\nimport { translate, useHydrated, useT } from '../lib/i18n'\nimport { probeDeployedFunctionsVersion } from '@elytracms/core/export-sync/deployment-ready'\nimport type {\n DeploymentReadinessReport,\n FunctionsVersionProbe,\n ReadinessCheck,\n} from '@elytracms/core/export-sync/deployment-ready'\n\n/**\n * Deployment-setup boot gate (EC-258) — the single readiness surface that\n * supersedes the separate `BackendGate` (missing `VITE_CONVEX_URL` / unreachable)\n * and `WorkspaceGate` (invalid config) screens by composing the shared\n * `checkDeploymentReady` contract. It renders ONE deterministic \"Deployment setup\"\n * screen listing each failing precondition with its copy-pasteable fix, instead of\n * letting the app boot into a misleading downstream error (most painfully the\n * Konditorei JWT-key footgun). All checks passing → children render untouched.\n *\n * Runs before auth and any adapter is constructed (it is the thing that keeps\n * `selectAuthAdapter`/`selectPersistenceAdapter` from throwing on a misconfigured\n * backend). Convex mode probes reachability once on the client (`adapter.status()`)\n * with a retry; playground and unconfigured resolve synchronously. SSR renders the\n * deterministic \"checking\" state. Auth-signing-keys is reported as `unknown` at boot\n * (the JWKS endpoint is CORS-blocked from the browser — EC-257's honest sign-in error\n * is the browser's actionable surface; `elytra doctor` probes it from node), and the\n * per-user bucket stays `unknown` (this gate runs pre-auth), so neither ever blocks boot.\n */\nexport function DeploymentSetupGate({ children }: { children: ReactNode }) {\n const config = useMemo(() => resolveBackendConfig(), [])\n // EC-399: a configured (and not disabled) `defineStudio({ ai })` block means the\n // AI route exists — its provider-key expectation joins the deployment-env checks.\n const aiRouteConfigured = useMemo(() => {\n const ai = resolveDeploymentAi()\n return Boolean(ai && ai.enabled !== false)\n }, [])\n\n const staticReport = useCallback(\n (): DeploymentReadinessReport =>\n studioDeploymentReadiness({\n backend: config,\n workspace: getWorkspaceState(),\n collections: resolveDeploymentCollections(),\n aiRouteConfigured,\n }),\n [config, aiRouteConfigured],\n )\n\n const [state, setState] = useState<GateState>(() =>\n config.kind === 'convex' ? { phase: 'checking' } : { phase: 'resolved', report: staticReport() },\n )\n\n const check = useCallback(() => {\n if (config.kind !== 'convex') {\n setState({ phase: 'resolved', report: staticReport() })\n return\n }\n const effectiveUrl = resolveDeploymentUrl(config.url)\n const resolve = (\n reachable: { available: boolean; detail?: string },\n functionsVersion: FunctionsVersionProbe,\n ) =>\n setState({\n phase: 'resolved',\n report: studioDeploymentReadiness({\n backend: config,\n effectiveUrl,\n workspace: getWorkspaceState(),\n collections: resolveDeploymentCollections(),\n reachable,\n functionsVersion,\n aiRouteConfigured,\n }),\n })\n setState({ phase: 'checking' })\n // EC-552: the version handshake probes in parallel with the reachability check —\n // same deployment URL, one extra public query. The probe never rejects (network\n // failures resolve to an honest `unknown`), so awaiting it in both paths is safe.\n const versionProbe = probeDeployedFunctionsVersion(effectiveUrl)\n void selectPersistenceAdapter()\n .then((adapter) => adapter.status())\n .then(async (status) =>\n resolve(\n { available: status.available, ...(status.detail ? { detail: status.detail } : {}) },\n await versionProbe,\n ),\n )\n .catch(async (error: unknown) =>\n resolve(\n { available: false, detail: error instanceof Error ? error.message : String(error) },\n await versionProbe,\n ),\n )\n }, [config, staticReport, aiRouteConfigured])\n\n useEffect(() => {\n if (config.kind === 'convex') check()\n }, [config.kind, check])\n\n if (state.phase === 'checking') return <DeploymentChecking />\n if (blockingFailures(state.report).length > 0) {\n return (\n <DeploymentSetupScreen\n report={state.report}\n onRetry={config.kind === 'convex' ? check : undefined}\n />\n )\n }\n return <>{children}</>\n}\n\ntype GateState = { phase: 'checking' } | { phase: 'resolved'; report: DeploymentReadinessReport }\n\n/**\n * The checks that BLOCK boot: a failed deployment-, deployment-env-, or app-level\n * precondition. The per-user bucket (\"not a member\") is not a boot blocker — it is\n * surfaced post-auth (\"ask an admin\"), and at boot it is `unknown` (pre-auth) anyway.\n * Deployment-env checks are `unknown` in the browser today (nothing gathered — EC-399),\n * so they never block in practice; `warn` findings never block anywhere.\n */\nfunction blockingFailures(report: DeploymentReadinessReport): ReadinessCheck[] {\n return report.checks.filter(\n (c) =>\n c.status === 'fail' &&\n (c.bucket === 'deployment' || c.bucket === 'deployment-env' || c.bucket === 'app'),\n )\n}\n\n/**\n * Deterministic boot state while the backend probe is in flight — and, in a\n * CONNECTED build, the body of the prerendered shell itself.\n *\n * **EC-525.** EC-523 removed the last localized string from the shell and proved it\n * against the built artefact — but against the *playground* build, which is the demo's\n * default and the one nobody deploys. Every real studio is connected, so\n * `config.kind === 'convex'` seeds `{ phase: 'checking' }` and *this* is what a\n * production cold load actually bakes: `Checking deployment…`, in the build machine's\n * locale, shown to every visitor whatever theirs is. The class was not closed until\n * both builds satisfied it.\n *\n * Same shape as `SessionLoading` (`router/root.tsx`), for the same reasons, which are\n * written out there: a build-time prerender has no request and therefore no locale, so\n * the shell shows a spinner and an EMPTY `role=\"status\"` region, and `useHydrated`\n * fills it in the visitor's real language once hydration lands. No `aria-label` — that\n * bakes the wrong-language string somewhere a screen-reader user cannot see it flip —\n * and no `aria-busy`, which would suppress the announcement this shape exists to make.\n *\n * Exported so the connected shell's pre-hydration markup is pinnable with\n * `renderToStaticMarkup` — same reason `SessionLoading` is. The built artefact is the\n * real proof; the pin is what fails when someone puts a string back.\n */\nexport function DeploymentChecking() {\n const hydrated = useHydrated()\n const t = useT()\n return (\n <BootScreenFrame>\n <div role=\"status\">\n <Loader2 className=\"size-6 animate-spin text-muted-foreground\" aria-hidden=\"true\" />\n {hydrated ? <span className=\"sr-only\">{t('shell.deploymentSetup.checking')}</span> : null}\n </div>\n </BootScreenFrame>\n )\n}\n\n/**\n * The unified \"Deployment setup\" screen (EC-258): every failing check + its exact\n * fix. Pure/presentational so it is fixture-reproducible and static-render testable.\n */\nexport function DeploymentSetupScreen({\n report,\n onRetry,\n}: {\n report: DeploymentReadinessReport\n onRetry?: () => void\n}) {\n const failures = report.checks.filter((c) => c.status === 'fail')\n const envChecks = report.checks.filter((c) => c.bucket === 'deployment-env')\n return (\n <BootScreenFrame>\n <Card className=\"max-w-2xl border-destructive/40\">\n <CardHeader>\n <CardTitle>{translate('shell.deploymentSetup.title')}</CardTitle>\n </CardHeader>\n <CardContent>\n <Stack gap=\"3\">\n <Text size=\"sm\" tone=\"muted\">\n {translate('shell.deploymentSetup.description')}\n </Text>\n <Stack gap=\"2\" role=\"list\" aria-label={translate('shell.deploymentSetup.issuesLabel')}>\n {failures.map((check) => (\n <CheckRow key={check.id} check={check} />\n ))}\n </Stack>\n {envChecks.length > 0 ? <ExpectedEnvSection checks={envChecks} /> : null}\n {onRetry ? (\n <Button variant=\"outline\" size=\"sm\" onClick={onRetry} className=\"self-start\">\n {translate('shell.deploymentSetup.retry')}\n </Button>\n ) : null}\n </Stack>\n </CardContent>\n </Card>\n </BootScreenFrame>\n )\n}\n\n/**\n * The expected-env facts (EC-399) — the SAME contract `elytra doctor` checks, so the\n * studio and CLI tell one story. Presence only: each row is a var name + set/missing/\n * unknown status text; values never exist in the report. In the browser the presence\n * sources aren't readable pre-auth, so rows typically read `unknown` and point at\n * `elytra doctor` — which is exactly the honest answer.\n */\nfunction ExpectedEnvSection({ checks }: { checks: ReadinessCheck[] }) {\n return (\n <Stack gap=\"2\">\n <Text size=\"sm\" className=\"font-medium\">\n {translate('shell.deploymentSetup.envSection')}\n </Text>\n <Text size=\"xs\" tone=\"muted\">\n {translate('shell.deploymentSetup.envSectionHint')}\n </Text>\n <Stack gap=\"1\" role=\"list\" aria-label={translate('shell.deploymentSetup.envSection')}>\n {checks.map((check) => (\n <EnvCheckRow key={check.id} check={check} />\n ))}\n </Stack>\n </Stack>\n )\n}\n\nconst ENV_STATUS_GLYPH: Record<ReadinessCheck['status'], string> = {\n ok: '✓',\n warn: '!',\n fail: '✗',\n unknown: '?',\n}\n\nfunction EnvCheckRow({ check }: { check: ReadinessCheck }) {\n const failing = check.status === 'fail'\n return (\n <div\n role=\"listitem\"\n className={`rounded-md border p-2 ${failing ? 'border-destructive/30 bg-destructive/5' : 'border-border bg-muted/30'}`}\n >\n <Text size=\"xs\" mono className={failing ? 'text-destructive' : undefined}>\n {ENV_STATUS_GLYPH[check.status]} [{check.id}]\n </Text>\n <Text size=\"xs\" tone=\"muted\">\n {check.detail}\n </Text>\n {check.fix && (check.status === 'fail' || check.status === 'warn') ? (\n <Text size=\"xs\" tone=\"muted\" className=\"mt-1\">\n {translate('shell.deploymentSetup.fixLabel')}: <Code className=\"break-all\">{check.fix}</Code>\n </Text>\n ) : null}\n </div>\n )\n}\n\nfunction CheckRow({ check }: { check: ReadinessCheck }) {\n return (\n <div role=\"listitem\" className=\"rounded-md border border-destructive/30 bg-destructive/5 p-2\">\n <Text size=\"xs\" mono className=\"text-destructive\">\n [{check.id}]\n </Text>\n <Text size=\"sm\">{check.detail}</Text>\n {check.fix ? (\n <Text size=\"xs\" tone=\"muted\" className=\"mt-1\">\n {translate('shell.deploymentSetup.fixLabel')}: <Code className=\"break-all\">{check.fix}</Code>\n </Text>\n ) : null}\n </div>\n )\n}\n\nfunction BootScreenFrame({ children }: { children: ReactNode }) {\n return <div className=\"grid min-h-dvh place-items-center bg-background p-6\">{children}</div>\n}\n"]}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { KeyRound } from 'lucide-react';
|
|
3
3
|
import { Badge } from '@elytracms/studio/ui';
|
|
4
|
-
import { isDevAutoLoginActive } from '
|
|
5
|
-
import { translate } from '
|
|
4
|
+
import { isDevAutoLoginActive } from '../lib/persistence/index.js';
|
|
5
|
+
import { translate } from '../lib/i18n/index.js';
|
|
6
6
|
/**
|
|
7
7
|
* Marker for the dev-only unattended auth (see `resolveDevAutoLogin`). When the
|
|
8
8
|
* studio auto-signs-in from `VITE_ELYTRA_DEV_LOGIN` in a dev build, this badge
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev-login-badge.js","sourceRoot":"","sources":["../../src/components/dev-login-badge.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvC,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAA;AAC5C,OAAO,EAAE,oBAAoB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"dev-login-badge.js","sourceRoot":"","sources":["../../src/components/dev-login-badge.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvC,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAA;AAC5C,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AACzD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAEvC;;;;;GAKG;AACH,MAAM,UAAU,aAAa;IAC3B,IAAI,CAAC,oBAAoB,EAAE;QAAE,OAAO,IAAI,CAAA;IACxC,OAAO,CACL,MAAC,KAAK,IAAC,OAAO,EAAC,SAAS,EAAC,KAAK,EAAE,SAAS,CAAC,qBAAqB,CAAC,iBAAc,iBAAiB,aAC7F,KAAC,QAAQ,0BAAe,EACvB,SAAS,CAAC,sBAAsB,CAAC,IAC5B,CACT,CAAA;AACH,CAAC","sourcesContent":["import { KeyRound } from 'lucide-react'\nimport { Badge } from '@elytracms/studio/ui'\nimport { isDevAutoLoginActive } from '../lib/persistence'\nimport { translate } from '../lib/i18n'\n\n/**\n * Marker for the dev-only unattended auth (see `resolveDevAutoLogin`). When the\n * studio auto-signs-in from `VITE_ELYTRA_DEV_LOGIN` in a dev build, this badge\n * is visible in the shell header so an auto-authenticated session is never\n * silent; in production builds and without the flag it renders nothing.\n */\nexport function DevLoginBadge() {\n if (!isDevAutoLoginActive()) return null\n return (\n <Badge variant=\"warning\" title={translate('shell.devLogin.hint')} data-testid=\"dev-login-badge\">\n <KeyRound aria-hidden />\n {translate('shell.devLogin.badge')}\n </Badge>\n )\n}\n"]}
|
|
@@ -3,7 +3,7 @@ import { useBlocker } from '@elytracms/studio/router';
|
|
|
3
3
|
import { TriangleAlert } from 'lucide-react';
|
|
4
4
|
import { useState } from 'react';
|
|
5
5
|
import { Button, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, Row, } from '@elytracms/studio/ui';
|
|
6
|
-
import { translate } from '
|
|
6
|
+
import { translate } from '../lib/i18n/index.js';
|
|
7
7
|
/**
|
|
8
8
|
* Reusable route-transition + unsaved-change guard (brief §8.1, §8.7). Blocks
|
|
9
9
|
* in-app navigation (and the browser unload) while `when` is true, then offers
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"guards.js","sourceRoot":"","sources":["../../src/components/guards.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAA;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAChC,OAAO,EACL,MAAM,EACN,MAAM,EACN,aAAa,EACb,iBAAiB,EACjB,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,GAAG,GACJ,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"guards.js","sourceRoot":"","sources":["../../src/components/guards.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAA;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAChC,OAAO,EACL,MAAM,EACN,MAAM,EACN,aAAa,EACb,iBAAiB,EACjB,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,GAAG,GACJ,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAevC;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,EAClC,IAAI,EACJ,MAAM,EACN,KAAK,GAAG,SAAS,CAAC,4BAA4B,CAAC,EAC/C,WAAW,GAAG,SAAS,CAAC,2BAA2B,CAAC,EACpD,SAAS,GAAG,SAAS,CAAC,oBAAoB,CAAC,GAClB;IACzB,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,UAAU,CAAC;QAC5C,aAAa,EAAE,GAAG,EAAE,CAAC,IAAI;QACzB,kBAAkB,EAAE,GAAG,EAAE,CAAC,IAAI;QAC9B,YAAY,EAAE,IAAI;KACnB,CAAC,CAAA;IACF,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IAC3C,MAAM,OAAO,GAAG,MAAM,KAAK,SAAS,CAAA;IAEpC,KAAK,UAAU,UAAU;QACvB,SAAS,CAAC,IAAI,CAAC,CAAA;QACf,IAAI,CAAC;YACH,MAAM,EAAE,GAAG,CAAC,MAAM,MAAM,EAAE,EAAE,CAAC,IAAI,IAAI,CAAA;YACrC,IAAI,EAAE;gBAAE,OAAO,EAAE,EAAE,CAAA;;gBACd,KAAK,EAAE,EAAE,CAAA;QAChB,CAAC;gBAAS,CAAC;YACT,SAAS,CAAC,KAAK,CAAC,CAAA;QAClB,CAAC;IACH,CAAC;IAED,OAAO,CACL,KAAC,MAAM,IAAC,IAAI,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,YAC5E,MAAC,aAAa,IAAC,SAAS,EAAC,UAAU,aACjC,MAAC,YAAY,eACX,MAAC,GAAG,IAAC,GAAG,EAAC,GAAG,aACV,eAAM,SAAS,EAAC,+FAA+F,YAC7G,KAAC,aAAa,IAAC,SAAS,EAAC,QAAQ,GAAG,GAC/B,EACP,KAAC,WAAW,cAAE,KAAK,GAAe,IAC9B,EACN,KAAC,iBAAiB,IAAC,SAAS,EAAC,MAAM,YAAE,WAAW,GAAqB,IACxD,EACf,MAAC,YAAY,IAAC,SAAS,EAAC,uDAAuD,aAC7E,KAAC,MAAM,IAAC,OAAO,EAAC,OAAO,EAAC,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,EAAE,EAAE,EAAE,QAAQ,EAAE,MAAM,YAC/D,SAAS,CAAC,eAAe,CAAC,GACpB,EACT,MAAC,GAAG,IAAC,GAAG,EAAC,GAAG,EAAC,OAAO,EAAC,KAAK,aACxB,KAAC,MAAM,IAAC,OAAO,EAAC,SAAS,EAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,MAAM,YACnE,SAAS,CAAC,gBAAgB,CAAC,GACrB,EACT,KAAC,MAAM,IAAC,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,YAC1C,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,6BAA6B,CAAC,CAAC,CAAC,CAAC,SAAS,GACvD,IACL,IACO,IACD,GACT,CACV,CAAA;AACH,CAAC","sourcesContent":["import { useBlocker } from '@elytracms/studio/router'\nimport { TriangleAlert } from 'lucide-react'\nimport { useState } from 'react'\nimport {\n Button,\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n Row,\n} from '@elytracms/studio/ui'\nimport { translate } from '../lib/i18n'\n\nexport interface UnsavedChangesGuardProps {\n /** Block navigation while true (i.e. the surface has unsaved edits). */\n when: boolean\n /**\n * Save handler. Resolve `true` to allow the pending navigation, `false` to\n * keep the user on the page (e.g. validation failed). Defaults to allowing.\n */\n onSave?: () => boolean | Promise<boolean>\n title?: string\n description?: string\n saveLabel?: string\n}\n\n/**\n * Reusable route-transition + unsaved-change guard (brief §8.1, §8.7). Blocks\n * in-app navigation (and the browser unload) while `when` is true, then offers\n * save / discard / cancel. Used by Settings now; reusable in the Builder later.\n */\nexport function UnsavedChangesGuard({\n when,\n onSave,\n title = translate('shell.unsavedChanges.title'),\n description = translate('shell.unsavedChanges.body'),\n saveLabel = translate('common.saveChanges'),\n}: UnsavedChangesGuardProps) {\n const { status, proceed, reset } = useBlocker({\n shouldBlockFn: () => when,\n enableBeforeUnload: () => when,\n withResolver: true,\n })\n const [saving, setSaving] = useState(false)\n const blocked = status === 'blocked'\n\n async function handleSave() {\n setSaving(true)\n try {\n const ok = (await onSave?.()) ?? true\n if (ok) proceed?.()\n else reset?.()\n } finally {\n setSaving(false)\n }\n }\n\n return (\n <Dialog open={blocked} onOpenChange={(open) => (!open ? reset?.() : undefined)}>\n <DialogContent className=\"max-w-md\">\n <DialogHeader>\n <Row gap=\"2\">\n <span className=\"grid size-8 place-items-center rounded-lg border border-warning/30 bg-warning/10 text-warning\">\n <TriangleAlert className=\"size-4\" />\n </span>\n <DialogTitle>{title}</DialogTitle>\n </Row>\n <DialogDescription className=\"pt-1\">{description}</DialogDescription>\n </DialogHeader>\n <DialogFooter className=\"flex-col-reverse gap-2 sm:flex-row sm:justify-between\">\n <Button variant=\"ghost\" onClick={() => reset?.()} disabled={saving}>\n {translate('common.cancel')}\n </Button>\n <Row gap=\"2\" justify=\"end\">\n <Button variant=\"outline\" onClick={() => proceed?.()} disabled={saving}>\n {translate('common.discard')}\n </Button>\n <Button onClick={handleSave} disabled={saving}>\n {saving ? translate('shell.unsavedChanges.saving') : saveLabel}\n </Button>\n </Row>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n )\n}\n"]}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { Locale } from '@elytracms/core/cms-core';
|
|
3
|
+
import type { OperationsClient } from '@elytracms/core/operations';
|
|
4
|
+
import type { AppViewQueryHooks } from '../app-views/module.js';
|
|
5
|
+
import type { InspectorPane } from './inspector-sidebar.js';
|
|
6
|
+
/**
|
|
7
|
+
* EC-618 — the DOCUMENT-PANE contribution seam (AD-19's edit-shell slot): a project — or
|
|
8
|
+
* an addon it installs — adds a pane to the document editor's inspector beside the
|
|
9
|
+
* frame's own Details / Versions / Issues, through ONE direct-value key:
|
|
10
|
+
*
|
|
11
|
+
* ```tsx
|
|
12
|
+
* // studio.config.tsx
|
|
13
|
+
* import { analyticsDocumentPane } from '@elytracms/analytics/studio'
|
|
14
|
+
* export default defineStudio({ sidebar: [ … ], documentPanes: [analyticsDocumentPane] })
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* A pane is told which document it is on and reaches the studio the way a view does
|
|
18
|
+
* (the operations client, the query hooks over the one cache, the content locale). It
|
|
19
|
+
* appears on CMS documents only — a record rail (users, redirects, an addon's kind) has no
|
|
20
|
+
* document to report on — and, with `collections`, only where it says. Its tab id is
|
|
21
|
+
* namespaced (`contributed:<index>:<id>`), so a contribution can never shadow or collide
|
|
22
|
+
* with the frame's own panes or with another contribution.
|
|
23
|
+
*/
|
|
24
|
+
/** What a contributed pane is told about the document it is on. */
|
|
25
|
+
export interface DocumentPaneContext {
|
|
26
|
+
projectId: string;
|
|
27
|
+
/** The document's collection id. */
|
|
28
|
+
collection: string;
|
|
29
|
+
documentId: string;
|
|
30
|
+
/** The studio's operations client — every read and write, authorized at the chokepoint. */
|
|
31
|
+
operations(): Promise<OperationsClient>;
|
|
32
|
+
/** The query hooks over the studio's one operations-backed cache. */
|
|
33
|
+
query: AppViewQueryHooks;
|
|
34
|
+
/** The ambient content locale (`null` until the project's locale config is installed). */
|
|
35
|
+
locale: Locale | null;
|
|
36
|
+
}
|
|
37
|
+
/** A pane contributed to the document editor's inspector (`defineStudio({ documentPanes })`). */
|
|
38
|
+
export interface DocumentPane {
|
|
39
|
+
/** Stable within the contributions (the tab id is namespaced — see the module header). */
|
|
40
|
+
id: string;
|
|
41
|
+
label: string;
|
|
42
|
+
icon?: ReactNode;
|
|
43
|
+
/** The collections it appears on; absent → every CMS collection. */
|
|
44
|
+
collections?: readonly string[];
|
|
45
|
+
render: (ctx: DocumentPaneContext) => ReactNode;
|
|
46
|
+
}
|
|
47
|
+
/** Typed identity helper for a contributed pane. */
|
|
48
|
+
export declare function documentPane(pane: DocumentPane): DocumentPane;
|
|
49
|
+
/** The contributed tab id — namespaced, so it can never equal a frame pane's (`details`, `versions`, `issues`). */
|
|
50
|
+
export declare function contributedPaneId(index: number, pane: DocumentPane): string;
|
|
51
|
+
/** PURE: the inspector panes the contributions yield on one document. */
|
|
52
|
+
export declare function contributedInspectorPanes(panes: readonly DocumentPane[] | undefined, ctx: DocumentPaneContext): InspectorPane[];
|
|
53
|
+
/** The contributed panes of one CMS document (the project's `defineStudio({ documentPanes })`). */
|
|
54
|
+
export declare function useContributedDocumentPanes(projectId: string, collection: string, documentId: string): InspectorPane[];
|