@elytracms/studio 0.0.92 → 0.0.94
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/convex/analytics-cost.test.ts +817 -0
- package/convex/asset-alt-migration.test.ts +127 -0
- package/convex/asset-replace.test.ts +168 -0
- package/convex/asset-revalidate.test.ts +6 -6
- package/convex/assets.ts +41 -5
- package/convex/auth.ts +5 -2
- package/convex/authority.ts +35 -11
- package/convex/cms.ts +25 -11
- package/convex/contract-parity.test.ts +25 -3
- package/convex/delivery.ts +12 -9
- package/convex/document-revalidate.test.ts +10 -12
- package/convex/guard.ts +9 -7
- package/convex/http-routes.test.ts +18 -8
- package/convex/http.ts +23 -17
- package/convex/ingest.test.ts +326 -0
- package/convex/ingest.ts +281 -0
- package/convex/invites.ts +13 -5
- package/convex/members.ts +10 -4
- package/convex/orphan-hydration.test.ts +116 -0
- package/convex/rail-write-parity.test.ts +11 -8
- package/convex/record-functions.ts +325 -0
- package/convex/records.test.ts +156 -0
- package/convex/redirect-migration.test.ts +205 -0
- package/convex/redirect-revalidate.test.ts +5 -1
- package/convex/redirects.ts +120 -79
- package/convex/role-policy.test.ts +315 -0
- package/convex/roles.ts +83 -0
- package/convex/schema.ts +36 -0
- package/convex/triggers.ts +21 -1
- package/dist/addon.d.ts +71 -0
- package/dist/addon.js +67 -0
- package/dist/addon.js.map +1 -0
- package/dist/components/account/account-settings.js +2 -2
- package/dist/components/account/account-settings.js.map +1 -1
- package/dist/components/account/credential-panel.js +2 -2
- package/dist/components/account/credential-panel.js.map +1 -1
- package/dist/components/account/self-account-schema.d.ts +6 -15
- package/dist/components/account/self-account-schema.js +52 -34
- package/dist/components/account/self-account-schema.js.map +1 -1
- package/dist/components/account/self-account-view-source.d.ts +1 -1
- package/dist/components/account/self-account-view-source.js +6 -4
- package/dist/components/account/self-account-view-source.js.map +1 -1
- package/dist/components/ai/ai-chat-panel-lazy.js +1 -1
- package/dist/components/ai/ai-chat-panel-lazy.js.map +1 -1
- package/dist/components/ai/ai-chat-panel.js +6 -6
- package/dist/components/ai/ai-chat-panel.js.map +1 -1
- package/dist/components/ai/ai-cms-panel.js +2 -2
- package/dist/components/ai/ai-cms-panel.js.map +1 -1
- package/dist/components/ai/ai-panel-toggle.js +4 -4
- package/dist/components/ai/ai-panel-toggle.js.map +1 -1
- package/dist/components/ai/chat-ui.js +2 -2
- package/dist/components/ai/chat-ui.js.map +1 -1
- package/dist/components/ai/proposal-card.d.ts +2 -2
- package/dist/components/ai/proposal-card.js +4 -3
- package/dist/components/ai/proposal-card.js.map +1 -1
- package/dist/components/ai/proposal-issues.js +2 -2
- package/dist/components/ai/proposal-issues.js.map +1 -1
- package/dist/components/ai/translate-document-action.js +2 -2
- package/dist/components/ai/translate-document-action.js.map +1 -1
- package/dist/components/ai/translate-document-dialog.js +10 -8
- package/dist/components/ai/translate-document-dialog.js.map +1 -1
- package/dist/components/ai/translate-skipped.d.ts +19 -0
- package/dist/components/ai/translate-skipped.js +46 -0
- package/dist/components/ai/translate-skipped.js.map +1 -0
- package/dist/components/app-views/default-modules.d.ts +50 -2
- package/dist/components/app-views/default-modules.js +65 -20
- package/dist/components/app-views/default-modules.js.map +1 -1
- package/dist/components/app-views/module.d.ts +53 -3
- package/dist/components/app-views/module.js +14 -7
- package/dist/components/app-views/module.js.map +1 -1
- package/dist/components/app-views/sidebar.js +1 -1
- package/dist/components/app-views/sidebar.js.map +1 -1
- package/dist/components/app-views/studio-config.d.ts +11 -2
- package/dist/components/app-views/studio-config.js.map +1 -1
- package/dist/components/assets/asset-detail.js +20 -10
- package/dist/components/assets/asset-detail.js.map +1 -1
- package/dist/components/assets/asset-grid.js +2 -2
- package/dist/components/assets/asset-grid.js.map +1 -1
- package/dist/components/assets/asset-metadata.d.ts +15 -4
- package/dist/components/assets/asset-metadata.js +15 -2
- package/dist/components/assets/asset-metadata.js.map +1 -1
- package/dist/components/assets/asset-search.js +4 -1
- package/dist/components/assets/asset-search.js.map +1 -1
- package/dist/components/assets/asset-selection.d.ts +0 -8
- package/dist/components/assets/asset-selection.js +4 -23
- package/dist/components/assets/asset-selection.js.map +1 -1
- package/dist/components/assets/asset-thumb.js +6 -5
- package/dist/components/assets/asset-thumb.js.map +1 -1
- package/dist/components/assets/assets-collection-view-source.d.ts +5 -5
- package/dist/components/assets/assets-collection-view-source.js +9 -5
- package/dist/components/assets/assets-collection-view-source.js.map +1 -1
- package/dist/components/assets/assets-module.js +1 -1
- package/dist/components/assets/assets-module.js.map +1 -1
- package/dist/components/assets/assets-store.d.ts +16 -2
- package/dist/components/assets/assets-store.js +28 -6
- package/dist/components/assets/assets-store.js.map +1 -1
- package/dist/components/assets/assets-view-preference.js +1 -1
- package/dist/components/assets/assets-view-preference.js.map +1 -1
- package/dist/components/assets/replace-file.d.ts +22 -0
- package/dist/components/assets/replace-file.js +59 -0
- package/dist/components/assets/replace-file.js.map +1 -0
- package/dist/components/auth-context.d.ts +1 -1
- package/dist/components/auth-context.js +6 -6
- package/dist/components/auth-context.js.map +1 -1
- package/dist/components/builder/blocks-model.d.ts +15 -27
- package/dist/components/builder/blocks-model.js +38 -44
- package/dist/components/builder/blocks-model.js.map +1 -1
- package/dist/components/builder/blocks-panel.d.ts +8 -4
- package/dist/components/builder/blocks-panel.js +10 -9
- package/dist/components/builder/blocks-panel.js.map +1 -1
- package/dist/components/builder/builder-store.d.ts +104 -15
- package/dist/components/builder/builder-store.js +157 -32
- package/dist/components/builder/builder-store.js.map +1 -1
- package/dist/components/builder/canvas-body.d.ts +30 -0
- package/dist/components/builder/canvas-body.js +38 -0
- package/dist/components/builder/canvas-body.js.map +1 -0
- package/dist/components/builder/canvas-breakpoints.js +1 -1
- package/dist/components/builder/canvas-breakpoints.js.map +1 -1
- package/dist/components/builder/canvas-chrome-css.js +7 -0
- package/dist/components/builder/canvas-chrome-css.js.map +1 -1
- package/dist/components/builder/canvas-dnd.d.ts +9 -9
- package/dist/components/builder/canvas-dnd.js +6 -6
- package/dist/components/builder/canvas-dnd.js.map +1 -1
- package/dist/components/builder/canvas-inline-editing.d.ts +1 -1
- package/dist/components/builder/canvas-inline-editing.js.map +1 -1
- package/dist/components/builder/canvas-overlay-geometry.d.ts +1 -1
- package/dist/components/builder/canvas-overlay-geometry.js.map +1 -1
- package/dist/components/builder/canvas-render-context.d.ts +28 -20
- package/dist/components/builder/canvas-render-context.js +46 -47
- package/dist/components/builder/canvas-render-context.js.map +1 -1
- package/dist/components/builder/canvas-runs.d.ts +45 -0
- package/dist/components/builder/canvas-runs.js +51 -0
- package/dist/components/builder/canvas-runs.js.map +1 -0
- package/dist/components/builder/canvas-theme-notice.js +2 -2
- package/dist/components/builder/canvas-theme-notice.js.map +1 -1
- package/dist/components/builder/canvas.js +35 -8
- package/dist/components/builder/canvas.js.map +1 -1
- package/dist/components/builder/clipboard-actions.d.ts +16 -1
- package/dist/components/builder/clipboard-actions.js +48 -11
- package/dist/components/builder/clipboard-actions.js.map +1 -1
- package/dist/components/builder/composition-field-editor-lazy.js +1 -1
- package/dist/components/builder/composition-field-editor-lazy.js.map +1 -1
- package/dist/components/builder/composition-field-editor.d.ts +4 -16
- package/dist/components/builder/composition-field-editor.js +32 -43
- package/dist/components/builder/composition-field-editor.js.map +1 -1
- package/dist/components/builder/delete-key.d.ts +5 -0
- package/dist/components/builder/delete-key.js +4 -0
- package/dist/components/builder/delete-key.js.map +1 -1
- package/dist/components/builder/drag-session.d.ts +1 -1
- package/dist/components/builder/drag-session.js.map +1 -1
- package/dist/components/builder/fixture-thumbnail.js +1 -1
- package/dist/components/builder/fixture-thumbnail.js.map +1 -1
- package/dist/components/builder/insert-context.d.ts +18 -26
- package/dist/components/builder/insert-context.js +47 -94
- package/dist/components/builder/insert-context.js.map +1 -1
- package/dist/components/builder/layout-inspector.js +1 -1
- package/dist/components/builder/layout-inspector.js.map +1 -1
- package/dist/components/builder/node-title.js +1 -1
- package/dist/components/builder/node-title.js.map +1 -1
- package/dist/components/builder/page-tree.d.ts +4 -4
- package/dist/components/builder/page-tree.js +8 -8
- package/dist/components/builder/page-tree.js.map +1 -1
- package/dist/components/builder/placement-refusal.d.ts +18 -0
- package/dist/components/builder/placement-refusal.js +46 -0
- package/dist/components/builder/placement-refusal.js.map +1 -0
- package/dist/components/builder/property-inspector.js +8 -4
- package/dist/components/builder/property-inspector.js.map +1 -1
- package/dist/components/builder/reuse-context.js +68 -30
- package/dist/components/builder/reuse-context.js.map +1 -1
- package/dist/components/builder/reuse.d.ts +79 -1
- package/dist/components/builder/reuse.js +70 -3
- package/dist/components/builder/reuse.js.map +1 -1
- package/dist/components/builder/rich-text-toolbar.js +1 -1
- package/dist/components/builder/rich-text-toolbar.js.map +1 -1
- package/dist/components/builder/run-inspector.d.ts +11 -0
- package/dist/components/builder/run-inspector.js +33 -0
- package/dist/components/builder/run-inspector.js.map +1 -0
- package/dist/components/builder/selection-overlay.js +3 -3
- package/dist/components/builder/selection-overlay.js.map +1 -1
- package/dist/components/builder/spacing-handles.d.ts +2 -2
- package/dist/components/builder/spacing-handles.js +3 -3
- package/dist/components/builder/spacing-handles.js.map +1 -1
- package/dist/components/cms/asset-field.d.ts +38 -1
- package/dist/components/cms/asset-field.js +184 -60
- package/dist/components/cms/asset-field.js.map +1 -1
- package/dist/components/cms/asset-picker-core.d.ts +16 -0
- package/dist/components/cms/asset-picker-core.js +20 -0
- package/dist/components/cms/asset-picker-core.js.map +1 -1
- package/dist/components/cms/asset-picker.js +14 -20
- package/dist/components/cms/asset-picker.js.map +1 -1
- package/dist/components/cms/bulk-actions.d.ts +4 -20
- package/dist/components/cms/bulk-actions.js +5 -26
- package/dist/components/cms/bulk-actions.js.map +1 -1
- package/dist/components/cms/bulk-bar.d.ts +2 -2
- package/dist/components/cms/bulk-bar.js +2 -2
- package/dist/components/cms/bulk-bar.js.map +1 -1
- package/dist/components/cms/cell-edit.d.ts +1 -1
- package/dist/components/cms/cell-edit.js +1 -1
- package/dist/components/cms/cell-edit.js.map +1 -1
- package/dist/components/cms/cell.d.ts +1 -1
- package/dist/components/cms/cell.js +3 -3
- package/dist/components/cms/cell.js.map +1 -1
- package/dist/components/cms/cms-index.js +2 -2
- package/dist/components/cms/cms-index.js.map +1 -1
- package/dist/components/cms/cms-labels.js +2 -2
- package/dist/components/cms/cms-labels.js.map +1 -1
- package/dist/components/cms/cms-store.d.ts +7 -6
- package/dist/components/cms/cms-store.js +23 -43
- package/dist/components/cms/cms-store.js.map +1 -1
- package/dist/components/cms/collection-table.js +7 -7
- package/dist/components/cms/collection-table.js.map +1 -1
- package/dist/components/cms/detail-editor.d.ts +5 -2
- package/dist/components/cms/detail-editor.js +20 -15
- package/dist/components/cms/detail-editor.js.map +1 -1
- package/dist/components/cms/details-pane.js +1 -1
- package/dist/components/cms/details-pane.js.map +1 -1
- package/dist/components/cms/doc-state.js +1 -1
- package/dist/components/cms/doc-state.js.map +1 -1
- package/dist/components/cms/document-diff-view.d.ts +3 -1
- package/dist/components/cms/document-diff-view.js +17 -12
- package/dist/components/cms/document-diff-view.js.map +1 -1
- package/dist/components/cms/document-editor-context.js +1 -1
- package/dist/components/cms/document-editor-context.js.map +1 -1
- package/dist/components/cms/document-staging.js +1 -1
- package/dist/components/cms/document-staging.js.map +1 -1
- package/dist/components/cms/document-url-path.js +2 -2
- package/dist/components/cms/document-url-path.js.map +1 -1
- package/dist/components/cms/duplicate-document.d.ts +1 -1
- package/dist/components/cms/duplicate-document.js.map +1 -1
- package/dist/components/cms/editable-cell.js +3 -3
- package/dist/components/cms/editable-cell.js.map +1 -1
- package/dist/components/cms/field-anchors.d.ts +7 -4
- package/dist/components/cms/field-anchors.js +9 -7
- package/dist/components/cms/field-anchors.js.map +1 -1
- package/dist/components/cms/field-collaboration.d.ts +1 -1
- package/dist/components/cms/field-collaboration.js +1 -1
- package/dist/components/cms/field-collaboration.js.map +1 -1
- package/dist/components/cms/field-connection.d.ts +1 -1
- package/dist/components/cms/field-connection.js +2 -2
- package/dist/components/cms/field-connection.js.map +1 -1
- package/dist/components/cms/field-control-lib.d.ts +15 -1
- package/dist/components/cms/field-control-lib.js +23 -4
- package/dist/components/cms/field-control-lib.js.map +1 -1
- package/dist/components/cms/field-control.js +4 -4
- package/dist/components/cms/field-control.js.map +1 -1
- package/dist/components/cms/field-renderer.d.ts +1 -1
- package/dist/components/cms/field-renderer.js +2 -2
- package/dist/components/cms/field-renderer.js.map +1 -1
- package/dist/components/cms/filter-builder.d.ts +1 -1
- package/dist/components/cms/filter-builder.js +3 -3
- package/dist/components/cms/filter-builder.js.map +1 -1
- package/dist/components/cms/gate-validation.d.ts +21 -30
- package/dist/components/cms/gate-validation.js +11 -32
- package/dist/components/cms/gate-validation.js.map +1 -1
- package/dist/components/cms/import-export.js +2 -2
- package/dist/components/cms/import-export.js.map +1 -1
- package/dist/components/cms/issue-copy.js +1 -1
- package/dist/components/cms/issue-copy.js.map +1 -1
- package/dist/components/cms/issues-pane.js +3 -3
- package/dist/components/cms/issues-pane.js.map +1 -1
- package/dist/components/cms/object-array-field.js +6 -6
- package/dist/components/cms/object-array-field.js.map +1 -1
- package/dist/components/cms/prop-fields.d.ts +7 -2
- package/dist/components/cms/prop-fields.js +14 -7
- package/dist/components/cms/prop-fields.js.map +1 -1
- package/dist/components/cms/reference-confirm.d.ts +1 -1
- package/dist/components/cms/reference-confirm.js +2 -2
- package/dist/components/cms/reference-confirm.js.map +1 -1
- package/dist/components/cms/relation-picker.js +2 -2
- package/dist/components/cms/relation-picker.js.map +1 -1
- package/dist/components/cms/rich-text/block-style.js +1 -1
- package/dist/components/cms/rich-text/block-style.js.map +1 -1
- package/dist/components/cms/rich-text/component-embed.js +4 -4
- package/dist/components/cms/rich-text/component-embed.js.map +1 -1
- package/dist/components/cms/rich-text/editor.js +3 -3
- package/dist/components/cms/rich-text/editor.js.map +1 -1
- package/dist/components/cms/rich-text/embed-picker.d.ts +1 -1
- package/dist/components/cms/rich-text/embed-picker.js +3 -3
- package/dist/components/cms/rich-text/embed-picker.js.map +1 -1
- package/dist/components/cms/rich-text/embed-props.js +3 -3
- package/dist/components/cms/rich-text/embed-props.js.map +1 -1
- package/dist/components/cms/rich-text/extensions.js +1 -1
- package/dist/components/cms/rich-text/extensions.js.map +1 -1
- package/dist/components/cms/rich-text/font-size-control.d.ts +1 -1
- package/dist/components/cms/rich-text/font-size-control.js +2 -2
- package/dist/components/cms/rich-text/font-size-control.js.map +1 -1
- package/dist/components/cms/rich-text/insert.d.ts +20 -9
- package/dist/components/cms/rich-text/insert.js +25 -17
- package/dist/components/cms/rich-text/insert.js.map +1 -1
- package/dist/components/cms/rich-text/lazy.js +1 -1
- package/dist/components/cms/rich-text/lazy.js.map +1 -1
- package/dist/components/cms/rich-text/link-control.js +2 -2
- package/dist/components/cms/rich-text/link-control.js.map +1 -1
- package/dist/components/cms/rich-text/slash-command.js +3 -3
- package/dist/components/cms/rich-text/slash-command.js.map +1 -1
- package/dist/components/cms/rich-text/toolbar.js +1 -1
- package/dist/components/cms/rich-text/toolbar.js.map +1 -1
- package/dist/components/cms/rich-text/unknown-node.js +2 -2
- package/dist/components/cms/rich-text/unknown-node.js.map +1 -1
- package/dist/components/cms/rich-text-preview.js +2 -2
- package/dist/components/cms/rich-text-preview.js.map +1 -1
- package/dist/components/cms/row-actions.d.ts +1 -1
- package/dist/components/cms/row-actions.js.map +1 -1
- package/dist/components/cms/scalar-value-editor.d.ts +1 -1
- package/dist/components/cms/scalar-value-editor.js +2 -2
- package/dist/components/cms/scalar-value-editor.js.map +1 -1
- package/dist/components/cms/use-discard-controller.d.ts +1 -1
- package/dist/components/cms/use-discard-controller.js +1 -1
- package/dist/components/cms/use-discard-controller.js.map +1 -1
- package/dist/components/cms/use-document-draft.d.ts +7 -1
- package/dist/components/cms/use-document-draft.js +16 -2
- package/dist/components/cms/use-document-draft.js.map +1 -1
- package/dist/components/cms/use-document-versions.js +1 -1
- package/dist/components/cms/use-document-versions.js.map +1 -1
- package/dist/components/cms/use-field-focus.d.ts +1 -1
- package/dist/components/cms/use-field-focus.js +2 -2
- package/dist/components/cms/use-field-focus.js.map +1 -1
- package/dist/components/cms/use-live-url.d.ts +1 -1
- package/dist/components/cms/use-live-url.js +3 -3
- package/dist/components/cms/use-live-url.js.map +1 -1
- package/dist/components/cms/use-publish-controller.d.ts +2 -3
- package/dist/components/cms/use-publish-controller.js +8 -20
- package/dist/components/cms/use-publish-controller.js.map +1 -1
- package/dist/components/cms/use-published-perspective.d.ts +1 -1
- package/dist/components/cms/use-published-perspective.js.map +1 -1
- package/dist/components/cms/use-restore-version.d.ts +1 -1
- package/dist/components/cms/use-restore-version.js +1 -1
- package/dist/components/cms/use-restore-version.js.map +1 -1
- package/dist/components/cms/version-compare-lazy.js +1 -1
- package/dist/components/cms/version-compare-lazy.js.map +1 -1
- package/dist/components/cms/version-compare.js +1 -1
- package/dist/components/cms/version-compare.js.map +1 -1
- package/dist/components/cms/versions-pane.js +11 -4
- package/dist/components/cms/versions-pane.js.map +1 -1
- package/dist/components/cms/view-bar.js +3 -3
- package/dist/components/cms/view-bar.js.map +1 -1
- package/dist/components/collection-view/cms-collection-view-source.d.ts +1 -1
- package/dist/components/collection-view/cms-collection-view-source.js +5 -3
- package/dist/components/collection-view/cms-collection-view-source.js.map +1 -1
- package/dist/components/collection-view/collection-view-context.d.ts +1 -1
- package/dist/components/collection-view/collection-view-context.js +1 -1
- package/dist/components/collection-view/collection-view-context.js.map +1 -1
- package/dist/components/collection-view/collection-view-surface.d.ts +13 -0
- package/dist/components/collection-view/collection-view-surface.js +18 -0
- package/dist/components/collection-view/collection-view-surface.js.map +1 -0
- package/dist/components/components/component-detail.js +10 -10
- package/dist/components/components/component-detail.js.map +1 -1
- package/dist/components/components/component-preview.d.ts +2 -2
- package/dist/components/components/component-preview.js +5 -5
- package/dist/components/components/component-preview.js.map +1 -1
- package/dist/components/components/component-status.d.ts +1 -1
- package/dist/components/components/component-status.js +1 -1
- package/dist/components/components/component-status.js.map +1 -1
- package/dist/components/components/components-index.js +2 -2
- package/dist/components/components/components-index.js.map +1 -1
- package/dist/components/components/field-inspector.js +1 -1
- package/dist/components/components/field-inspector.js.map +1 -1
- package/dist/components/components/registry-browser.js +3 -3
- package/dist/components/components/registry-browser.js.map +1 -1
- package/dist/components/components/taxonomy.d.ts +1 -1
- package/dist/components/components/taxonomy.js.map +1 -1
- package/dist/components/components/transfer-dialog.js +4 -3
- package/dist/components/components/transfer-dialog.js.map +1 -1
- package/dist/components/components/transfer.d.ts +10 -10
- package/dist/components/components/transfer.js +13 -12
- package/dist/components/components/transfer.js.map +1 -1
- package/dist/components/current-user-menu.js +1 -1
- package/dist/components/current-user-menu.js.map +1 -1
- package/dist/components/delivery-health-banner.js +2 -2
- package/dist/components/delivery-health-banner.js.map +1 -1
- package/dist/components/deployment-setup-gate.js +3 -3
- package/dist/components/deployment-setup-gate.js.map +1 -1
- package/dist/components/dev-login-badge.js +2 -2
- package/dist/components/dev-login-badge.js.map +1 -1
- package/dist/components/guards.js +1 -1
- package/dist/components/guards.js.map +1 -1
- package/dist/components/inspector/document-panes.d.ts +54 -0
- package/dist/components/inspector/document-panes.js +41 -0
- package/dist/components/inspector/document-panes.js.map +1 -0
- package/dist/components/inspector/inspector-sidebar.js +1 -1
- package/dist/components/inspector/inspector-sidebar.js.map +1 -1
- package/dist/components/nav-issues-registry.d.ts +53 -0
- package/dist/components/nav-issues-registry.js +69 -0
- package/dist/components/nav-issues-registry.js.map +1 -0
- package/dist/components/nav-issues.js +4 -4
- package/dist/components/nav-issues.js.map +1 -1
- package/dist/components/operation-error.d.ts +1 -1
- package/dist/components/operation-error.js +4 -4
- package/dist/components/operation-error.js.map +1 -1
- package/dist/components/playground-badge.js +2 -2
- package/dist/components/playground-badge.js.map +1 -1
- package/dist/components/project-list.js +3 -3
- package/dist/components/project-list.js.map +1 -1
- package/dist/components/project-session.d.ts +2 -2
- package/dist/components/project-session.js +5 -5
- package/dist/components/project-session.js.map +1 -1
- package/dist/components/project-switcher.js +2 -2
- package/dist/components/project-switcher.js.map +1 -1
- package/dist/components/rail-store-status.js +1 -1
- package/dist/components/rail-store-status.js.map +1 -1
- package/dist/components/rail-view-source.d.ts +6 -5
- package/dist/components/rail-view-source.js +4 -3
- package/dist/components/rail-view-source.js.map +1 -1
- package/dist/components/records/record-lens.d.ts +51 -0
- package/dist/components/records/record-lens.js +156 -0
- package/dist/components/records/record-lens.js.map +1 -0
- package/dist/components/records/record-rail.d.ts +86 -0
- package/dist/components/records/record-rail.js +241 -0
- package/dist/components/records/record-rail.js.map +1 -0
- package/dist/components/redirects/redirects-schema.js +1 -1
- package/dist/components/redirects/redirects-schema.js.map +1 -1
- package/dist/components/references/references-panel.js +2 -2
- package/dist/components/references/references-panel.js.map +1 -1
- package/dist/components/section-nav.js +27 -11
- package/dist/components/section-nav.js.map +1 -1
- package/dist/components/section-state.js +2 -2
- package/dist/components/section-state.js.map +1 -1
- package/dist/components/section-surface.js +2 -2
- package/dist/components/section-surface.js.map +1 -1
- package/dist/components/session-expired-overlay.js +3 -3
- package/dist/components/session-expired-overlay.js.map +1 -1
- package/dist/components/settings-form.js +1 -1
- package/dist/components/settings-form.js.map +1 -1
- package/dist/components/sign-in-needed-badge.js +1 -1
- package/dist/components/sign-in-needed-badge.js.map +1 -1
- package/dist/components/sign-in-view.d.ts +2 -2
- package/dist/components/sign-in-view.js +5 -5
- package/dist/components/sign-in-view.js.map +1 -1
- package/dist/components/studio-context.js +3 -3
- package/dist/components/studio-context.js.map +1 -1
- package/dist/components/studio-project-screen.js +30 -3
- package/dist/components/studio-project-screen.js.map +1 -1
- package/dist/components/studio-shell.js +6 -5
- package/dist/components/studio-shell.js.map +1 -1
- package/dist/components/theme-context.d.ts +1 -1
- package/dist/components/theme-context.js +2 -2
- package/dist/components/theme-context.js.map +1 -1
- package/dist/components/use-hydration-phase.d.ts +1 -1
- package/dist/components/use-hydration-phase.js +1 -1
- package/dist/components/use-hydration-phase.js.map +1 -1
- package/dist/components/users/invite-user-bar.d.ts +27 -0
- package/dist/components/users/invite-user-bar.js +25 -9
- package/dist/components/users/invite-user-bar.js.map +1 -1
- package/dist/components/users/role-policy-drift-issues.d.ts +38 -0
- package/dist/components/users/role-policy-drift-issues.js +71 -0
- package/dist/components/users/role-policy-drift-issues.js.map +1 -0
- package/dist/components/users/users-collection-view-source.d.ts +1 -1
- package/dist/components/users/users-collection-view-source.js +4 -2
- package/dist/components/users/users-collection-view-source.js.map +1 -1
- package/dist/components/users/users-module.js +1 -1
- package/dist/components/users/users-module.js.map +1 -1
- package/dist/components/users/users-schema.d.ts +10 -1
- package/dist/components/users/users-schema.js +45 -25
- package/dist/components/users/users-schema.js.map +1 -1
- package/dist/components/users/users-store.js +3 -3
- package/dist/components/users/users-store.js.map +1 -1
- package/dist/components/workspace-projects.d.ts +1 -1
- package/dist/components/workspace-projects.js +1 -1
- package/dist/components/workspace-projects.js.map +1 -1
- package/dist/lib/ai/apply-content.js +56 -116
- package/dist/lib/ai/apply-content.js.map +1 -1
- package/dist/lib/ai/chat-context.d.ts +8 -0
- package/dist/lib/ai/chat-context.js +37 -6
- package/dist/lib/ai/chat-context.js.map +1 -1
- package/dist/lib/ai/chat-state.d.ts +6 -0
- package/dist/lib/ai/chat-state.js.map +1 -1
- package/dist/lib/ai/constraints.d.ts +20 -5
- package/dist/lib/ai/constraints.js +125 -33
- package/dist/lib/ai/constraints.js.map +1 -1
- package/dist/lib/ai/fake-provider.d.ts +16 -3
- package/dist/lib/ai/fake-provider.js +22 -6
- package/dist/lib/ai/fake-provider.js.map +1 -1
- package/dist/lib/ai/index.d.ts +3 -1
- package/dist/lib/ai/index.js +3 -1
- package/dist/lib/ai/index.js.map +1 -1
- package/dist/lib/ai/leash.d.ts +34 -0
- package/dist/lib/ai/leash.js +28 -0
- package/dist/lib/ai/leash.js.map +1 -0
- package/dist/lib/ai/node-handler.d.ts +1 -1
- package/dist/lib/ai/node-handler.js.map +1 -1
- package/dist/lib/ai/rich-text-input.d.ts +8 -8
- package/dist/lib/ai/rich-text-input.js +2 -1
- package/dist/lib/ai/rich-text-input.js.map +1 -1
- package/dist/lib/ai/rich-text-translate.d.ts +27 -4
- package/dist/lib/ai/rich-text-translate.js +28 -10
- package/dist/lib/ai/rich-text-translate.js.map +1 -1
- package/dist/lib/ai/shape.d.ts +13 -13
- package/dist/lib/ai/shape.js +69 -93
- package/dist/lib/ai/shape.js.map +1 -1
- package/dist/lib/ai/tools.d.ts +36 -41
- package/dist/lib/ai/tools.js +83 -87
- package/dist/lib/ai/tools.js.map +1 -1
- package/dist/lib/ai/translate-document.d.ts +32 -4
- package/dist/lib/ai/translate-document.js +145 -47
- package/dist/lib/ai/translate-document.js.map +1 -1
- package/dist/lib/ai/types.d.ts +34 -23
- package/dist/lib/ai/types.js.map +1 -1
- package/dist/lib/ai/update-fields.d.ts +15 -12
- package/dist/lib/ai/update-fields.js +112 -188
- package/dist/lib/ai/update-fields.js.map +1 -1
- package/dist/lib/ai/writer-form.d.ts +116 -0
- package/dist/lib/ai/writer-form.js +429 -0
- package/dist/lib/ai/writer-form.js.map +1 -0
- package/dist/lib/assets/upload.d.ts +31 -2
- package/dist/lib/assets/upload.js +61 -6
- package/dist/lib/assets/upload.js.map +1 -1
- package/dist/lib/clipboard/os-clipboard.d.ts +10 -2
- package/dist/lib/clipboard/os-clipboard.js +8 -4
- package/dist/lib/clipboard/os-clipboard.js.map +1 -1
- package/dist/lib/clipboard/paste-issues.js +1 -1
- package/dist/lib/clipboard/paste-issues.js.map +1 -1
- package/dist/lib/clipboard/paste-target.d.ts +18 -9
- package/dist/lib/clipboard/paste-target.js +10 -9
- package/dist/lib/clipboard/paste-target.js.map +1 -1
- package/dist/lib/cms/field-patch.d.ts +0 -10
- package/dist/lib/cms/field-patch.js +4 -30
- package/dist/lib/cms/field-patch.js.map +1 -1
- package/dist/lib/cms/published-documents.js +1 -1
- package/dist/lib/cms/published-documents.js.map +1 -1
- package/dist/lib/cms-views/view-model.d.ts +9 -9
- package/dist/lib/cms-views/view-model.js +2 -2
- package/dist/lib/cms-views/view-model.js.map +1 -1
- package/dist/lib/collection-view/collection-view-source.d.ts +14 -5
- package/dist/lib/collection-view/collection-view-source.js.map +1 -1
- package/dist/lib/data/collection-content.js +3 -2
- package/dist/lib/data/collection-content.js.map +1 -1
- package/dist/lib/data/content-session-refresh.d.ts +1 -1
- package/dist/lib/data/content-session-refresh.js.map +1 -1
- package/dist/lib/data/playground-unload-guard.d.ts +1 -1
- package/dist/lib/data/playground-unload-guard.js +1 -1
- package/dist/lib/data/playground-unload-guard.js.map +1 -1
- package/dist/lib/dnd/drop-rules.d.ts +25 -31
- package/dist/lib/dnd/drop-rules.js +53 -59
- package/dist/lib/dnd/drop-rules.js.map +1 -1
- package/dist/lib/engine-version.d.ts +1 -1
- package/dist/lib/engine-version.js +1 -1
- package/dist/lib/engine-version.js.map +1 -1
- package/dist/lib/i18n/i18n.js +1 -1
- package/dist/lib/i18n/i18n.js.map +1 -1
- package/dist/lib/i18n/messages.de.js +44 -3
- package/dist/lib/i18n/messages.de.js.map +1 -1
- package/dist/lib/i18n/messages.en.d.ts +38 -2
- package/dist/lib/i18n/messages.en.js +51 -3
- package/dist/lib/i18n/messages.en.js.map +1 -1
- package/dist/lib/operations/project-runtime-config.d.ts +34 -6
- package/dist/lib/operations/project-runtime-config.js +50 -10
- package/dist/lib/operations/project-runtime-config.js.map +1 -1
- package/dist/lib/operations/publish-with-revision.d.ts +33 -1
- package/dist/lib/operations/publish-with-revision.js +32 -0
- package/dist/lib/operations/publish-with-revision.js.map +1 -1
- package/dist/lib/operations/runtime-validator.d.ts +19 -68
- package/dist/lib/operations/runtime-validator.js +12 -138
- package/dist/lib/operations/runtime-validator.js.map +1 -1
- package/dist/lib/operations/studio-operations.d.ts +17 -2
- package/dist/lib/operations/studio-operations.js +18 -11
- package/dist/lib/operations/studio-operations.js.map +1 -1
- package/dist/lib/persistence/convex-adapter.d.ts +16 -3
- package/dist/lib/persistence/convex-adapter.js +83 -47
- package/dist/lib/persistence/convex-adapter.js.map +1 -1
- package/dist/lib/persistence/index.js +14 -1
- package/dist/lib/persistence/index.js.map +1 -1
- package/dist/lib/persona/labels.js +4 -2
- package/dist/lib/persona/labels.js.map +1 -1
- package/dist/lib/query/operations-query.d.ts +1 -1
- package/dist/lib/query/operations-query.js +1 -1
- package/dist/lib/query/operations-query.js.map +1 -1
- package/dist/lib/references/reference-model.js +1 -1
- package/dist/lib/references/reference-model.js.map +1 -1
- package/dist/lib/references/references-store.js +1 -1
- package/dist/lib/references/references-store.js.map +1 -1
- package/dist/lib/workspace/index.d.ts +14 -11
- package/dist/lib/workspace/index.js +20 -24
- package/dist/lib/workspace/index.js.map +1 -1
- package/dist/mount.d.ts +28 -0
- package/dist/mount.js +27 -0
- package/dist/mount.js.map +1 -0
- package/dist/router/root.js +10 -10
- package/dist/router/root.js.map +1 -1
- package/dist/ui/badge.d.ts +1 -1
- package/dist/ui/button.d.ts +2 -2
- package/package.json +15 -11
- package/dist/components/cms/cms-composition-validators.d.ts +0 -16
- package/dist/components/cms/cms-composition-validators.js +0 -133
- package/dist/components/cms/cms-composition-validators.js.map +0 -1
- package/dist/components/cms/publish-clean.d.ts +0 -28
- package/dist/components/cms/publish-clean.js +0 -38
- package/dist/components/cms/publish-clean.js.map +0 -1
- package/dist/components/redirects/redirects-collection-view-source.d.ts +0 -53
- package/dist/components/redirects/redirects-collection-view-source.js +0 -146
- package/dist/components/redirects/redirects-collection-view-source.js.map +0 -1
- package/dist/components/redirects/redirects-module.d.ts +0 -21
- package/dist/components/redirects/redirects-module.js +0 -40
- package/dist/components/redirects/redirects-module.js.map +0 -1
- package/dist/components/redirects/redirects-store.d.ts +0 -68
- package/dist/components/redirects/redirects-store.js +0 -132
- package/dist/components/redirects/redirects-store.js.map +0 -1
- package/dist/lib/assets/asset-placements.d.ts +0 -28
- package/dist/lib/assets/asset-placements.js +0 -83
- package/dist/lib/assets/asset-placements.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"canvas.js","sourceRoot":"","sources":["../../../src/components/builder/canvas.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react'\nimport { createPortal } from 'react-dom'\nimport { renderPageComposition, setEditingMarkers } from '@elytracms/core/runtime-renderer'\nimport { getNode } from '@elytracms/core/project-graph'\nimport type { ComponentNode } from '@elytracms/core/project-graph'\nimport { getStudioRegistry } from '@elytracms/studio/lib/registry'\nimport { translate } from '@elytracms/studio/lib/i18n'\nimport { DRAFT_PERSPECTIVE } from '@elytracms/core/cms-core'\nimport type { Perspective } from '@elytracms/core/cms-core'\nimport { resolveDeploymentCanvasTheme } from '@elytracms/studio/lib/workspace'\nimport { wireCanvasChrome } from './canvas-chrome'\nimport type { CanvasThemeStatus } from './canvas-chrome'\nimport { CanvasThemeNotice } from './canvas-theme-notice'\nimport { getCanvasStylesheetUrl } from './canvas-stylesheet'\nimport { useBuilder } from './builder-store'\nimport { useCanvasViewports, viewportWidth } from './canvas-breakpoints'\nimport { nodeIdAt, wrapperAt } from './canvas-dom'\nimport { useCanvasDnd } from './canvas-dnd'\nimport { useCanvasInlineEditing } from './canvas-inline-editing'\nimport { useCanvasRenderContext } from './canvas-render-context'\nimport {\n ClipboardNoticeOverlay,\n attachClipboardListeners,\n useStudioClipboard,\n} from './clipboard-actions'\nimport { RichTextToolbar } from './rich-text-toolbar'\nimport { SpacingHandles, hasSpacingHandles } from './spacing-handles'\nimport { SelectionOverlay } from './selection-overlay'\nimport { attachDeleteKeyListener } from './delete-key'\nimport { usePropSync } from './prop-sync-context'\nimport { resolvePropClick } from './prop-selection'\n\n// EC-408: the studio canvas is the ONE surface that turns editing markers on, so\n// `ecProp` in the base primitives emits `data-ec-prop` here. Delivery\n// (`@elytracms/next`) is a separate build that never imports this module, so its\n// singleton stays off and shipped pages carry zero marker noise. Module-level\n// (not an effect) so markers are on before the first primitive renders.\nsetEditingMarkers(true)\n\n\n/**\n * Iframe-isolated page preview (brief §8.2, EC-061, EC-064). The graph renders\n * into a same-origin iframe via a React portal — true style isolation + true\n * responsive widths. Each node is wrapped with an editable marker so clicks\n * select and hover/selection draw outlines. Native listeners bridge the iframe\n * boundary (React's synthetic events don't cross frames); the two big gesture\n * state machines live in their own tested modules (EC-330): drag & drop in\n * `canvas-dnd`, inline text/rich-text editing in `canvas-inline-editing`, and\n * the render-context assembly in `canvas-render-context`.\n */\nexport function BuilderCanvas({\n perspective = DRAFT_PERSPECTIVE,\n}: {\n /** Resolve references against this perspective (EC-300/AD-16). The editable Draft\n * view passes `DRAFT_PERSPECTIVE` (a just-made reusable shows immediately); the\n * read-only Published view passes `'published'` (honest preview of what delivery\n * serves). */\n perspective?: Perspective\n} = {}) {\n const builder = useBuilder()\n const { dispatch } = builder\n const studio = getStudioRegistry()\n\n // The edited composition root, composed for preview. EC-287 retired the project\n // graph and its published-graph preview; the canvas always renders the live,\n // editable draft (content publishing is per document).\n const activeRoot: ComponentNode = builder.present\n const iframeRef = useRef<HTMLIFrameElement>(null)\n const [doc, setDoc] = useState<Document | null>(null)\n\n // Graph/selection refs let the native iframe listeners read the *current*\n // state without re-subscribing on every edit.\n const presentRef = useRef(builder.present)\n presentRef.current = builder.present\n const selectedIdRef = useRef(builder.selectedId)\n selectedIdRef.current = builder.selectedId\n\n // EC-414 AD-D: the settled canvas-theme classification (legacy merged shape /\n // broken stylesheet) drives the dev-only, never-blocking notice overlay.\n const [themeStatus, setThemeStatus] = useState<CanvasThemeStatus | null>(null)\n\n const wireUp = useCallback(() => {\n const d = iframeRef.current?.contentDocument\n if (!d || !d.body) return\n // Deliberately no `.dark` here: this iframe renders the REAL page (plus\n // studio editing chrome portaled alongside it), and its backdrop must read\n // as a light, site-like page — never the studio chrome's current theme (a\n // black canvas is exactly what leaks through when the studio is\n // dark-themed and the iframe root inherits that class). The injected\n // canvas stylesheet (new world: the chrome's neutral backdrop; legacy\n // world: the studio `:root` tokens) supplies that backdrop once `.dark` is\n // never added here (mirrors the components-preview surface, which wires\n // through the same chrome). The editing chrome (selection/hover outlines,\n // drop indicators, the rich-text toolbar, locale-fallback chips) uses\n // hardcoded colors rather than tokens, so it stays visible against a light\n // page regardless. The full injection — stylesheet link, editing chrome,\n // the consumer's `defineStudio({ canvas })` theme (EC-389, injected LAST so\n // it wins), and the EC-414 world probe — lives in\n // `wireCanvasChrome`/`applyCanvasChrome`.\n wireCanvasChrome(d, {\n stylesheetUrl: getCanvasStylesheetUrl(),\n theme: resolveDeploymentCanvasTheme(),\n onStatus: (status) =>\n setThemeStatus((prev) => (prev?.kind === status.kind ? prev : status)),\n })\n setDoc(d)\n }, [])\n\n // Handle the already-loaded case (srcDoc may load before onLoad attaches).\n useEffect(() => {\n if (iframeRef.current?.contentDocument?.readyState === 'complete') wireUp()\n }, [wireUp])\n\n // Inline editing (EC-158/EC-159): the dblclick-to-edit state machine, the\n // live session ref other listeners consult, the rich-text host (mark\n // toolbar), and the per-node remount epochs (decorate keys).\n const inline = useCanvasInlineEditing(doc, { presentRef, dispatch, studio })\n const { editingRef, richEditHost, richEditEpochs, editing } = inline\n\n // EC-408: the field↔component sync channel (null in the component preview,\n // which has no inspector). Read live through a ref — the native iframe\n // listeners outlive renders.\n const propSync = usePropSync()\n const propSyncRef = useRef(propSync)\n propSyncRef.current = propSync\n\n // Native selection/hover listeners inside the iframe document.\n useEffect(() => {\n if (!doc) return\n const onClick = (e: Event) => {\n // Clicks inside the active inline editor are caret moves, not selection.\n if (editingRef.current?.host.contains(e.target as Node)) return\n const wrapper = wrapperAt(e.target)\n if (!wrapper) return\n const id = wrapper.getAttribute('data-ec-node-id')!\n e.preventDefault()\n dispatch({ type: 'select', id })\n // EC-408: resolve the clicked sub-part to its `data-ec-prop` and, unless it\n // is inline-editable (inline editing stays primary), ask the inspector to\n // focus that prop's control. Unannotated components resolve no marker → the\n // node-level select above is the whole behavior, exactly as before.\n const sync = propSyncRef.current\n if (!sync) return\n const node = getNode(presentRef.current, id)\n const manifest = node ? studio.registry.getManifest(node.componentId) : undefined\n const { propName, focusField } = resolvePropClick({\n target: e.target as Element | null,\n wrapper,\n manifest,\n })\n if (propName && focusField) sync.requestFocus({ nodeId: id, propName })\n }\n const onOver = (e: Event) => dispatch({ type: 'hover', id: nodeIdAt(e.target) })\n const onLeave = () => dispatch({ type: 'hover', id: null })\n doc.addEventListener('click', onClick, true)\n doc.addEventListener('mouseover', onOver)\n doc.addEventListener('mouseleave', onLeave, true)\n return () => {\n doc.removeEventListener('click', onClick, true)\n doc.removeEventListener('mouseover', onOver)\n doc.removeEventListener('mouseleave', onLeave, true)\n }\n }, [doc, dispatch, editingRef, presentRef, studio])\n\n // Drag & drop on the canvas (EC-160/EC-162) — see `canvas-dnd`.\n // EC-554: the field's top-level `allow` rides with the store (`builder.field`).\n useCanvasDnd(doc, { presentRef, dispatch, studio, editingRef, fieldAllow: builder.field.allow })\n\n // Copy/cut/paste on the canvas (EC-175): the same studio clipboard the page\n // tree and blocks panel use, wired to the *iframe* document (events never\n // cross frames). Shortcuts stay inert while an inline editor is live and in\n // form fields; the iframe's own navigator.clipboard does the async I/O\n // (Chromium scopes clipboard access to the focused document).\n const clipboard = useStudioClipboard()\n useEffect(() => {\n if (!doc) return\n return attachClipboardListeners(doc, {\n clipboard,\n getSelectedId: () => selectedIdRef.current,\n isBlocked: () => editingRef.current !== null,\n asyncClipboard: doc.defaultView?.navigator.clipboard,\n getSelection: () => doc.getSelection(),\n })\n }, [doc, clipboard, editingRef])\n\n // Delete/Backspace removes the selected node (EC-404) — the keyboard remove\n // the studio was missing, wired beside the clipboard listeners on the iframe\n // document, guarded so typing in an inline editor edits text instead.\n useEffect(() => {\n if (!doc) return\n return attachDeleteKeyListener(doc, {\n getSelectedId: () => selectedIdRef.current,\n remove: (id) => dispatch({ type: 'remove', id }),\n getRootId: () => presentRef.current.id,\n })\n }, [doc, dispatch])\n\n // The render context: the seven delivery-parity resolvers + the editable-\n // wrapper decorator (selection/hover, locale chips, remount epochs) — see\n // `canvas-render-context`.\n const { ctx, pageNodeIds } = useCanvasRenderContext({\n present: builder.present,\n selectedId: builder.selectedId,\n hoveredId: builder.hoveredId,\n studio,\n perspective,\n richEditEpochs,\n })\n\n // Tokenized spacing handles (EC-163): a selected Stack/Grid with 2+ children\n // gets draggable gap handles between them, portaled into the iframe. Suppressed\n // while an inline text/rich-text edit owns the node (EC-406 scope 4) — the\n // [data-ec-editing] outline owns that state, exactly as the selection overlay\n // yields; a content-drag is suppressed inside SpacingHandles via the drag session.\n const spacingTarget =\n doc && !editing && hasSpacingHandles(builder.selectedNode, builder.layout) && pageNodeIds.has(builder.selectedNode.id)\n ? builder.selectedNode\n : null\n\n // EC-250: the preview frame width derives from the config-sourced breakpoints\n // (Tailwind defaults when unconfigured), the same resolved set the toolbar\n // switcher offers.\n const { viewports } = useCanvasViewports()\n\n return (\n <div className=\"canvas-grid relative flex min-h-0 flex-1 items-stretch justify-center overflow-auto p-6\">\n {/* Copy/cut/paste outcomes (EC-175): non-blocking, with issue deep links. */}\n <ClipboardNoticeOverlay />\n {/* Canvas-theme classification notice (EC-414 AD-D): dev-only, never blocks. */}\n <CanvasThemeNotice status={themeStatus} />\n <div style={{ width: viewportWidth(viewports, builder.viewport), maxWidth: '100%' }} className=\"flex\">\n <iframe\n ref={iframeRef}\n onLoad={wireUp}\n title={translate('builder.canvas.pagePreview')}\n className=\"min-h-[480px] w-full rounded-lg border border-border bg-background shadow-md\"\n srcDoc=\"<!doctype html><html><head></head><body></body></html>\"\n />\n {doc\n ? createPortal(\n // Render the composition root directly (EC-287: no layout\n // indirection — a page renders its blocks).\n renderPageComposition(activeRoot, ctx),\n doc.body,\n )\n : null}\n {/* Selection/hover frames + frame controls, topmost over the page (EC-404). */}\n {doc ? <SelectionOverlay doc={doc} editing={editing} /> : null}\n {spacingTarget && doc ? (\n <SpacingHandles key={spacingTarget.id} doc={doc} node={spacingTarget} />\n ) : null}\n {/* Floating mark toolbar for the live rich-text session (EC-159). */}\n {richEditHost && doc ? <RichTextToolbar doc={doc} host={richEditHost} /> : null}\n </div>\n </div>\n )\n}\n"]}
|
|
1
|
+
{"version":3,"file":"canvas.js","sourceRoot":"","sources":["../../../src/components/builder/canvas.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react'\nimport { createPortal } from 'react-dom'\nimport { setEditingMarkers } from '@elytracms/core/runtime-renderer'\nimport { getNode } from '@elytracms/core/project-graph'\nimport type { ComponentNode } from '@elytracms/core/project-graph'\nimport { getStudioRegistry } from '../../lib/registry'\nimport { translate } from '../../lib/i18n'\nimport { DRAFT_PERSPECTIVE } from '@elytracms/core/cms-core'\nimport type { Perspective } from '@elytracms/core/cms-core'\nimport { resolveDeploymentCanvasTheme } from '../../lib/workspace'\nimport { wireCanvasChrome } from './canvas-chrome'\nimport type { CanvasThemeStatus } from './canvas-chrome'\nimport { CanvasThemeNotice } from './canvas-theme-notice'\nimport { getCanvasStylesheetUrl } from './canvas-stylesheet'\nimport { useBuilder } from './builder-store'\nimport { useCanvasViewports, viewportWidth } from './canvas-breakpoints'\nimport { nodeIdAt, wrapperAt } from './canvas-dom'\nimport { useCanvasDnd } from './canvas-dnd'\nimport { useCanvasInlineEditing } from './canvas-inline-editing'\nimport { useCanvasRenderContext } from './canvas-render-context'\nimport {\n ClipboardNoticeOverlay,\n attachClipboardListeners,\n useStudioClipboard,\n} from './clipboard-actions'\nimport { RichTextToolbar } from './rich-text-toolbar'\nimport { SpacingHandles, hasSpacingHandles } from './spacing-handles'\nimport { SelectionOverlay } from './selection-overlay'\nimport { attachDeleteKeyListener } from './delete-key'\nimport { usePropSync } from './prop-sync-context'\nimport { renderCanvasBody } from './canvas-body'\nimport { resolvePropClick } from './prop-selection'\n\n// EC-408: the studio canvas is the ONE surface that turns editing markers on, so\n// `ecProp` in the base primitives emits `data-ec-prop` here. Delivery\n// (`@elytracms/next`) is a separate build that never imports this module, so its\n// singleton stays off and shipped pages carry zero marker noise. Module-level\n// (not an effect) so markers are on before the first primitive renders.\nsetEditingMarkers(true)\n\n\n/**\n * Iframe-isolated page preview (brief §8.2, EC-061, EC-064). The graph renders\n * into a same-origin iframe via a React portal — true style isolation + true\n * responsive widths. Each node is wrapped with an editable marker so clicks\n * select and hover/selection draw outlines. Native listeners bridge the iframe\n * boundary (React's synthetic events don't cross frames); the two big gesture\n * state machines live in their own tested modules (EC-330): drag & drop in\n * `canvas-dnd`, inline text/rich-text editing in `canvas-inline-editing`, and\n * the render-context assembly in `canvas-render-context`.\n */\nexport function BuilderCanvas({\n perspective = DRAFT_PERSPECTIVE,\n}: {\n /** Resolve references against this perspective (EC-300/AD-16). The editable Draft\n * view passes `DRAFT_PERSPECTIVE` (a just-made reusable shows immediately); the\n * read-only Published view passes `'published'` (honest preview of what delivery\n * serves). */\n perspective?: Perspective\n} = {}) {\n const builder = useBuilder()\n const { dispatch } = builder\n const studio = getStudioRegistry()\n\n // The edited composition root, composed for preview. EC-287 retired the project\n // graph and its published-graph preview; the canvas always renders the live,\n // editable draft (content publishing is per document).\n const activeRoot: ComponentNode = builder.present\n const iframeRef = useRef<HTMLIFrameElement>(null)\n const [doc, setDoc] = useState<Document | null>(null)\n\n // Graph/selection refs let the native iframe listeners read the *current*\n // state without re-subscribing on every edit.\n const presentRef = useRef(builder.present)\n presentRef.current = builder.present\n const selectedIdRef = useRef(builder.selectedId)\n selectedIdRef.current = builder.selectedId\n const selectedRunRef = useRef(builder.selectedRun)\n selectedRunRef.current = builder.selectedRun\n\n // EC-414 AD-D: the settled canvas-theme classification (legacy merged shape /\n // broken stylesheet) drives the dev-only, never-blocking notice overlay.\n const [themeStatus, setThemeStatus] = useState<CanvasThemeStatus | null>(null)\n\n const wireUp = useCallback(() => {\n const d = iframeRef.current?.contentDocument\n if (!d || !d.body) return\n // Deliberately no `.dark` here: this iframe renders the REAL page (plus\n // studio editing chrome portaled alongside it), and its backdrop must read\n // as a light, site-like page — never the studio chrome's current theme (a\n // black canvas is exactly what leaks through when the studio is\n // dark-themed and the iframe root inherits that class). The injected\n // canvas stylesheet (new world: the chrome's neutral backdrop; legacy\n // world: the studio `:root` tokens) supplies that backdrop once `.dark` is\n // never added here (mirrors the components-preview surface, which wires\n // through the same chrome). The editing chrome (selection/hover outlines,\n // drop indicators, the rich-text toolbar, locale-fallback chips) uses\n // hardcoded colors rather than tokens, so it stays visible against a light\n // page regardless. The full injection — stylesheet link, editing chrome,\n // the consumer's `defineStudio({ canvas })` theme (EC-389, injected LAST so\n // it wins), and the EC-414 world probe — lives in\n // `wireCanvasChrome`/`applyCanvasChrome`.\n wireCanvasChrome(d, {\n stylesheetUrl: getCanvasStylesheetUrl(),\n theme: resolveDeploymentCanvasTheme(),\n onStatus: (status) =>\n setThemeStatus((prev) => (prev?.kind === status.kind ? prev : status)),\n })\n setDoc(d)\n }, [])\n\n // Handle the already-loaded case (srcDoc may load before onLoad attaches).\n useEffect(() => {\n if (iframeRef.current?.contentDocument?.readyState === 'complete') wireUp()\n }, [wireUp])\n\n // Inline editing (EC-158/EC-159): the dblclick-to-edit state machine, the\n // live session ref other listeners consult, the rich-text host (mark\n // toolbar), and the per-node remount epochs (decorate keys).\n const inline = useCanvasInlineEditing(doc, { presentRef, dispatch, studio })\n const { editingRef, richEditHost, richEditEpochs, editing } = inline\n\n // EC-408: the field↔component sync channel (null in the component preview,\n // which has no inspector). Read live through a ref — the native iframe\n // listeners outlive renders.\n const propSync = usePropSync()\n const propSyncRef = useRef(propSync)\n propSyncRef.current = propSync\n\n // Native selection/hover listeners inside the iframe document.\n useEffect(() => {\n if (!doc) return\n const onClick = (e: Event) => {\n // Clicks inside the active inline editor are caret moves, not selection.\n if (editingRef.current?.host.contains(e.target as Node)) return\n // EC-549: the NEAREST canvas item is either a block (`data-ec-node-id`) or a\n // typography run (`data-ec-run`, path-addressed) — a run selects as a run.\n const item = (e.target as Element | null)?.closest?.('[data-ec-run],[data-ec-node-id]')\n const runKey = item?.getAttribute('data-ec-run')\n if (runKey != null) {\n e.preventDefault()\n dispatch({ type: 'selectRun', run: Number(runKey) })\n return\n }\n const wrapper = wrapperAt(e.target)\n if (!wrapper) return\n const id = wrapper.getAttribute('data-ec-node-id')!\n e.preventDefault()\n dispatch({ type: 'select', id })\n // EC-408: resolve the clicked sub-part to its `data-ec-prop` and, unless it\n // is inline-editable (inline editing stays primary), ask the inspector to\n // focus that prop's control. Unannotated components resolve no marker → the\n // node-level select above is the whole behavior, exactly as before.\n const sync = propSyncRef.current\n if (!sync) return\n const node = getNode(presentRef.current, id)\n const manifest = node ? studio.registry.getManifest(node.componentId) : undefined\n const { propName, focusField } = resolvePropClick({\n target: e.target as Element | null,\n wrapper,\n manifest,\n })\n if (propName && focusField) sync.requestFocus({ nodeId: id, propName })\n }\n const onOver = (e: Event) => dispatch({ type: 'hover', id: nodeIdAt(e.target) })\n const onLeave = () => dispatch({ type: 'hover', id: null })\n doc.addEventListener('click', onClick, true)\n doc.addEventListener('mouseover', onOver)\n doc.addEventListener('mouseleave', onLeave, true)\n return () => {\n doc.removeEventListener('click', onClick, true)\n doc.removeEventListener('mouseover', onOver)\n doc.removeEventListener('mouseleave', onLeave, true)\n }\n }, [doc, dispatch, editingRef, presentRef, studio])\n\n // Drag & drop on the canvas (EC-160/EC-162) — see `canvas-dnd`.\n // EC-554: the field's top-level `allow` rides with the store (`builder.field`).\n useCanvasDnd(doc, { presentRef, dispatch, studio, editingRef, placement: builder.placement })\n\n // Copy/cut/paste on the canvas (EC-175): the same studio clipboard the page\n // tree and blocks panel use, wired to the *iframe* document (events never\n // cross frames). Shortcuts stay inert while an inline editor is live and in\n // form fields; the iframe's own navigator.clipboard does the async I/O\n // (Chromium scopes clipboard access to the focused document).\n const clipboard = useStudioClipboard()\n useEffect(() => {\n if (!doc) return\n return attachClipboardListeners(doc, {\n clipboard,\n getSelectedId: () => selectedIdRef.current,\n isBlocked: () => editingRef.current !== null,\n asyncClipboard: doc.defaultView?.navigator.clipboard,\n getSelection: () => doc.getSelection(),\n })\n }, [doc, clipboard, editingRef])\n\n // Delete/Backspace removes the selected node (EC-404) — the keyboard remove\n // the studio was missing, wired beside the clipboard listeners on the iframe\n // document, guarded so typing in an inline editor edits text instead.\n useEffect(() => {\n if (!doc) return\n return attachDeleteKeyListener(doc, {\n getSelectedId: () => selectedIdRef.current,\n remove: (id) => dispatch({ type: 'remove', id }),\n getRootId: () => presentRef.current.id,\n // EC-549: a selected typography run deletes as a run (one undo restores it).\n removeSelectedRun: () => {\n const run = selectedRunRef.current\n if (run === null) return false\n dispatch({ type: 'removeRun', run })\n return true\n },\n })\n }, [doc, dispatch])\n\n // The render context: the seven delivery-parity resolvers + the editable-\n // wrapper decorator (selection/hover, locale chips, remount epochs) — see\n // `canvas-render-context`.\n const { ctx, pageNodeIds } = useCanvasRenderContext({\n present: builder.present,\n selectedId: builder.selectedId,\n hoveredId: builder.hoveredId,\n studio,\n perspective,\n richEditEpochs,\n })\n\n // Tokenized spacing handles (EC-163): a selected Stack/Grid with 2+ children\n // gets draggable gap handles between them, portaled into the iframe. Suppressed\n // while an inline text/rich-text edit owns the node (EC-406 scope 4) — the\n // [data-ec-editing] outline owns that state, exactly as the selection overlay\n // yields; a content-drag is suppressed inside SpacingHandles via the drag session.\n const spacingTarget =\n doc && !editing && hasSpacingHandles(builder.selectedNode, builder.layout) && pageNodeIds.has(builder.selectedNode.id)\n ? builder.selectedNode\n : null\n\n // EC-250: the preview frame width derives from the config-sourced breakpoints\n // (Tailwind defaults when unconfigured), the same resolved set the toolbar\n // switcher offers.\n const { viewports } = useCanvasViewports()\n\n return (\n <div className=\"canvas-grid relative flex min-h-0 flex-1 items-stretch justify-center overflow-auto p-6\">\n {/* Copy/cut/paste outcomes (EC-175): non-blocking, with issue deep links. */}\n <ClipboardNoticeOverlay />\n {/* Canvas-theme classification notice (EC-414 AD-D): dev-only, never blocks. */}\n <CanvasThemeNotice status={themeStatus} />\n <div style={{ width: viewportWidth(viewports, builder.viewport), maxWidth: '100%' }} className=\"flex\">\n <iframe\n ref={iframeRef}\n onLoad={wireUp}\n title={translate('builder.canvas.pagePreview')}\n className=\"min-h-[480px] w-full rounded-lg border border-border bg-background shadow-md\"\n srcDoc=\"<!doctype html><html><head></head><body></body></html>\"\n />\n {doc\n ? createPortal(\n // EC-592 + EC-549: the FIELD VALUE through delivery's one entry point —\n // blocks as bare siblings, typography runs in their true positions —\n // inside the decorated (never rendered) synthetic root.\n renderCanvasBody({\n root: activeRoot,\n preserved: builder.preserved,\n ctx,\n selectedId: builder.selectedId,\n selectedRun: builder.selectedRun,\n }),\n doc.body,\n )\n : null}\n {/* Selection/hover frames + frame controls, topmost over the page (EC-404). */}\n {doc ? <SelectionOverlay doc={doc} editing={editing} /> : null}\n {spacingTarget && doc ? (\n <SpacingHandles key={spacingTarget.id} doc={doc} node={spacingTarget} />\n ) : null}\n {/* Floating mark toolbar for the live rich-text session (EC-159). */}\n {richEditHost && doc ? <RichTextToolbar doc={doc} host={richEditHost} /> : null}\n </div>\n </div>\n )\n}\n"]}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import type { ComponentNode } from '@elytracms/core/project-graph';
|
|
1
2
|
import type { ClipboardFragment } from '@elytracms/core/clipboard';
|
|
2
|
-
import type { AsyncClipboard, PasteIssue, PasteRejection } from '
|
|
3
|
+
import type { AsyncClipboard, PasteIssue, PasteRejection } from '../../lib/clipboard/index.js';
|
|
4
|
+
import type { StudioRegistry } from '../../lib/registry/index.js';
|
|
3
5
|
/**
|
|
4
6
|
* Copy/cut/paste everywhere (EC-175, vision 3.3/3.7).
|
|
5
7
|
*
|
|
@@ -70,6 +72,19 @@ export interface StudioClipboard {
|
|
|
70
72
|
pasteFromClipboard(anchorId?: string | null, asyncClipboard?: AsyncClipboard): Promise<void>;
|
|
71
73
|
}
|
|
72
74
|
export declare function useStudioClipboard(): StudioClipboard;
|
|
75
|
+
/**
|
|
76
|
+
* The visible reason a refused paste shows (EC-545) — pure, so the notice
|
|
77
|
+
* wording per refusal is pinned without mounting the hook. EC-554: a field-leash
|
|
78
|
+
* refusal names the FIELD (`fieldLabel`), never the invisible synthetic root's
|
|
79
|
+
* slot — the rejection carries no destination label to misuse.
|
|
80
|
+
*/
|
|
81
|
+
/**
|
|
82
|
+
* The node plain text pastes as (EC-549, ratified 4d): the component filling the `text`
|
|
83
|
+
* role with the text in its role-contract prop (`value`, `ROLE_CONTRACTS`) — never a
|
|
84
|
+
* loose typography item. `null` when no component fills the role (the gesture is off;
|
|
85
|
+
* the canvas's role notice says so). Ids are minted on insert.
|
|
86
|
+
*/
|
|
87
|
+
export declare function plainTextNode(studio: StudioRegistry, text: string): ComponentNode | null;
|
|
73
88
|
export declare function pasteRejectionMessage(rejection: PasteRejection, fieldLabel: string): string;
|
|
74
89
|
export interface ClipboardListenerOptions {
|
|
75
90
|
clipboard: StudioClipboard;
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useMemo, useRef, useSyncExternalStore } from 'react';
|
|
3
3
|
import { AlertTriangle, CircleCheck, Crosshair, X } from 'lucide-react';
|
|
4
|
-
import { cloneWithNewIds } from '@elytracms/core/project-graph';
|
|
4
|
+
import { cloneWithNewIds, subtreePlacementRefusal } from '@elytracms/core/project-graph';
|
|
5
5
|
import { buildFragmentFromComposition } from '@elytracms/core/clipboard';
|
|
6
|
-
import { readFragmentFromClipboard, readFragmentFromDataTransfer, resolvePasteTarget, diffPastedSubtree, pasteSummaryMessage, writeFragmentToClipboard, writeFragmentToDataTransfer, } from '
|
|
7
|
-
import { getStudioRegistry } from '
|
|
8
|
-
import { clipboardErrorMessage, componentDisplayName } from '
|
|
9
|
-
import { translate } from '
|
|
6
|
+
import { readFragmentFromClipboard, readFragmentFromDataTransfer, resolvePasteTarget, diffPastedSubtree, pasteSummaryMessage, writeFragmentToClipboard, writeFragmentToDataTransfer, } from '../../lib/clipboard/index.js';
|
|
7
|
+
import { getStudioRegistry } from '../../lib/registry/index.js';
|
|
8
|
+
import { clipboardErrorMessage, componentDisplayName } from '../../lib/persona/labels.js';
|
|
9
|
+
import { translate } from '../../lib/i18n/index.js';
|
|
10
10
|
import { useProjectSession } from '../project-session.js';
|
|
11
11
|
import { Badge, Row, Text } from '@elytracms/studio/ui';
|
|
12
12
|
import { useBuilder } from './builder-store.js';
|
|
13
|
+
import { placementRefusalMessage } from './placement-refusal.js';
|
|
13
14
|
let currentNotice = null;
|
|
14
15
|
const noticeListeners = new Set();
|
|
15
16
|
export function publishClipboardNotice(notice) {
|
|
@@ -85,8 +86,21 @@ export function useStudioClipboard() {
|
|
|
85
86
|
};
|
|
86
87
|
const applyPaste = (result, anchorId) => {
|
|
87
88
|
const { builder: b } = stateRef.current;
|
|
88
|
-
|
|
89
|
-
|
|
89
|
+
// EC-549 (ratified 4d): the canvas never creates loose prose — plain text pastes
|
|
90
|
+
// as a `text`-role component node (no fragment on the clipboard, a text component
|
|
91
|
+
// in the list); otherwise a failed parse says why.
|
|
92
|
+
const pasted = result.ok
|
|
93
|
+
? result.fragment.payload.root
|
|
94
|
+
: result.plainText !== undefined
|
|
95
|
+
? plainTextNode(studio, result.plainText)
|
|
96
|
+
: null;
|
|
97
|
+
if (!pasted) {
|
|
98
|
+
const message = result.ok
|
|
99
|
+
? ''
|
|
100
|
+
: result.plainText !== undefined
|
|
101
|
+
? translate('builder.roles.noText') // plain text, but no component fills the text role
|
|
102
|
+
: clipboardErrorMessage(result.error);
|
|
103
|
+
publishClipboardNotice({ kind: 'error', message });
|
|
90
104
|
return;
|
|
91
105
|
}
|
|
92
106
|
const root = currentRoot();
|
|
@@ -95,10 +109,11 @@ export function useStudioClipboard() {
|
|
|
95
109
|
const resolution = resolvePasteTarget({
|
|
96
110
|
root,
|
|
97
111
|
anchorId: anchorId !== undefined ? anchorId : b.selectedId,
|
|
98
|
-
componentId:
|
|
112
|
+
componentId: pasted.componentId,
|
|
99
113
|
getManifest: (id) => studio.registry.getManifest(id),
|
|
100
|
-
// EC-554: the
|
|
101
|
-
|
|
114
|
+
// EC-554 / EC-584: the store's placement scope — the field's vocabulary and
|
|
115
|
+
// the manifests the one judge reads.
|
|
116
|
+
placement: b.placement,
|
|
102
117
|
});
|
|
103
118
|
if (!resolution.ok) {
|
|
104
119
|
// EC-545 / EC-554: the slot leash or the field leash refuses the pasted
|
|
@@ -110,7 +125,14 @@ export function useStudioClipboard() {
|
|
|
110
125
|
// Mint the inserted subtree's ids HERE, from the store's own factory (EC-578), and
|
|
111
126
|
// insert exactly that subtree (`minted`) — so the issue deep links point at the
|
|
112
127
|
// very ids that land, with no mirror to diverge.
|
|
113
|
-
const inserted = cloneWithNewIds(
|
|
128
|
+
const inserted = cloneWithNewIds(pasted, b.newId);
|
|
129
|
+
// EC-584: the reducer judges the WHOLE pasted subtree; ask the same judge first so
|
|
130
|
+
// the notice never claims a paste the sink refuses (a nested child outside its slot).
|
|
131
|
+
const verdict = subtreePlacementRefusal(root, inserted, { parentId: target.parentId, slot: target.slot }, b.placement);
|
|
132
|
+
if (verdict) {
|
|
133
|
+
publishClipboardNotice({ kind: 'error', message: placementRefusalMessage(verdict, b.field.label) });
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
114
136
|
// ONE dispatch: one history entry, one undo.
|
|
115
137
|
b.dispatch({ type: 'insert', target, node: inserted, minted: true });
|
|
116
138
|
const diff = diffPastedSubtree({
|
|
@@ -162,11 +184,26 @@ export function useStudioClipboard() {
|
|
|
162
184
|
* refusal names the FIELD (`fieldLabel`), never the invisible synthetic root's
|
|
163
185
|
* slot — the rejection carries no destination label to misuse.
|
|
164
186
|
*/
|
|
187
|
+
/**
|
|
188
|
+
* The node plain text pastes as (EC-549, ratified 4d): the component filling the `text`
|
|
189
|
+
* role with the text in its role-contract prop (`value`, `ROLE_CONTRACTS`) — never a
|
|
190
|
+
* loose typography item. `null` when no component fills the role (the gesture is off;
|
|
191
|
+
* the canvas's role notice says so). Ids are minted on insert.
|
|
192
|
+
*/
|
|
193
|
+
export function plainTextNode(studio, text) {
|
|
194
|
+
const textComponent = studio.registry.roleComponent('text');
|
|
195
|
+
if (!textComponent)
|
|
196
|
+
return null;
|
|
197
|
+
return { type: 'elytra/component', id: 'paste-text', componentId: textComponent.id, props: { value: { value: text } } };
|
|
198
|
+
}
|
|
165
199
|
/** A component's label in a notice — its manifest title (EC-591: never a stripped id prefix). */
|
|
166
200
|
function componentLabel(componentId) {
|
|
167
201
|
return componentDisplayName(componentId, getStudioRegistry().registry.getManifest(componentId));
|
|
168
202
|
}
|
|
169
203
|
export function pasteRejectionMessage(rejection, fieldLabel) {
|
|
204
|
+
if (rejection.code === 'server-only') {
|
|
205
|
+
return translate('builder.placement.serverOnly', { component: componentLabel(rejection.componentId) });
|
|
206
|
+
}
|
|
170
207
|
if (rejection.code === 'field-not-allowed') {
|
|
171
208
|
return translate('builder.clipboard.notAllowedByField', {
|
|
172
209
|
component: componentLabel(rejection.componentId),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clipboard-actions.js","sourceRoot":"","sources":["../../../src/components/builder/clipboard-actions.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useMemo, useRef, useSyncExternalStore } from 'react'\nimport { AlertTriangle, CircleCheck, Crosshair, X } from 'lucide-react'\nimport { cloneWithNewIds } from '@elytracms/core/project-graph'\nimport type { ComponentNode } from '@elytracms/core/project-graph'\nimport { buildFragmentFromComposition } from '@elytracms/core/clipboard'\nimport type { ClipboardFragment, ParseFragmentResult } from '@elytracms/core/clipboard'\nimport {\n readFragmentFromClipboard,\n readFragmentFromDataTransfer,\n resolvePasteTarget,\n diffPastedSubtree,\n pasteSummaryMessage,\n writeFragmentToClipboard,\n writeFragmentToDataTransfer,\n} from '@elytracms/studio/lib/clipboard'\nimport type { AsyncClipboard, PasteIssue, PasteRejection } from '@elytracms/studio/lib/clipboard'\nimport { getStudioRegistry } from '@elytracms/studio/lib/registry'\nimport { clipboardErrorMessage, componentDisplayName } from '@elytracms/studio/lib/persona/labels'\nimport { translate } from '@elytracms/studio/lib/i18n'\nimport { useProjectSession } from '../project-session'\nimport { Badge, Row, Text } from '@elytracms/studio/ui'\nimport { useBuilder } from './builder-store'\n\n/**\n * Copy/cut/paste everywhere (EC-175, vision 3.3/3.7).\n *\n * One hook ({@link useStudioClipboard}) backs every affordance — canvas\n * keyboard shortcuts (wired inside the iframe by the canvas), page-tree\n * shortcuts and row buttons, and the blocks panel's \"copy exact\n * configuration\" — so they all produce/consume the same EC-174 fragment via\n * the OS clipboard (`lib/clipboard/os-clipboard`), never an app-internal\n * buffer. That is what makes paste work across documents, pages, projects,\n * and browser tabs.\n *\n * Dispatch contract: a paste is exactly ONE `insert` action (the fragment\n * root is a single subtree) → one history entry → one mirrored\n * `graph.insertNode` command — one undo step removes everything the paste\n * inserted. A cut is copy + exactly one `remove`; one undo restores it.\n *\n * Paste is PERMISSIVE: missing components and unresolvable bindings insert\n * anyway (the renderer's fallbacks keep them visible) and surface in a\n * notice — \"Pasted — 2 bindings unresolved, 1 component missing\" — whose\n * issues deep-link to the inserted nodes. Only structurally invalid clipboard\n * content is reported as a non-blocking error, never a crash or silent no-op.\n *\n * The one thing paste REFUSES is the leash: a pasted root the resolved slot's\n * `allow` excludes (EC-545), or — landing as a direct child of the composition\n * root — the composition field's top-level `allow` excludes (EC-554), is\n * rejected with a visible notice and no dispatch — the same verdict\n * drag-and-drop gives. Permissiveness is about what the target project can\n * *resolve*; the leash is what the component and field authors *declared*.\n */\n\n// ---------------------------------------------------------------------------\n// Notice store — module-level pub/sub (the drag-session pattern) so any\n// surface can publish and the canvas overlay renders it.\n// ---------------------------------------------------------------------------\n\nexport type ClipboardNotice =\n | { kind: 'copied'; label: string }\n | { kind: 'cut'; label: string }\n | { kind: 'pasted'; summary: string; destinationLabel: string; issues: PasteIssue[] }\n | { kind: 'error'; message: string }\n\nlet currentNotice: ClipboardNotice | null = null\nconst noticeListeners = new Set<() => void>()\n\nexport function publishClipboardNotice(notice: ClipboardNotice | null): void {\n currentNotice = notice\n for (const listener of [...noticeListeners]) listener()\n}\n\nfunction subscribeNotice(listener: () => void): () => void {\n noticeListeners.add(listener)\n return () => noticeListeners.delete(listener)\n}\n\nfunction getNotice(): ClipboardNotice | null {\n return currentNotice\n}\n\nexport function useClipboardNotice(): ClipboardNotice | null {\n return useSyncExternalStore(subscribeNotice, getNotice, getNotice)\n}\n\n// ---------------------------------------------------------------------------\n// Shortcut scoping\n// ---------------------------------------------------------------------------\n\n/**\n * Whether a clipboard shortcut/event must stay inert for this target: form\n * fields and any contenteditable (which includes the EC-158/EC-159 inline\n * editors) own their own native clipboard behaviour.\n */\nexport function clipboardEventBlocked(target: EventTarget | null): boolean {\n const el = target as HTMLElement | null\n if (!el) return false\n const tag = el.tagName\n if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') return true\n if (typeof el.closest === 'function' && el.closest('[contenteditable]')) return true\n return el.isContentEditable === true\n}\n\n// ---------------------------------------------------------------------------\n// The clipboard surface\n// ---------------------------------------------------------------------------\n\nexport interface StudioClipboard {\n /**\n * Copy a canvas/tree node's subtree. Writes synchronously when a\n * ClipboardEvent's DataTransfer is given, else via the async Clipboard API\n * (`asyncClipboard` lets the canvas pass the iframe's own navigator —\n * Chromium scopes clipboard access to the *focused* document).\n */\n copyNode(nodeId: string, dt?: DataTransfer | null, asyncClipboard?: AsyncClipboard): void\n /** Copy, then remove the source in exactly one dispatch (one undo restores). */\n cutNode(nodeId: string, dt?: DataTransfer | null, asyncClipboard?: AsyncClipboard): void\n /** Copy a prebuilt fragment (blocks-panel \"copy exact configuration\"). */\n copyFragment(fragment: ClipboardFragment, label: string): void\n /** Paste from a paste event's DataTransfer (no permissions needed). */\n pasteFromDataTransfer(dt: DataTransfer, anchorId?: string | null): void\n /** Paste via the async Clipboard API (explicit affordances + shortcuts). */\n pasteFromClipboard(anchorId?: string | null, asyncClipboard?: AsyncClipboard): Promise<void>\n}\n\nexport function useStudioClipboard(): StudioClipboard {\n const builder = useBuilder()\n const { projectId } = useProjectSession()\n const studio = getStudioRegistry()\n\n // Event listeners outlive renders; refs keep them on the current state.\n const stateRef = useRef({ builder, projectId })\n stateRef.current = { builder, projectId }\n\n return useMemo<StudioClipboard>(() => {\n const fragmentFor = (nodeId: string): { fragment: ClipboardFragment; label: string } | null => {\n const { builder: b, projectId } = stateRef.current\n const built = buildFragmentFromComposition(b.present, nodeId, { source: { projectId } })\n if (!built.ok) {\n // EC-494: the engine's clipboard errors carry a catalog ref, so the notice\n // reads in the active studio locale instead of English inside German chrome.\n publishClipboardNotice({ kind: 'error', message: clipboardErrorMessage(built.error) })\n return null\n }\n const rootId = built.fragment.payload.root.componentId\n return { fragment: built.fragment, label: componentDisplayName(rootId, getStudioRegistry().registry.getManifest(rootId)) }\n }\n\n const write = (\n fragment: ClipboardFragment,\n label: string,\n kind: 'copied' | 'cut',\n dt?: DataTransfer | null,\n asyncClipboard?: AsyncClipboard,\n ): void => {\n if (dt) {\n writeFragmentToDataTransfer(dt, fragment)\n publishClipboardNotice({ kind, label })\n return\n }\n writeFragmentToClipboard(asyncClipboard ?? navigator.clipboard, fragment)\n .then(() => publishClipboardNotice({ kind, label }))\n .catch((cause: unknown) => {\n const detail = cause instanceof Error ? cause.message : String(cause)\n publishClipboardNotice({ kind: 'error', message: translate('builder.clipboard.writeError', { detail }) })\n })\n }\n\n const currentRoot = (): ComponentNode | undefined => {\n const { builder: b } = stateRef.current\n return b.present\n }\n\n const applyPaste = (result: ParseFragmentResult, anchorId?: string | null): void => {\n const { builder: b } = stateRef.current\n if (!result.ok) {\n publishClipboardNotice({ kind: 'error', message: clipboardErrorMessage(result.error) })\n return\n }\n const root = currentRoot()\n if (!root) return\n const resolution = resolvePasteTarget({\n root,\n anchorId: anchorId !== undefined ? anchorId : b.selectedId,\n componentId: result.fragment.payload.root.componentId,\n getManifest: (id) => studio.registry.getManifest(id),\n // EC-554: the composition field's top-level vocabulary, from the store.\n fieldAllow: b.field.allow,\n })\n if (!resolution.ok) {\n // EC-545 / EC-554: the slot leash or the field leash refuses the pasted\n // root — the same verdict a drop gets, made visible. Nothing is dispatched.\n publishClipboardNotice({ kind: 'error', message: pasteRejectionMessage(resolution, b.field.label) })\n return\n }\n const { target, destinationLabel } = resolution\n // Mint the inserted subtree's ids HERE, from the store's own factory (EC-578), and\n // insert exactly that subtree (`minted`) — so the issue deep links point at the\n // very ids that land, with no mirror to diverge.\n const inserted = cloneWithNewIds(result.fragment.payload.root, b.newId)\n // ONE dispatch: one history entry, one undo.\n b.dispatch({ type: 'insert', target, node: inserted, minted: true })\n const diff = diffPastedSubtree({\n node: inserted,\n hasComponent: (id) => studio.registry.getManifest(id) !== undefined,\n })\n publishClipboardNotice({\n kind: 'pasted',\n summary: pasteSummaryMessage(diff),\n destinationLabel,\n issues: diff.issues,\n })\n }\n\n return {\n copyNode(nodeId, dt, asyncClipboard) {\n const built = fragmentFor(nodeId)\n if (built) write(built.fragment, built.label, 'copied', dt, asyncClipboard)\n },\n cutNode(nodeId, dt, asyncClipboard) {\n const { builder: b } = stateRef.current\n const root = currentRoot()\n if (root && nodeId === root.id) {\n publishClipboardNotice({ kind: 'error', message: translate('builder.clipboard.rootCannotBeCut') })\n return\n }\n const built = fragmentFor(nodeId)\n if (!built) return\n write(built.fragment, built.label, 'cut', dt, asyncClipboard)\n // Exactly one structural dispatch: a single undo restores the cut node.\n b.dispatch({ type: 'remove', id: nodeId })\n },\n copyFragment(fragment, label) {\n write(fragment, label, 'copied')\n },\n pasteFromDataTransfer(dt, anchorId) {\n applyPaste(readFragmentFromDataTransfer(dt), anchorId)\n },\n async pasteFromClipboard(anchorId, asyncClipboard) {\n applyPaste(await readFragmentFromClipboard(asyncClipboard ?? navigator.clipboard), anchorId)\n },\n }\n }, [studio])\n}\n\n/**\n * The visible reason a refused paste shows (EC-545) — pure, so the notice\n * wording per refusal is pinned without mounting the hook. EC-554: a field-leash\n * refusal names the FIELD (`fieldLabel`), never the invisible synthetic root's\n * slot — the rejection carries no destination label to misuse.\n */\n/** A component's label in a notice — its manifest title (EC-591: never a stripped id prefix). */\nfunction componentLabel(componentId: string): string {\n return componentDisplayName(componentId, getStudioRegistry().registry.getManifest(componentId))\n}\n\nexport function pasteRejectionMessage(rejection: PasteRejection, fieldLabel: string): string {\n if (rejection.code === 'field-not-allowed') {\n return translate('builder.clipboard.notAllowedByField', {\n component: componentLabel(rejection.componentId),\n field: fieldLabel,\n })\n }\n return translate('builder.clipboard.notAllowed', {\n component: componentLabel(rejection.componentId),\n destination: rejection.destinationLabel,\n })\n}\n\n// ---------------------------------------------------------------------------\n// Listener wiring — one implementation for the page-tree window and the\n// canvas iframe document (events never cross the frame boundary).\n// ---------------------------------------------------------------------------\n\nexport interface ClipboardListenerOptions {\n clipboard: StudioClipboard\n /** Current selection: the copy/cut subject and the paste anchor. */\n getSelectedId: () => string | null\n /** Extra guard (e.g. the canvas's live inline-edit session). */\n isBlocked?: (target: EventTarget | null) => boolean\n /** The async Clipboard API of the listening document's own window. */\n asyncClipboard?: AsyncClipboard\n /** The listening document's selection — real text selections stay native. */\n getSelection?: () => Selection | null\n}\n\ninterface ListenerTarget {\n addEventListener(type: string, listener: (e: Event) => void): void\n removeEventListener(type: string, listener: (e: Event) => void): void\n}\n\n/**\n * Wire Cmd/Ctrl+C/X/V plus native copy/cut/paste ClipboardEvents.\n *\n * Shortcuts are handled on `keydown` (a selected node has no text selection,\n * so the browser would not fire native clipboard events for it); the\n * `preventDefault` there suppresses the native event, so the ClipboardEvent\n * listeners only ever see *other* triggers — context-menu copy/paste and\n * synthetic events — and the two paths can never double-handle one gesture.\n * Everything stays inert in form fields and inside the inline editors.\n */\nexport function attachClipboardListeners(target: ListenerTarget, opts: ClipboardListenerOptions): () => void {\n const blocked = (t: EventTarget | null): boolean =>\n clipboardEventBlocked(t) || (opts.isBlocked?.(t) ?? false)\n const hasTextSelection = (): boolean => {\n const selection = opts.getSelection?.()\n return selection !== null && selection !== undefined && !selection.isCollapsed\n }\n\n const onKeyDown = (e: Event) => {\n const event = e as KeyboardEvent\n if (!(event.metaKey || event.ctrlKey) || event.altKey || event.shiftKey) return\n const key = event.key.toLowerCase()\n if (key !== 'c' && key !== 'x' && key !== 'v') return\n if (blocked(event.target)) return\n if ((key === 'c' || key === 'x') && hasTextSelection()) return\n const selectedId = opts.getSelectedId()\n if (key === 'v') {\n event.preventDefault()\n void opts.clipboard.pasteFromClipboard(selectedId, opts.asyncClipboard)\n return\n }\n if (!selectedId) return\n event.preventDefault()\n if (key === 'c') opts.clipboard.copyNode(selectedId, null, opts.asyncClipboard)\n else opts.clipboard.cutNode(selectedId, null, opts.asyncClipboard)\n }\n\n const onCopyOrCut = (e: Event) => {\n const event = e as ClipboardEvent\n if (blocked(event.target) || hasTextSelection()) return\n const selectedId = opts.getSelectedId()\n if (!selectedId || !event.clipboardData) return\n event.preventDefault()\n if (event.type === 'copy') opts.clipboard.copyNode(selectedId, event.clipboardData)\n else opts.clipboard.cutNode(selectedId, event.clipboardData)\n }\n\n const onPaste = (e: Event) => {\n const event = e as ClipboardEvent\n if (blocked(event.target) || !event.clipboardData) return\n event.preventDefault()\n opts.clipboard.pasteFromDataTransfer(event.clipboardData, opts.getSelectedId())\n }\n\n target.addEventListener('keydown', onKeyDown)\n target.addEventListener('copy', onCopyOrCut)\n target.addEventListener('cut', onCopyOrCut)\n target.addEventListener('paste', onPaste)\n return () => {\n target.removeEventListener('keydown', onKeyDown)\n target.removeEventListener('copy', onCopyOrCut)\n target.removeEventListener('cut', onCopyOrCut)\n target.removeEventListener('paste', onPaste)\n }\n}\n\n// ---------------------------------------------------------------------------\n// Notice overlay — rendered by the canvas; issues deep-link to their nodes.\n// ---------------------------------------------------------------------------\n\nexport function ClipboardNoticeOverlay() {\n const notice = useClipboardNotice()\n const builder = useBuilder()\n if (!notice) return null\n\n const tone =\n notice.kind === 'error' ? 'error' : notice.kind === 'pasted' && notice.issues.length > 0 ? 'warning' : 'success'\n\n return (\n <div\n data-testid=\"clipboard-notice\"\n data-notice-kind={notice.kind}\n className=\"absolute bottom-4 left-1/2 z-20 w-max max-w-[min(420px,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 {tone === 'error' ? (\n <AlertTriangle className=\"mt-0.5 size-3.5 shrink-0 text-destructive\" aria-hidden />\n ) : tone === 'warning' ? (\n <AlertTriangle className=\"mt-0.5 size-3.5 shrink-0 text-warning\" aria-hidden />\n ) : (\n <CircleCheck className=\"mt-0.5 size-3.5 shrink-0 text-success\" aria-hidden />\n )}\n <Text size=\"xs\" className=\"min-w-0 flex-1\">\n {notice.kind === 'copied'\n ? translate('builder.clipboard.copied', { label: notice.label })\n : notice.kind === 'cut'\n ? translate('builder.clipboard.cutNotice', { label: notice.label })\n : notice.kind === 'pasted'\n ? translate('builder.clipboard.pastedInto', {\n summary: notice.summary,\n destination: notice.destinationLabel,\n })\n : notice.message}\n </Text>\n <button\n type=\"button\"\n aria-label={translate('common.dismiss')}\n onClick={() => publishClipboardNotice(null)}\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 {notice.kind === 'pasted' && notice.issues.length > 0 ? (\n <div className=\"mt-1.5 flex flex-col gap-1\">\n {notice.issues.map((issue, i) => (\n <button\n key={`${issue.nodeId}-${i}`}\n type=\"button\"\n data-testid=\"clipboard-notice-issue\"\n onClick={() => builder.dispatch({ type: 'select', id: issue.nodeId })}\n className=\"flex w-full items-center gap-2 rounded-md p-1 text-left transition-colors hover:bg-muted\"\n >\n <Badge variant=\"warning\" className=\"shrink-0\">\n {translate('builder.clipboard.issueComponent')}\n </Badge>\n <Text size=\"xs\" tone=\"muted\" className=\"min-w-0 flex-1 truncate\">\n {issue.message}\n </Text>\n <Crosshair className=\"size-3.5 shrink-0 text-muted-foreground\" aria-hidden />\n </button>\n ))}\n </div>\n ) : null}\n </div>\n </div>\n )\n}\n"]}
|
|
1
|
+
{"version":3,"file":"clipboard-actions.js","sourceRoot":"","sources":["../../../src/components/builder/clipboard-actions.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useMemo, useRef, useSyncExternalStore } from 'react'\nimport { AlertTriangle, CircleCheck, Crosshair, X } from 'lucide-react'\nimport { cloneWithNewIds, subtreePlacementRefusal } from '@elytracms/core/project-graph'\nimport type { ComponentNode } from '@elytracms/core/project-graph'\nimport { buildFragmentFromComposition } from '@elytracms/core/clipboard'\nimport type { ClipboardFragment } from '@elytracms/core/clipboard'\nimport {\n readFragmentFromClipboard,\n readFragmentFromDataTransfer,\n resolvePasteTarget,\n diffPastedSubtree,\n pasteSummaryMessage,\n writeFragmentToClipboard,\n writeFragmentToDataTransfer,\n} from '../../lib/clipboard'\nimport type { AsyncClipboard, ClipboardRead, PasteIssue, PasteRejection } from '../../lib/clipboard'\nimport type { StudioRegistry } from '../../lib/registry'\nimport { getStudioRegistry } from '../../lib/registry'\nimport { clipboardErrorMessage, componentDisplayName } from '../../lib/persona/labels'\nimport { translate } from '../../lib/i18n'\nimport { useProjectSession } from '../project-session'\nimport { Badge, Row, Text } from '@elytracms/studio/ui'\nimport { useBuilder } from './builder-store'\nimport { placementRefusalMessage } from './placement-refusal'\n\n/**\n * Copy/cut/paste everywhere (EC-175, vision 3.3/3.7).\n *\n * One hook ({@link useStudioClipboard}) backs every affordance — canvas\n * keyboard shortcuts (wired inside the iframe by the canvas), page-tree\n * shortcuts and row buttons, and the blocks panel's \"copy exact\n * configuration\" — so they all produce/consume the same EC-174 fragment via\n * the OS clipboard (`lib/clipboard/os-clipboard`), never an app-internal\n * buffer. That is what makes paste work across documents, pages, projects,\n * and browser tabs.\n *\n * Dispatch contract: a paste is exactly ONE `insert` action (the fragment\n * root is a single subtree) → one history entry → one mirrored\n * `graph.insertNode` command — one undo step removes everything the paste\n * inserted. A cut is copy + exactly one `remove`; one undo restores it.\n *\n * Paste is PERMISSIVE: missing components and unresolvable bindings insert\n * anyway (the renderer's fallbacks keep them visible) and surface in a\n * notice — \"Pasted — 2 bindings unresolved, 1 component missing\" — whose\n * issues deep-link to the inserted nodes. Only structurally invalid clipboard\n * content is reported as a non-blocking error, never a crash or silent no-op.\n *\n * The one thing paste REFUSES is the leash: a pasted root the resolved slot's\n * `allow` excludes (EC-545), or — landing as a direct child of the composition\n * root — the composition field's top-level `allow` excludes (EC-554), is\n * rejected with a visible notice and no dispatch — the same verdict\n * drag-and-drop gives. Permissiveness is about what the target project can\n * *resolve*; the leash is what the component and field authors *declared*.\n */\n\n// ---------------------------------------------------------------------------\n// Notice store — module-level pub/sub (the drag-session pattern) so any\n// surface can publish and the canvas overlay renders it.\n// ---------------------------------------------------------------------------\n\nexport type ClipboardNotice =\n | { kind: 'copied'; label: string }\n | { kind: 'cut'; label: string }\n | { kind: 'pasted'; summary: string; destinationLabel: string; issues: PasteIssue[] }\n | { kind: 'error'; message: string }\n\nlet currentNotice: ClipboardNotice | null = null\nconst noticeListeners = new Set<() => void>()\n\nexport function publishClipboardNotice(notice: ClipboardNotice | null): void {\n currentNotice = notice\n for (const listener of [...noticeListeners]) listener()\n}\n\nfunction subscribeNotice(listener: () => void): () => void {\n noticeListeners.add(listener)\n return () => noticeListeners.delete(listener)\n}\n\nfunction getNotice(): ClipboardNotice | null {\n return currentNotice\n}\n\nexport function useClipboardNotice(): ClipboardNotice | null {\n return useSyncExternalStore(subscribeNotice, getNotice, getNotice)\n}\n\n// ---------------------------------------------------------------------------\n// Shortcut scoping\n// ---------------------------------------------------------------------------\n\n/**\n * Whether a clipboard shortcut/event must stay inert for this target: form\n * fields and any contenteditable (which includes the EC-158/EC-159 inline\n * editors) own their own native clipboard behaviour.\n */\nexport function clipboardEventBlocked(target: EventTarget | null): boolean {\n const el = target as HTMLElement | null\n if (!el) return false\n const tag = el.tagName\n if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') return true\n if (typeof el.closest === 'function' && el.closest('[contenteditable]')) return true\n return el.isContentEditable === true\n}\n\n// ---------------------------------------------------------------------------\n// The clipboard surface\n// ---------------------------------------------------------------------------\n\nexport interface StudioClipboard {\n /**\n * Copy a canvas/tree node's subtree. Writes synchronously when a\n * ClipboardEvent's DataTransfer is given, else via the async Clipboard API\n * (`asyncClipboard` lets the canvas pass the iframe's own navigator —\n * Chromium scopes clipboard access to the *focused* document).\n */\n copyNode(nodeId: string, dt?: DataTransfer | null, asyncClipboard?: AsyncClipboard): void\n /** Copy, then remove the source in exactly one dispatch (one undo restores). */\n cutNode(nodeId: string, dt?: DataTransfer | null, asyncClipboard?: AsyncClipboard): void\n /** Copy a prebuilt fragment (blocks-panel \"copy exact configuration\"). */\n copyFragment(fragment: ClipboardFragment, label: string): void\n /** Paste from a paste event's DataTransfer (no permissions needed). */\n pasteFromDataTransfer(dt: DataTransfer, anchorId?: string | null): void\n /** Paste via the async Clipboard API (explicit affordances + shortcuts). */\n pasteFromClipboard(anchorId?: string | null, asyncClipboard?: AsyncClipboard): Promise<void>\n}\n\nexport function useStudioClipboard(): StudioClipboard {\n const builder = useBuilder()\n const { projectId } = useProjectSession()\n const studio = getStudioRegistry()\n\n // Event listeners outlive renders; refs keep them on the current state.\n const stateRef = useRef({ builder, projectId })\n stateRef.current = { builder, projectId }\n\n return useMemo<StudioClipboard>(() => {\n const fragmentFor = (nodeId: string): { fragment: ClipboardFragment; label: string } | null => {\n const { builder: b, projectId } = stateRef.current\n const built = buildFragmentFromComposition(b.present, nodeId, { source: { projectId } })\n if (!built.ok) {\n // EC-494: the engine's clipboard errors carry a catalog ref, so the notice\n // reads in the active studio locale instead of English inside German chrome.\n publishClipboardNotice({ kind: 'error', message: clipboardErrorMessage(built.error) })\n return null\n }\n const rootId = built.fragment.payload.root.componentId\n return { fragment: built.fragment, label: componentDisplayName(rootId, getStudioRegistry().registry.getManifest(rootId)) }\n }\n\n const write = (\n fragment: ClipboardFragment,\n label: string,\n kind: 'copied' | 'cut',\n dt?: DataTransfer | null,\n asyncClipboard?: AsyncClipboard,\n ): void => {\n if (dt) {\n writeFragmentToDataTransfer(dt, fragment)\n publishClipboardNotice({ kind, label })\n return\n }\n writeFragmentToClipboard(asyncClipboard ?? navigator.clipboard, fragment)\n .then(() => publishClipboardNotice({ kind, label }))\n .catch((cause: unknown) => {\n const detail = cause instanceof Error ? cause.message : String(cause)\n publishClipboardNotice({ kind: 'error', message: translate('builder.clipboard.writeError', { detail }) })\n })\n }\n\n const currentRoot = (): ComponentNode | undefined => {\n const { builder: b } = stateRef.current\n return b.present\n }\n\n const applyPaste = (result: ClipboardRead, anchorId?: string | null): void => {\n const { builder: b } = stateRef.current\n // EC-549 (ratified 4d): the canvas never creates loose prose — plain text pastes\n // as a `text`-role component node (no fragment on the clipboard, a text component\n // in the list); otherwise a failed parse says why.\n const pasted = result.ok\n ? result.fragment.payload.root\n : result.plainText !== undefined\n ? plainTextNode(studio, result.plainText)\n : null\n if (!pasted) {\n const message = result.ok\n ? ''\n : result.plainText !== undefined\n ? translate('builder.roles.noText') // plain text, but no component fills the text role\n : clipboardErrorMessage(result.error)\n publishClipboardNotice({ kind: 'error', message })\n return\n }\n const root = currentRoot()\n if (!root) return\n const resolution = resolvePasteTarget({\n root,\n anchorId: anchorId !== undefined ? anchorId : b.selectedId,\n componentId: pasted.componentId,\n getManifest: (id) => studio.registry.getManifest(id),\n // EC-554 / EC-584: the store's placement scope — the field's vocabulary and\n // the manifests the one judge reads.\n placement: b.placement,\n })\n if (!resolution.ok) {\n // EC-545 / EC-554: the slot leash or the field leash refuses the pasted\n // root — the same verdict a drop gets, made visible. Nothing is dispatched.\n publishClipboardNotice({ kind: 'error', message: pasteRejectionMessage(resolution, b.field.label) })\n return\n }\n const { target, destinationLabel } = resolution\n // Mint the inserted subtree's ids HERE, from the store's own factory (EC-578), and\n // insert exactly that subtree (`minted`) — so the issue deep links point at the\n // very ids that land, with no mirror to diverge.\n const inserted = cloneWithNewIds(pasted, b.newId)\n // EC-584: the reducer judges the WHOLE pasted subtree; ask the same judge first so\n // the notice never claims a paste the sink refuses (a nested child outside its slot).\n const verdict = subtreePlacementRefusal(root, inserted, { parentId: target.parentId, slot: target.slot }, b.placement)\n if (verdict) {\n publishClipboardNotice({ kind: 'error', message: placementRefusalMessage(verdict, b.field.label) })\n return\n }\n // ONE dispatch: one history entry, one undo.\n b.dispatch({ type: 'insert', target, node: inserted, minted: true })\n const diff = diffPastedSubtree({\n node: inserted,\n hasComponent: (id) => studio.registry.getManifest(id) !== undefined,\n })\n publishClipboardNotice({\n kind: 'pasted',\n summary: pasteSummaryMessage(diff),\n destinationLabel,\n issues: diff.issues,\n })\n }\n\n return {\n copyNode(nodeId, dt, asyncClipboard) {\n const built = fragmentFor(nodeId)\n if (built) write(built.fragment, built.label, 'copied', dt, asyncClipboard)\n },\n cutNode(nodeId, dt, asyncClipboard) {\n const { builder: b } = stateRef.current\n const root = currentRoot()\n if (root && nodeId === root.id) {\n publishClipboardNotice({ kind: 'error', message: translate('builder.clipboard.rootCannotBeCut') })\n return\n }\n const built = fragmentFor(nodeId)\n if (!built) return\n write(built.fragment, built.label, 'cut', dt, asyncClipboard)\n // Exactly one structural dispatch: a single undo restores the cut node.\n b.dispatch({ type: 'remove', id: nodeId })\n },\n copyFragment(fragment, label) {\n write(fragment, label, 'copied')\n },\n pasteFromDataTransfer(dt, anchorId) {\n applyPaste(readFragmentFromDataTransfer(dt), anchorId)\n },\n async pasteFromClipboard(anchorId, asyncClipboard) {\n applyPaste(await readFragmentFromClipboard(asyncClipboard ?? navigator.clipboard), anchorId)\n },\n }\n }, [studio])\n}\n\n/**\n * The visible reason a refused paste shows (EC-545) — pure, so the notice\n * wording per refusal is pinned without mounting the hook. EC-554: a field-leash\n * refusal names the FIELD (`fieldLabel`), never the invisible synthetic root's\n * slot — the rejection carries no destination label to misuse.\n */\n/**\n * The node plain text pastes as (EC-549, ratified 4d): the component filling the `text`\n * role with the text in its role-contract prop (`value`, `ROLE_CONTRACTS`) — never a\n * loose typography item. `null` when no component fills the role (the gesture is off;\n * the canvas's role notice says so). Ids are minted on insert.\n */\nexport function plainTextNode(studio: StudioRegistry, text: string): ComponentNode | null {\n const textComponent = studio.registry.roleComponent('text')\n if (!textComponent) return null\n return { type: 'elytra/component', id: 'paste-text', componentId: textComponent.id, props: { value: { value: text } } }\n}\n\n/** A component's label in a notice — its manifest title (EC-591: never a stripped id prefix). */\nfunction componentLabel(componentId: string): string {\n return componentDisplayName(componentId, getStudioRegistry().registry.getManifest(componentId))\n}\n\nexport function pasteRejectionMessage(rejection: PasteRejection, fieldLabel: string): string {\n if (rejection.code === 'server-only') {\n return translate('builder.placement.serverOnly', { component: componentLabel(rejection.componentId) })\n }\n if (rejection.code === 'field-not-allowed') {\n return translate('builder.clipboard.notAllowedByField', {\n component: componentLabel(rejection.componentId),\n field: fieldLabel,\n })\n }\n return translate('builder.clipboard.notAllowed', {\n component: componentLabel(rejection.componentId),\n destination: rejection.destinationLabel,\n })\n}\n\n// ---------------------------------------------------------------------------\n// Listener wiring — one implementation for the page-tree window and the\n// canvas iframe document (events never cross the frame boundary).\n// ---------------------------------------------------------------------------\n\nexport interface ClipboardListenerOptions {\n clipboard: StudioClipboard\n /** Current selection: the copy/cut subject and the paste anchor. */\n getSelectedId: () => string | null\n /** Extra guard (e.g. the canvas's live inline-edit session). */\n isBlocked?: (target: EventTarget | null) => boolean\n /** The async Clipboard API of the listening document's own window. */\n asyncClipboard?: AsyncClipboard\n /** The listening document's selection — real text selections stay native. */\n getSelection?: () => Selection | null\n}\n\ninterface ListenerTarget {\n addEventListener(type: string, listener: (e: Event) => void): void\n removeEventListener(type: string, listener: (e: Event) => void): void\n}\n\n/**\n * Wire Cmd/Ctrl+C/X/V plus native copy/cut/paste ClipboardEvents.\n *\n * Shortcuts are handled on `keydown` (a selected node has no text selection,\n * so the browser would not fire native clipboard events for it); the\n * `preventDefault` there suppresses the native event, so the ClipboardEvent\n * listeners only ever see *other* triggers — context-menu copy/paste and\n * synthetic events — and the two paths can never double-handle one gesture.\n * Everything stays inert in form fields and inside the inline editors.\n */\nexport function attachClipboardListeners(target: ListenerTarget, opts: ClipboardListenerOptions): () => void {\n const blocked = (t: EventTarget | null): boolean =>\n clipboardEventBlocked(t) || (opts.isBlocked?.(t) ?? false)\n const hasTextSelection = (): boolean => {\n const selection = opts.getSelection?.()\n return selection !== null && selection !== undefined && !selection.isCollapsed\n }\n\n const onKeyDown = (e: Event) => {\n const event = e as KeyboardEvent\n if (!(event.metaKey || event.ctrlKey) || event.altKey || event.shiftKey) return\n const key = event.key.toLowerCase()\n if (key !== 'c' && key !== 'x' && key !== 'v') return\n if (blocked(event.target)) return\n if ((key === 'c' || key === 'x') && hasTextSelection()) return\n const selectedId = opts.getSelectedId()\n if (key === 'v') {\n event.preventDefault()\n void opts.clipboard.pasteFromClipboard(selectedId, opts.asyncClipboard)\n return\n }\n if (!selectedId) return\n event.preventDefault()\n if (key === 'c') opts.clipboard.copyNode(selectedId, null, opts.asyncClipboard)\n else opts.clipboard.cutNode(selectedId, null, opts.asyncClipboard)\n }\n\n const onCopyOrCut = (e: Event) => {\n const event = e as ClipboardEvent\n if (blocked(event.target) || hasTextSelection()) return\n const selectedId = opts.getSelectedId()\n if (!selectedId || !event.clipboardData) return\n event.preventDefault()\n if (event.type === 'copy') opts.clipboard.copyNode(selectedId, event.clipboardData)\n else opts.clipboard.cutNode(selectedId, event.clipboardData)\n }\n\n const onPaste = (e: Event) => {\n const event = e as ClipboardEvent\n if (blocked(event.target) || !event.clipboardData) return\n event.preventDefault()\n opts.clipboard.pasteFromDataTransfer(event.clipboardData, opts.getSelectedId())\n }\n\n target.addEventListener('keydown', onKeyDown)\n target.addEventListener('copy', onCopyOrCut)\n target.addEventListener('cut', onCopyOrCut)\n target.addEventListener('paste', onPaste)\n return () => {\n target.removeEventListener('keydown', onKeyDown)\n target.removeEventListener('copy', onCopyOrCut)\n target.removeEventListener('cut', onCopyOrCut)\n target.removeEventListener('paste', onPaste)\n }\n}\n\n// ---------------------------------------------------------------------------\n// Notice overlay — rendered by the canvas; issues deep-link to their nodes.\n// ---------------------------------------------------------------------------\n\nexport function ClipboardNoticeOverlay() {\n const notice = useClipboardNotice()\n const builder = useBuilder()\n if (!notice) return null\n\n const tone =\n notice.kind === 'error' ? 'error' : notice.kind === 'pasted' && notice.issues.length > 0 ? 'warning' : 'success'\n\n return (\n <div\n data-testid=\"clipboard-notice\"\n data-notice-kind={notice.kind}\n className=\"absolute bottom-4 left-1/2 z-20 w-max max-w-[min(420px,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 {tone === 'error' ? (\n <AlertTriangle className=\"mt-0.5 size-3.5 shrink-0 text-destructive\" aria-hidden />\n ) : tone === 'warning' ? (\n <AlertTriangle className=\"mt-0.5 size-3.5 shrink-0 text-warning\" aria-hidden />\n ) : (\n <CircleCheck className=\"mt-0.5 size-3.5 shrink-0 text-success\" aria-hidden />\n )}\n <Text size=\"xs\" className=\"min-w-0 flex-1\">\n {notice.kind === 'copied'\n ? translate('builder.clipboard.copied', { label: notice.label })\n : notice.kind === 'cut'\n ? translate('builder.clipboard.cutNotice', { label: notice.label })\n : notice.kind === 'pasted'\n ? translate('builder.clipboard.pastedInto', {\n summary: notice.summary,\n destination: notice.destinationLabel,\n })\n : notice.message}\n </Text>\n <button\n type=\"button\"\n aria-label={translate('common.dismiss')}\n onClick={() => publishClipboardNotice(null)}\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 {notice.kind === 'pasted' && notice.issues.length > 0 ? (\n <div className=\"mt-1.5 flex flex-col gap-1\">\n {notice.issues.map((issue, i) => (\n <button\n key={`${issue.nodeId}-${i}`}\n type=\"button\"\n data-testid=\"clipboard-notice-issue\"\n onClick={() => builder.dispatch({ type: 'select', id: issue.nodeId })}\n className=\"flex w-full items-center gap-2 rounded-md p-1 text-left transition-colors hover:bg-muted\"\n >\n <Badge variant=\"warning\" className=\"shrink-0\">\n {translate('builder.clipboard.issueComponent')}\n </Badge>\n <Text size=\"xs\" tone=\"muted\" className=\"min-w-0 flex-1 truncate\">\n {issue.message}\n </Text>\n <Crosshair className=\"size-3.5 shrink-0 text-muted-foreground\" aria-hidden />\n </button>\n ))}\n </div>\n ) : null}\n </div>\n </div>\n )\n}\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { Suspense, lazy } from 'react';
|
|
3
3
|
import { Text } from '@elytracms/studio/ui';
|
|
4
|
-
import { translate } from '
|
|
4
|
+
import { translate } from '../../lib/i18n/index.js';
|
|
5
5
|
/**
|
|
6
6
|
* Lazy boundary for the block-field page builder (EC-322 load-time pass).
|
|
7
7
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"composition-field-editor-lazy.js","sourceRoot":"","sources":["../../../src/components/builder/composition-field-editor-lazy.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,OAAO,CAAA;AAEtC,OAAO,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAA;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"composition-field-editor-lazy.js","sourceRoot":"","sources":["../../../src/components/builder/composition-field-editor-lazy.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,OAAO,CAAA;AAEtC,OAAO,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAA;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAE1C;;;;;;;;;;;;;;GAcG;AACH,MAAM,0BAA0B,GAAG,IAAI,CAAC,GAAG,EAAE,CAC3C,MAAM,CAAC,4BAA4B,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACrD,OAAO,EAAE,MAAM,CAAC,sBAAsB;CACvC,CAAC,CAAC,CACJ,CAAA;AAED,SAAS,cAAc;IACrB,OAAO,CACL,cAAK,SAAS,EAAC,4CAA4C,YACzD,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,OAAO,YACzB,SAAS,CAAC,wBAAwB,CAAC,GAC/B,GACH,CACP,CAAA;AACH,CAAC;AAED,MAAM,UAAU,sBAAsB,CACpC,KAAwD;IAExD,OAAO,CACL,KAAC,QAAQ,IAAC,QAAQ,EAAE,KAAC,cAAc,KAAG,YACpC,KAAC,0BAA0B,OAAK,KAAK,GAAI,GAChC,CACZ,CAAA;AACH,CAAC","sourcesContent":["import { Suspense, lazy } from 'react'\nimport type { ComponentProps } from 'react'\nimport { Text } from '@elytracms/studio/ui'\nimport { translate } from '../../lib/i18n'\n\n/**\n * Lazy boundary for the block-field page builder (EC-322 load-time pass).\n *\n * `CompositionFieldEditor` mounts the full page-builder canvas — the canvas,\n * page tree, block inserter, property inspector, clipboard, drag/reorder and the\n * builder store (the studio's single heaviest UI subtree). The detail editor\n * imports it statically but only renders it for documents that actually carry a\n * `blocks` field, so every ordinary document editor was paying the whole builder's\n * download cost for a surface it never shows.\n *\n * Splitting it behind {@link lazy} keeps the builder out of the CMS detail-view\n * critical path; it downloads on demand the first time a `blocks` field opens. The\n * detail editor's seam is unchanged — it still imports `CompositionFieldEditor`,\n * now the lazy-wrapped one, and renders it behind the same `canvasField` gate.\n */\nconst CompositionFieldEditorImpl = lazy(() =>\n import('./composition-field-editor').then((module) => ({\n default: module.CompositionFieldEditor,\n })),\n)\n\nfunction CanvasFallback() {\n return (\n <div className=\"grid min-h-0 flex-1 place-items-center p-8\">\n <Text size=\"sm\" tone=\"muted\">\n {translate('builder.canvas.loading')}\n </Text>\n </div>\n )\n}\n\nexport function CompositionFieldEditor(\n props: ComponentProps<typeof CompositionFieldEditorImpl>,\n) {\n return (\n <Suspense fallback={<CanvasFallback />}>\n <CompositionFieldEditorImpl {...props} />\n </Suspense>\n )\n}\n"]}
|
|
@@ -3,7 +3,7 @@ import type { ComponentNode, PreservedSequenceItem, SplitBlockRoots } from '@ely
|
|
|
3
3
|
import type { BlocksFieldDef } from '@elytracms/core/cms-core';
|
|
4
4
|
import type { ComponentRegistry } from '@elytracms/core/component-registry';
|
|
5
5
|
import type { BlockNode, BlockSequenceValue } from '@elytracms/core/rich-text';
|
|
6
|
-
import type { MessageKey } from '
|
|
6
|
+
import type { MessageKey } from '../../lib/i18n/index.js';
|
|
7
7
|
import type { CompositionFieldScope } from './builder-store.js';
|
|
8
8
|
import type { InspectorPane } from '../inspector/inspector-sidebar.js';
|
|
9
9
|
/**
|
|
@@ -40,8 +40,9 @@ export declare function splitCompositionValue(value: unknown): SplitCompositionV
|
|
|
40
40
|
export declare function mergeCompositionValue(roots: readonly ComponentNode[], preserved: readonly PreservedBlockNode[]): BlockSequenceValue;
|
|
41
41
|
/**
|
|
42
42
|
* The field scope the editor's store carries (EC-554): the field's label (named
|
|
43
|
-
* by refusal notices) and its top-level
|
|
44
|
-
*
|
|
43
|
+
* by refusal notices) and its EFFECTIVE top-level vocabulary (EC-584 — read through
|
|
44
|
+
* the per-FieldType definition, `fieldAllowList`: the declared `allow`, else the
|
|
45
|
+
* `blocks` default, any canvas-eligible component).
|
|
45
46
|
*/
|
|
46
47
|
export declare function compositionFieldScope(field: BlocksFieldDef): CompositionFieldScope;
|
|
47
48
|
/**
|
|
@@ -78,16 +79,3 @@ export declare function CompositionFieldEditor({ field, value, onChange, readOnl
|
|
|
78
79
|
* `Properties` pane (EC-224 — the frame-owned `Versions` pane). */
|
|
79
80
|
trailingPanes?: readonly InspectorPane[];
|
|
80
81
|
}): import("react").JSX.Element;
|
|
81
|
-
/**
|
|
82
|
-
* EC-549 interim (scope item 3, the honesty floor): a mixed sequence's typography
|
|
83
|
-
* items are preserved for write-back but NOT rendered on the canvas — and a canvas
|
|
84
|
-
* that silently shows half the sequence lets an editor rearrange around content
|
|
85
|
-
* they cannot see. Until typography items are first-class, path-addressed canvas
|
|
86
|
-
* items (EC-549's main scope), the surface must SAY what it is not showing: a
|
|
87
|
-
* persistent, non-dismissible count above the canvas. Deliberately NOT the
|
|
88
|
-
* dismissible overlay shape (`CanvasThemeNotice`) — this states a standing truth
|
|
89
|
-
* about the VALUE, not a one-time migration hint, and it must not cover blocks.
|
|
90
|
-
*/
|
|
91
|
-
export declare function PreservedItemsNotice({ count }: {
|
|
92
|
-
count: number;
|
|
93
|
-
}): import("react").JSX.Element | null;
|