@elytracms/studio 0.0.93 → 0.0.95
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 +2 -2
- package/convex/asset-meta-patch.test.ts +218 -0
- package/convex/asset-replace-delete-race.test.ts +127 -0
- package/convex/assets.ts +27 -2
- package/convex/auth.ts +5 -2
- package/convex/authority.ts +35 -11
- package/convex/contract-parity.test.ts +7 -0
- package/convex/delivery-redirects.test.ts +167 -0
- package/convex/delivery.ts +23 -10
- 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/rail-write-parity.test.ts +6 -6
- 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 +2 -2
- 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 +6 -6
- package/dist/components/ai/translate-document-dialog.js.map +1 -1
- package/dist/components/ai/translate-skipped.d.ts +1 -1
- package/dist/components/ai/translate-skipped.js +2 -2
- package/dist/components/ai/translate-skipped.js.map +1 -1
- 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 +28 -13
- 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-locale.d.ts +9 -0
- package/dist/components/assets/asset-locale.js +16 -0
- package/dist/components/assets/asset-locale.js.map +1 -0
- package/dist/components/assets/asset-meta-label.d.ts +12 -0
- package/dist/components/assets/asset-meta-label.js +15 -0
- package/dist/components/assets/asset-meta-label.js.map +1 -0
- package/dist/components/assets/asset-metadata.d.ts +69 -15
- package/dist/components/assets/asset-metadata.js +99 -11
- package/dist/components/assets/asset-metadata.js.map +1 -1
- package/dist/components/assets/asset-search.js +8 -2
- package/dist/components/assets/asset-search.js.map +1 -1
- package/dist/components/assets/asset-thumb.js +9 -5
- package/dist/components/assets/asset-thumb.js.map +1 -1
- package/dist/components/assets/assets-collection-view-source.d.ts +25 -11
- package/dist/components/assets/assets-collection-view-source.js +78 -26
- 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 +2 -2
- package/dist/components/assets/assets-store.js +22 -9
- 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.js +1 -1
- package/dist/components/assets/replace-file.js.map +1 -1
- 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 +1 -1
- package/dist/components/builder/blocks-model.js +3 -3
- package/dist/components/builder/blocks-model.js.map +1 -1
- package/dist/components/builder/blocks-panel.d.ts +1 -1
- package/dist/components/builder/blocks-panel.js +4 -4
- package/dist/components/builder/blocks-panel.js.map +1 -1
- package/dist/components/builder/builder-store.d.ts +21 -0
- package/dist/components/builder/builder-store.js +24 -2
- package/dist/components/builder/builder-store.js.map +1 -1
- package/dist/components/builder/canvas-breakpoints.js +1 -1
- package/dist/components/builder/canvas-breakpoints.js.map +1 -1
- package/dist/components/builder/canvas-commit-boundary.d.ts +22 -0
- package/dist/components/builder/canvas-commit-boundary.js +27 -0
- package/dist/components/builder/canvas-commit-boundary.js.map +1 -0
- package/dist/components/builder/canvas-dnd.d.ts +2 -2
- package/dist/components/builder/canvas-dnd.js +1 -1
- 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-rebase.d.ts +77 -0
- package/dist/components/builder/canvas-rebase.js +169 -0
- package/dist/components/builder/canvas-rebase.js.map +1 -0
- package/dist/components/builder/canvas-render-context.d.ts +7 -4
- package/dist/components/builder/canvas-render-context.js +21 -13
- package/dist/components/builder/canvas-render-context.js.map +1 -1
- 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 +3 -3
- package/dist/components/builder/canvas.js.map +1 -1
- package/dist/components/builder/clipboard-actions.d.ts +2 -2
- package/dist/components/builder/clipboard-actions.js +4 -4
- 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 +21 -3
- package/dist/components/builder/composition-field-editor.js +48 -30
- package/dist/components/builder/composition-field-editor.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 +1 -1
- package/dist/components/builder/insert-context.js +3 -3
- 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-factory.js +15 -3
- package/dist/components/builder/node-factory.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 +1 -1
- package/dist/components/builder/page-tree.js +5 -5
- package/dist/components/builder/page-tree.js.map +1 -1
- package/dist/components/builder/placement-refusal.js +3 -3
- package/dist/components/builder/placement-refusal.js.map +1 -1
- package/dist/components/builder/property-inspector.js +7 -5
- package/dist/components/builder/property-inspector.js.map +1 -1
- package/dist/components/builder/reuse-context.js +4 -4
- package/dist/components/builder/reuse-context.js.map +1 -1
- package/dist/components/builder/reuse.js +1 -1
- 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.js +11 -7
- package/dist/components/builder/run-inspector.js.map +1 -1
- 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 +5 -2
- package/dist/components/cms/asset-field.js +36 -19
- package/dist/components/cms/asset-field.js.map +1 -1
- package/dist/components/cms/asset-picker.js +5 -3
- package/dist/components/cms/asset-picker.js.map +1 -1
- package/dist/components/cms/bulk-actions.d.ts +1 -1
- package/dist/components/cms/bulk-actions.js +2 -2
- 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/canvas-commit.d.ts +56 -0
- package/dist/components/cms/canvas-commit.js +35 -0
- package/dist/components/cms/canvas-commit.js.map +1 -0
- 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 +2 -2
- package/dist/components/cms/cms-store.js +10 -10
- package/dist/components/cms/cms-store.js.map +1 -1
- package/dist/components/cms/collection-table.js +30 -14
- package/dist/components/cms/collection-table.js.map +1 -1
- package/dist/components/cms/commit-policy.d.ts +6 -2
- package/dist/components/cms/commit-policy.js +1 -1
- package/dist/components/cms/commit-policy.js.map +1 -1
- package/dist/components/cms/detail-editor.d.ts +5 -2
- package/dist/components/cms/detail-editor.js +31 -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.js +5 -5
- 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-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.js +1 -1
- 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.js +1 -1
- 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 +2 -2
- package/dist/components/cms/object-array-field.js.map +1 -1
- package/dist/components/cms/prop-fields.js +2 -2
- 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.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/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 +69 -5
- package/dist/components/cms/use-document-draft.js +118 -34
- 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 +7 -2
- package/dist/components/cms/use-live-url.js +15 -12
- package/dist/components/cms/use-live-url.js.map +1 -1
- package/dist/components/cms/use-publish-controller.d.ts +1 -1
- package/dist/components/cms/use-publish-controller.js +1 -1
- 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 +3 -3
- 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 +11 -7
- 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 +3 -3
- package/dist/components/components/transfer-dialog.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 +13 -4
- 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 +151 -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/index.d.ts +1 -1
- package/dist/lib/ai/index.js +1 -1
- package/dist/lib/ai/index.js.map +1 -1
- package/dist/lib/ai/node-handler.d.ts +1 -1
- package/dist/lib/ai/node-handler.js.map +1 -1
- package/dist/lib/ai/translate-document.js +1 -1
- package/dist/lib/ai/translate-document.js.map +1 -1
- package/dist/lib/assets/upload.d.ts +25 -7
- package/dist/lib/assets/upload.js +30 -21
- package/dist/lib/assets/upload.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.js +2 -2
- package/dist/lib/clipboard/paste-target.js.map +1 -1
- package/dist/lib/cms/published-documents.d.ts +24 -6
- package/dist/lib/cms/published-documents.js +39 -6
- package/dist/lib/cms/published-documents.js.map +1 -1
- package/dist/lib/cms-views/view-model.d.ts +12 -99
- package/dist/lib/cms-views/view-model.js +22 -103
- package/dist/lib/cms-views/view-model.js.map +1 -1
- package/dist/lib/collection-view/collection-view-source.d.ts +13 -3
- package/dist/lib/collection-view/collection-view-source.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/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 +11 -3
- package/dist/lib/i18n/messages.de.js.map +1 -1
- package/dist/lib/i18n/messages.en.d.ts +9 -1
- package/dist/lib/i18n/messages.en.js +16 -4
- package/dist/lib/i18n/messages.en.js.map +1 -1
- package/dist/lib/operations/config-manifest-lookup.d.ts +1 -1
- package/dist/lib/operations/config-manifest-lookup.js +3 -3
- package/dist/lib/operations/config-manifest-lookup.js.map +1 -1
- package/dist/lib/operations/project-runtime-config.d.ts +9 -0
- package/dist/lib/operations/project-runtime-config.js +13 -0
- package/dist/lib/operations/project-runtime-config.js.map +1 -1
- package/dist/lib/operations/studio-operations.d.ts +9 -1
- package/dist/lib/operations/studio-operations.js +6 -1
- 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 +78 -47
- package/dist/lib/persistence/convex-adapter.js.map +1 -1
- package/dist/lib/persistence/deployment-readiness.js +14 -11
- package/dist/lib/persistence/deployment-readiness.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 +2 -1
- package/dist/lib/persona/labels.js.map +1 -1
- package/dist/lib/query/index.d.ts +2 -2
- package/dist/lib/query/index.js +1 -1
- package/dist/lib/query/index.js.map +1 -1
- package/dist/lib/query/operations-query.d.ts +27 -1
- package/dist/lib/query/operations-query.js +13 -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/registry/index.js +2 -2
- package/dist/lib/registry/index.js.map +1 -1
- package/dist/lib/registry/preview.d.ts +10 -0
- package/dist/lib/registry/preview.js +13 -4
- package/dist/lib/registry/preview.js.map +1 -1
- package/dist/lib/routes/route-issues.js +6 -3
- package/dist/lib/routes/route-issues.js.map +1 -1
- package/dist/lib/workspace/index.d.ts +6 -0
- package/dist/lib/workspace/index.js +8 -0
- package/dist/lib/workspace/index.js.map +1 -1
- package/dist/lib/workspace/workspace-source.js +6 -7
- package/dist/lib/workspace/workspace-source.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/previews/entry-node.d.ts +14 -0
- package/dist/previews/entry-node.js +22 -0
- package/dist/previews/entry-node.js.map +1 -0
- package/dist/previews/harness.js +2 -16
- package/dist/previews/harness.js.map +1 -1
- 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/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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bulk-bar.js","sourceRoot":"","sources":["../../../src/components/cms/bulk-bar.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useState } from 'react'\nimport { Check, CheckCircle2, CircleSlash, PencilLine, Trash2, X } from 'lucide-react'\nimport type { CollectionDef } from '@elytracms/core/cms-core'\nimport type { FilterScalar } from '@elytracms/core/content'\nimport { Badge, Button, Code, Row, Select, Spacer, Stack, Text } from '@elytracms/studio/ui'\nimport { translate } from '@elytracms/studio/lib/i18n'\nimport { filterableFieldOptions } from '@elytracms/studio/lib/cms-views'\nimport type { FilterableFieldOption } from '@elytracms/studio/lib/cms-views'\nimport type { CollectionViewCapabilities } from '@elytracms/studio/lib/collection-view/collection-view-source'\nimport { bulkSummary } from './bulk-actions'\nimport type { BulkAction, BulkReport, BulkSetField } from './bulk-actions'\nimport { cellEditKind } from './cell-edit'\nimport { ScalarFieldValueEditor } from './scalar-value-editor'\n\n/**\n * The scalar filterable fields a bulk set-field may target (EC-247): the\n * schema's `filterable` fields whose type edits inline (text/number/boolean/\n * select/date — `cellEditKind` 'inline'). Relation/asset (picker) and the\n * publication-state pseudo field are excluded — publish/unpublish own state,\n * and a raw relation id is not a safe bulk control.\n */\nexport function setFieldOptions(collection: CollectionDef): FilterableFieldOption[] {\n return filterableFieldOptions(collection).filter(\n (option) => option.def !== null && cellEditKind(option.def, false) === 'inline',\n )\n}\n\n/** A sensible starting value for a freshly chosen set-field target. */\nexport function initialSetValue(option: FilterableFieldOption | undefined): FilterScalar {\n return option?.def?.type === 'boolean' ? false : ''\n}\n\n/**\n * The bulk-action bar of the collection table (EC-169): appears with a row\n * selection, offers publish/unpublish/delete and a set-field assignment\n * (EC-247) over it. Unpublish and delete are confirmed by the owner through the\n * reference-aware EC-173 dialog (`ReferenceConfirmDialog`), which enumerates\n * every inbound reference that will break before anything dispatches — this bar\n * only raises the action. Set-field is a draft edit, so it applies directly.\n *\n * Every action is CAPABILITY-gated (EC-433), mirroring the row `⋮` menu's\n * `rowActionsFor` (EC-316): a rail without publish never shows\n * publish/unpublish, one without remove never shows Delete, and set-field\n * follows `inlineEdit` (it's a bulk cell write). Before this gate the bar\n * offered every action to every rail — the assets rail's bulk Delete then\n * confirmed into a source without a bulk runner and silently no-oped.\n */\nexport function BulkBar({\n collection,\n capabilities,\n count,\n busy,\n onAction,\n onSetField,\n onClear,\n}: {\n collection: CollectionDef\n /** The rail's capability matrix (AD-19) — the SAME gate the row menu uses. */\n capabilities: Pick<CollectionViewCapabilities, 'publish' | 'remove' | 'inlineEdit'>\n /** Selected rows in the current view. */\n count: number\n busy: boolean\n onAction: (action: BulkAction) => void\n onSetField: (set: BulkSetField) => void\n onClear: () => void\n}) {\n const [setMode, setSetMode] = useState(false)\n if (count === 0) return null\n\n const options = setFieldOptions(collection)\n const settable = options.length > 0\n\n return (\n <Stack gap=\"none\" className=\"shrink-0\" data-testid=\"bulk-bar\">\n <Row gap=\"2\" className=\"border-b border-border bg-muted/40 px-4 py-2 sm:px-6\">\n <Text size=\"sm\" weight=\"medium\" data-testid=\"bulk-count\">\n {translate('cms.list.bulk.count', { count })}\n </Text>\n {capabilities.publish ? (\n <>\n <Button\n variant=\"outline\"\n size=\"sm\"\n disabled={busy}\n data-testid=\"bulk-publish\"\n onClick={() => onAction('publish')}\n >\n <CheckCircle2 /> {translate('cms.list.bulk.publish')}\n </Button>\n <Button\n variant=\"outline\"\n size=\"sm\"\n disabled={busy}\n data-testid=\"bulk-unpublish\"\n onClick={() => onAction('unpublish')}\n >\n <CircleSlash /> {translate('cms.list.bulk.unpublish')}\n </Button>\n </>\n ) : null}\n {capabilities.inlineEdit ? (\n <Button\n variant=\"outline\"\n size=\"sm\"\n disabled={busy || !settable}\n aria-pressed={setMode}\n title={settable ? undefined : translate('cms.list.bulk.noSettableFields')}\n data-testid=\"bulk-set-field\"\n onClick={() => setSetMode((prev) => !prev)}\n >\n <PencilLine /> {translate('cms.list.bulk.setField')}\n </Button>\n ) : null}\n {capabilities.remove ? (\n <Button\n variant=\"destructive\"\n size=\"sm\"\n disabled={busy}\n data-testid=\"bulk-delete\"\n onClick={() => onAction('delete')}\n >\n <Trash2 /> {translate('common.delete')}\n </Button>\n ) : null}\n <Spacer />\n <Button variant=\"ghost\" size=\"sm\" disabled={busy} data-testid=\"bulk-clear\" onClick={onClear}>\n <X /> {translate('cms.list.bulk.clearSelection')}\n </Button>\n </Row>\n {setMode && settable && capabilities.inlineEdit ? (\n <SetFieldPanel\n options={options}\n busy={busy}\n onApply={(set) => {\n onSetField(set)\n setSetMode(false)\n }}\n onCancel={() => setSetMode(false)}\n />\n ) : null}\n </Stack>\n )\n}\n\n/** The inline \"set <field> to <value>\" composer over the current selection. */\nfunction SetFieldPanel({\n options,\n busy,\n onApply,\n onCancel,\n}: {\n options: FilterableFieldOption[]\n busy: boolean\n onApply: (set: BulkSetField) => void\n onCancel: () => void\n}) {\n const [field, setField] = useState(options[0]!.field)\n const option = options.find((candidate) => candidate.field === field) ?? options[0]!\n const [value, setValue] = useState<FilterScalar>(() => initialSetValue(option))\n\n function changeField(next: string) {\n setField(next)\n setValue(initialSetValue(options.find((candidate) => candidate.field === next)))\n }\n\n return (\n <Row\n gap=\"2\"\n align=\"center\"\n className=\"border-b border-border bg-muted/20 px-4 py-2 sm:px-6\"\n data-testid=\"bulk-set-panel\"\n >\n <Text size=\"sm\" tone=\"muted\">\n {translate('cms.list.bulk.set.set')}\n </Text>\n <Select\n aria-label={translate('cms.list.bulk.setField')}\n value={field}\n onChange={(event) => changeField(event.target.value)}\n className=\"h-7 w-auto py-0 text-xs\"\n data-testid=\"bulk-set-field-select\"\n >\n {options.map((candidate) => (\n <option key={candidate.field} value={candidate.field}>\n {candidate.label}\n </option>\n ))}\n </Select>\n <Text size=\"sm\" tone=\"muted\">\n {translate('cms.list.bulk.set.to')}\n </Text>\n <ScalarFieldValueEditor\n key={field}\n option={option}\n value={value}\n ariaLabel={translate('cms.list.bulk.set.valueAria')}\n testId=\"bulk-set-value\"\n onChange={setValue}\n />\n <Spacer />\n <Button\n variant=\"outline\"\n size=\"sm\"\n disabled={busy}\n data-testid=\"bulk-set-apply\"\n onClick={() => onApply({ field, value })}\n >\n <Check /> {translate('cms.list.bulk.set.apply')}\n </Button>\n <Button variant=\"ghost\" size=\"sm\" disabled={busy} data-testid=\"bulk-set-cancel\" onClick={onCancel}>\n {translate('common.cancel')}\n </Button>\n </Row>\n )\n}\n\n/**\n * Per-item bulk results (EC-169): the completion summary plus every failure,\n * named and with its reason — a partial failure is enumerated, never silent.\n */\nexport function BulkReportPanel({\n report,\n onDismiss,\n}: {\n report: BulkReport\n onDismiss: () => void\n}) {\n const { succeeded, failed } = bulkSummary(report)\n const failures = report.results.filter((result) => !result.ok)\n const actionLabel =\n report.action === 'set-field' && report.setField\n ? translate('cms.list.bulk.report.verb.setField', { field: report.setField.field })\n : translate(`cms.list.bulk.report.verb.${report.action}`)\n\n return (\n <Stack gap=\"1\" className=\"shrink-0 border-b border-border px-4 py-2 sm:px-6\" data-testid=\"bulk-report\">\n <Row gap=\"2\">\n <Text size=\"sm\" weight=\"medium\" data-testid=\"bulk-report-summary\">\n {translate('cms.list.bulk.report.summary', { action: actionLabel, succeeded })}\n {failed > 0 ? translate('cms.list.bulk.report.failedSuffix', { count: failed }) : ''}\n </Text>\n {failed > 0 ? (\n <Badge variant=\"error\">{translate('cms.list.bulk.report.failedBadge', { count: failed })}</Badge>\n ) : (\n <Badge variant=\"success\">{translate('cms.list.bulk.report.done')}</Badge>\n )}\n <Spacer />\n <Button variant=\"ghost\" size=\"sm\" data-testid=\"bulk-report-dismiss\" onClick={onDismiss}>\n {translate('common.dismiss')}\n </Button>\n </Row>\n {failures.map((failure) => (\n <Row gap=\"2\" key={failure.ref.id} data-testid={`bulk-failure-${failure.ref.id}`}>\n <Text size=\"xs\" className=\"text-destructive\">\n {failure.title}\n </Text>\n <Code tone=\"muted\">{failure.ref.id}</Code>\n <Text size=\"xs\" tone=\"muted\">\n {failure.reason}\n </Text>\n </Row>\n ))}\n </Stack>\n )\n}\n"]}
|
|
1
|
+
{"version":3,"file":"bulk-bar.js","sourceRoot":"","sources":["../../../src/components/cms/bulk-bar.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useState } from 'react'\nimport { Check, CheckCircle2, CircleSlash, PencilLine, Trash2, X } from 'lucide-react'\nimport type { CollectionDef } from '@elytracms/core/cms-core'\nimport type { FilterScalar } from '@elytracms/core/content'\nimport { Badge, Button, Code, Row, Select, Spacer, Stack, Text } from '@elytracms/studio/ui'\nimport { translate } from '../../lib/i18n'\nimport { filterableFieldOptions } from '../../lib/cms-views'\nimport type { FilterableFieldOption } from '../../lib/cms-views'\nimport type { CollectionViewCapabilities } from '../../lib/collection-view/collection-view-source'\nimport { bulkSummary } from './bulk-actions'\nimport type { BulkAction, BulkReport, BulkSetField } from './bulk-actions'\nimport { cellEditKind } from './cell-edit'\nimport { ScalarFieldValueEditor } from './scalar-value-editor'\n\n/**\n * The scalar filterable fields a bulk set-field may target (EC-247): the\n * schema's `filterable` fields whose type edits inline (text/number/boolean/\n * select/date — `cellEditKind` 'inline'). Relation/asset (picker) and the\n * publication-state pseudo field are excluded — publish/unpublish own state,\n * and a raw relation id is not a safe bulk control.\n */\nexport function setFieldOptions(collection: CollectionDef): FilterableFieldOption[] {\n return filterableFieldOptions(collection).filter(\n (option) => option.def !== null && cellEditKind(option.def, false) === 'inline',\n )\n}\n\n/** A sensible starting value for a freshly chosen set-field target. */\nexport function initialSetValue(option: FilterableFieldOption | undefined): FilterScalar {\n return option?.def?.type === 'boolean' ? false : ''\n}\n\n/**\n * The bulk-action bar of the collection table (EC-169): appears with a row\n * selection, offers publish/unpublish/delete and a set-field assignment\n * (EC-247) over it. Unpublish and delete are confirmed by the owner through the\n * reference-aware EC-173 dialog (`ReferenceConfirmDialog`), which enumerates\n * every inbound reference that will break before anything dispatches — this bar\n * only raises the action. Set-field is a draft edit, so it applies directly.\n *\n * Every action is CAPABILITY-gated (EC-433), mirroring the row `⋮` menu's\n * `rowActionsFor` (EC-316): a rail without publish never shows\n * publish/unpublish, one without remove never shows Delete, and set-field\n * follows `inlineEdit` (it's a bulk cell write). Before this gate the bar\n * offered every action to every rail — the assets rail's bulk Delete then\n * confirmed into a source without a bulk runner and silently no-oped.\n */\nexport function BulkBar({\n collection,\n capabilities,\n count,\n busy,\n onAction,\n onSetField,\n onClear,\n}: {\n collection: CollectionDef\n /** The rail's capability matrix (AD-19) — the SAME gate the row menu uses. */\n capabilities: Pick<CollectionViewCapabilities, 'publish' | 'remove' | 'inlineEdit'>\n /** Selected rows in the current view. */\n count: number\n busy: boolean\n onAction: (action: BulkAction) => void\n onSetField: (set: BulkSetField) => void\n onClear: () => void\n}) {\n const [setMode, setSetMode] = useState(false)\n if (count === 0) return null\n\n const options = setFieldOptions(collection)\n const settable = options.length > 0\n\n return (\n <Stack gap=\"none\" className=\"shrink-0\" data-testid=\"bulk-bar\">\n <Row gap=\"2\" className=\"border-b border-border bg-muted/40 px-4 py-2 sm:px-6\">\n <Text size=\"sm\" weight=\"medium\" data-testid=\"bulk-count\">\n {translate('cms.list.bulk.count', { count })}\n </Text>\n {capabilities.publish ? (\n <>\n <Button\n variant=\"outline\"\n size=\"sm\"\n disabled={busy}\n data-testid=\"bulk-publish\"\n onClick={() => onAction('publish')}\n >\n <CheckCircle2 /> {translate('cms.list.bulk.publish')}\n </Button>\n <Button\n variant=\"outline\"\n size=\"sm\"\n disabled={busy}\n data-testid=\"bulk-unpublish\"\n onClick={() => onAction('unpublish')}\n >\n <CircleSlash /> {translate('cms.list.bulk.unpublish')}\n </Button>\n </>\n ) : null}\n {capabilities.inlineEdit ? (\n <Button\n variant=\"outline\"\n size=\"sm\"\n disabled={busy || !settable}\n aria-pressed={setMode}\n title={settable ? undefined : translate('cms.list.bulk.noSettableFields')}\n data-testid=\"bulk-set-field\"\n onClick={() => setSetMode((prev) => !prev)}\n >\n <PencilLine /> {translate('cms.list.bulk.setField')}\n </Button>\n ) : null}\n {capabilities.remove ? (\n <Button\n variant=\"destructive\"\n size=\"sm\"\n disabled={busy}\n data-testid=\"bulk-delete\"\n onClick={() => onAction('delete')}\n >\n <Trash2 /> {translate('common.delete')}\n </Button>\n ) : null}\n <Spacer />\n <Button variant=\"ghost\" size=\"sm\" disabled={busy} data-testid=\"bulk-clear\" onClick={onClear}>\n <X /> {translate('cms.list.bulk.clearSelection')}\n </Button>\n </Row>\n {setMode && settable && capabilities.inlineEdit ? (\n <SetFieldPanel\n options={options}\n busy={busy}\n onApply={(set) => {\n onSetField(set)\n setSetMode(false)\n }}\n onCancel={() => setSetMode(false)}\n />\n ) : null}\n </Stack>\n )\n}\n\n/** The inline \"set <field> to <value>\" composer over the current selection. */\nfunction SetFieldPanel({\n options,\n busy,\n onApply,\n onCancel,\n}: {\n options: FilterableFieldOption[]\n busy: boolean\n onApply: (set: BulkSetField) => void\n onCancel: () => void\n}) {\n const [field, setField] = useState(options[0]!.field)\n const option = options.find((candidate) => candidate.field === field) ?? options[0]!\n const [value, setValue] = useState<FilterScalar>(() => initialSetValue(option))\n\n function changeField(next: string) {\n setField(next)\n setValue(initialSetValue(options.find((candidate) => candidate.field === next)))\n }\n\n return (\n <Row\n gap=\"2\"\n align=\"center\"\n className=\"border-b border-border bg-muted/20 px-4 py-2 sm:px-6\"\n data-testid=\"bulk-set-panel\"\n >\n <Text size=\"sm\" tone=\"muted\">\n {translate('cms.list.bulk.set.set')}\n </Text>\n <Select\n aria-label={translate('cms.list.bulk.setField')}\n value={field}\n onChange={(event) => changeField(event.target.value)}\n className=\"h-7 w-auto py-0 text-xs\"\n data-testid=\"bulk-set-field-select\"\n >\n {options.map((candidate) => (\n <option key={candidate.field} value={candidate.field}>\n {candidate.label}\n </option>\n ))}\n </Select>\n <Text size=\"sm\" tone=\"muted\">\n {translate('cms.list.bulk.set.to')}\n </Text>\n <ScalarFieldValueEditor\n key={field}\n option={option}\n value={value}\n ariaLabel={translate('cms.list.bulk.set.valueAria')}\n testId=\"bulk-set-value\"\n onChange={setValue}\n />\n <Spacer />\n <Button\n variant=\"outline\"\n size=\"sm\"\n disabled={busy}\n data-testid=\"bulk-set-apply\"\n onClick={() => onApply({ field, value })}\n >\n <Check /> {translate('cms.list.bulk.set.apply')}\n </Button>\n <Button variant=\"ghost\" size=\"sm\" disabled={busy} data-testid=\"bulk-set-cancel\" onClick={onCancel}>\n {translate('common.cancel')}\n </Button>\n </Row>\n )\n}\n\n/**\n * Per-item bulk results (EC-169): the completion summary plus every failure,\n * named and with its reason — a partial failure is enumerated, never silent.\n */\nexport function BulkReportPanel({\n report,\n onDismiss,\n}: {\n report: BulkReport\n onDismiss: () => void\n}) {\n const { succeeded, failed } = bulkSummary(report)\n const failures = report.results.filter((result) => !result.ok)\n const actionLabel =\n report.action === 'set-field' && report.setField\n ? translate('cms.list.bulk.report.verb.setField', { field: report.setField.field })\n : translate(`cms.list.bulk.report.verb.${report.action}`)\n\n return (\n <Stack gap=\"1\" className=\"shrink-0 border-b border-border px-4 py-2 sm:px-6\" data-testid=\"bulk-report\">\n <Row gap=\"2\">\n <Text size=\"sm\" weight=\"medium\" data-testid=\"bulk-report-summary\">\n {translate('cms.list.bulk.report.summary', { action: actionLabel, succeeded })}\n {failed > 0 ? translate('cms.list.bulk.report.failedSuffix', { count: failed }) : ''}\n </Text>\n {failed > 0 ? (\n <Badge variant=\"error\">{translate('cms.list.bulk.report.failedBadge', { count: failed })}</Badge>\n ) : (\n <Badge variant=\"success\">{translate('cms.list.bulk.report.done')}</Badge>\n )}\n <Spacer />\n <Button variant=\"ghost\" size=\"sm\" data-testid=\"bulk-report-dismiss\" onClick={onDismiss}>\n {translate('common.dismiss')}\n </Button>\n </Row>\n {failures.map((failure) => (\n <Row gap=\"2\" key={failure.ref.id} data-testid={`bulk-failure-${failure.ref.id}`}>\n <Text size=\"xs\" className=\"text-destructive\">\n {failure.title}\n </Text>\n <Code tone=\"muted\">{failure.ref.id}</Code>\n <Text size=\"xs\" tone=\"muted\">\n {failure.reason}\n </Text>\n </Row>\n ))}\n </Stack>\n )\n}\n"]}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { DocumentPatch, ValuePath } from '@elytracms/core/cms-core';
|
|
2
|
+
import type { CommitPolicy } from './commit-policy.js';
|
|
3
|
+
import type { DraftCommitter } from './draft-commit.js';
|
|
4
|
+
/**
|
|
5
|
+
* EC-630 — the canvas inspector commits like every other editor: on its control kind's
|
|
6
|
+
* quiescence policy (`commit-policy.ts`, EC-595 A2), never per keystroke (pure; the house
|
|
7
|
+
* pattern).
|
|
8
|
+
*
|
|
9
|
+
* The canvas lifts every store change into the draft at once (live validation reads it).
|
|
10
|
+
* A burst of typing in an inspector control used to be committed per keystroke too —
|
|
11
|
+
* every canvas write went straight to the persister under the canvas's `change` policy:
|
|
12
|
+
* 13 revisions for one typography-run edit, measured live (wave-0094), and one per
|
|
13
|
+
* keystroke on any text prop. The canvas's own actions (insert, move, delete, undo, a
|
|
14
|
+
* discrete control) ARE discrete commits; a control's typing is not. So the inspector
|
|
15
|
+
* controls stand inside a commit boundary (focus / blur / Enter, the form's
|
|
16
|
+
* `FieldCommitBoundary`) at a POSITION — a node's prop, or a typography run, which is
|
|
17
|
+
* the field itself (prose is one position, EC-595 binding point 3) — with the policy
|
|
18
|
+
* THE declaration gives that control kind (`commitPolicyOf`), and THE committer
|
|
19
|
+
* (`draft-commit.ts`, shared with the form fields) holds the burst as that position's
|
|
20
|
+
* uncommitted edit: committed on blur, Enter or the idle pause, with the base revision
|
|
21
|
+
* of its first keystroke, overlaid on every rebase meanwhile, and in every forced flush.
|
|
22
|
+
*/
|
|
23
|
+
/** Where a canvas inspector control writes: a node's prop, or a typography run (the field). */
|
|
24
|
+
export type CanvasControlPosition = {
|
|
25
|
+
readonly node: string;
|
|
26
|
+
readonly prop: string;
|
|
27
|
+
} | {
|
|
28
|
+
readonly run: number;
|
|
29
|
+
};
|
|
30
|
+
/** A focused canvas inspector control, as the committer knows it. */
|
|
31
|
+
export interface CanvasControl {
|
|
32
|
+
/** The committer's key for it (never a field name — those carry no `:`). */
|
|
33
|
+
readonly key: string;
|
|
34
|
+
/** The position it writes. */
|
|
35
|
+
readonly path: ValuePath;
|
|
36
|
+
readonly policy: CommitPolicy;
|
|
37
|
+
}
|
|
38
|
+
/** The control at `position` of the canvas field at `field` (the field's position path, locale included). */
|
|
39
|
+
export declare function canvasControl(field: ValuePath, position: CanvasControlPosition, policy: CommitPolicy): CanvasControl;
|
|
40
|
+
/** Whether a committer key names a canvas control. */
|
|
41
|
+
export declare function isCanvasControlKey(key: string): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Route one canvas write's patches. When they are exactly the focused control's value
|
|
44
|
+
* write at its position and its policy waits for quiescence, the write is that control's
|
|
45
|
+
* uncommitted position (`deferred` — the committer commits the latest value later).
|
|
46
|
+
* Anything else — a canvas action, a discrete control, an edit with no control focused —
|
|
47
|
+
* commits now (`committed`), after any canvas position still uncommitted, so positions
|
|
48
|
+
* reach the persister in the order the editor made them.
|
|
49
|
+
*/
|
|
50
|
+
export declare function routeCanvasWrite(input: {
|
|
51
|
+
patches: readonly DocumentPatch[];
|
|
52
|
+
/** The focused canvas control, if focus is on one. */
|
|
53
|
+
focused: CanvasControl | undefined;
|
|
54
|
+
committer: DraftCommitter;
|
|
55
|
+
commitNow: (patches: readonly DocumentPatch[]) => void;
|
|
56
|
+
}): 'deferred' | 'committed';
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { formatValuePath } from '@elytracms/core/cms-core';
|
|
2
|
+
/** The control at `position` of the canvas field at `field` (the field's position path, locale included). */
|
|
3
|
+
export function canvasControl(field, position, policy) {
|
|
4
|
+
const path = 'node' in position
|
|
5
|
+
? [...field, { kind: 'node', id: position.node }, { kind: 'prop', name: position.prop }]
|
|
6
|
+
: field;
|
|
7
|
+
return { key: `canvas:${formatValuePath(path)}`, path, policy };
|
|
8
|
+
}
|
|
9
|
+
/** Whether a committer key names a canvas control. */
|
|
10
|
+
export function isCanvasControlKey(key) {
|
|
11
|
+
return key.startsWith('canvas:');
|
|
12
|
+
}
|
|
13
|
+
function writesExactly(patch, path) {
|
|
14
|
+
return (patch.op === 'set' || patch.op === 'unset') && formatValuePath(patch.path) === formatValuePath(path);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Route one canvas write's patches. When they are exactly the focused control's value
|
|
18
|
+
* write at its position and its policy waits for quiescence, the write is that control's
|
|
19
|
+
* uncommitted position (`deferred` — the committer commits the latest value later).
|
|
20
|
+
* Anything else — a canvas action, a discrete control, an edit with no control focused —
|
|
21
|
+
* commits now (`committed`), after any canvas position still uncommitted, so positions
|
|
22
|
+
* reach the persister in the order the editor made them.
|
|
23
|
+
*/
|
|
24
|
+
export function routeCanvasWrite(input) {
|
|
25
|
+
const { patches, focused, committer } = input;
|
|
26
|
+
if (focused && focused.policy !== 'change' && patches.length === 1 && writesExactly(patches[0], focused.path)) {
|
|
27
|
+
committer.edited(focused.key, focused.path);
|
|
28
|
+
return 'deferred';
|
|
29
|
+
}
|
|
30
|
+
if (committer.uncommitted().some((position) => isCanvasControlKey(position.field)))
|
|
31
|
+
committer.commitAll();
|
|
32
|
+
input.commitNow(patches);
|
|
33
|
+
return 'committed';
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=canvas-commit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canvas-commit.js","sourceRoot":"","sources":["../../../src/components/cms/canvas-commit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAqC1D,6GAA6G;AAC7G,MAAM,UAAU,aAAa,CAAC,KAAgB,EAAE,QAA+B,EAAE,MAAoB;IACnG,MAAM,IAAI,GACR,MAAM,IAAI,QAAQ;QAChB,CAAC,CAAC,CAAC,GAAG,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,CAAC,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC;QACxF,CAAC,CAAC,KAAK,CAAA;IACX,OAAO,EAAE,GAAG,EAAE,UAAU,eAAe,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAA;AACjE,CAAC;AAED,sDAAsD;AACtD,MAAM,UAAU,kBAAkB,CAAC,GAAW;IAC5C,OAAO,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC,CAAA;AAClC,CAAC;AAED,SAAS,aAAa,CAAC,KAAoB,EAAE,IAAe;IAC1D,OAAO,CAAC,KAAK,CAAC,EAAE,KAAK,KAAK,IAAI,KAAK,CAAC,EAAE,KAAK,OAAO,CAAC,IAAI,eAAe,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,eAAe,CAAC,IAAI,CAAC,CAAA;AAC9G,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAMhC;IACC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,KAAK,CAAA;IAC7C,IAAI,OAAO,IAAI,OAAO,CAAC,MAAM,KAAK,QAAQ,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,aAAa,CAAC,OAAO,CAAC,CAAC,CAAE,EAAE,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/G,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;QAC3C,OAAO,UAAU,CAAA;IACnB,CAAC;IACD,IAAI,SAAS,CAAC,WAAW,EAAE,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,kBAAkB,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QAAE,SAAS,CAAC,SAAS,EAAE,CAAA;IACzG,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,CAAA;IACxB,OAAO,WAAW,CAAA;AACpB,CAAC","sourcesContent":["import { formatValuePath } from '@elytracms/core/cms-core'\nimport type { DocumentPatch, ValuePath } from '@elytracms/core/cms-core'\nimport type { CommitPolicy } from './commit-policy'\nimport type { DraftCommitter } from './draft-commit'\n\n/**\n * EC-630 — the canvas inspector commits like every other editor: on its control kind's\n * quiescence policy (`commit-policy.ts`, EC-595 A2), never per keystroke (pure; the house\n * pattern).\n *\n * The canvas lifts every store change into the draft at once (live validation reads it).\n * A burst of typing in an inspector control used to be committed per keystroke too —\n * every canvas write went straight to the persister under the canvas's `change` policy:\n * 13 revisions for one typography-run edit, measured live (wave-0094), and one per\n * keystroke on any text prop. The canvas's own actions (insert, move, delete, undo, a\n * discrete control) ARE discrete commits; a control's typing is not. So the inspector\n * controls stand inside a commit boundary (focus / blur / Enter, the form's\n * `FieldCommitBoundary`) at a POSITION — a node's prop, or a typography run, which is\n * the field itself (prose is one position, EC-595 binding point 3) — with the policy\n * THE declaration gives that control kind (`commitPolicyOf`), and THE committer\n * (`draft-commit.ts`, shared with the form fields) holds the burst as that position's\n * uncommitted edit: committed on blur, Enter or the idle pause, with the base revision\n * of its first keystroke, overlaid on every rebase meanwhile, and in every forced flush.\n */\n\n/** Where a canvas inspector control writes: a node's prop, or a typography run (the field). */\nexport type CanvasControlPosition = { readonly node: string; readonly prop: string } | { readonly run: number }\n\n/** A focused canvas inspector control, as the committer knows it. */\nexport interface CanvasControl {\n /** The committer's key for it (never a field name — those carry no `:`). */\n readonly key: string\n /** The position it writes. */\n readonly path: ValuePath\n readonly policy: CommitPolicy\n}\n\n/** The control at `position` of the canvas field at `field` (the field's position path, locale included). */\nexport function canvasControl(field: ValuePath, position: CanvasControlPosition, policy: CommitPolicy): CanvasControl {\n const path: ValuePath =\n 'node' in position\n ? [...field, { kind: 'node', id: position.node }, { kind: 'prop', name: position.prop }]\n : field\n return { key: `canvas:${formatValuePath(path)}`, path, policy }\n}\n\n/** Whether a committer key names a canvas control. */\nexport function isCanvasControlKey(key: string): boolean {\n return key.startsWith('canvas:')\n}\n\nfunction writesExactly(patch: DocumentPatch, path: ValuePath): boolean {\n return (patch.op === 'set' || patch.op === 'unset') && formatValuePath(patch.path) === formatValuePath(path)\n}\n\n/**\n * Route one canvas write's patches. When they are exactly the focused control's value\n * write at its position and its policy waits for quiescence, the write is that control's\n * uncommitted position (`deferred` — the committer commits the latest value later).\n * Anything else — a canvas action, a discrete control, an edit with no control focused —\n * commits now (`committed`), after any canvas position still uncommitted, so positions\n * reach the persister in the order the editor made them.\n */\nexport function routeCanvasWrite(input: {\n patches: readonly DocumentPatch[]\n /** The focused canvas control, if focus is on one. */\n focused: CanvasControl | undefined\n committer: DraftCommitter\n commitNow: (patches: readonly DocumentPatch[]) => void\n}): 'deferred' | 'committed' {\n const { patches, focused, committer } = input\n if (focused && focused.policy !== 'change' && patches.length === 1 && writesExactly(patches[0]!, focused.path)) {\n committer.edited(focused.key, focused.path)\n return 'deferred'\n }\n if (committer.uncommitted().some((position) => isCanvasControlKey(position.field))) committer.commitAll()\n input.commitNow(patches)\n return 'committed'\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { CmsDocument, CmsValidationIssue, CollectionDef, CollectionRegistry, DocumentRef, FieldDef, Locale, LocaleConfig } from '@elytracms/core/cms-core';
|
|
2
2
|
import type { FindAsset } from './gate-validation.js';
|
|
3
|
-
import type { OperationError, OperationResult } from '
|
|
3
|
+
import type { OperationError, OperationResult } from '../../lib/operations/index.js';
|
|
4
4
|
/**
|
|
5
5
|
* Inline cell editing for the collection table (EC-168) — the pure half.
|
|
6
6
|
*
|
|
@@ -2,7 +2,7 @@ import { createDocumentStore, isFieldLocalized, validateDocument, writeField } f
|
|
|
2
2
|
import { gateValidateOptions } from './gate-validation.js';
|
|
3
3
|
import { anchorFieldName } from './inline-field-errors.js';
|
|
4
4
|
import { replaceDoc } from './document-ref-utils.js';
|
|
5
|
-
import { cmsValidationIssueMessage, operationErrorMessage, operationIssueMessage, } from '
|
|
5
|
+
import { cmsValidationIssueMessage, operationErrorMessage, operationIssueMessage, } from '../../lib/persona/labels.js';
|
|
6
6
|
/** Stable key for a cell (state maps, DOM lookups). */
|
|
7
7
|
export function cellKey(coord) {
|
|
8
8
|
return `${coord.docId}::${coord.field}`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cell-edit.js","sourceRoot":"","sources":["../../../src/components/cms/cell-edit.ts"],"names":[],"mappings":"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","sourcesContent":["import { createDocumentStore, isFieldLocalized, validateDocument, writeField } from '@elytracms/core/cms-core'\nimport type {\n CmsDocument,\n CmsValidationIssue,\n CollectionDef,\n CollectionRegistry,\n DocumentRef,\n FieldDef,\n Locale,\n LocaleConfig,\n} from '@elytracms/core/cms-core'\nimport { gateValidateOptions } from './gate-validation'\nimport type { FindAsset } from './gate-validation'\nimport { anchorFieldName } from './inline-field-errors'\nimport type { OperationError, OperationResult } from '@elytracms/studio/lib/operations'\nimport { replaceDoc } from './document-ref-utils'\nimport {\n cmsValidationIssueMessage,\n operationErrorMessage,\n operationIssueMessage,\n} from '@elytracms/studio/lib/persona/labels'\n\n/**\n * Inline cell editing for the collection table (EC-168) — the pure half.\n *\n * Everything here is plain data + functions so the cell-edit state machine is\n * testable without a DOM: which cells are editable, how Tab/Enter advance,\n * how a draft value is folded into a document (active locale only), and how a\n * commit flows through exactly one field patch (`documents.patch`, through the\n * session's persister queue — EC-595 A2) with optimistic apply + rollback on rejection.\n */\n\n// --- Cell identity -----------------------------------------------------------\n\n/** A cell in the collection table: one document × one field column. */\nexport interface CellCoord {\n docId: string\n field: string\n}\n\n/** Stable key for a cell (state maps, DOM lookups). */\nexport function cellKey(coord: CellCoord): string {\n return `${coord.docId}::${coord.field}`\n}\n\n// --- Editability -------------------------------------------------------------\n\n/** How a cell edits: inline control, anchored picker dialog, or not at all. */\nexport type CellEditKind = 'inline' | 'picker' | 'none'\n\nconst INLINE_TYPES: ReadonlySet<FieldDef['type']> = new Set([\n 'text',\n 'number',\n 'boolean',\n 'select',\n 'date',\n])\n\n/**\n * Classify a field's in-table editing surface. Rich text is deliberately not\n * inline-editable (the detail editor owns it); read-only fields never edit.\n */\nexport function cellEditKind(field: FieldDef, readOnly: boolean): CellEditKind {\n if (readOnly) return 'none'\n if (field.type === 'relation' || field.type === 'asset') return 'picker'\n return INLINE_TYPES.has(field.type) ? 'inline' : 'none'\n}\n\n// --- Keyboard advance --------------------------------------------------------\n\n/** Where a committed edit moves next: Tab → next, Shift-Tab → prev, Enter → down. */\nexport type AdvanceDirection = 'next' | 'prev' | 'down'\n\n/**\n * The next editable cell in the table grid, or `null` at a boundary.\n * `docIds` is the rendered row order; `fields` the editable column order.\n * 'next'/'prev' walk row-major (wrapping across rows); 'down' stays in the\n * same column.\n */\nexport function nextEditableCell(\n docIds: readonly string[],\n fields: readonly string[],\n current: CellCoord,\n direction: AdvanceDirection,\n): CellCoord | null {\n const row = docIds.indexOf(current.docId)\n const col = fields.indexOf(current.field)\n if (row < 0 || col < 0 || fields.length === 0) return null\n if (direction === 'down') {\n const nextDoc = docIds[row + 1]\n return nextDoc === undefined ? null : { docId: nextDoc, field: current.field }\n }\n const flat = row * fields.length + col + (direction === 'next' ? 1 : -1)\n if (flat < 0 || flat >= docIds.length * fields.length) return null\n const docId = docIds[Math.floor(flat / fields.length)]!\n return { docId, field: fields[flat % fields.length]! }\n}\n\n// --- Applying a draft value ----------------------------------------------------\n\n// Folding one field value into a document — localized fields write ONLY the given\n// locale's bag (EC-018), shared fields the `values` bag, `undefined` clears — is\n// core's `writeField` (EC-580); this module's `applyCellValue` was a copy of it.\n\n// --- Validation gate -----------------------------------------------------------\n\n/**\n * Error-severity issues that the edited cell itself caused. Localized fields\n * only reject on issues for the edited locale — a missing translation in some\n * *other* locale must not block this commit.\n *\n * EC-497: validates with THE bag (`gateValidateOptions`), not the `store` +\n * `localeConfig` subset it used to assemble here. A cell edit is not a weaker\n * KIND of validation — it is the same validation, FILTERED to the cell being\n * committed, which is the whole option-set contract in one function: surfaces\n * differ in what they do with the issues, never in what they validated with.\n *\n * EC-516: an issue belongs to the edited cell when the TOP-LEVEL field its\n * `path` resolves to is the edited column — `anchorFieldName`, the grammar the\n * document form's inline errors already use. `meta.field` is never compared\n * here: `validateObject` stamps it with the LEAF subfield name, so a broken\n * `items[0].title` used to pass for a `title` issue and block an unrelated\n * `title` edit (and the owning `items` column saw nothing).\n */\nexport function cellEditIssues(\n registry: CollectionRegistry,\n documents: readonly CmsDocument[],\n localeConfig: LocaleConfig,\n collection: CollectionDef,\n next: CmsDocument,\n fieldName: string,\n locale: Locale,\n oracles: CellEditOracles = {},\n): CmsValidationIssue[] {\n // Validate against the session with the edited document swapped in, so\n // self-relations and same-document targets resolve.\n const store = createDocumentStore(replaceDoc(documents, next))\n const issues = validateDocument(\n registry,\n next,\n gateValidateOptions({\n store,\n localeConfig,\n isPublished: oracles.isPublished ?? (() => false),\n ...(oracles.findAsset ? { findAsset: oracles.findAsset } : {}),\n }),\n )\n const localized = isFieldLocalized(collection, fieldName)\n return issues.filter((issue) => {\n if (issue.severity !== 'error') return false\n if (anchorFieldName(collection, issue) !== fieldName) return false\n if (!localized) return true\n // Localized field paths are ['documents', key, 'localized', <locale>, <field>, …].\n const at = issue.path.indexOf('localized')\n return at < 0 || issue.path[at + 1] === locale\n })\n}\n\n// --- Commit pipeline -------------------------------------------------------------\n\n/** Outcome of one cell commit: the new document, or a structured rejection. */\nexport type CellCommitOutcome =\n | { ok: true; doc: CmsDocument }\n | { ok: false; error: OperationError }\n\nexport interface CellCommitInput {\n collection: CollectionDef\n /** The document as currently rendered (the rollback target). */\n doc: CmsDocument\n field: FieldDef\n /** The active locale at the point of edit (table locale resolution). */\n locale: Locale\n /** The committed draft value (`undefined` clears the field). */\n value: unknown\n}\n\n/**\n * The session oracles the canonical option bag needs beyond the store + locales\n * (EC-497). Optional so a fixture-only caller still gets the rest of the bag;\n * absent `isPublished` means \"nothing is published\", the engine's own default.\n */\nexport interface CellEditOracles {\n isPublished?: (ref: DocumentRef) => boolean\n /** The asset-library oracle (EC-588) — record lookup; absent = no asset checks. */\n findAsset?: FindAsset\n}\n\nexport interface CellCommitContext extends CellEditOracles {\n registry: CollectionRegistry\n /** Current session documents (relation/asset target resolution). */\n documents: readonly CmsDocument[]\n localeConfig: LocaleConfig\n /** Optimistically apply the edited document to the session. */\n applySession: (next: CmsDocument) => void\n /** Restore the pre-edit document after a rejected dispatch. */\n rollbackSession: (previous: CmsDocument) => void\n /**\n * Dispatch the ONE write for this commit — the field's patch through the persister's\n * queue (`documents.patch`, EC-595 A2). Absent in fixture-only sessions (no project\n * scope) — the commit is session-only then.\n */\n dispatch?: (next: CmsDocument) => Promise<OperationResult<unknown>>\n}\n\n/**\n * Commit one cell edit:\n * 1. fold the value into the document (active locale only),\n * 2. gate on field validation — rejected edits dispatch NOTHING and report a\n * structured validation error for the cell,\n * 3. apply optimistically, dispatch exactly one operations command,\n * 4. roll the session back when the operation result is a rejection.\n */\nexport async function commitCellEdit(\n context: CellCommitContext,\n input: CellCommitInput,\n): Promise<CellCommitOutcome> {\n const next = writeField(input.collection, input.doc, input.field.name, input.locale, input.value)\n\n const issues = cellEditIssues(\n context.registry,\n context.documents,\n context.localeConfig,\n input.collection,\n next,\n input.field.name,\n input.locale,\n {\n ...(context.isPublished ? { isPublished: context.isPublished } : {}),\n ...(context.findAsset ? { findAsset: context.findAsset } : {}),\n },\n )\n if (issues.length > 0) {\n return {\n ok: false,\n error: {\n code: 'validation',\n // EC-487/EC-494: this producer is CLIENT-side and already holds the structured\n // issue, so it resolves the message here. (Since EC-494 the wire also carries\n // the ref, which is what makes the SERVER-side publish gate localize.)\n message: cmsValidationIssueMessage(issues[0]!),\n issues: issues.map((issue) => ({\n code: issue.code,\n message: cmsValidationIssueMessage(issue),\n path: [...issue.path],\n domain: 'cms',\n })),\n },\n }\n }\n\n context.applySession(next)\n if (context.dispatch) {\n let result: OperationResult<unknown>\n try {\n result = await context.dispatch(next)\n } catch (cause) {\n // Operations never throw by contract; treat a broken transport the same\n // as a failed command so the cell still rolls back visibly.\n result = {\n ok: false,\n error: { code: 'unknown', message: cause instanceof Error ? cause.message : String(cause) },\n }\n }\n if (!result.ok) {\n context.rollbackSession(input.doc)\n return { ok: false, error: result.error }\n }\n }\n return { ok: true, doc: next }\n}\n\n/** The message the cell renders for a rejection: the first issue beats the envelope. */\nexport function cellErrorMessage(error: OperationError): string {\n // EC-494: an OperationIssue now carries its catalog ref across the wire, so a\n // rejection built SERVER-SIDE (the publish gate) localizes here too — not only the\n // studio-built ones above.\n if (error.code === 'validation' && error.issues.length > 0)\n return operationIssueMessage(error.issues[0]!)\n return operationErrorMessage(error)\n}\n"]}
|
|
1
|
+
{"version":3,"file":"cell-edit.js","sourceRoot":"","sources":["../../../src/components/cms/cell-edit.ts"],"names":[],"mappings":"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","sourcesContent":["import { createDocumentStore, isFieldLocalized, validateDocument, writeField } from '@elytracms/core/cms-core'\nimport type {\n CmsDocument,\n CmsValidationIssue,\n CollectionDef,\n CollectionRegistry,\n DocumentRef,\n FieldDef,\n Locale,\n LocaleConfig,\n} from '@elytracms/core/cms-core'\nimport { gateValidateOptions } from './gate-validation'\nimport type { FindAsset } from './gate-validation'\nimport { anchorFieldName } from './inline-field-errors'\nimport type { OperationError, OperationResult } from '../../lib/operations'\nimport { replaceDoc } from './document-ref-utils'\nimport {\n cmsValidationIssueMessage,\n operationErrorMessage,\n operationIssueMessage,\n} from '../../lib/persona/labels'\n\n/**\n * Inline cell editing for the collection table (EC-168) — the pure half.\n *\n * Everything here is plain data + functions so the cell-edit state machine is\n * testable without a DOM: which cells are editable, how Tab/Enter advance,\n * how a draft value is folded into a document (active locale only), and how a\n * commit flows through exactly one field patch (`documents.patch`, through the\n * session's persister queue — EC-595 A2) with optimistic apply + rollback on rejection.\n */\n\n// --- Cell identity -----------------------------------------------------------\n\n/** A cell in the collection table: one document × one field column. */\nexport interface CellCoord {\n docId: string\n field: string\n}\n\n/** Stable key for a cell (state maps, DOM lookups). */\nexport function cellKey(coord: CellCoord): string {\n return `${coord.docId}::${coord.field}`\n}\n\n// --- Editability -------------------------------------------------------------\n\n/** How a cell edits: inline control, anchored picker dialog, or not at all. */\nexport type CellEditKind = 'inline' | 'picker' | 'none'\n\nconst INLINE_TYPES: ReadonlySet<FieldDef['type']> = new Set([\n 'text',\n 'number',\n 'boolean',\n 'select',\n 'date',\n])\n\n/**\n * Classify a field's in-table editing surface. Rich text is deliberately not\n * inline-editable (the detail editor owns it); read-only fields never edit.\n */\nexport function cellEditKind(field: FieldDef, readOnly: boolean): CellEditKind {\n if (readOnly) return 'none'\n if (field.type === 'relation' || field.type === 'asset') return 'picker'\n return INLINE_TYPES.has(field.type) ? 'inline' : 'none'\n}\n\n// --- Keyboard advance --------------------------------------------------------\n\n/** Where a committed edit moves next: Tab → next, Shift-Tab → prev, Enter → down. */\nexport type AdvanceDirection = 'next' | 'prev' | 'down'\n\n/**\n * The next editable cell in the table grid, or `null` at a boundary.\n * `docIds` is the rendered row order; `fields` the editable column order.\n * 'next'/'prev' walk row-major (wrapping across rows); 'down' stays in the\n * same column.\n */\nexport function nextEditableCell(\n docIds: readonly string[],\n fields: readonly string[],\n current: CellCoord,\n direction: AdvanceDirection,\n): CellCoord | null {\n const row = docIds.indexOf(current.docId)\n const col = fields.indexOf(current.field)\n if (row < 0 || col < 0 || fields.length === 0) return null\n if (direction === 'down') {\n const nextDoc = docIds[row + 1]\n return nextDoc === undefined ? null : { docId: nextDoc, field: current.field }\n }\n const flat = row * fields.length + col + (direction === 'next' ? 1 : -1)\n if (flat < 0 || flat >= docIds.length * fields.length) return null\n const docId = docIds[Math.floor(flat / fields.length)]!\n return { docId, field: fields[flat % fields.length]! }\n}\n\n// --- Applying a draft value ----------------------------------------------------\n\n// Folding one field value into a document — localized fields write ONLY the given\n// locale's bag (EC-018), shared fields the `values` bag, `undefined` clears — is\n// core's `writeField` (EC-580); this module's `applyCellValue` was a copy of it.\n\n// --- Validation gate -----------------------------------------------------------\n\n/**\n * Error-severity issues that the edited cell itself caused. Localized fields\n * only reject on issues for the edited locale — a missing translation in some\n * *other* locale must not block this commit.\n *\n * EC-497: validates with THE bag (`gateValidateOptions`), not the `store` +\n * `localeConfig` subset it used to assemble here. A cell edit is not a weaker\n * KIND of validation — it is the same validation, FILTERED to the cell being\n * committed, which is the whole option-set contract in one function: surfaces\n * differ in what they do with the issues, never in what they validated with.\n *\n * EC-516: an issue belongs to the edited cell when the TOP-LEVEL field its\n * `path` resolves to is the edited column — `anchorFieldName`, the grammar the\n * document form's inline errors already use. `meta.field` is never compared\n * here: `validateObject` stamps it with the LEAF subfield name, so a broken\n * `items[0].title` used to pass for a `title` issue and block an unrelated\n * `title` edit (and the owning `items` column saw nothing).\n */\nexport function cellEditIssues(\n registry: CollectionRegistry,\n documents: readonly CmsDocument[],\n localeConfig: LocaleConfig,\n collection: CollectionDef,\n next: CmsDocument,\n fieldName: string,\n locale: Locale,\n oracles: CellEditOracles = {},\n): CmsValidationIssue[] {\n // Validate against the session with the edited document swapped in, so\n // self-relations and same-document targets resolve.\n const store = createDocumentStore(replaceDoc(documents, next))\n const issues = validateDocument(\n registry,\n next,\n gateValidateOptions({\n store,\n localeConfig,\n isPublished: oracles.isPublished ?? (() => false),\n ...(oracles.findAsset ? { findAsset: oracles.findAsset } : {}),\n }),\n )\n const localized = isFieldLocalized(collection, fieldName)\n return issues.filter((issue) => {\n if (issue.severity !== 'error') return false\n if (anchorFieldName(collection, issue) !== fieldName) return false\n if (!localized) return true\n // Localized field paths are ['documents', key, 'localized', <locale>, <field>, …].\n const at = issue.path.indexOf('localized')\n return at < 0 || issue.path[at + 1] === locale\n })\n}\n\n// --- Commit pipeline -------------------------------------------------------------\n\n/** Outcome of one cell commit: the new document, or a structured rejection. */\nexport type CellCommitOutcome =\n | { ok: true; doc: CmsDocument }\n | { ok: false; error: OperationError }\n\nexport interface CellCommitInput {\n collection: CollectionDef\n /** The document as currently rendered (the rollback target). */\n doc: CmsDocument\n field: FieldDef\n /** The active locale at the point of edit (table locale resolution). */\n locale: Locale\n /** The committed draft value (`undefined` clears the field). */\n value: unknown\n}\n\n/**\n * The session oracles the canonical option bag needs beyond the store + locales\n * (EC-497). Optional so a fixture-only caller still gets the rest of the bag;\n * absent `isPublished` means \"nothing is published\", the engine's own default.\n */\nexport interface CellEditOracles {\n isPublished?: (ref: DocumentRef) => boolean\n /** The asset-library oracle (EC-588) — record lookup; absent = no asset checks. */\n findAsset?: FindAsset\n}\n\nexport interface CellCommitContext extends CellEditOracles {\n registry: CollectionRegistry\n /** Current session documents (relation/asset target resolution). */\n documents: readonly CmsDocument[]\n localeConfig: LocaleConfig\n /** Optimistically apply the edited document to the session. */\n applySession: (next: CmsDocument) => void\n /** Restore the pre-edit document after a rejected dispatch. */\n rollbackSession: (previous: CmsDocument) => void\n /**\n * Dispatch the ONE write for this commit — the field's patch through the persister's\n * queue (`documents.patch`, EC-595 A2). Absent in fixture-only sessions (no project\n * scope) — the commit is session-only then.\n */\n dispatch?: (next: CmsDocument) => Promise<OperationResult<unknown>>\n}\n\n/**\n * Commit one cell edit:\n * 1. fold the value into the document (active locale only),\n * 2. gate on field validation — rejected edits dispatch NOTHING and report a\n * structured validation error for the cell,\n * 3. apply optimistically, dispatch exactly one operations command,\n * 4. roll the session back when the operation result is a rejection.\n */\nexport async function commitCellEdit(\n context: CellCommitContext,\n input: CellCommitInput,\n): Promise<CellCommitOutcome> {\n const next = writeField(input.collection, input.doc, input.field.name, input.locale, input.value)\n\n const issues = cellEditIssues(\n context.registry,\n context.documents,\n context.localeConfig,\n input.collection,\n next,\n input.field.name,\n input.locale,\n {\n ...(context.isPublished ? { isPublished: context.isPublished } : {}),\n ...(context.findAsset ? { findAsset: context.findAsset } : {}),\n },\n )\n if (issues.length > 0) {\n return {\n ok: false,\n error: {\n code: 'validation',\n // EC-487/EC-494: this producer is CLIENT-side and already holds the structured\n // issue, so it resolves the message here. (Since EC-494 the wire also carries\n // the ref, which is what makes the SERVER-side publish gate localize.)\n message: cmsValidationIssueMessage(issues[0]!),\n issues: issues.map((issue) => ({\n code: issue.code,\n message: cmsValidationIssueMessage(issue),\n path: [...issue.path],\n domain: 'cms',\n })),\n },\n }\n }\n\n context.applySession(next)\n if (context.dispatch) {\n let result: OperationResult<unknown>\n try {\n result = await context.dispatch(next)\n } catch (cause) {\n // Operations never throw by contract; treat a broken transport the same\n // as a failed command so the cell still rolls back visibly.\n result = {\n ok: false,\n error: { code: 'unknown', message: cause instanceof Error ? cause.message : String(cause) },\n }\n }\n if (!result.ok) {\n context.rollbackSession(input.doc)\n return { ok: false, error: result.error }\n }\n }\n return { ok: true, doc: next }\n}\n\n/** The message the cell renders for a rejection: the first issue beats the envelope. */\nexport function cellErrorMessage(error: OperationError): string {\n // EC-494: an OperationIssue now carries its catalog ref across the wire, so a\n // rejection built SERVER-SIDE (the publish gate) localizes here too — not only the\n // studio-built ones above.\n if (error.code === 'validation' && error.issues.length > 0)\n return operationIssueMessage(error.issues[0]!)\n return operationErrorMessage(error)\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CmsDocument, CollectionDef, DocumentRef, FieldDef } from '@elytracms/core/cms-core';
|
|
2
|
-
import type { DisplayedFallback } from '
|
|
2
|
+
import type { DisplayedFallback } from '../../lib/locale/index.js';
|
|
3
3
|
/**
|
|
4
4
|
* Render a document's field value for a table cell, by field type (brief
|
|
5
5
|
* §8.4, EC-070), resolved in the studio's ambient active locale (EC-176).
|
|
@@ -4,9 +4,9 @@ import { readAssetId, readDocumentRef } from '@elytracms/core/cms-core';
|
|
|
4
4
|
import { blockRootsOf } from '@elytracms/core/project-graph';
|
|
5
5
|
import { richTextPlainText } from '@elytracms/core/rich-text';
|
|
6
6
|
import { Badge, Text } from '@elytracms/studio/ui';
|
|
7
|
-
import { translate } from '
|
|
8
|
-
import { localeChipLabel, resolveDisplayedValue } from '
|
|
9
|
-
import { isDisplayableAssetUrl } from '
|
|
7
|
+
import { translate } from '../../lib/i18n/index.js';
|
|
8
|
+
import { localeChipLabel, resolveDisplayedValue } from '../../lib/locale/index.js';
|
|
9
|
+
import { isDisplayableAssetUrl } from '../../lib/assets/index.js';
|
|
10
10
|
import { useCollectionView } from '../collection-view/collection-view-context.js';
|
|
11
11
|
import { AssetThumb, useAssetRecord } from '../assets/asset-thumb.js';
|
|
12
12
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cell.js","sourceRoot":"","sources":["../../../src/components/cms/cell.tsx"],"names":[],"mappings":"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","sourcesContent":["import { Boxes, Check, Image, Link2, Minus } from 'lucide-react'\nimport { readAssetId, readDocumentRef } from '@elytracms/core/cms-core'\nimport type { CmsDocument, CollectionDef, DocumentRef, FieldDef } from '@elytracms/core/cms-core'\nimport { blockRootsOf } from '@elytracms/core/project-graph'\nimport { richTextPlainText } from '@elytracms/core/rich-text'\nimport { Badge, Text } from '@elytracms/studio/ui'\nimport { translate } from '@elytracms/studio/lib/i18n'\nimport { localeChipLabel, resolveDisplayedValue } from '@elytracms/studio/lib/locale'\nimport type { DisplayedFallback } from '@elytracms/studio/lib/locale'\nimport type { AssetDisplay } from '@elytracms/studio/lib/collection-view/collection-view-source'\nimport { isDisplayableAssetUrl } from '@elytracms/studio/lib/assets'\nimport { useCollectionView } from '../collection-view/collection-view-context'\nimport { AssetThumb, useAssetRecord } from '../assets/asset-thumb'\n\n/**\n * Render a document's field value for a table cell, by field type (brief\n * §8.4, EC-070), resolved in the studio's ambient active locale (EC-176).\n * A value served via EC-018 locale fallback carries a visible chip — never\n * silently mixed in as if it were translated.\n */\nexport function CellValue({\n collection,\n doc,\n field,\n}: {\n collection: CollectionDef\n doc: CmsDocument\n field: FieldDef\n}) {\n const source = useCollectionView()\n const { value, fallback } = resolveDisplayedValue(\n collection,\n doc,\n field.name,\n source.activeLocale,\n source.localeConfig,\n )\n\n if (value === undefined || value === null || value === '') {\n return <Minus className=\"size-3.5 text-muted-foreground/50\" />\n }\n\n return (\n <span className=\"inline-flex min-w-0 max-w-full items-center gap-1.5\">\n <TypedCellValue field={field} value={value} />\n <FallbackChip fallback={fallback} testId={`cell-fallback-${doc.id}-${field.name}`} />\n </span>\n )\n}\n\n/**\n * Subtle source-locale chip on a fallback-displayed value (EC-176): e.g. a\n * German view showing untranslated English content gets an \"EN\" marker.\n */\nexport function FallbackChip({\n fallback,\n testId,\n}: {\n fallback: DisplayedFallback | null\n testId: string\n}) {\n if (fallback === null) return null\n return (\n <Badge\n variant=\"outline\"\n data-testid={testId}\n title={fallbackTitle(fallback)}\n className=\"shrink-0 px-1 py-0 text-[0.6rem] uppercase tracking-wide text-muted-foreground\"\n >\n {localeChipLabel(fallback.sourceLocale)}\n </Badge>\n )\n}\n\n/** Plain-language tooltip of a fallback chip. */\nexport function fallbackTitle(fallback: DisplayedFallback): string {\n return translate('cms.cell.fallbackTitle', {\n source: localeChipLabel(fallback.sourceLocale),\n requested: localeChipLabel(fallback.requestedLocale),\n })\n}\n\nfunction TypedCellValue({ field, value }: { field: FieldDef; value: unknown }) {\n switch (field.type) {\n case 'boolean':\n return value === true ? (\n <Check className=\"size-4 text-success\" />\n ) : (\n <Minus className=\"size-3.5 text-muted-foreground/50\" />\n )\n case 'select':\n return <Badge variant=\"outline\">{String(value)}</Badge>\n case 'number':\n return <Text size=\"sm\" mono>{String(value)}</Text>\n case 'date':\n return (\n <Text size=\"sm\" tone=\"muted\" mono>\n {String(value).slice(0, 10)}\n </Text>\n )\n case 'richText':\n return (\n <Text size=\"xs\" tone=\"subtle\">\n {richTextCellSummary(value).slice(0, 40) || translate('cms.cell.richText')}\n </Text>\n )\n case 'relation':\n return <RelationCell value={value} />\n case 'asset':\n return <AssetCell value={value} />\n case 'blocks':\n return <BlocksCell value={value} />\n case 'object': {\n // EC-253: list-view summary, never `[object Object]`. A repeater shows its\n // item count; a single object is edited in the detail form, not the cell.\n if (field.multiple) {\n const count = Array.isArray(value) ? value.length : 0\n return (\n <Badge variant=\"outline\">\n {translate(count === 1 ? 'cms.cell.itemCount.one' : 'cms.cell.itemCount.other', { count })}\n </Badge>\n )\n }\n return (\n <Text size=\"xs\" tone=\"subtle\">\n {value && typeof value === 'object' ? translate('cms.cell.object') : '—'}\n </Text>\n )\n }\n default:\n return (\n <Text size=\"sm\" className=\"truncate\">\n {String(value)}\n </Text>\n )\n }\n}\n\nfunction RelationCell({ value }: { value: unknown }) {\n const source = useCollectionView()\n const refs = toRefArray(value)\n if (refs.length === 0) return <Minus className=\"size-3.5 text-muted-foreground/50\" />\n // EC-246c: calm de-pilled read — a single muted link glyph + the title(s), not\n // a row of info pills. Matches the relation FIELD's row treatment; the pill was\n // a design cue the list doesn't need.\n return (\n <span className=\"inline-flex min-w-0 items-center gap-1.5 text-sm\">\n <Link2 className=\"size-3.5 shrink-0 text-muted-foreground\" />\n <span className=\"min-w-0 truncate\">\n {refs.slice(0, 3).map((ref) => source.titleOf(ref)).join(', ')}\n </span>\n {refs.length > 3 ? (\n <Text size=\"xs\" tone=\"subtle\" className=\"shrink-0\">\n +{refs.length - 3}\n </Text>\n ) : null}\n </span>\n )\n}\n\n/**\n * A blocks/composition cell: a count of TOP-LEVEL blocks (EC-212) — matching\n * what the canvas/page-tree present as the block list, never `[object Object]`.\n */\nfunction BlocksCell({ value }: { value: unknown }) {\n const count = blockRootsOf(value).length\n if (count === 0) return <Minus className=\"size-3.5 text-muted-foreground/50\" />\n return (\n <Badge variant=\"outline\">\n <Boxes className=\"size-3\" />{' '}\n {translate(count === 1 ? 'cms.cell.blockCount.one' : 'cms.cell.blockCount.other', { count })}\n </Badge>\n )\n}\n\nfunction AssetCell({ value }: { value: unknown }) {\n const source = useCollectionView()\n const assetCollectionId = source.assetCollectionId ?? 'asset'\n // EC-277: tolerate self-typed `{ type:'asset', id }` envelopes and legacy bare ids.\n const ids = (Array.isArray(value) ? value : [value])\n .map(readAssetId)\n .filter((id): id is string => id !== undefined)\n if (ids.length === 0) return <Minus className=\"size-3.5 text-muted-foreground/50\" />\n // EC-346: multiple assets show the first, plus a \"+N\" count for the rest —\n // this IS the field cell, not a gallery editor.\n const first = ids[0]!\n return (\n <div className=\"flex flex-wrap items-center gap-1.5\">\n <AssetFieldThumb assetId={first} assetCollectionId={assetCollectionId} />\n {ids.length > 1 ? <Text size=\"xs\" tone=\"subtle\">+{ids.length - 1}</Text> : null}\n </div>\n )\n}\n\n/**\n * EC-346 — an asset-type FIELD column's cell in a document list: a small\n * thumbnail (`size-8`, matching the 0.0.39 filename-column pattern), reusing the\n * SAME {@link AssetThumb} the assets rail uses so a focal point (EC-230/248)\n * crops the preview identically everywhere it appears. Resolves the full record\n * from the ambient asset library (`useAssetRecord` — ProjectProviders already\n * mounts an `<AssetsProvider>` at the project level, EC-195, so every CMS list\n * surface sees it) and prefers it over the source's own summary because only the\n * ambient record carries the focal point. Falls back to the source's\n * `resolveAsset` label/best-effort-image (the AD-19 \"no `<AssetsProvider>`\"\n * degradation a portable rail — or a test double — without a mounted library\n * still needs) so this never regresses the prior behaviour. Never crashes on an\n * id that resolves to nothing — both paths render a placeholder, never a badge\n * defaulted from a missing record.\n */\nfunction AssetFieldThumb({\n assetId,\n assetCollectionId,\n}: {\n assetId: string\n assetCollectionId: string\n}) {\n const source = useCollectionView()\n const ambient = useAssetRecord(assetId)\n if (ambient) return <AssetThumb record={ambient} className=\"size-8 shrink-0\" />\n const resolved = source.resolveAsset?.(assetId)\n const label =\n resolved?.title ?? resolved?.filename ?? source.titleOf({ collection: assetCollectionId, id: assetId })\n return <AssetPreview record={resolved} label={label} />\n}\n\n/**\n * The pre-EC-346 fallback preview: an image thumbnail for image assets with a\n * resolvable URL (EC-212), otherwise the labelled icon badge (and the same\n * badge whenever the source can't resolve the record — e.g. no asset backend).\n * Used only when no ambient asset library is mounted (see {@link AssetFieldThumb}).\n */\nfunction AssetPreview({ record, label }: { record: AssetDisplay | undefined; label: string }) {\n const isImage = record?.contentType?.startsWith('image/') ?? false\n const url = record?.url\n if (isImage && isDisplayableAssetUrl(url)) {\n return (\n <span className=\"inline-flex min-w-0 items-center gap-1.5\">\n <img\n src={url}\n alt={record?.alt ?? label}\n loading=\"lazy\"\n className=\"size-7 shrink-0 rounded border border-border bg-muted object-cover\"\n />\n <Text size=\"xs\" tone=\"subtle\" className=\"max-w-[10rem] truncate\">\n {label}\n </Text>\n </span>\n )\n }\n return (\n <Badge variant=\"outline\">\n <Image className=\"size-3\" /> {label}\n </Badge>\n )\n}\n\n/**\n * Normalize a relation value (single ref or array of refs) into an array of\n * `DocumentRef`s. EC-277: unwraps self-typed `{ type:'reference', ... }` envelopes\n * and legacy bare `{ collection, id }` via the shared tolerant reader.\n */\nexport function toRefArray(value: unknown): DocumentRef[] {\n const list = Array.isArray(value) ? value : [value]\n return list.map(readDocumentRef).filter((ref): ref is DocumentRef => ref !== undefined)\n}\n\nfunction stripHtml(html: string): string {\n return html.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ').trim()\n}\n\n/**\n * EC-348 — a richText field's stored value is the rich-text envelope\n * (`{ version, doc }` TipTap JSON, EC-314), not an HTML string: `stripHtml`\n * alone here yielded the literal `[object Object]`. Fallback chain: a valid\n * envelope extracts to plain text (`richTextPlainText`); a legacy plain/HTML\n * string (pre-EC-314 content, or any other non-envelope string) still goes\n * through `stripHtml` as before; anything else (garbage, wrong field-type data)\n * yields `''` so the cell's placeholder shows — never a crash, never\n * `[object Object]`.\n */\nfunction richTextCellSummary(value: unknown): string {\n const extracted = richTextPlainText(value)\n if (extracted !== undefined) return extracted\n return typeof value === 'string' ? stripHtml(value) : ''\n}\n"]}
|
|
1
|
+
{"version":3,"file":"cell.js","sourceRoot":"","sources":["../../../src/components/cms/cell.tsx"],"names":[],"mappings":"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","sourcesContent":["import { Boxes, Check, Image, Link2, Minus } from 'lucide-react'\nimport { readAssetId, readDocumentRef } from '@elytracms/core/cms-core'\nimport type { CmsDocument, CollectionDef, DocumentRef, FieldDef } from '@elytracms/core/cms-core'\nimport { blockRootsOf } from '@elytracms/core/project-graph'\nimport { richTextPlainText } from '@elytracms/core/rich-text'\nimport { Badge, Text } from '@elytracms/studio/ui'\nimport { translate } from '../../lib/i18n'\nimport { localeChipLabel, resolveDisplayedValue } from '../../lib/locale'\nimport type { DisplayedFallback } from '../../lib/locale'\nimport type { AssetDisplay } from '../../lib/collection-view/collection-view-source'\nimport { isDisplayableAssetUrl } from '../../lib/assets'\nimport { useCollectionView } from '../collection-view/collection-view-context'\nimport { AssetThumb, useAssetRecord } from '../assets/asset-thumb'\n\n/**\n * Render a document's field value for a table cell, by field type (brief\n * §8.4, EC-070), resolved in the studio's ambient active locale (EC-176).\n * A value served via EC-018 locale fallback carries a visible chip — never\n * silently mixed in as if it were translated.\n */\nexport function CellValue({\n collection,\n doc,\n field,\n}: {\n collection: CollectionDef\n doc: CmsDocument\n field: FieldDef\n}) {\n const source = useCollectionView()\n const { value, fallback } = resolveDisplayedValue(\n collection,\n doc,\n field.name,\n source.activeLocale,\n source.localeConfig,\n )\n\n if (value === undefined || value === null || value === '') {\n return <Minus className=\"size-3.5 text-muted-foreground/50\" />\n }\n\n return (\n <span className=\"inline-flex min-w-0 max-w-full items-center gap-1.5\">\n <TypedCellValue field={field} value={value} />\n <FallbackChip fallback={fallback} testId={`cell-fallback-${doc.id}-${field.name}`} />\n </span>\n )\n}\n\n/**\n * Subtle source-locale chip on a fallback-displayed value (EC-176): e.g. a\n * German view showing untranslated English content gets an \"EN\" marker.\n */\nexport function FallbackChip({\n fallback,\n testId,\n}: {\n fallback: DisplayedFallback | null\n testId: string\n}) {\n if (fallback === null) return null\n return (\n <Badge\n variant=\"outline\"\n data-testid={testId}\n title={fallbackTitle(fallback)}\n className=\"shrink-0 px-1 py-0 text-[0.6rem] uppercase tracking-wide text-muted-foreground\"\n >\n {localeChipLabel(fallback.sourceLocale)}\n </Badge>\n )\n}\n\n/** Plain-language tooltip of a fallback chip. */\nexport function fallbackTitle(fallback: DisplayedFallback): string {\n return translate('cms.cell.fallbackTitle', {\n source: localeChipLabel(fallback.sourceLocale),\n requested: localeChipLabel(fallback.requestedLocale),\n })\n}\n\nfunction TypedCellValue({ field, value }: { field: FieldDef; value: unknown }) {\n switch (field.type) {\n case 'boolean':\n return value === true ? (\n <Check className=\"size-4 text-success\" />\n ) : (\n <Minus className=\"size-3.5 text-muted-foreground/50\" />\n )\n case 'select':\n return <Badge variant=\"outline\">{String(value)}</Badge>\n case 'number':\n return <Text size=\"sm\" mono>{String(value)}</Text>\n case 'date':\n return (\n <Text size=\"sm\" tone=\"muted\" mono>\n {String(value).slice(0, 10)}\n </Text>\n )\n case 'richText':\n return (\n <Text size=\"xs\" tone=\"subtle\">\n {richTextCellSummary(value).slice(0, 40) || translate('cms.cell.richText')}\n </Text>\n )\n case 'relation':\n return <RelationCell value={value} />\n case 'asset':\n return <AssetCell value={value} />\n case 'blocks':\n return <BlocksCell value={value} />\n case 'object': {\n // EC-253: list-view summary, never `[object Object]`. A repeater shows its\n // item count; a single object is edited in the detail form, not the cell.\n if (field.multiple) {\n const count = Array.isArray(value) ? value.length : 0\n return (\n <Badge variant=\"outline\">\n {translate(count === 1 ? 'cms.cell.itemCount.one' : 'cms.cell.itemCount.other', { count })}\n </Badge>\n )\n }\n return (\n <Text size=\"xs\" tone=\"subtle\">\n {value && typeof value === 'object' ? translate('cms.cell.object') : '—'}\n </Text>\n )\n }\n default:\n return (\n <Text size=\"sm\" className=\"truncate\">\n {String(value)}\n </Text>\n )\n }\n}\n\nfunction RelationCell({ value }: { value: unknown }) {\n const source = useCollectionView()\n const refs = toRefArray(value)\n if (refs.length === 0) return <Minus className=\"size-3.5 text-muted-foreground/50\" />\n // EC-246c: calm de-pilled read — a single muted link glyph + the title(s), not\n // a row of info pills. Matches the relation FIELD's row treatment; the pill was\n // a design cue the list doesn't need.\n return (\n <span className=\"inline-flex min-w-0 items-center gap-1.5 text-sm\">\n <Link2 className=\"size-3.5 shrink-0 text-muted-foreground\" />\n <span className=\"min-w-0 truncate\">\n {refs.slice(0, 3).map((ref) => source.titleOf(ref)).join(', ')}\n </span>\n {refs.length > 3 ? (\n <Text size=\"xs\" tone=\"subtle\" className=\"shrink-0\">\n +{refs.length - 3}\n </Text>\n ) : null}\n </span>\n )\n}\n\n/**\n * A blocks/composition cell: a count of TOP-LEVEL blocks (EC-212) — matching\n * what the canvas/page-tree present as the block list, never `[object Object]`.\n */\nfunction BlocksCell({ value }: { value: unknown }) {\n const count = blockRootsOf(value).length\n if (count === 0) return <Minus className=\"size-3.5 text-muted-foreground/50\" />\n return (\n <Badge variant=\"outline\">\n <Boxes className=\"size-3\" />{' '}\n {translate(count === 1 ? 'cms.cell.blockCount.one' : 'cms.cell.blockCount.other', { count })}\n </Badge>\n )\n}\n\nfunction AssetCell({ value }: { value: unknown }) {\n const source = useCollectionView()\n const assetCollectionId = source.assetCollectionId ?? 'asset'\n // EC-277: tolerate self-typed `{ type:'asset', id }` envelopes and legacy bare ids.\n const ids = (Array.isArray(value) ? value : [value])\n .map(readAssetId)\n .filter((id): id is string => id !== undefined)\n if (ids.length === 0) return <Minus className=\"size-3.5 text-muted-foreground/50\" />\n // EC-346: multiple assets show the first, plus a \"+N\" count for the rest —\n // this IS the field cell, not a gallery editor.\n const first = ids[0]!\n return (\n <div className=\"flex flex-wrap items-center gap-1.5\">\n <AssetFieldThumb assetId={first} assetCollectionId={assetCollectionId} />\n {ids.length > 1 ? <Text size=\"xs\" tone=\"subtle\">+{ids.length - 1}</Text> : null}\n </div>\n )\n}\n\n/**\n * EC-346 — an asset-type FIELD column's cell in a document list: a small\n * thumbnail (`size-8`, matching the 0.0.39 filename-column pattern), reusing the\n * SAME {@link AssetThumb} the assets rail uses so a focal point (EC-230/248)\n * crops the preview identically everywhere it appears. Resolves the full record\n * from the ambient asset library (`useAssetRecord` — ProjectProviders already\n * mounts an `<AssetsProvider>` at the project level, EC-195, so every CMS list\n * surface sees it) and prefers it over the source's own summary because only the\n * ambient record carries the focal point. Falls back to the source's\n * `resolveAsset` label/best-effort-image (the AD-19 \"no `<AssetsProvider>`\"\n * degradation a portable rail — or a test double — without a mounted library\n * still needs) so this never regresses the prior behaviour. Never crashes on an\n * id that resolves to nothing — both paths render a placeholder, never a badge\n * defaulted from a missing record.\n */\nfunction AssetFieldThumb({\n assetId,\n assetCollectionId,\n}: {\n assetId: string\n assetCollectionId: string\n}) {\n const source = useCollectionView()\n const ambient = useAssetRecord(assetId)\n if (ambient) return <AssetThumb record={ambient} className=\"size-8 shrink-0\" />\n const resolved = source.resolveAsset?.(assetId)\n const label =\n resolved?.title ?? resolved?.filename ?? source.titleOf({ collection: assetCollectionId, id: assetId })\n return <AssetPreview record={resolved} label={label} />\n}\n\n/**\n * The pre-EC-346 fallback preview: an image thumbnail for image assets with a\n * resolvable URL (EC-212), otherwise the labelled icon badge (and the same\n * badge whenever the source can't resolve the record — e.g. no asset backend).\n * Used only when no ambient asset library is mounted (see {@link AssetFieldThumb}).\n */\nfunction AssetPreview({ record, label }: { record: AssetDisplay | undefined; label: string }) {\n const isImage = record?.contentType?.startsWith('image/') ?? false\n const url = record?.url\n if (isImage && isDisplayableAssetUrl(url)) {\n return (\n <span className=\"inline-flex min-w-0 items-center gap-1.5\">\n <img\n src={url}\n alt={record?.alt ?? label}\n loading=\"lazy\"\n className=\"size-7 shrink-0 rounded border border-border bg-muted object-cover\"\n />\n <Text size=\"xs\" tone=\"subtle\" className=\"max-w-[10rem] truncate\">\n {label}\n </Text>\n </span>\n )\n }\n return (\n <Badge variant=\"outline\">\n <Image className=\"size-3\" /> {label}\n </Badge>\n )\n}\n\n/**\n * Normalize a relation value (single ref or array of refs) into an array of\n * `DocumentRef`s. EC-277: unwraps self-typed `{ type:'reference', ... }` envelopes\n * and legacy bare `{ collection, id }` via the shared tolerant reader.\n */\nexport function toRefArray(value: unknown): DocumentRef[] {\n const list = Array.isArray(value) ? value : [value]\n return list.map(readDocumentRef).filter((ref): ref is DocumentRef => ref !== undefined)\n}\n\nfunction stripHtml(html: string): string {\n return html.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ').trim()\n}\n\n/**\n * EC-348 — a richText field's stored value is the rich-text envelope\n * (`{ version, doc }` TipTap JSON, EC-314), not an HTML string: `stripHtml`\n * alone here yielded the literal `[object Object]`. Fallback chain: a valid\n * envelope extracts to plain text (`richTextPlainText`); a legacy plain/HTML\n * string (pre-EC-314 content, or any other non-envelope string) still goes\n * through `stripHtml` as before; anything else (garbage, wrong field-type data)\n * yields `''` so the cell's placeholder shows — never a crash, never\n * `[object Object]`.\n */\nfunction richTextCellSummary(value: unknown): string {\n const extracted = richTextPlainText(value)\n if (extracted !== undefined) return extracted\n return typeof value === 'string' ? stripHtml(value) : ''\n}\n"]}
|
|
@@ -2,10 +2,10 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { Database, FileCog } from 'lucide-react';
|
|
3
3
|
import { isSingleton } from '@elytracms/core/cms-core';
|
|
4
4
|
import { Link, STUDIO_PROJECT_ROUTE, studioPath } from '@elytracms/studio/router';
|
|
5
|
-
import { useCms } from '
|
|
5
|
+
import { useCms } from './cms-store.js';
|
|
6
6
|
import { focusCollectionList } from './collection-focus.js';
|
|
7
7
|
import { Card, Code, Heading, Spacer, Stack, Text } from '@elytracms/studio/ui';
|
|
8
|
-
import { localizedLabel, useT } from '
|
|
8
|
+
import { localizedLabel, useT } from '../../lib/i18n/index.js';
|
|
9
9
|
/**
|
|
10
10
|
* The CMS section landing view (EC-223 Stage 6): the collection grid, shown when
|
|
11
11
|
* no collection is selected. Takes `projectId` as a prop; the splat resolver
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cms-index.js","sourceRoot":"","sources":["../../../src/components/cms/cms-index.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AACtD,OAAO,EAAE,IAAI,EAAE,oBAAoB,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAA;AACjF,OAAO,EAAE,MAAM,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"cms-index.js","sourceRoot":"","sources":["../../../src/components/cms/cms-index.tsx"],"names":[],"mappings":"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","sourcesContent":["import { Database, FileCog } from 'lucide-react'\nimport { isSingleton } from '@elytracms/core/cms-core'\nimport { Link, STUDIO_PROJECT_ROUTE, studioPath } from '@elytracms/studio/router'\nimport { useCms } from './cms-store'\nimport { focusCollectionList } from './collection-focus'\nimport { Card, Code, Heading, Spacer, Stack, Text } from '@elytracms/studio/ui'\nimport { localizedLabel, useT } from '../../lib/i18n'\n\n/**\n * The CMS section landing view (EC-223 Stage 6): the collection grid, shown when\n * no collection is selected. Takes `projectId` as a prop; the splat resolver\n * mounts it inside the CMS provider so {@link useCms} resolves. `focusCollections`\n * (EC-239) narrows the grid for a focused CMS module instance.\n */\nexport function CmsIndex({\n projectId,\n focusCollections,\n}: {\n projectId: string\n focusCollections?: readonly string[]\n}) {\n const cms = useCms()\n const t = useT()\n const collections = focusCollectionList(cms.collections, focusCollections)\n\n return (\n <div className=\"mx-auto w-full max-w-3xl p-4 sm:p-6\">\n <Stack gap=\"6\">\n <Stack gap=\"1\">\n <Heading size=\"display\">{t('cms.index.heading')}</Heading>\n <Text tone=\"muted\">{t('cms.index.description')}</Text>\n </Stack>\n <div className=\"grid gap-3 sm:grid-cols-2\">\n {collections.map((collection) => {\n // EC-217: a singleton reads as one fixed document — a distinct icon\n // and a \"Single document\" note instead of a (always-1) row count.\n const singleton = isSingleton(collection)\n const Icon = singleton ? FileCog : Database\n return (\n <Link\n key={collection.id}\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId, _splat: studioPath.cmsCollection(collection.id) }}\n >\n <Card className=\"flex items-center gap-3 p-4 transition-colors hover:border-primary/30\">\n <span className=\"grid size-9 shrink-0 place-items-center rounded-lg bg-muted text-muted-foreground\">\n <Icon className=\"size-4\" />\n </span>\n <Stack gap=\"none\" className=\"min-w-0\">\n <Text weight=\"semibold\" className=\"truncate\">\n {localizedLabel(collection.form?.label, collection.id)}\n </Text>\n <Code tone=\"muted\" className=\"w-fit\">\n {collection.id} ·{' '}\n {t(\n collection.fields.length === 1\n ? 'cms.index.fieldCount.one'\n : 'cms.index.fieldCount.other',\n { count: collection.fields.length },\n )}\n </Code>\n </Stack>\n <Spacer />\n {singleton ? (\n <Text size=\"xs\" tone=\"muted\" className=\"whitespace-nowrap\">\n {t('cms.index.singleDocument')}\n </Text>\n ) : (\n <Text size=\"sm\" tone=\"muted\" mono>\n {cms.documentsOf(collection.id).length}\n </Text>\n )}\n </Card>\n </Link>\n )\n })}\n </div>\n </Stack>\n </div>\n )\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { resolveLabel } from '@elytracms/core/cms-core';
|
|
2
|
-
import { localizedLabel, resolveMsgLabel, translate } from '
|
|
3
|
-
import { PUBLISHED_AT_FIELD, STATE_FIELD, UPDATED_AT_FIELD, URL_FIELD, } from '
|
|
2
|
+
import { localizedLabel, resolveMsgLabel, translate } from '../../lib/i18n/index.js';
|
|
3
|
+
import { PUBLISHED_AT_FIELD, STATE_FIELD, UPDATED_AT_FIELD, URL_FIELD, } from '../../lib/cms-views/index.js';
|
|
4
4
|
/**
|
|
5
5
|
* Pure label formatters (EC-326) — no `CmsProvider`/store coupling, so
|
|
6
6
|
* portable surfaces (AD-19: `collection-table.tsx`, `detail-editor.tsx`, and
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cms-labels.js","sourceRoot":"","sources":["../../../src/components/cms/cms-labels.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AACvD,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"cms-labels.js","sourceRoot":"","sources":["../../../src/components/cms/cms-labels.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AACvD,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC3E,OAAO,EACL,kBAAkB,EAClB,WAAW,EACX,gBAAgB,EAChB,SAAS,GACV,MAAM,qBAAqB,CAAA;AAE5B;;;;;GAKG;AAEH;;;;;;GAMG;AACH,MAAM,UAAU,UAAU,CAAC,KAAe,EAAE,MAAe;IACzD,8EAA8E;IAC9E,oFAAoF;IACpF,oCAAoC;IACpC,MAAM,MAAM,GAAG,eAAe,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IACjD,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,MAAM,CAAA;IACvC,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,YAAY,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,IAAI,KAAK,CAAC,IAAI,CAAA;IACtF,OAAO,cAAc,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,CAAA;AACtD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,WAAW;YACd,OAAO,SAAS,CAAC,uBAAuB,CAAC,CAAA;QAC3C,KAAK,SAAS;YACZ,OAAO,SAAS,CAAC,qBAAqB,CAAC,CAAA;QACzC,KAAK,gBAAgB;YACnB,OAAO,SAAS,CAAC,2BAA2B,CAAC,CAAA;QAC/C,KAAK,kBAAkB;YACrB,OAAO,SAAS,CAAC,6BAA6B,CAAC,CAAA;QACjD;YACE,OAAO,IAAI,CAAA;IACf,CAAC;AACH,CAAC","sourcesContent":["import type { FieldDef } from '@elytracms/core/cms-core'\nimport { resolveLabel } from '@elytracms/core/cms-core'\nimport { localizedLabel, resolveMsgLabel, translate } from '../../lib/i18n'\nimport {\n PUBLISHED_AT_FIELD,\n STATE_FIELD,\n UPDATED_AT_FIELD,\n URL_FIELD,\n} from '../../lib/cms-views'\n\n/**\n * Pure label formatters (EC-326) — no `CmsProvider`/store coupling, so\n * portable surfaces (AD-19: `collection-table.tsx`, `detail-editor.tsx`, and\n * friends) can format a field label without statically depending on the\n * 1000+ line `cms-store.tsx` module for a single formatter.\n */\n\n/**\n * A field's display label (EC-241): resolve its (possibly localized) `form.label`,\n * falling back to the field name. **`locale` defaults to the active UI locale** —\n * a bare `fieldLabel(field)` localizes a `{ en, de }` map against the browser locale\n * (via the non-hook `localizedLabel`), so a German editor sees German labels. Pass an\n * explicit `locale` only to force a specific one (e.g. a content-locale variant view).\n */\nexport function fieldLabel(field: FieldDef, locale?: string): string {\n // A catalog-backed built-in-rail label (msgLabel) always resolves through the\n // catalog against the active UI locale, ignoring a content-locale override — a rail\n // is chrome, not localized content.\n const chrome = resolveMsgLabel(field.form?.label)\n if (chrome !== undefined) return chrome\n if (locale !== undefined) return resolveLabel(field.form?.label, locale) ?? field.name\n return localizedLabel(field.form?.label, field.name)\n}\n\n/**\n * The i18n header/picker label for a reserved pseudo-column (EC-445), by its\n * `$`-key. Shared by the list table (headers/cells) and the Display picker so the\n * two never disagree on a pseudo-column's name.\n */\nexport function pseudoColumnLabel(name: string): string {\n switch (name) {\n case STATE_FIELD:\n return translate('cms.list.column.state')\n case URL_FIELD:\n return translate('cms.list.column.url')\n case UPDATED_AT_FIELD:\n return translate('cms.list.column.updatedAt')\n case PUBLISHED_AT_FIELD:\n return translate('cms.list.column.publishedAt')\n default:\n return name\n }\n}\n"]}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
2
|
import type { CmsDocument, CmsValidationIssue, CollectionDef, CollectionRegistry, DocumentPatch, DocumentRef, FieldDef, ValuePath, Locale, LocaleConfig, LocalizedString, RestoreScope, ValidateDocumentOptions } from '@elytracms/core/cms-core';
|
|
3
3
|
import type { CmsDocumentRecord, DocumentVersionRecord, IdFactory, PresenceEntry } from '@elytracms/core/persistence';
|
|
4
|
-
import type { CommitOutcome, DocumentConflict, OperationError, OperationsClient } from '
|
|
5
|
-
import type { TableViewState } from '
|
|
4
|
+
import type { CommitOutcome, DocumentConflict, OperationError, OperationsClient } from '../../lib/operations/index.js';
|
|
5
|
+
import type { TableViewState } from '../../lib/cms-views/index.js';
|
|
6
6
|
import type { CellCommitInput, CellCommitOutcome } from './cell-edit.js';
|
|
7
7
|
import type { BulkAction, BulkReport, BulkSetField } from './bulk-actions.js';
|
|
8
8
|
/**
|
|
@@ -2,19 +2,19 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
3
|
import { applyDocumentPatches, createCollectionRegistry, createDocumentStore, documentKey, documentReachabilityIssues, documentSchema, resolveDocumentTitle, seedDefaults, titleFieldOf, validateDocument, valuesInLocale, writeField, } from '@elytracms/core/cms-core';
|
|
4
4
|
import { createUuidIdFactory } from '@elytracms/core/persistence';
|
|
5
|
-
import { translate } from '
|
|
6
|
-
import { configureStudioLocales, useStudioLocale } from '
|
|
7
|
-
import { studioManifestLookup } from '
|
|
8
|
-
import { resolveDeploymentCollections, resolveDeploymentLocaleConfig, resolveDeploymentRoutes, } from '
|
|
5
|
+
import { translate } from '../../lib/i18n/index.js';
|
|
6
|
+
import { configureStudioLocales, useStudioLocale } from '../../lib/locale/index.js';
|
|
7
|
+
import { studioManifestLookup } from '../../lib/registry/index.js';
|
|
8
|
+
import { resolveDeploymentCollections, resolveDeploymentLocaleConfig, resolveDeploymentRoutes, } from '../../lib/workspace/index.js';
|
|
9
9
|
import { Heading, Panel, Stack, Text } from '@elytracms/studio/ui';
|
|
10
10
|
import { assetLibraryOracle, gateValidateOptions } from './gate-validation.js';
|
|
11
11
|
import { matchesRef, removeDoc, replaceDoc, upsertDoc } from './document-ref-utils.js';
|
|
12
|
-
import { createRequiredContext } from '
|
|
13
|
-
import { createCmsPersister, getStudioActor, getStudioOperations } from '
|
|
14
|
-
import { publishInSession, publishWithRevision as publishRevision, } from '
|
|
15
|
-
import { operationErrorMessage } from '
|
|
16
|
-
import { fieldWritePatch } from '
|
|
17
|
-
import { ALL_VIEW_ID, DEFAULT_VIEW_OVERRIDE_ID, canonicalViewState, configViewIssues, defaultConfigViewId, defaultViewState, deprecatedDefaultFlagViewIds, hasDefaultViewOverride, pseudoColumns, resolveCollectionViews, viewStatesEqual, } from '
|
|
12
|
+
import { createRequiredContext } from '../../lib/required-context.js';
|
|
13
|
+
import { createCmsPersister, getStudioActor, getStudioOperations } from '../../lib/operations/index.js';
|
|
14
|
+
import { publishInSession, publishWithRevision as publishRevision, } from '../../lib/operations/publish-with-revision.js';
|
|
15
|
+
import { operationErrorMessage } from '../../lib/persona/labels.js';
|
|
16
|
+
import { fieldWritePatch } from '../../lib/cms/field-patch.js';
|
|
17
|
+
import { ALL_VIEW_ID, DEFAULT_VIEW_OVERRIDE_ID, canonicalViewState, configViewIssues, defaultConfigViewId, defaultViewState, deprecatedDefaultFlagViewIds, hasDefaultViewOverride, pseudoColumns, resolveCollectionViews, viewStatesEqual, } from '../../lib/cms-views/index.js';
|
|
18
18
|
import { useOptionalAssets } from '../assets/assets-store.js';
|
|
19
19
|
import { consumeStagedDocuments } from './document-staging.js';
|
|
20
20
|
import { commitCellEdit } from './cell-edit.js';
|