@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
|
@@ -3,7 +3,7 @@ import type { AssetRecord } from '@elytracms/core/persistence';
|
|
|
3
3
|
import type { ListingQuery, RenderAsset, RenderContext, ResolveAsset, ResolveLink, ResolveListing, ResolveRelation } from '@elytracms/core/runtime-renderer';
|
|
4
4
|
import type { ContentQueryError } from '@elytracms/core/content';
|
|
5
5
|
import type { ComponentNode } from '@elytracms/core/project-graph';
|
|
6
|
-
import type { StudioRegistry } from '
|
|
6
|
+
import type { StudioRegistry } from '../../lib/registry/index.js';
|
|
7
7
|
import type { ReuseCapability } from './reuse.js';
|
|
8
8
|
/**
|
|
9
9
|
* The canvas's `RenderContext` (EC-330, extracted from `BuilderCanvas`): the
|
|
@@ -32,7 +32,13 @@ export declare function unresolvedReuseStatus(node: ComponentNode, capability: R
|
|
|
32
32
|
collection: string;
|
|
33
33
|
id: string;
|
|
34
34
|
}, perspective: Perspective) => unknown, perspective: Perspective): 'unpublished' | 'missing' | null;
|
|
35
|
-
/**
|
|
35
|
+
/**
|
|
36
|
+
* Everything the pure context factory needs — the live SOURCE (resolvers over the
|
|
37
|
+
* content session, and the session schema's verdict on a listing query) plus the
|
|
38
|
+
* editor's decoration state. EC-592: the factory is a call of core's ONE
|
|
39
|
+
* `createRenderContext` (`mode: 'canvas'`) — the delivery host calls the same one
|
|
40
|
+
* over its baked source.
|
|
41
|
+
*/
|
|
36
42
|
export interface CanvasRenderContextDeps {
|
|
37
43
|
registry: RenderContext['registry'];
|
|
38
44
|
implementations: RenderContext['implementations'];
|
|
@@ -48,28 +54,30 @@ export interface CanvasRenderContextDeps {
|
|
|
48
54
|
richEditEpochs: Record<string, number>;
|
|
49
55
|
unresolvedReuse(node: ComponentNode): 'unpublished' | 'missing' | null;
|
|
50
56
|
/**
|
|
51
|
-
* EC-460 (never-silent)
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
57
|
+
* EC-460 (never-silent) / EC-581 6a: why a runnable listing query is STATICALLY
|
|
58
|
+
* invalid against the active session schema (unknown collection, non-filterable
|
|
59
|
+
* filter/sort field) — localized messages, or `null` when it can run. The render
|
|
60
|
+
* context judges the declaration itself and shows the notice above the block —
|
|
61
|
+
* the same judgment the delivery host reads from its baked refusals.
|
|
56
62
|
*/
|
|
57
|
-
|
|
63
|
+
listingFailure(query: ListingQuery): readonly string[] | null;
|
|
58
64
|
}
|
|
59
65
|
/**
|
|
60
|
-
* The
|
|
61
|
-
*
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
66
|
+
* The session schema's verdict on a listing query, localized (EC-494:
|
|
67
|
+
* `ContentQueryError` carries a catalog ref) — the canvas's `listingFailure` source.
|
|
68
|
+
*/
|
|
69
|
+
export declare function sessionListingFailure(validate: (query: ListingQuery) => readonly ContentQueryError[]): (query: ListingQuery) => readonly string[] | null;
|
|
70
|
+
/**
|
|
71
|
+
* The canvas notice text for a listing node that cannot run, or `null` (EC-460, the
|
|
72
|
+
* never-silent rule) — core's ONE judgment (`listingFailureOf`: the registry's verdict
|
|
73
|
+
* on the declaration, then `validate`'s on the built query; a listing awaiting its
|
|
74
|
+
* bound value keeps the block's own empty state). Pure, exported for the node test.
|
|
68
75
|
*/
|
|
69
76
|
export declare function canvasListingIssue(node: ComponentNode, registry: RenderContext['registry'], validate: (query: ListingQuery) => readonly ContentQueryError[]): string | null;
|
|
70
77
|
/**
|
|
71
|
-
* Assemble the canvas `RenderContext
|
|
72
|
-
*
|
|
78
|
+
* Assemble the canvas `RenderContext` — core's ONE `createRenderContext` over the live
|
|
79
|
+
* source, `mode: 'canvas'`, with the editor's decoration. Pure (no hooks): the same
|
|
80
|
+
* deps produce the same context, so the decorator's wrapper markup is pinnable.
|
|
73
81
|
*/
|
|
74
82
|
export declare function createCanvasRenderContext(deps: CanvasRenderContextDeps): RenderContext;
|
|
75
83
|
/**
|
|
@@ -84,8 +92,8 @@ export declare function createCanvasRenderContext(deps: CanvasRenderContextDeps)
|
|
|
84
92
|
* only and drop `meta` — so a component's AI badge (`meta.aiGenerated`) and an
|
|
85
93
|
* editor-set `meta.alt` rendered on the site but not on the canvas. One reader
|
|
86
94
|
* (`resolvedAsset`, `content/assets.ts`) means the canvas and the host cannot
|
|
87
|
-
* disagree about an asset's own facts; `RenderAsset`
|
|
88
|
-
*
|
|
95
|
+
* disagree about an asset's own facts; `RenderAsset` IS `ResolvedAsset` (EC-587 —
|
|
96
|
+
* one resolved shape in every position). Pure, exported for the node test.
|
|
89
97
|
*/
|
|
90
98
|
export declare function canvasRenderAsset(record: AssetRecord | undefined): RenderAsset | null;
|
|
91
99
|
export declare function useCanvasRenderContext(args: {
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { jsx as _jsx,
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useEffect, useMemo, useSyncExternalStore } from 'react';
|
|
3
3
|
import { DRAFT_PERSPECTIVE } from '@elytracms/core/cms-core';
|
|
4
4
|
import { resolvedAsset } from '@elytracms/core/content';
|
|
5
|
-
import {
|
|
6
|
-
import { translate } from '
|
|
7
|
-
import { contentQueryErrorMessage } from '
|
|
8
|
-
import { acquireContentSessionConsumer, contentSessionEpoch, resolveCanvasLink, resolveCanvasListing, resolveCanvasRelation, subscribeContentSession, validateCanvasListingQuery, } from '
|
|
9
|
-
import { useStudioLocale } from '
|
|
5
|
+
import { createRenderContext, listingFailureOf } from '@elytracms/core/runtime-renderer';
|
|
6
|
+
import { translate } from '../../lib/i18n/index.js';
|
|
7
|
+
import { contentQueryErrorMessage } from '../../lib/persona/labels.js';
|
|
8
|
+
import { acquireContentSessionConsumer, contentSessionEpoch, resolveCanvasLink, resolveCanvasListing, resolveCanvasRelation, subscribeContentSession, validateCanvasListingQuery, } from '../../lib/data/index.js';
|
|
9
|
+
import { useStudioLocale } from '../../lib/locale/index.js';
|
|
10
10
|
import { useOptionalAssets } from '../assets/assets-store.js';
|
|
11
11
|
import { useReuse } from './reuse-context.js';
|
|
12
12
|
import { reuseEmbedRef } from './reuse.js';
|
|
@@ -51,42 +51,44 @@ export function unresolvedReuseStatus(node, capability, resolveAt, perspective)
|
|
|
51
51
|
return resolveAt(ref, DRAFT_PERSPECTIVE) ? 'unpublished' : 'missing';
|
|
52
52
|
}
|
|
53
53
|
/**
|
|
54
|
-
* The
|
|
55
|
-
*
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
54
|
+
* The session schema's verdict on a listing query, localized (EC-494:
|
|
55
|
+
* `ContentQueryError` carries a catalog ref) — the canvas's `listingFailure` source.
|
|
56
|
+
*/
|
|
57
|
+
export function sessionListingFailure(validate) {
|
|
58
|
+
return (query) => {
|
|
59
|
+
const errors = validate(query);
|
|
60
|
+
return errors.length === 0 ? null : errors.map((error) => contentQueryErrorMessage(error));
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* The canvas notice text for a listing node that cannot run, or `null` (EC-460, the
|
|
65
|
+
* never-silent rule) — core's ONE judgment (`listingFailureOf`: the registry's verdict
|
|
66
|
+
* on the declaration, then `validate`'s on the built query; a listing awaiting its
|
|
67
|
+
* bound value keeps the block's own empty state). Pure, exported for the node test.
|
|
62
68
|
*/
|
|
63
69
|
export function canvasListingIssue(node, registry, validate) {
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
return null;
|
|
67
|
-
const listing = nodeListingOf(node, manifest, registry);
|
|
68
|
-
if (!listing || listing.state === 'awaiting')
|
|
69
|
-
return null;
|
|
70
|
-
if (listing.state === 'invalid')
|
|
71
|
-
return listing.messages.join(' ');
|
|
72
|
-
const errors = validate(listing.query);
|
|
73
|
-
if (errors.length === 0)
|
|
74
|
-
return null;
|
|
75
|
-
// EC-494: `ContentQueryError` carries a catalog ref, so this notice localizes.
|
|
76
|
-
return errors.map((error) => contentQueryErrorMessage(error)).join(' ');
|
|
70
|
+
const failure = listingFailureOf(node, registry, { listingFailure: sessionListingFailure(validate) });
|
|
71
|
+
return failure ? failure.join(' ') : null;
|
|
77
72
|
}
|
|
78
73
|
/**
|
|
79
|
-
* Assemble the canvas `RenderContext
|
|
80
|
-
*
|
|
74
|
+
* Assemble the canvas `RenderContext` — core's ONE `createRenderContext` over the live
|
|
75
|
+
* source, `mode: 'canvas'`, with the editor's decoration. Pure (no hooks): the same
|
|
76
|
+
* deps produce the same context, so the decorator's wrapper markup is pinnable.
|
|
81
77
|
*/
|
|
82
78
|
export function createCanvasRenderContext(deps) {
|
|
83
|
-
return {
|
|
84
|
-
registry: deps.registry,
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
79
|
+
return createRenderContext({
|
|
80
|
+
components: { registry: deps.registry, implementations: deps.implementations },
|
|
81
|
+
mode: 'canvas',
|
|
82
|
+
source: {
|
|
83
|
+
resolveAsset: deps.resolveAsset,
|
|
84
|
+
resolveRelation: deps.resolveRelation,
|
|
85
|
+
resolveListing: deps.resolveListing,
|
|
86
|
+
resolveLink: deps.resolveLink,
|
|
87
|
+
listingFailure: deps.listingFailure,
|
|
88
|
+
},
|
|
89
|
+
// EC-460: a listing that cannot run gets the canvas's own (chrome-styled,
|
|
90
|
+
// localized) notice ABOVE the block, which keeps its empty state below.
|
|
91
|
+
listingNotice: (_node, messages) => _jsx(CanvasListingIssueNotice, { message: messages.join(' ') }),
|
|
90
92
|
decorateNode: (node, rendered) => {
|
|
91
93
|
if (!deps.pageNodeIds.has(node.id))
|
|
92
94
|
return rendered;
|
|
@@ -94,12 +96,9 @@ export function createCanvasRenderContext(deps) {
|
|
|
94
96
|
// transclusion; the node stays selectable, so its inspector relation chip
|
|
95
97
|
// (EC-296) deep-links to the reusable to edit/publish it.
|
|
96
98
|
const unresolved = deps.unresolvedReuse(node);
|
|
97
|
-
|
|
98
|
-
// block (which keeps its own empty state below) — never a silent empty grid.
|
|
99
|
-
const listingIssue = deps.listingIssueOf(node);
|
|
100
|
-
return (_jsx("div", { "data-ec-node-id": node.id, className: "ec-editable", "data-selected": node.id === deps.selectedId || undefined, "data-hovered": node.id === deps.hoveredId || undefined, children: unresolved ? (_jsx(UnresolvedReusablePlaceholder, { status: unresolved })) : (_jsxs(_Fragment, { children: [listingIssue !== null ? _jsx(CanvasListingIssueNotice, { message: listingIssue }) : null, rendered] })) }, `${node.id}#${deps.richEditEpochs[node.id] ?? 0}`));
|
|
99
|
+
return (_jsx("div", { "data-ec-node-id": node.id, className: "ec-editable", "data-selected": node.id === deps.selectedId || undefined, "data-hovered": node.id === deps.hoveredId || undefined, children: unresolved ? _jsx(UnresolvedReusablePlaceholder, { status: unresolved }) : rendered }, `${node.id}#${deps.richEditEpochs[node.id] ?? 0}`));
|
|
101
100
|
},
|
|
102
|
-
};
|
|
101
|
+
});
|
|
103
102
|
}
|
|
104
103
|
/**
|
|
105
104
|
* The live canvas render context: binds the pure factory to the studio's
|
|
@@ -113,8 +112,8 @@ export function createCanvasRenderContext(deps) {
|
|
|
113
112
|
* only and drop `meta` — so a component's AI badge (`meta.aiGenerated`) and an
|
|
114
113
|
* editor-set `meta.alt` rendered on the site but not on the canvas. One reader
|
|
115
114
|
* (`resolvedAsset`, `content/assets.ts`) means the canvas and the host cannot
|
|
116
|
-
* disagree about an asset's own facts; `RenderAsset`
|
|
117
|
-
*
|
|
115
|
+
* disagree about an asset's own facts; `RenderAsset` IS `ResolvedAsset` (EC-587 —
|
|
116
|
+
* one resolved shape in every position). Pure, exported for the node test.
|
|
118
117
|
*/
|
|
119
118
|
export function canvasRenderAsset(record) {
|
|
120
119
|
if (!record?.url)
|
|
@@ -175,8 +174,8 @@ export function useCanvasRenderContext(args) {
|
|
|
175
174
|
// target → null → the rich-text renderer shows the visible `unresolved-ref` issue.
|
|
176
175
|
const resolveLink = useCallback((ref) => resolveCanvasLink(ref, activeLocale, perspective), [activeLocale, perspective, sessionEpoch]);
|
|
177
176
|
const unresolvedReuse = useCallback((node) => unresolvedReuseStatus(node, reuse?.capability, (ref, p) => resolveCanvasRelation(ref, activeLocale, p), perspective), [reuse, activeLocale, perspective, sessionEpoch]);
|
|
178
|
-
// EC-460 (never-silent):
|
|
179
|
-
const
|
|
177
|
+
// EC-460 (never-silent): the session schema's verdict on a listing query.
|
|
178
|
+
const listingFailure = useMemo(() => sessionListingFailure((query) => validateCanvasListingQuery(query)), [sessionEpoch]);
|
|
180
179
|
const ctx = useMemo(() => createCanvasRenderContext({
|
|
181
180
|
registry: studio.registry,
|
|
182
181
|
implementations: studio.implementations,
|
|
@@ -189,8 +188,8 @@ export function useCanvasRenderContext(args) {
|
|
|
189
188
|
hoveredId,
|
|
190
189
|
richEditEpochs,
|
|
191
190
|
unresolvedReuse,
|
|
192
|
-
|
|
193
|
-
}), [studio, resolveAsset, resolveRelation, resolveListing, resolveLink, unresolvedReuse, pageNodeIds, selectedId, hoveredId, richEditEpochs,
|
|
191
|
+
listingFailure,
|
|
192
|
+
}), [studio, resolveAsset, resolveRelation, resolveListing, resolveLink, unresolvedReuse, pageNodeIds, selectedId, hoveredId, richEditEpochs, listingFailure]);
|
|
194
193
|
return { ctx, pageNodeIds };
|
|
195
194
|
}
|
|
196
195
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"canvas-render-context.js","sourceRoot":"","sources":["../../../src/components/builder/canvas-render-context.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useEffect, useMemo, useSyncExternalStore } from 'react'\nimport { DRAFT_PERSPECTIVE } from '@elytracms/core/cms-core'\nimport type { Perspective } from '@elytracms/core/cms-core'\nimport { resolvedAsset } from '@elytracms/core/content'\nimport type { AssetRecord } from '@elytracms/core/persistence'\nimport { nodeListingOf } from '@elytracms/core/runtime-renderer'\nimport type {\n ListingQuery,\n RenderAsset,\n RenderContext,\n ResolveAsset,\n ResolveLink,\n ResolveListing,\n ResolveRelation,\n} from '@elytracms/core/runtime-renderer'\nimport type { ContentQueryError } from '@elytracms/core/content'\nimport type { ComponentNode } from '@elytracms/core/project-graph'\nimport type { StudioRegistry } from '@elytracms/studio/lib/registry'\nimport { translate } from '@elytracms/studio/lib/i18n'\nimport { contentQueryErrorMessage } from '@elytracms/studio/lib/persona/labels'\nimport {\n acquireContentSessionConsumer,\n contentSessionEpoch,\n resolveCanvasLink,\n resolveCanvasListing,\n resolveCanvasRelation,\n subscribeContentSession,\n validateCanvasListingQuery,\n} from '@elytracms/studio/lib/data'\nimport { useStudioLocale } from '@elytracms/studio/lib/locale'\nimport { useOptionalAssets } from '../assets/assets-store'\nimport { useReuse } from './reuse-context'\nimport { reuseEmbedRef } from './reuse'\nimport type { ReuseCapability } from './reuse'\n\n/**\n * The canvas's `RenderContext` (EC-330, extracted from `BuilderCanvas`): the\n * injected resolvers that make the iframe preview match delivery — assets\n * (EC-195), relations (EC-254), listings (EC-255), rich-text links (EC-291) —\n * plus the per-node decorator that stamps the editable wrappers (selection/hover\n * state, rich-text remount epochs, and the EC-300 unresolved-reusable\n * placeholder). The binding resolver and the EC-176 locale-fallback chips (which\n * read bound props) went with the data-binding subsystem (EC-574).\n *\n * The context assembly is a pure factory ({@link createCanvasRenderContext})\n * over a pure decision helper ({@link unresolvedReuseStatus}), so the decoration\n * and resolver wiring are unit-testable without providers;\n * {@link useCanvasRenderContext} binds them to the live stores.\n */\n\n/** Collect every node id under a root (the page subtree). */\nexport function collectIds(node: ComponentNode, out: Set<string> = new Set()): Set<string> {\n out.add(node.id)\n for (const children of Object.values(node.slots ?? {})) {\n for (const child of children) collectIds(child, out)\n }\n return out\n}\n\n/**\n * EC-300/AD-16: a transcluding embed whose target doesn't resolve in the active\n * perspective renders a placeholder, never an empty section. `unpublished` = the\n * target exists as a draft but isn't live (Published view of a draft-only reusable);\n * `missing` = no resolvable target at all (deleted). `null` = resolves fine, or the\n * node isn't an embed / has no target picked yet (its own empty state owns that).\n */\nexport function unresolvedReuseStatus(\n node: ComponentNode,\n capability: ReuseCapability | null | undefined,\n resolveAt: (ref: { collection: string; id: string }, perspective: Perspective) => unknown,\n perspective: Perspective,\n): 'unpublished' | 'missing' | null {\n if (!capability) return null\n const ref = reuseEmbedRef(node, capability)\n if (!ref) return null\n if (resolveAt(ref, perspective)) return null\n return resolveAt(ref, DRAFT_PERSPECTIVE) ? 'unpublished' : 'missing'\n}\n\n/** Everything the pure context factory needs — resolvers plus decoration state. */\nexport interface CanvasRenderContextDeps {\n registry: RenderContext['registry']\n implementations: RenderContext['implementations']\n resolveAsset: ResolveAsset\n resolveRelation: ResolveRelation\n resolveListing: ResolveListing\n resolveLink: ResolveLink\n /** Only page nodes are editable; layout chrome must not be decorated. */\n pageNodeIds: ReadonlySet<string>\n selectedId: string | null\n hoveredId: string | null\n /** Per-node remount epochs from the inline rich-text editing (EC-159). */\n richEditEpochs: Record<string, number>\n unresolvedReuse(node: ComponentNode): 'unpublished' | 'missing' | null\n /**\n * EC-460 (never-silent): a human-readable message when the node declares a\n * `listing` whose built query is STATICALLY invalid against the active session\n * schema (unknown collection, non-filterable filter/sort field) — the canvas\n * then shows a visible notice above the block instead of an empty grid that\n * is indistinguishable from \"no content yet\". `null` = healthy (or no listing).\n */\n listingIssueOf(node: ComponentNode): string | null\n}\n\n/**\n * The canvas notice for a listing node that cannot run, or `null` (EC-460, the\n * never-silent rule). Reads the shared `nodeListingOf` — the same reader the\n * renderer and the delivery host use — so notice and resolution never disagree\n * about WHICH query was judged. EC-581 6a: a declaration the registry rejected\n * (`invalid-listing`) shows its registration issue; a runnable query is judged by\n * `validate` (the session schema's `validateWhere`/`validateSort`/`validateLimit`);\n * a listing awaiting its bound value keeps the block's own empty state. Pure,\n * exported for the node test.\n */\nexport function canvasListingIssue(\n node: ComponentNode,\n registry: RenderContext['registry'],\n validate: (query: ListingQuery) => readonly ContentQueryError[],\n): string | null {\n const manifest = registry.getManifest(node.componentId)\n if (!manifest) return null\n const listing = nodeListingOf(node, manifest, registry)\n if (!listing || listing.state === 'awaiting') return null\n if (listing.state === 'invalid') return listing.messages.join(' ')\n const errors = validate(listing.query)\n if (errors.length === 0) return null\n // EC-494: `ContentQueryError` carries a catalog ref, so this notice localizes.\n return errors.map((error) => contentQueryErrorMessage(error)).join(' ')\n}\n\n/**\n * Assemble the canvas `RenderContext`. Pure (no hooks): the same deps produce\n * the same context, so the decorator's wrapper markup is pinnable in tests.\n */\nexport function createCanvasRenderContext(deps: CanvasRenderContextDeps): RenderContext {\n return {\n registry: deps.registry,\n implementations: deps.implementations,\n resolveAsset: deps.resolveAsset,\n resolveRelation: deps.resolveRelation,\n resolveListing: deps.resolveListing,\n resolveLink: deps.resolveLink,\n decorateNode: (node, rendered) => {\n if (!deps.pageNodeIds.has(node.id)) return rendered\n // An unresolved hard reference shows the placeholder in place of the empty\n // transclusion; the node stays selectable, so its inspector relation chip\n // (EC-296) deep-links to the reusable to edit/publish it.\n const unresolved = deps.unresolvedReuse(node)\n // EC-460: a config-invalid listing query gets a visible notice ABOVE the\n // block (which keeps its own empty state below) — never a silent empty grid.\n const listingIssue = deps.listingIssueOf(node)\n return (\n <div\n // The epoch suffix forces a remount after a rich-text editing\n // session mutated this node's rendered DOM (see richEditEpochs).\n key={`${node.id}#${deps.richEditEpochs[node.id] ?? 0}`}\n data-ec-node-id={node.id}\n className=\"ec-editable\"\n data-selected={node.id === deps.selectedId || undefined}\n data-hovered={node.id === deps.hoveredId || undefined}\n >\n {unresolved ? (\n <UnresolvedReusablePlaceholder status={unresolved} />\n ) : (\n <>\n {listingIssue !== null ? <CanvasListingIssueNotice message={listingIssue} /> : null}\n {rendered}\n </>\n )}\n </div>\n )\n },\n }\n}\n\n/**\n * The live canvas render context: binds the pure factory to the studio's\n * stores (asset library, content session, active locale, reuse capability) for\n * the given perspective. Returns the context plus the page-node id set the\n * canvas also needs for spacing-handle gating.\n */\n/**\n * The canvas's asset reader IS delivery's (NK-049 finding, 2026-10-23). This used to\n * hand-pick `url/width/height/alt/focalPoint`, read `alt` from the legacy column\n * only and drop `meta` — so a component's AI badge (`meta.aiGenerated`) and an\n * editor-set `meta.alt` rendered on the site but not on the canvas. One reader\n * (`resolvedAsset`, `content/assets.ts`) means the canvas and the host cannot\n * disagree about an asset's own facts; `RenderAsset` is a subset of\n * `ResolvedAsset`, `lqip` included. Pure, exported for the node test.\n */\nexport function canvasRenderAsset(record: AssetRecord | undefined): RenderAsset | null {\n if (!record?.url) return null\n return resolvedAsset(record, record.url)\n}\n\nexport function useCanvasRenderContext(args: {\n /** The edited composition root (`builder.present`). */\n present: ComponentNode\n selectedId: string | null\n hoveredId: string | null\n studio: StudioRegistry\n perspective: Perspective\n richEditEpochs: Record<string, number>\n}): { ctx: RenderContext; pageNodeIds: ReadonlySet<string> } {\n const { present, selectedId, hoveredId, studio, perspective, richEditEpochs } = args\n\n // EC-460: the canvas follows the preview content session reactively. The\n // epoch bumps whenever `configureContentSession` re-points the session (the\n // write-sync refresh, a CMS-surface hydration, boot) — it keys the resolver\n // callbacks below, so the context identity changes and the canvas re-renders\n // against the fresh session. Registering as a CONSUMER tells the write-sync a\n // preview surface is live, so a stale session actually refreshes (with no\n // consumer it only flags — connected mode never pays a per-autosave query\n // while nobody is previewing).\n const sessionEpoch = useSyncExternalStore(subscribeContentSession, contentSessionEpoch)\n useEffect(() => acquireContentSessionConsumer(), [])\n\n // Only page nodes are editable; layout chrome (rendered around the page) is\n // read-only context and must not be selectable (it isn't in the page tree).\n const pageNodeIds = useMemo(() => collectIds(present), [present])\n\n // EC-195: resolve an `asset`-typed Image prop (an asset id) from the live asset\n // library, so the canvas previews exactly what the host renders. A record\n // without a resolved url (e.g. a never-served seed) → null → the\n // implementation's visible empty state.\n const assetsStore = useOptionalAssets()\n const resolveAsset: ResolveAsset = useCallback(\n (assetId) => canvasRenderAsset(assetsStore?.assets.find((a) => a.id === assetId)),\n [assetsStore],\n )\n\n // The canvas's active locale for relation/listing/link resolution below.\n // EC-479: the ambient locale is `null` until a project's config is installed (it\n // no longer opens on the engine's `en/de` fixture). `undefined` means \"you pick\" to\n // the content engine, which then resolves in the SESSION's own default locale —\n // never a fabricated one — so a canvas mounted outside a CMS session still previews\n // in the project's locale.\n const { locale } = useStudioLocale()\n const activeLocale = locale ?? undefined\n // EC-300: the reuse capability identifies a transcluding embed + its relation prop,\n // so the canvas can detect a HARD reference whose target doesn't resolve and show a\n // placeholder instead of an empty section. Null-safe (no provider / no capability).\n const reuse = useReuse()\n\n // EC-254: populate a block's `relation` prop (the editor's document pick — a\n // ProductGrid's products, a Reusable's element) to delivery shape from the\n // active content session, in the canvas's active locale, so the canvas previews\n // picked documents exactly as the host renders them. The studio analog of the\n // host's baked relations map. A missing/invisible target → null → the block's\n // own empty state.\n const resolveRelation: ResolveRelation = useCallback(\n (ref) => resolveCanvasRelation(ref, activeLocale, perspective),\n // sessionEpoch: re-resolve when the content session was re-pointed (EC-460).\n [activeLocale, perspective, sessionEpoch],\n )\n\n // EC-255: resolve a listing block's query (a ProductGrid keyed to a category) to\n // its documents from the active content session, in the canvas's active locale, so\n // the canvas previews a dynamic grid exactly as the host renders it. The studio\n // analog of the host's baked listings map; an empty/invalid query → [] → the\n // block's own empty state.\n const resolveListing: ResolveListing = useCallback(\n (query) => resolveCanvasListing(query, activeLocale, perspective),\n [activeLocale, perspective, sessionEpoch],\n )\n\n // EC-291: resolve a rich-text internal link (`{type:'reference'}`) to its target's\n // URL path from the active content session, in the canvas's active locale — the\n // studio analog of the host's baked links map. A missing/unpublished/unroutable\n // target → null → the rich-text renderer shows the visible `unresolved-ref` issue.\n const resolveLink: ResolveLink = useCallback(\n (ref) => resolveCanvasLink(ref, activeLocale, perspective),\n [activeLocale, perspective, sessionEpoch],\n )\n\n const unresolvedReuse = useCallback(\n (node: ComponentNode): 'unpublished' | 'missing' | null =>\n unresolvedReuseStatus(\n node,\n reuse?.capability,\n (ref, p) => resolveCanvasRelation(ref, activeLocale, p),\n perspective,\n ),\n [reuse, activeLocale, perspective, sessionEpoch],\n )\n\n // EC-460 (never-silent): flag a listing that cannot run (see `canvasListingIssue`).\n const listingIssueOf = useCallback(\n (node: ComponentNode): string | null =>\n canvasListingIssue(node, studio.registry, (query) => validateCanvasListingQuery(query)),\n [studio, sessionEpoch],\n )\n\n const ctx: RenderContext = useMemo(\n () =>\n createCanvasRenderContext({\n registry: studio.registry,\n implementations: studio.implementations,\n resolveAsset,\n resolveRelation,\n resolveListing,\n resolveLink,\n pageNodeIds,\n selectedId,\n hoveredId,\n richEditEpochs,\n unresolvedReuse,\n listingIssueOf,\n }),\n [studio, resolveAsset, resolveRelation, resolveListing, resolveLink, unresolvedReuse, pageNodeIds, selectedId, hoveredId, richEditEpochs, listingIssueOf],\n )\n\n return { ctx, pageNodeIds }\n}\n\n/**\n * In-canvas placeholder for a transcluding embed whose target doesn't resolve\n * (EC-300/AD-16) — shown in place of the empty section so a hard reference never\n * silently vanishes. Rendered INTO the iframe (portal), which carries only the\n * project's stylesheet, so it uses inline styles rather than studio UI classes\n * (the EC-414 AD-B chrome contract), with the `.ec-chrome` containment recipe\n * on its root. Exported for the chrome contract test. The node stays\n * selectable; the inspector's `element` relation chip (EC-296) deep-links to\n * the reusable to edit or publish it.\n */\n/**\n * In-canvas notice for a listing block whose query is statically invalid\n * against the active session schema (EC-460 — the never-silent rule): the\n * message renders ABOVE the block (which keeps its own empty state), so a\n * misdeclared `listing` manifest (non-filterable sort/filter field, unknown\n * collection) is an explicit, visible state — never an empty grid an editor\n * can't tell apart from \"no content yet\". Rendered INTO the iframe (project\n * stylesheet only), so it uses inline styles + the `.ec-chrome` containment\n * recipe, exactly like {@link UnresolvedReusablePlaceholder}. Exported for the\n * chrome contract test.\n */\nexport function CanvasListingIssueNotice({ message }: { message: string }) {\n return (\n <div\n className=\"ec-chrome\"\n data-testid=\"canvas-listing-issue\"\n style={{\n boxSizing: 'border-box',\n border: '1px dashed rgba(200,140,40,0.65)',\n borderRadius: 8,\n padding: '10px 14px',\n margin: '8px 16px',\n background: 'rgba(230,160,40,0.08)',\n color: 'rgba(120,80,10,0.95)',\n font: '13px/1.5 system-ui, -apple-system, sans-serif',\n }}\n >\n <span style={{ fontWeight: 600 }}>⚠ {translate('builder.canvas.listingInvalidTitle')}</span>{' '}\n <span style={{ opacity: 0.9 }}>{message}</span>\n </div>\n )\n}\n\nexport function UnresolvedReusablePlaceholder({ status }: { status: 'unpublished' | 'missing' }) {\n const title =\n status === 'unpublished'\n ? translate('builder.canvas.reusableUnpublishedTitle')\n : translate('builder.canvas.reusableMissingTitle')\n const hint =\n status === 'unpublished'\n ? translate('builder.canvas.reusableUnpublishedHint')\n : translate('builder.canvas.reusableMissingHint')\n return (\n <div\n className=\"ec-chrome\"\n style={{\n boxSizing: 'border-box',\n border: '1px dashed rgba(120,120,120,0.5)',\n borderRadius: 8,\n padding: '24px 28px',\n margin: '12px 16px',\n background: 'rgba(127,127,127,0.06)',\n color: 'rgba(90,90,90,0.95)',\n font: '14px/1.5 system-ui, -apple-system, sans-serif',\n textAlign: 'center',\n }}\n >\n <div style={{ fontWeight: 600 }}>⚠ {title}</div>\n <div style={{ marginTop: 6, fontSize: 13, opacity: 0.85 }}>{hint}</div>\n </div>\n )\n}\n"]}
|
|
1
|
+
{"version":3,"file":"canvas-render-context.js","sourceRoot":"","sources":["../../../src/components/builder/canvas-render-context.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useEffect, useMemo, useSyncExternalStore } from 'react'\nimport { DRAFT_PERSPECTIVE } from '@elytracms/core/cms-core'\nimport type { Perspective } from '@elytracms/core/cms-core'\nimport { resolvedAsset } from '@elytracms/core/content'\nimport type { AssetRecord } from '@elytracms/core/persistence'\nimport { createRenderContext, listingFailureOf } from '@elytracms/core/runtime-renderer'\nimport type {\n ListingQuery,\n RenderAsset,\n RenderContext,\n ResolveAsset,\n ResolveLink,\n ResolveListing,\n ResolveRelation,\n} from '@elytracms/core/runtime-renderer'\nimport type { ContentQueryError } from '@elytracms/core/content'\nimport type { ComponentNode } from '@elytracms/core/project-graph'\nimport type { StudioRegistry } from '../../lib/registry'\nimport { translate } from '../../lib/i18n'\nimport { contentQueryErrorMessage } from '../../lib/persona/labels'\nimport {\n acquireContentSessionConsumer,\n contentSessionEpoch,\n resolveCanvasLink,\n resolveCanvasListing,\n resolveCanvasRelation,\n subscribeContentSession,\n validateCanvasListingQuery,\n} from '../../lib/data'\nimport { useStudioLocale } from '../../lib/locale'\nimport { useOptionalAssets } from '../assets/assets-store'\nimport { useReuse } from './reuse-context'\nimport { reuseEmbedRef } from './reuse'\nimport type { ReuseCapability } from './reuse'\n\n/**\n * The canvas's `RenderContext` (EC-330, extracted from `BuilderCanvas`): the\n * injected resolvers that make the iframe preview match delivery — assets\n * (EC-195), relations (EC-254), listings (EC-255), rich-text links (EC-291) —\n * plus the per-node decorator that stamps the editable wrappers (selection/hover\n * state, rich-text remount epochs, and the EC-300 unresolved-reusable\n * placeholder). The binding resolver and the EC-176 locale-fallback chips (which\n * read bound props) went with the data-binding subsystem (EC-574).\n *\n * The context assembly is a pure factory ({@link createCanvasRenderContext})\n * over a pure decision helper ({@link unresolvedReuseStatus}), so the decoration\n * and resolver wiring are unit-testable without providers;\n * {@link useCanvasRenderContext} binds them to the live stores.\n */\n\n/** Collect every node id under a root (the page subtree). */\nexport function collectIds(node: ComponentNode, out: Set<string> = new Set()): Set<string> {\n out.add(node.id)\n for (const children of Object.values(node.slots ?? {})) {\n for (const child of children) collectIds(child, out)\n }\n return out\n}\n\n/**\n * EC-300/AD-16: a transcluding embed whose target doesn't resolve in the active\n * perspective renders a placeholder, never an empty section. `unpublished` = the\n * target exists as a draft but isn't live (Published view of a draft-only reusable);\n * `missing` = no resolvable target at all (deleted). `null` = resolves fine, or the\n * node isn't an embed / has no target picked yet (its own empty state owns that).\n */\nexport function unresolvedReuseStatus(\n node: ComponentNode,\n capability: ReuseCapability | null | undefined,\n resolveAt: (ref: { collection: string; id: string }, perspective: Perspective) => unknown,\n perspective: Perspective,\n): 'unpublished' | 'missing' | null {\n if (!capability) return null\n const ref = reuseEmbedRef(node, capability)\n if (!ref) return null\n if (resolveAt(ref, perspective)) return null\n return resolveAt(ref, DRAFT_PERSPECTIVE) ? 'unpublished' : 'missing'\n}\n\n/**\n * Everything the pure context factory needs — the live SOURCE (resolvers over the\n * content session, and the session schema's verdict on a listing query) plus the\n * editor's decoration state. EC-592: the factory is a call of core's ONE\n * `createRenderContext` (`mode: 'canvas'`) — the delivery host calls the same one\n * over its baked source.\n */\nexport interface CanvasRenderContextDeps {\n registry: RenderContext['registry']\n implementations: RenderContext['implementations']\n resolveAsset: ResolveAsset\n resolveRelation: ResolveRelation\n resolveListing: ResolveListing\n resolveLink: ResolveLink\n /** Only page nodes are editable; layout chrome must not be decorated. */\n pageNodeIds: ReadonlySet<string>\n selectedId: string | null\n hoveredId: string | null\n /** Per-node remount epochs from the inline rich-text editing (EC-159). */\n richEditEpochs: Record<string, number>\n unresolvedReuse(node: ComponentNode): 'unpublished' | 'missing' | null\n /**\n * EC-460 (never-silent) / EC-581 6a: why a runnable listing query is STATICALLY\n * invalid against the active session schema (unknown collection, non-filterable\n * filter/sort field) — localized messages, or `null` when it can run. The render\n * context judges the declaration itself and shows the notice above the block —\n * the same judgment the delivery host reads from its baked refusals.\n */\n listingFailure(query: ListingQuery): readonly string[] | null\n}\n\n/**\n * The session schema's verdict on a listing query, localized (EC-494:\n * `ContentQueryError` carries a catalog ref) — the canvas's `listingFailure` source.\n */\nexport function sessionListingFailure(\n validate: (query: ListingQuery) => readonly ContentQueryError[],\n): (query: ListingQuery) => readonly string[] | null {\n return (query) => {\n const errors = validate(query)\n return errors.length === 0 ? null : errors.map((error) => contentQueryErrorMessage(error))\n }\n}\n\n/**\n * The canvas notice text for a listing node that cannot run, or `null` (EC-460, the\n * never-silent rule) — core's ONE judgment (`listingFailureOf`: the registry's verdict\n * on the declaration, then `validate`'s on the built query; a listing awaiting its\n * bound value keeps the block's own empty state). Pure, exported for the node test.\n */\nexport function canvasListingIssue(\n node: ComponentNode,\n registry: RenderContext['registry'],\n validate: (query: ListingQuery) => readonly ContentQueryError[],\n): string | null {\n const failure = listingFailureOf(node, registry, { listingFailure: sessionListingFailure(validate) })\n return failure ? failure.join(' ') : null\n}\n\n/**\n * Assemble the canvas `RenderContext` — core's ONE `createRenderContext` over the live\n * source, `mode: 'canvas'`, with the editor's decoration. Pure (no hooks): the same\n * deps produce the same context, so the decorator's wrapper markup is pinnable.\n */\nexport function createCanvasRenderContext(deps: CanvasRenderContextDeps): RenderContext {\n return createRenderContext({\n components: { registry: deps.registry, implementations: deps.implementations },\n mode: 'canvas',\n source: {\n resolveAsset: deps.resolveAsset,\n resolveRelation: deps.resolveRelation,\n resolveListing: deps.resolveListing,\n resolveLink: deps.resolveLink,\n listingFailure: deps.listingFailure,\n },\n // EC-460: a listing that cannot run gets the canvas's own (chrome-styled,\n // localized) notice ABOVE the block, which keeps its empty state below.\n listingNotice: (_node, messages) => <CanvasListingIssueNotice message={messages.join(' ')} />,\n decorateNode: (node, rendered) => {\n if (!deps.pageNodeIds.has(node.id)) return rendered\n // An unresolved hard reference shows the placeholder in place of the empty\n // transclusion; the node stays selectable, so its inspector relation chip\n // (EC-296) deep-links to the reusable to edit/publish it.\n const unresolved = deps.unresolvedReuse(node)\n return (\n <div\n // The epoch suffix forces a remount after a rich-text editing\n // session mutated this node's rendered DOM (see richEditEpochs).\n key={`${node.id}#${deps.richEditEpochs[node.id] ?? 0}`}\n data-ec-node-id={node.id}\n className=\"ec-editable\"\n data-selected={node.id === deps.selectedId || undefined}\n data-hovered={node.id === deps.hoveredId || undefined}\n >\n {unresolved ? <UnresolvedReusablePlaceholder status={unresolved} /> : rendered}\n </div>\n )\n },\n })\n}\n\n/**\n * The live canvas render context: binds the pure factory to the studio's\n * stores (asset library, content session, active locale, reuse capability) for\n * the given perspective. Returns the context plus the page-node id set the\n * canvas also needs for spacing-handle gating.\n */\n/**\n * The canvas's asset reader IS delivery's (NK-049 finding, 2026-10-23). This used to\n * hand-pick `url/width/height/alt/focalPoint`, read `alt` from the legacy column\n * only and drop `meta` — so a component's AI badge (`meta.aiGenerated`) and an\n * editor-set `meta.alt` rendered on the site but not on the canvas. One reader\n * (`resolvedAsset`, `content/assets.ts`) means the canvas and the host cannot\n * disagree about an asset's own facts; `RenderAsset` IS `ResolvedAsset` (EC-587 —\n * one resolved shape in every position). Pure, exported for the node test.\n */\nexport function canvasRenderAsset(record: AssetRecord | undefined): RenderAsset | null {\n if (!record?.url) return null\n return resolvedAsset(record, record.url)\n}\n\nexport function useCanvasRenderContext(args: {\n /** The edited composition root (`builder.present`). */\n present: ComponentNode\n selectedId: string | null\n hoveredId: string | null\n studio: StudioRegistry\n perspective: Perspective\n richEditEpochs: Record<string, number>\n}): { ctx: RenderContext; pageNodeIds: ReadonlySet<string> } {\n const { present, selectedId, hoveredId, studio, perspective, richEditEpochs } = args\n\n // EC-460: the canvas follows the preview content session reactively. The\n // epoch bumps whenever `configureContentSession` re-points the session (the\n // write-sync refresh, a CMS-surface hydration, boot) — it keys the resolver\n // callbacks below, so the context identity changes and the canvas re-renders\n // against the fresh session. Registering as a CONSUMER tells the write-sync a\n // preview surface is live, so a stale session actually refreshes (with no\n // consumer it only flags — connected mode never pays a per-autosave query\n // while nobody is previewing).\n const sessionEpoch = useSyncExternalStore(subscribeContentSession, contentSessionEpoch)\n useEffect(() => acquireContentSessionConsumer(), [])\n\n // Only page nodes are editable; layout chrome (rendered around the page) is\n // read-only context and must not be selectable (it isn't in the page tree).\n const pageNodeIds = useMemo(() => collectIds(present), [present])\n\n // EC-195: resolve an `asset`-typed Image prop (an asset id) from the live asset\n // library, so the canvas previews exactly what the host renders. A record\n // without a resolved url (e.g. a never-served seed) → null → the\n // implementation's visible empty state.\n const assetsStore = useOptionalAssets()\n const resolveAsset: ResolveAsset = useCallback(\n (assetId) => canvasRenderAsset(assetsStore?.assets.find((a) => a.id === assetId)),\n [assetsStore],\n )\n\n // The canvas's active locale for relation/listing/link resolution below.\n // EC-479: the ambient locale is `null` until a project's config is installed (it\n // no longer opens on the engine's `en/de` fixture). `undefined` means \"you pick\" to\n // the content engine, which then resolves in the SESSION's own default locale —\n // never a fabricated one — so a canvas mounted outside a CMS session still previews\n // in the project's locale.\n const { locale } = useStudioLocale()\n const activeLocale = locale ?? undefined\n // EC-300: the reuse capability identifies a transcluding embed + its relation prop,\n // so the canvas can detect a HARD reference whose target doesn't resolve and show a\n // placeholder instead of an empty section. Null-safe (no provider / no capability).\n const reuse = useReuse()\n\n // EC-254: populate a block's `relation` prop (the editor's document pick — a\n // ProductGrid's products, a Reusable's element) to delivery shape from the\n // active content session, in the canvas's active locale, so the canvas previews\n // picked documents exactly as the host renders them. The studio analog of the\n // host's baked relations map. A missing/invisible target → null → the block's\n // own empty state.\n const resolveRelation: ResolveRelation = useCallback(\n (ref) => resolveCanvasRelation(ref, activeLocale, perspective),\n // sessionEpoch: re-resolve when the content session was re-pointed (EC-460).\n [activeLocale, perspective, sessionEpoch],\n )\n\n // EC-255: resolve a listing block's query (a ProductGrid keyed to a category) to\n // its documents from the active content session, in the canvas's active locale, so\n // the canvas previews a dynamic grid exactly as the host renders it. The studio\n // analog of the host's baked listings map; an empty/invalid query → [] → the\n // block's own empty state.\n const resolveListing: ResolveListing = useCallback(\n (query) => resolveCanvasListing(query, activeLocale, perspective),\n [activeLocale, perspective, sessionEpoch],\n )\n\n // EC-291: resolve a rich-text internal link (`{type:'reference'}`) to its target's\n // URL path from the active content session, in the canvas's active locale — the\n // studio analog of the host's baked links map. A missing/unpublished/unroutable\n // target → null → the rich-text renderer shows the visible `unresolved-ref` issue.\n const resolveLink: ResolveLink = useCallback(\n (ref) => resolveCanvasLink(ref, activeLocale, perspective),\n [activeLocale, perspective, sessionEpoch],\n )\n\n const unresolvedReuse = useCallback(\n (node: ComponentNode): 'unpublished' | 'missing' | null =>\n unresolvedReuseStatus(\n node,\n reuse?.capability,\n (ref, p) => resolveCanvasRelation(ref, activeLocale, p),\n perspective,\n ),\n [reuse, activeLocale, perspective, sessionEpoch],\n )\n\n // EC-460 (never-silent): the session schema's verdict on a listing query.\n const listingFailure = useMemo(\n () => sessionListingFailure((query) => validateCanvasListingQuery(query)),\n [sessionEpoch],\n )\n\n const ctx: RenderContext = useMemo(\n () =>\n createCanvasRenderContext({\n registry: studio.registry,\n implementations: studio.implementations,\n resolveAsset,\n resolveRelation,\n resolveListing,\n resolveLink,\n pageNodeIds,\n selectedId,\n hoveredId,\n richEditEpochs,\n unresolvedReuse,\n listingFailure,\n }),\n [studio, resolveAsset, resolveRelation, resolveListing, resolveLink, unresolvedReuse, pageNodeIds, selectedId, hoveredId, richEditEpochs, listingFailure],\n )\n\n return { ctx, pageNodeIds }\n}\n\n/**\n * In-canvas placeholder for a transcluding embed whose target doesn't resolve\n * (EC-300/AD-16) — shown in place of the empty section so a hard reference never\n * silently vanishes. Rendered INTO the iframe (portal), which carries only the\n * project's stylesheet, so it uses inline styles rather than studio UI classes\n * (the EC-414 AD-B chrome contract), with the `.ec-chrome` containment recipe\n * on its root. Exported for the chrome contract test. The node stays\n * selectable; the inspector's `element` relation chip (EC-296) deep-links to\n * the reusable to edit or publish it.\n */\n/**\n * In-canvas notice for a listing block whose query is statically invalid\n * against the active session schema (EC-460 — the never-silent rule): the\n * message renders ABOVE the block (which keeps its own empty state), so a\n * misdeclared `listing` manifest (non-filterable sort/filter field, unknown\n * collection) is an explicit, visible state — never an empty grid an editor\n * can't tell apart from \"no content yet\". Rendered INTO the iframe (project\n * stylesheet only), so it uses inline styles + the `.ec-chrome` containment\n * recipe, exactly like {@link UnresolvedReusablePlaceholder}. Exported for the\n * chrome contract test.\n */\nexport function CanvasListingIssueNotice({ message }: { message: string }) {\n return (\n <div\n className=\"ec-chrome\"\n data-testid=\"canvas-listing-issue\"\n style={{\n boxSizing: 'border-box',\n border: '1px dashed rgba(200,140,40,0.65)',\n borderRadius: 8,\n padding: '10px 14px',\n margin: '8px 16px',\n background: 'rgba(230,160,40,0.08)',\n color: 'rgba(120,80,10,0.95)',\n font: '13px/1.5 system-ui, -apple-system, sans-serif',\n }}\n >\n <span style={{ fontWeight: 600 }}>⚠ {translate('builder.canvas.listingInvalidTitle')}</span>{' '}\n <span style={{ opacity: 0.9 }}>{message}</span>\n </div>\n )\n}\n\nexport function UnresolvedReusablePlaceholder({ status }: { status: 'unpublished' | 'missing' }) {\n const title =\n status === 'unpublished'\n ? translate('builder.canvas.reusableUnpublishedTitle')\n : translate('builder.canvas.reusableMissingTitle')\n const hint =\n status === 'unpublished'\n ? translate('builder.canvas.reusableUnpublishedHint')\n : translate('builder.canvas.reusableMissingHint')\n return (\n <div\n className=\"ec-chrome\"\n style={{\n boxSizing: 'border-box',\n border: '1px dashed rgba(120,120,120,0.5)',\n borderRadius: 8,\n padding: '24px 28px',\n margin: '12px 16px',\n background: 'rgba(127,127,127,0.06)',\n color: 'rgba(90,90,90,0.95)',\n font: '14px/1.5 system-ui, -apple-system, sans-serif',\n textAlign: 'center',\n }}\n >\n <div style={{ fontWeight: 600 }}>⚠ {title}</div>\n <div style={{ marginTop: 6, fontSize: 13, opacity: 0.85 }}>{hint}</div>\n </div>\n )\n}\n"]}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { PreservedSequenceItem } from '@elytracms/core/project-graph';
|
|
2
|
+
/**
|
|
3
|
+
* The canvas's typography runs (EC-549, owner-ratified 4d) — PATH-addressed items.
|
|
4
|
+
*
|
|
5
|
+
* A `blocks` value may legitimately carry loose typography (a surface switch, a
|
|
6
|
+
* transfer, an AI write, a prose-first body promoted to a canvas). Typography carries no
|
|
7
|
+
* ids (EC-500 D1), so the canvas addresses it by position: a RUN is one maximal group of
|
|
8
|
+
* consecutive typography items of the merged sequence — exactly the region delivery's
|
|
9
|
+
* body classification renders as one (`documentBodyItems`), since the merge never puts a
|
|
10
|
+
* component inside a group of preserved items. The store holds the preserved items
|
|
11
|
+
* (`BuilderState.preserved`, with their component anchors); a run is keyed by the index
|
|
12
|
+
* of its FIRST preserved item, which is stable under every component edit (they never
|
|
13
|
+
* reorder preserved items), and located in the field value by its `range` — the
|
|
14
|
+
* half-open index range of its items in the merged sequence's `content`.
|
|
15
|
+
*/
|
|
16
|
+
export type PreservedItem = PreservedSequenceItem<unknown>;
|
|
17
|
+
/** One typography run of the merged sequence. */
|
|
18
|
+
export interface CanvasRun {
|
|
19
|
+
/** The run's key: the index of its first preserved item. */
|
|
20
|
+
key: number;
|
|
21
|
+
/** Its preserved items' indices, in order. */
|
|
22
|
+
members: number[];
|
|
23
|
+
/** Its path in the merged sequence: `content[start..end)`. */
|
|
24
|
+
range: {
|
|
25
|
+
start: number;
|
|
26
|
+
end: number;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/** Every run of the merged sequence, in order. Pure. */
|
|
30
|
+
export declare function canvasRuns(rootCount: number, preserved: readonly PreservedItem[]): CanvasRun[];
|
|
31
|
+
/** The run whose key is `key`, or `undefined` (no such run any more). */
|
|
32
|
+
export declare function runByKey(rootCount: number, preserved: readonly PreservedItem[], key: number): CanvasRun | undefined;
|
|
33
|
+
/** The run that starts at sequence index `start` — what a rendered region's path names. */
|
|
34
|
+
export declare function runAtPath(rootCount: number, preserved: readonly PreservedItem[], start: number): CanvasRun | undefined;
|
|
35
|
+
/** The run's typography nodes, as the v2 value the inspector's rich-text editor edits. */
|
|
36
|
+
export declare function runValue(preserved: readonly PreservedItem[], run: CanvasRun): {
|
|
37
|
+
version: 2;
|
|
38
|
+
content: unknown[];
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Replace a run's items with `content` (the inspector's edit) — anchored where the run
|
|
42
|
+
* was, so the merge puts the new items in the same place. An empty `content` deletes the
|
|
43
|
+
* run. Pure; the preserved list keeps every other item byte-for-byte.
|
|
44
|
+
*/
|
|
45
|
+
export declare function replaceRun(preserved: readonly PreservedItem[], run: CanvasRun, content: readonly unknown[]): PreservedItem[];
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { blockRootsOrigins } from '@elytracms/core/project-graph';
|
|
2
|
+
/** Every run of the merged sequence, in order. Pure. */
|
|
3
|
+
export function canvasRuns(rootCount, preserved) {
|
|
4
|
+
const runs = [];
|
|
5
|
+
let current = null;
|
|
6
|
+
blockRootsOrigins(rootCount, preserved).forEach((origin, position) => {
|
|
7
|
+
if (origin.kind === 'root') {
|
|
8
|
+
current = null;
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
if (current === null) {
|
|
12
|
+
current = { key: origin.index, members: [], range: { start: position, end: position } };
|
|
13
|
+
runs.push(current);
|
|
14
|
+
}
|
|
15
|
+
current.members.push(origin.index);
|
|
16
|
+
current.range = { start: current.range.start, end: position + 1 };
|
|
17
|
+
});
|
|
18
|
+
return runs;
|
|
19
|
+
}
|
|
20
|
+
/** The run whose key is `key`, or `undefined` (no such run any more). */
|
|
21
|
+
export function runByKey(rootCount, preserved, key) {
|
|
22
|
+
return canvasRuns(rootCount, preserved).find((run) => run.key === key);
|
|
23
|
+
}
|
|
24
|
+
/** The run that starts at sequence index `start` — what a rendered region's path names. */
|
|
25
|
+
export function runAtPath(rootCount, preserved, start) {
|
|
26
|
+
return canvasRuns(rootCount, preserved).find((run) => run.range.start === start);
|
|
27
|
+
}
|
|
28
|
+
/** The run's typography nodes, as the v2 value the inspector's rich-text editor edits. */
|
|
29
|
+
export function runValue(preserved, run) {
|
|
30
|
+
return { version: 2, content: run.members.map((index) => preserved[index].node) };
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Replace a run's items with `content` (the inspector's edit) — anchored where the run
|
|
34
|
+
* was, so the merge puts the new items in the same place. An empty `content` deletes the
|
|
35
|
+
* run. Pure; the preserved list keeps every other item byte-for-byte.
|
|
36
|
+
*/
|
|
37
|
+
export function replaceRun(preserved, run, content) {
|
|
38
|
+
const first = run.members[0];
|
|
39
|
+
const anchor = preserved[first].componentPrefix;
|
|
40
|
+
const members = new Set(run.members);
|
|
41
|
+
const out = [];
|
|
42
|
+
preserved.forEach((item, index) => {
|
|
43
|
+
if (index === first)
|
|
44
|
+
for (const node of content)
|
|
45
|
+
out.push({ node, componentPrefix: anchor });
|
|
46
|
+
if (!members.has(index))
|
|
47
|
+
out.push(item);
|
|
48
|
+
});
|
|
49
|
+
return out;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=canvas-runs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canvas-runs.js","sourceRoot":"","sources":["../../../src/components/builder/canvas-runs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAA;AA8BjE,wDAAwD;AACxD,MAAM,UAAU,UAAU,CAAC,SAAiB,EAAE,SAAmC;IAC/E,MAAM,IAAI,GAAgB,EAAE,CAAA;IAC5B,IAAI,OAAO,GAAqB,IAAI,CAAA;IACpC,iBAAiB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,QAAQ,EAAE,EAAE;QACnE,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAC3B,OAAO,GAAG,IAAI,CAAA;YACd,OAAM;QACR,CAAC;QACD,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACrB,OAAO,GAAG,EAAE,GAAG,EAAE,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE,CAAA;YACvF,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QACpB,CAAC;QACD,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QAClC,OAAO,CAAC,KAAK,GAAG,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,QAAQ,GAAG,CAAC,EAAE,CAAA;IACnE,CAAC,CAAC,CAAA;IACF,OAAO,IAAI,CAAA;AACb,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,QAAQ,CAAC,SAAiB,EAAE,SAAmC,EAAE,GAAW;IAC1F,OAAO,UAAU,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,KAAK,GAAG,CAAC,CAAA;AACxE,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,SAAS,CAAC,SAAiB,EAAE,SAAmC,EAAE,KAAa;IAC7F,OAAO,UAAU,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,KAAK,KAAK,CAAC,CAAA;AAClF,CAAC;AAED,0FAA0F;AAC1F,MAAM,UAAU,QAAQ,CAAC,SAAmC,EAAE,GAAc;IAC1E,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,SAAS,CAAC,KAAK,CAAE,CAAC,IAAI,CAAC,EAAE,CAAA;AACpF,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,SAAmC,EAAE,GAAc,EAAE,OAA2B;IACzG,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC,CAAE,CAAA;IAC7B,MAAM,MAAM,GAAG,SAAS,CAAC,KAAK,CAAE,CAAC,eAAe,CAAA;IAChD,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;IACpC,MAAM,GAAG,GAAoB,EAAE,CAAA;IAC/B,SAAS,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;QAChC,IAAI,KAAK,KAAK,KAAK;YAAE,KAAK,MAAM,IAAI,IAAI,OAAO;gBAAE,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE,CAAC,CAAA;QAC5F,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC;YAAE,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACzC,CAAC,CAAC,CAAA;IACF,OAAO,GAAG,CAAA;AACZ,CAAC","sourcesContent":["import { blockRootsOrigins } from '@elytracms/core/project-graph'\nimport type { PreservedSequenceItem } from '@elytracms/core/project-graph'\n\n/**\n * The canvas's typography runs (EC-549, owner-ratified 4d) — PATH-addressed items.\n *\n * A `blocks` value may legitimately carry loose typography (a surface switch, a\n * transfer, an AI write, a prose-first body promoted to a canvas). Typography carries no\n * ids (EC-500 D1), so the canvas addresses it by position: a RUN is one maximal group of\n * consecutive typography items of the merged sequence — exactly the region delivery's\n * body classification renders as one (`documentBodyItems`), since the merge never puts a\n * component inside a group of preserved items. The store holds the preserved items\n * (`BuilderState.preserved`, with their component anchors); a run is keyed by the index\n * of its FIRST preserved item, which is stable under every component edit (they never\n * reorder preserved items), and located in the field value by its `range` — the\n * half-open index range of its items in the merged sequence's `content`.\n */\n\nexport type PreservedItem = PreservedSequenceItem<unknown>\n\n/** One typography run of the merged sequence. */\nexport interface CanvasRun {\n /** The run's key: the index of its first preserved item. */\n key: number\n /** Its preserved items' indices, in order. */\n members: number[]\n /** Its path in the merged sequence: `content[start..end)`. */\n range: { start: number; end: number }\n}\n\n/** Every run of the merged sequence, in order. Pure. */\nexport function canvasRuns(rootCount: number, preserved: readonly PreservedItem[]): CanvasRun[] {\n const runs: CanvasRun[] = []\n let current: CanvasRun | null = null\n blockRootsOrigins(rootCount, preserved).forEach((origin, position) => {\n if (origin.kind === 'root') {\n current = null\n return\n }\n if (current === null) {\n current = { key: origin.index, members: [], range: { start: position, end: position } }\n runs.push(current)\n }\n current.members.push(origin.index)\n current.range = { start: current.range.start, end: position + 1 }\n })\n return runs\n}\n\n/** The run whose key is `key`, or `undefined` (no such run any more). */\nexport function runByKey(rootCount: number, preserved: readonly PreservedItem[], key: number): CanvasRun | undefined {\n return canvasRuns(rootCount, preserved).find((run) => run.key === key)\n}\n\n/** The run that starts at sequence index `start` — what a rendered region's path names. */\nexport function runAtPath(rootCount: number, preserved: readonly PreservedItem[], start: number): CanvasRun | undefined {\n return canvasRuns(rootCount, preserved).find((run) => run.range.start === start)\n}\n\n/** The run's typography nodes, as the v2 value the inspector's rich-text editor edits. */\nexport function runValue(preserved: readonly PreservedItem[], run: CanvasRun): { version: 2; content: unknown[] } {\n return { version: 2, content: run.members.map((index) => preserved[index]!.node) }\n}\n\n/**\n * Replace a run's items with `content` (the inspector's edit) — anchored where the run\n * was, so the merge puts the new items in the same place. An empty `content` deletes the\n * run. Pure; the preserved list keeps every other item byte-for-byte.\n */\nexport function replaceRun(preserved: readonly PreservedItem[], run: CanvasRun, content: readonly unknown[]): PreservedItem[] {\n const first = run.members[0]!\n const anchor = preserved[first]!.componentPrefix\n const members = new Set(run.members)\n const out: PreservedItem[] = []\n preserved.forEach((item, index) => {\n if (index === first) for (const node of content) out.push({ node, componentPrefix: anchor })\n if (!members.has(index)) out.push(item)\n })\n return out\n}\n"]}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useState } from 'react';
|
|
3
3
|
import { AlertTriangle, X } from 'lucide-react';
|
|
4
|
-
import { translate } from '
|
|
5
|
-
import { isDevRuntime } from '
|
|
4
|
+
import { translate } from '../../lib/i18n/index.js';
|
|
5
|
+
import { isDevRuntime } from '../../lib/persistence/index.js';
|
|
6
6
|
import { Row, Text } from '@elytracms/studio/ui';
|
|
7
7
|
/**
|
|
8
8
|
* EC-414 AD-D item 2 — the canvas-theme runtime notice, in the established
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"canvas-theme-notice.js","sourceRoot":"","sources":["../../../src/components/builder/canvas-theme-notice.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAChC,OAAO,EAAE,aAAa,EAAE,CAAC,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"canvas-theme-notice.js","sourceRoot":"","sources":["../../../src/components/builder/canvas-theme-notice.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAChC,OAAO,EAAE,aAAa,EAAE,CAAC,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AACpD,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAA;AAGhD;;;;;;;;;;;;;;GAcG;AAEH,MAAM,cAAc,GAAG,IAAI,GAAG,EAA6B,CAAA;AAE3D,sDAAsD;AACtD,MAAM,UAAU,8BAA8B;IAC5C,cAAc,CAAC,KAAK,EAAE,CAAA;AACxB,CAAC;AAED,MAAM,WAAW,GAAuD;IACtE,MAAM,EAAE,kCAAkC;IAC1C,kBAAkB,EAAE,2CAA2C;IAC/D,iBAAiB,EAAE,yCAAyC;IAC5D,eAAe,EAAE,wCAAwC;CAC1D,CAAA;AAED,MAAM,UAAU,iBAAiB,CAAC,EAAE,MAAM,EAAwC;IAChF,+EAA+E;IAC/E,MAAM,CAAC,EAAE,cAAc,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAA;IACtC,IAAI,CAAC,MAAM,IAAI,CAAC,YAAY,EAAE;QAAE,OAAO,IAAI,CAAA;IAC3C,MAAM,UAAU,GAAG,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;IAC3C,IAAI,CAAC,UAAU,IAAI,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAA;IAE/D,OAAO,CACL,6BACc,qBAAqB,sBACf,MAAM,CAAC,IAAI,EAC7B,SAAS,EAAC,4EAA4E,YAEtF,cAAK,SAAS,EAAC,kFAAkF,YAC/F,MAAC,GAAG,IAAC,GAAG,EAAC,GAAG,EAAC,KAAK,EAAC,OAAO,aACxB,KAAC,aAAa,IAAC,SAAS,EAAC,uCAAuC,wBAAe,EAC/E,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,SAAS,EAAC,gBAAgB,YACvC,SAAS,CAAC,UAAU,CAAC,GACjB,EACP,iBACE,IAAI,EAAC,QAAQ,gBACD,SAAS,CAAC,gBAAgB,CAAC,EACvC,OAAO,EAAE,GAAG,EAAE;4BACZ,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;4BAC/B,cAAc,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAA;wBAC5B,CAAC,EACD,SAAS,EAAC,mFAAmF,YAE7F,KAAC,CAAC,IAAC,SAAS,EAAC,UAAU,GAAG,GACnB,IACL,GACF,GACF,CACP,CAAA;AACH,CAAC","sourcesContent":["import { useState } from 'react'\nimport { AlertTriangle, X } from 'lucide-react'\nimport { translate } from '../../lib/i18n'\nimport { isDevRuntime } from '../../lib/persistence'\nimport { Row, Text } from '@elytracms/studio/ui'\nimport type { CanvasThemeStatus } from './canvas-chrome'\n\n/**\n * EC-414 AD-D item 2 — the canvas-theme runtime notice, in the established\n * validation-surface style (the `ClipboardNoticeOverlay` shape: a dismissible,\n * NEVER-blocking overlay above the canvas, in the STUDIO document — studio UI\n * primitives are fine here, this is not in-frame chrome). Dev builds only:\n *\n * - `legacy`: the probe found the merged pre-EC-414 canvas.css — a one-time\n * notice naming the migration (the canvas keeps working exactly as before).\n * - `stylesheet-error` / `no-theme-tokens` / `no-stylesheet`: the canvas\n * stylesheet is broken or missing — the invalid state is named explicitly\n * instead of silently rendering unstyled blocks.\n *\n * \"One-time\": dismissing a kind suppresses it for the rest of the studio\n * session (module state survives canvas remounts/navigation).\n */\n\nconst dismissedKinds = new Set<CanvasThemeStatus['kind']>()\n\n/** Test hook: reset the session-sticky dismissals. */\nexport function resetCanvasThemeNoticeForTests(): void {\n dismissedKinds.clear()\n}\n\nconst NOTICE_KEYS: Partial<Record<CanvasThemeStatus['kind'], string>> = {\n legacy: 'builder.canvasTheme.legacyNotice',\n 'stylesheet-error': 'builder.canvasTheme.stylesheetErrorNotice',\n 'no-theme-tokens': 'builder.canvasTheme.noThemeTokensNotice',\n 'no-stylesheet': 'builder.canvasTheme.noStylesheetNotice',\n}\n\nexport function CanvasThemeNotice({ status }: { status: CanvasThemeStatus | null }) {\n // Local state only forces the re-render on dismiss; the truth is module-level.\n const [, setDismissedAt] = useState(0)\n if (!status || !isDevRuntime()) return null\n const messageKey = NOTICE_KEYS[status.kind]\n if (!messageKey || dismissedKinds.has(status.kind)) return null\n\n return (\n <div\n data-testid=\"canvas-theme-notice\"\n data-notice-kind={status.kind}\n className=\"absolute top-4 left-1/2 z-20 w-max max-w-[min(460px,90%)] -translate-x-1/2\"\n >\n <div className=\"rounded-lg border border-border bg-popover p-2 text-popover-foreground shadow-lg\">\n <Row gap=\"2\" align=\"start\">\n <AlertTriangle className=\"mt-0.5 size-3.5 shrink-0 text-warning\" aria-hidden />\n <Text size=\"xs\" className=\"min-w-0 flex-1\">\n {translate(messageKey)}\n </Text>\n <button\n type=\"button\"\n aria-label={translate('common.dismiss')}\n onClick={() => {\n dismissedKinds.add(status.kind)\n setDismissedAt(Date.now())\n }}\n className=\"shrink-0 rounded p-0.5 text-muted-foreground hover:bg-muted hover:text-foreground\"\n >\n <X className=\"size-3.5\" />\n </button>\n </Row>\n </div>\n </div>\n )\n}\n"]}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
3
3
|
import { createPortal } from 'react-dom';
|
|
4
|
-
import {
|
|
4
|
+
import { setEditingMarkers } from '@elytracms/core/runtime-renderer';
|
|
5
5
|
import { getNode } from '@elytracms/core/project-graph';
|
|
6
|
-
import { getStudioRegistry } from '
|
|
7
|
-
import { translate } from '
|
|
6
|
+
import { getStudioRegistry } from '../../lib/registry/index.js';
|
|
7
|
+
import { translate } from '../../lib/i18n/index.js';
|
|
8
8
|
import { DRAFT_PERSPECTIVE } from '@elytracms/core/cms-core';
|
|
9
|
-
import { resolveDeploymentCanvasTheme } from '
|
|
9
|
+
import { resolveDeploymentCanvasTheme } from '../../lib/workspace/index.js';
|
|
10
10
|
import { wireCanvasChrome } from './canvas-chrome.js';
|
|
11
11
|
import { CanvasThemeNotice } from './canvas-theme-notice.js';
|
|
12
12
|
import { getCanvasStylesheetUrl } from './canvas-stylesheet.js';
|
|
@@ -22,6 +22,7 @@ import { SpacingHandles, hasSpacingHandles } from './spacing-handles.js';
|
|
|
22
22
|
import { SelectionOverlay } from './selection-overlay.js';
|
|
23
23
|
import { attachDeleteKeyListener } from './delete-key.js';
|
|
24
24
|
import { usePropSync } from './prop-sync-context.js';
|
|
25
|
+
import { renderCanvasBody } from './canvas-body.js';
|
|
25
26
|
import { resolvePropClick } from './prop-selection.js';
|
|
26
27
|
// EC-408: the studio canvas is the ONE surface that turns editing markers on, so
|
|
27
28
|
// `ecProp` in the base primitives emits `data-ec-prop` here. Delivery
|
|
@@ -55,6 +56,8 @@ export function BuilderCanvas({ perspective = DRAFT_PERSPECTIVE, } = {}) {
|
|
|
55
56
|
presentRef.current = builder.present;
|
|
56
57
|
const selectedIdRef = useRef(builder.selectedId);
|
|
57
58
|
selectedIdRef.current = builder.selectedId;
|
|
59
|
+
const selectedRunRef = useRef(builder.selectedRun);
|
|
60
|
+
selectedRunRef.current = builder.selectedRun;
|
|
58
61
|
// EC-414 AD-D: the settled canvas-theme classification (legacy merged shape /
|
|
59
62
|
// broken stylesheet) drives the dev-only, never-blocking notice overlay.
|
|
60
63
|
const [themeStatus, setThemeStatus] = useState(null);
|
|
@@ -108,6 +111,15 @@ export function BuilderCanvas({ perspective = DRAFT_PERSPECTIVE, } = {}) {
|
|
|
108
111
|
// Clicks inside the active inline editor are caret moves, not selection.
|
|
109
112
|
if (editingRef.current?.host.contains(e.target))
|
|
110
113
|
return;
|
|
114
|
+
// EC-549: the NEAREST canvas item is either a block (`data-ec-node-id`) or a
|
|
115
|
+
// typography run (`data-ec-run`, path-addressed) — a run selects as a run.
|
|
116
|
+
const item = e.target?.closest?.('[data-ec-run],[data-ec-node-id]');
|
|
117
|
+
const runKey = item?.getAttribute('data-ec-run');
|
|
118
|
+
if (runKey != null) {
|
|
119
|
+
e.preventDefault();
|
|
120
|
+
dispatch({ type: 'selectRun', run: Number(runKey) });
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
111
123
|
const wrapper = wrapperAt(e.target);
|
|
112
124
|
if (!wrapper)
|
|
113
125
|
return;
|
|
@@ -144,7 +156,7 @@ export function BuilderCanvas({ perspective = DRAFT_PERSPECTIVE, } = {}) {
|
|
|
144
156
|
}, [doc, dispatch, editingRef, presentRef, studio]);
|
|
145
157
|
// Drag & drop on the canvas (EC-160/EC-162) — see `canvas-dnd`.
|
|
146
158
|
// EC-554: the field's top-level `allow` rides with the store (`builder.field`).
|
|
147
|
-
useCanvasDnd(doc, { presentRef, dispatch, studio, editingRef,
|
|
159
|
+
useCanvasDnd(doc, { presentRef, dispatch, studio, editingRef, placement: builder.placement });
|
|
148
160
|
// Copy/cut/paste on the canvas (EC-175): the same studio clipboard the page
|
|
149
161
|
// tree and blocks panel use, wired to the *iframe* document (events never
|
|
150
162
|
// cross frames). Shortcuts stay inert while an inline editor is live and in
|
|
@@ -172,6 +184,14 @@ export function BuilderCanvas({ perspective = DRAFT_PERSPECTIVE, } = {}) {
|
|
|
172
184
|
getSelectedId: () => selectedIdRef.current,
|
|
173
185
|
remove: (id) => dispatch({ type: 'remove', id }),
|
|
174
186
|
getRootId: () => presentRef.current.id,
|
|
187
|
+
// EC-549: a selected typography run deletes as a run (one undo restores it).
|
|
188
|
+
removeSelectedRun: () => {
|
|
189
|
+
const run = selectedRunRef.current;
|
|
190
|
+
if (run === null)
|
|
191
|
+
return false;
|
|
192
|
+
dispatch({ type: 'removeRun', run });
|
|
193
|
+
return true;
|
|
194
|
+
},
|
|
175
195
|
});
|
|
176
196
|
}, [doc, dispatch]);
|
|
177
197
|
// The render context: the seven delivery-parity resolvers + the editable-
|
|
@@ -199,9 +219,16 @@ export function BuilderCanvas({ perspective = DRAFT_PERSPECTIVE, } = {}) {
|
|
|
199
219
|
const { viewports } = useCanvasViewports();
|
|
200
220
|
return (_jsxs("div", { className: "canvas-grid relative flex min-h-0 flex-1 items-stretch justify-center overflow-auto p-6", children: [_jsx(ClipboardNoticeOverlay, {}), _jsx(CanvasThemeNotice, { status: themeStatus }), _jsxs("div", { style: { width: viewportWidth(viewports, builder.viewport), maxWidth: '100%' }, className: "flex", children: [_jsx("iframe", { ref: iframeRef, onLoad: wireUp, title: translate('builder.canvas.pagePreview'), className: "min-h-[480px] w-full rounded-lg border border-border bg-background shadow-md", srcDoc: "<!doctype html><html><head></head><body></body></html>" }), doc
|
|
201
221
|
? createPortal(
|
|
202
|
-
//
|
|
203
|
-
//
|
|
204
|
-
|
|
222
|
+
// EC-592 + EC-549: the FIELD VALUE through delivery's one entry point —
|
|
223
|
+
// blocks as bare siblings, typography runs in their true positions —
|
|
224
|
+
// inside the decorated (never rendered) synthetic root.
|
|
225
|
+
renderCanvasBody({
|
|
226
|
+
root: activeRoot,
|
|
227
|
+
preserved: builder.preserved,
|
|
228
|
+
ctx,
|
|
229
|
+
selectedId: builder.selectedId,
|
|
230
|
+
selectedRun: builder.selectedRun,
|
|
231
|
+
}), doc.body)
|
|
205
232
|
: null, doc ? _jsx(SelectionOverlay, { doc: doc, editing: editing }) : null, spacingTarget && doc ? (_jsx(SpacingHandles, { doc: doc, node: spacingTarget }, spacingTarget.id)) : null, richEditHost && doc ? _jsx(RichTextToolbar, { doc: doc, host: richEditHost }) : null] })] }));
|
|
206
233
|
}
|
|
207
234
|
//# sourceMappingURL=canvas.js.map
|