@elytracms/studio 0.0.92 → 0.0.94
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/convex/analytics-cost.test.ts +817 -0
- package/convex/asset-alt-migration.test.ts +127 -0
- package/convex/asset-replace.test.ts +168 -0
- package/convex/asset-revalidate.test.ts +6 -6
- package/convex/assets.ts +41 -5
- package/convex/auth.ts +5 -2
- package/convex/authority.ts +35 -11
- package/convex/cms.ts +25 -11
- package/convex/contract-parity.test.ts +25 -3
- package/convex/delivery.ts +12 -9
- package/convex/document-revalidate.test.ts +10 -12
- package/convex/guard.ts +9 -7
- package/convex/http-routes.test.ts +18 -8
- package/convex/http.ts +23 -17
- package/convex/ingest.test.ts +326 -0
- package/convex/ingest.ts +281 -0
- package/convex/invites.ts +13 -5
- package/convex/members.ts +10 -4
- package/convex/orphan-hydration.test.ts +116 -0
- package/convex/rail-write-parity.test.ts +11 -8
- package/convex/record-functions.ts +325 -0
- package/convex/records.test.ts +156 -0
- package/convex/redirect-migration.test.ts +205 -0
- package/convex/redirect-revalidate.test.ts +5 -1
- package/convex/redirects.ts +120 -79
- package/convex/role-policy.test.ts +315 -0
- package/convex/roles.ts +83 -0
- package/convex/schema.ts +36 -0
- package/convex/triggers.ts +21 -1
- package/dist/addon.d.ts +71 -0
- package/dist/addon.js +67 -0
- package/dist/addon.js.map +1 -0
- package/dist/components/account/account-settings.js +2 -2
- package/dist/components/account/account-settings.js.map +1 -1
- package/dist/components/account/credential-panel.js +2 -2
- package/dist/components/account/credential-panel.js.map +1 -1
- package/dist/components/account/self-account-schema.d.ts +6 -15
- package/dist/components/account/self-account-schema.js +52 -34
- package/dist/components/account/self-account-schema.js.map +1 -1
- package/dist/components/account/self-account-view-source.d.ts +1 -1
- package/dist/components/account/self-account-view-source.js +6 -4
- package/dist/components/account/self-account-view-source.js.map +1 -1
- package/dist/components/ai/ai-chat-panel-lazy.js +1 -1
- package/dist/components/ai/ai-chat-panel-lazy.js.map +1 -1
- package/dist/components/ai/ai-chat-panel.js +6 -6
- package/dist/components/ai/ai-chat-panel.js.map +1 -1
- package/dist/components/ai/ai-cms-panel.js +2 -2
- package/dist/components/ai/ai-cms-panel.js.map +1 -1
- package/dist/components/ai/ai-panel-toggle.js +4 -4
- package/dist/components/ai/ai-panel-toggle.js.map +1 -1
- package/dist/components/ai/chat-ui.js +2 -2
- package/dist/components/ai/chat-ui.js.map +1 -1
- package/dist/components/ai/proposal-card.d.ts +2 -2
- package/dist/components/ai/proposal-card.js +4 -3
- package/dist/components/ai/proposal-card.js.map +1 -1
- package/dist/components/ai/proposal-issues.js +2 -2
- package/dist/components/ai/proposal-issues.js.map +1 -1
- package/dist/components/ai/translate-document-action.js +2 -2
- package/dist/components/ai/translate-document-action.js.map +1 -1
- package/dist/components/ai/translate-document-dialog.js +10 -8
- package/dist/components/ai/translate-document-dialog.js.map +1 -1
- package/dist/components/ai/translate-skipped.d.ts +19 -0
- package/dist/components/ai/translate-skipped.js +46 -0
- package/dist/components/ai/translate-skipped.js.map +1 -0
- package/dist/components/app-views/default-modules.d.ts +50 -2
- package/dist/components/app-views/default-modules.js +65 -20
- package/dist/components/app-views/default-modules.js.map +1 -1
- package/dist/components/app-views/module.d.ts +53 -3
- package/dist/components/app-views/module.js +14 -7
- package/dist/components/app-views/module.js.map +1 -1
- package/dist/components/app-views/sidebar.js +1 -1
- package/dist/components/app-views/sidebar.js.map +1 -1
- package/dist/components/app-views/studio-config.d.ts +11 -2
- package/dist/components/app-views/studio-config.js.map +1 -1
- package/dist/components/assets/asset-detail.js +20 -10
- package/dist/components/assets/asset-detail.js.map +1 -1
- package/dist/components/assets/asset-grid.js +2 -2
- package/dist/components/assets/asset-grid.js.map +1 -1
- package/dist/components/assets/asset-metadata.d.ts +15 -4
- package/dist/components/assets/asset-metadata.js +15 -2
- package/dist/components/assets/asset-metadata.js.map +1 -1
- package/dist/components/assets/asset-search.js +4 -1
- package/dist/components/assets/asset-search.js.map +1 -1
- package/dist/components/assets/asset-selection.d.ts +0 -8
- package/dist/components/assets/asset-selection.js +4 -23
- package/dist/components/assets/asset-selection.js.map +1 -1
- package/dist/components/assets/asset-thumb.js +6 -5
- package/dist/components/assets/asset-thumb.js.map +1 -1
- package/dist/components/assets/assets-collection-view-source.d.ts +5 -5
- package/dist/components/assets/assets-collection-view-source.js +9 -5
- package/dist/components/assets/assets-collection-view-source.js.map +1 -1
- package/dist/components/assets/assets-module.js +1 -1
- package/dist/components/assets/assets-module.js.map +1 -1
- package/dist/components/assets/assets-store.d.ts +16 -2
- package/dist/components/assets/assets-store.js +28 -6
- package/dist/components/assets/assets-store.js.map +1 -1
- package/dist/components/assets/assets-view-preference.js +1 -1
- package/dist/components/assets/assets-view-preference.js.map +1 -1
- package/dist/components/assets/replace-file.d.ts +22 -0
- package/dist/components/assets/replace-file.js +59 -0
- package/dist/components/assets/replace-file.js.map +1 -0
- package/dist/components/auth-context.d.ts +1 -1
- package/dist/components/auth-context.js +6 -6
- package/dist/components/auth-context.js.map +1 -1
- package/dist/components/builder/blocks-model.d.ts +15 -27
- package/dist/components/builder/blocks-model.js +38 -44
- package/dist/components/builder/blocks-model.js.map +1 -1
- package/dist/components/builder/blocks-panel.d.ts +8 -4
- package/dist/components/builder/blocks-panel.js +10 -9
- package/dist/components/builder/blocks-panel.js.map +1 -1
- package/dist/components/builder/builder-store.d.ts +104 -15
- package/dist/components/builder/builder-store.js +157 -32
- package/dist/components/builder/builder-store.js.map +1 -1
- package/dist/components/builder/canvas-body.d.ts +30 -0
- package/dist/components/builder/canvas-body.js +38 -0
- package/dist/components/builder/canvas-body.js.map +1 -0
- package/dist/components/builder/canvas-breakpoints.js +1 -1
- package/dist/components/builder/canvas-breakpoints.js.map +1 -1
- package/dist/components/builder/canvas-chrome-css.js +7 -0
- package/dist/components/builder/canvas-chrome-css.js.map +1 -1
- package/dist/components/builder/canvas-dnd.d.ts +9 -9
- package/dist/components/builder/canvas-dnd.js +6 -6
- package/dist/components/builder/canvas-dnd.js.map +1 -1
- package/dist/components/builder/canvas-inline-editing.d.ts +1 -1
- package/dist/components/builder/canvas-inline-editing.js.map +1 -1
- package/dist/components/builder/canvas-overlay-geometry.d.ts +1 -1
- package/dist/components/builder/canvas-overlay-geometry.js.map +1 -1
- package/dist/components/builder/canvas-render-context.d.ts +28 -20
- package/dist/components/builder/canvas-render-context.js +46 -47
- package/dist/components/builder/canvas-render-context.js.map +1 -1
- package/dist/components/builder/canvas-runs.d.ts +45 -0
- package/dist/components/builder/canvas-runs.js +51 -0
- package/dist/components/builder/canvas-runs.js.map +1 -0
- package/dist/components/builder/canvas-theme-notice.js +2 -2
- package/dist/components/builder/canvas-theme-notice.js.map +1 -1
- package/dist/components/builder/canvas.js +35 -8
- package/dist/components/builder/canvas.js.map +1 -1
- package/dist/components/builder/clipboard-actions.d.ts +16 -1
- package/dist/components/builder/clipboard-actions.js +48 -11
- package/dist/components/builder/clipboard-actions.js.map +1 -1
- package/dist/components/builder/composition-field-editor-lazy.js +1 -1
- package/dist/components/builder/composition-field-editor-lazy.js.map +1 -1
- package/dist/components/builder/composition-field-editor.d.ts +4 -16
- package/dist/components/builder/composition-field-editor.js +32 -43
- package/dist/components/builder/composition-field-editor.js.map +1 -1
- package/dist/components/builder/delete-key.d.ts +5 -0
- package/dist/components/builder/delete-key.js +4 -0
- package/dist/components/builder/delete-key.js.map +1 -1
- package/dist/components/builder/drag-session.d.ts +1 -1
- package/dist/components/builder/drag-session.js.map +1 -1
- package/dist/components/builder/fixture-thumbnail.js +1 -1
- package/dist/components/builder/fixture-thumbnail.js.map +1 -1
- package/dist/components/builder/insert-context.d.ts +18 -26
- package/dist/components/builder/insert-context.js +47 -94
- package/dist/components/builder/insert-context.js.map +1 -1
- package/dist/components/builder/layout-inspector.js +1 -1
- package/dist/components/builder/layout-inspector.js.map +1 -1
- package/dist/components/builder/node-title.js +1 -1
- package/dist/components/builder/node-title.js.map +1 -1
- package/dist/components/builder/page-tree.d.ts +4 -4
- package/dist/components/builder/page-tree.js +8 -8
- package/dist/components/builder/page-tree.js.map +1 -1
- package/dist/components/builder/placement-refusal.d.ts +18 -0
- package/dist/components/builder/placement-refusal.js +46 -0
- package/dist/components/builder/placement-refusal.js.map +1 -0
- package/dist/components/builder/property-inspector.js +8 -4
- package/dist/components/builder/property-inspector.js.map +1 -1
- package/dist/components/builder/reuse-context.js +68 -30
- package/dist/components/builder/reuse-context.js.map +1 -1
- package/dist/components/builder/reuse.d.ts +79 -1
- package/dist/components/builder/reuse.js +70 -3
- package/dist/components/builder/reuse.js.map +1 -1
- package/dist/components/builder/rich-text-toolbar.js +1 -1
- package/dist/components/builder/rich-text-toolbar.js.map +1 -1
- package/dist/components/builder/run-inspector.d.ts +11 -0
- package/dist/components/builder/run-inspector.js +33 -0
- package/dist/components/builder/run-inspector.js.map +1 -0
- package/dist/components/builder/selection-overlay.js +3 -3
- package/dist/components/builder/selection-overlay.js.map +1 -1
- package/dist/components/builder/spacing-handles.d.ts +2 -2
- package/dist/components/builder/spacing-handles.js +3 -3
- package/dist/components/builder/spacing-handles.js.map +1 -1
- package/dist/components/cms/asset-field.d.ts +38 -1
- package/dist/components/cms/asset-field.js +184 -60
- package/dist/components/cms/asset-field.js.map +1 -1
- package/dist/components/cms/asset-picker-core.d.ts +16 -0
- package/dist/components/cms/asset-picker-core.js +20 -0
- package/dist/components/cms/asset-picker-core.js.map +1 -1
- package/dist/components/cms/asset-picker.js +14 -20
- package/dist/components/cms/asset-picker.js.map +1 -1
- package/dist/components/cms/bulk-actions.d.ts +4 -20
- package/dist/components/cms/bulk-actions.js +5 -26
- package/dist/components/cms/bulk-actions.js.map +1 -1
- package/dist/components/cms/bulk-bar.d.ts +2 -2
- package/dist/components/cms/bulk-bar.js +2 -2
- package/dist/components/cms/bulk-bar.js.map +1 -1
- package/dist/components/cms/cell-edit.d.ts +1 -1
- package/dist/components/cms/cell-edit.js +1 -1
- package/dist/components/cms/cell-edit.js.map +1 -1
- package/dist/components/cms/cell.d.ts +1 -1
- package/dist/components/cms/cell.js +3 -3
- package/dist/components/cms/cell.js.map +1 -1
- package/dist/components/cms/cms-index.js +2 -2
- package/dist/components/cms/cms-index.js.map +1 -1
- package/dist/components/cms/cms-labels.js +2 -2
- package/dist/components/cms/cms-labels.js.map +1 -1
- package/dist/components/cms/cms-store.d.ts +7 -6
- package/dist/components/cms/cms-store.js +23 -43
- package/dist/components/cms/cms-store.js.map +1 -1
- package/dist/components/cms/collection-table.js +7 -7
- package/dist/components/cms/collection-table.js.map +1 -1
- package/dist/components/cms/detail-editor.d.ts +5 -2
- package/dist/components/cms/detail-editor.js +20 -15
- package/dist/components/cms/detail-editor.js.map +1 -1
- package/dist/components/cms/details-pane.js +1 -1
- package/dist/components/cms/details-pane.js.map +1 -1
- package/dist/components/cms/doc-state.js +1 -1
- package/dist/components/cms/doc-state.js.map +1 -1
- package/dist/components/cms/document-diff-view.d.ts +3 -1
- package/dist/components/cms/document-diff-view.js +17 -12
- package/dist/components/cms/document-diff-view.js.map +1 -1
- package/dist/components/cms/document-editor-context.js +1 -1
- package/dist/components/cms/document-editor-context.js.map +1 -1
- package/dist/components/cms/document-staging.js +1 -1
- package/dist/components/cms/document-staging.js.map +1 -1
- package/dist/components/cms/document-url-path.js +2 -2
- package/dist/components/cms/document-url-path.js.map +1 -1
- package/dist/components/cms/duplicate-document.d.ts +1 -1
- package/dist/components/cms/duplicate-document.js.map +1 -1
- package/dist/components/cms/editable-cell.js +3 -3
- package/dist/components/cms/editable-cell.js.map +1 -1
- package/dist/components/cms/field-anchors.d.ts +7 -4
- package/dist/components/cms/field-anchors.js +9 -7
- package/dist/components/cms/field-anchors.js.map +1 -1
- package/dist/components/cms/field-collaboration.d.ts +1 -1
- package/dist/components/cms/field-collaboration.js +1 -1
- package/dist/components/cms/field-collaboration.js.map +1 -1
- package/dist/components/cms/field-connection.d.ts +1 -1
- package/dist/components/cms/field-connection.js +2 -2
- package/dist/components/cms/field-connection.js.map +1 -1
- package/dist/components/cms/field-control-lib.d.ts +15 -1
- package/dist/components/cms/field-control-lib.js +23 -4
- package/dist/components/cms/field-control-lib.js.map +1 -1
- package/dist/components/cms/field-control.js +4 -4
- package/dist/components/cms/field-control.js.map +1 -1
- package/dist/components/cms/field-renderer.d.ts +1 -1
- package/dist/components/cms/field-renderer.js +2 -2
- package/dist/components/cms/field-renderer.js.map +1 -1
- package/dist/components/cms/filter-builder.d.ts +1 -1
- package/dist/components/cms/filter-builder.js +3 -3
- package/dist/components/cms/filter-builder.js.map +1 -1
- package/dist/components/cms/gate-validation.d.ts +21 -30
- package/dist/components/cms/gate-validation.js +11 -32
- package/dist/components/cms/gate-validation.js.map +1 -1
- package/dist/components/cms/import-export.js +2 -2
- package/dist/components/cms/import-export.js.map +1 -1
- package/dist/components/cms/issue-copy.js +1 -1
- package/dist/components/cms/issue-copy.js.map +1 -1
- package/dist/components/cms/issues-pane.js +3 -3
- package/dist/components/cms/issues-pane.js.map +1 -1
- package/dist/components/cms/object-array-field.js +6 -6
- package/dist/components/cms/object-array-field.js.map +1 -1
- package/dist/components/cms/prop-fields.d.ts +7 -2
- package/dist/components/cms/prop-fields.js +14 -7
- package/dist/components/cms/prop-fields.js.map +1 -1
- package/dist/components/cms/reference-confirm.d.ts +1 -1
- package/dist/components/cms/reference-confirm.js +2 -2
- package/dist/components/cms/reference-confirm.js.map +1 -1
- package/dist/components/cms/relation-picker.js +2 -2
- package/dist/components/cms/relation-picker.js.map +1 -1
- package/dist/components/cms/rich-text/block-style.js +1 -1
- package/dist/components/cms/rich-text/block-style.js.map +1 -1
- package/dist/components/cms/rich-text/component-embed.js +4 -4
- package/dist/components/cms/rich-text/component-embed.js.map +1 -1
- package/dist/components/cms/rich-text/editor.js +3 -3
- package/dist/components/cms/rich-text/editor.js.map +1 -1
- package/dist/components/cms/rich-text/embed-picker.d.ts +1 -1
- package/dist/components/cms/rich-text/embed-picker.js +3 -3
- package/dist/components/cms/rich-text/embed-picker.js.map +1 -1
- package/dist/components/cms/rich-text/embed-props.js +3 -3
- package/dist/components/cms/rich-text/embed-props.js.map +1 -1
- package/dist/components/cms/rich-text/extensions.js +1 -1
- package/dist/components/cms/rich-text/extensions.js.map +1 -1
- package/dist/components/cms/rich-text/font-size-control.d.ts +1 -1
- package/dist/components/cms/rich-text/font-size-control.js +2 -2
- package/dist/components/cms/rich-text/font-size-control.js.map +1 -1
- package/dist/components/cms/rich-text/insert.d.ts +20 -9
- package/dist/components/cms/rich-text/insert.js +25 -17
- package/dist/components/cms/rich-text/insert.js.map +1 -1
- package/dist/components/cms/rich-text/lazy.js +1 -1
- package/dist/components/cms/rich-text/lazy.js.map +1 -1
- package/dist/components/cms/rich-text/link-control.js +2 -2
- package/dist/components/cms/rich-text/link-control.js.map +1 -1
- package/dist/components/cms/rich-text/slash-command.js +3 -3
- package/dist/components/cms/rich-text/slash-command.js.map +1 -1
- package/dist/components/cms/rich-text/toolbar.js +1 -1
- package/dist/components/cms/rich-text/toolbar.js.map +1 -1
- package/dist/components/cms/rich-text/unknown-node.js +2 -2
- package/dist/components/cms/rich-text/unknown-node.js.map +1 -1
- package/dist/components/cms/rich-text-preview.js +2 -2
- package/dist/components/cms/rich-text-preview.js.map +1 -1
- package/dist/components/cms/row-actions.d.ts +1 -1
- package/dist/components/cms/row-actions.js.map +1 -1
- package/dist/components/cms/scalar-value-editor.d.ts +1 -1
- package/dist/components/cms/scalar-value-editor.js +2 -2
- package/dist/components/cms/scalar-value-editor.js.map +1 -1
- package/dist/components/cms/use-discard-controller.d.ts +1 -1
- package/dist/components/cms/use-discard-controller.js +1 -1
- package/dist/components/cms/use-discard-controller.js.map +1 -1
- package/dist/components/cms/use-document-draft.d.ts +7 -1
- package/dist/components/cms/use-document-draft.js +16 -2
- package/dist/components/cms/use-document-draft.js.map +1 -1
- package/dist/components/cms/use-document-versions.js +1 -1
- package/dist/components/cms/use-document-versions.js.map +1 -1
- package/dist/components/cms/use-field-focus.d.ts +1 -1
- package/dist/components/cms/use-field-focus.js +2 -2
- package/dist/components/cms/use-field-focus.js.map +1 -1
- package/dist/components/cms/use-live-url.d.ts +1 -1
- package/dist/components/cms/use-live-url.js +3 -3
- package/dist/components/cms/use-live-url.js.map +1 -1
- package/dist/components/cms/use-publish-controller.d.ts +2 -3
- package/dist/components/cms/use-publish-controller.js +8 -20
- package/dist/components/cms/use-publish-controller.js.map +1 -1
- package/dist/components/cms/use-published-perspective.d.ts +1 -1
- package/dist/components/cms/use-published-perspective.js.map +1 -1
- package/dist/components/cms/use-restore-version.d.ts +1 -1
- package/dist/components/cms/use-restore-version.js +1 -1
- package/dist/components/cms/use-restore-version.js.map +1 -1
- package/dist/components/cms/version-compare-lazy.js +1 -1
- package/dist/components/cms/version-compare-lazy.js.map +1 -1
- package/dist/components/cms/version-compare.js +1 -1
- package/dist/components/cms/version-compare.js.map +1 -1
- package/dist/components/cms/versions-pane.js +11 -4
- package/dist/components/cms/versions-pane.js.map +1 -1
- package/dist/components/cms/view-bar.js +3 -3
- package/dist/components/cms/view-bar.js.map +1 -1
- package/dist/components/collection-view/cms-collection-view-source.d.ts +1 -1
- package/dist/components/collection-view/cms-collection-view-source.js +5 -3
- package/dist/components/collection-view/cms-collection-view-source.js.map +1 -1
- package/dist/components/collection-view/collection-view-context.d.ts +1 -1
- package/dist/components/collection-view/collection-view-context.js +1 -1
- package/dist/components/collection-view/collection-view-context.js.map +1 -1
- package/dist/components/collection-view/collection-view-surface.d.ts +13 -0
- package/dist/components/collection-view/collection-view-surface.js +18 -0
- package/dist/components/collection-view/collection-view-surface.js.map +1 -0
- package/dist/components/components/component-detail.js +10 -10
- package/dist/components/components/component-detail.js.map +1 -1
- package/dist/components/components/component-preview.d.ts +2 -2
- package/dist/components/components/component-preview.js +5 -5
- package/dist/components/components/component-preview.js.map +1 -1
- package/dist/components/components/component-status.d.ts +1 -1
- package/dist/components/components/component-status.js +1 -1
- package/dist/components/components/component-status.js.map +1 -1
- package/dist/components/components/components-index.js +2 -2
- package/dist/components/components/components-index.js.map +1 -1
- package/dist/components/components/field-inspector.js +1 -1
- package/dist/components/components/field-inspector.js.map +1 -1
- package/dist/components/components/registry-browser.js +3 -3
- package/dist/components/components/registry-browser.js.map +1 -1
- package/dist/components/components/taxonomy.d.ts +1 -1
- package/dist/components/components/taxonomy.js.map +1 -1
- package/dist/components/components/transfer-dialog.js +4 -3
- package/dist/components/components/transfer-dialog.js.map +1 -1
- package/dist/components/components/transfer.d.ts +10 -10
- package/dist/components/components/transfer.js +13 -12
- package/dist/components/components/transfer.js.map +1 -1
- package/dist/components/current-user-menu.js +1 -1
- package/dist/components/current-user-menu.js.map +1 -1
- package/dist/components/delivery-health-banner.js +2 -2
- package/dist/components/delivery-health-banner.js.map +1 -1
- package/dist/components/deployment-setup-gate.js +3 -3
- package/dist/components/deployment-setup-gate.js.map +1 -1
- package/dist/components/dev-login-badge.js +2 -2
- package/dist/components/dev-login-badge.js.map +1 -1
- package/dist/components/guards.js +1 -1
- package/dist/components/guards.js.map +1 -1
- package/dist/components/inspector/document-panes.d.ts +54 -0
- package/dist/components/inspector/document-panes.js +41 -0
- package/dist/components/inspector/document-panes.js.map +1 -0
- package/dist/components/inspector/inspector-sidebar.js +1 -1
- package/dist/components/inspector/inspector-sidebar.js.map +1 -1
- package/dist/components/nav-issues-registry.d.ts +53 -0
- package/dist/components/nav-issues-registry.js +69 -0
- package/dist/components/nav-issues-registry.js.map +1 -0
- package/dist/components/nav-issues.js +4 -4
- package/dist/components/nav-issues.js.map +1 -1
- package/dist/components/operation-error.d.ts +1 -1
- package/dist/components/operation-error.js +4 -4
- package/dist/components/operation-error.js.map +1 -1
- package/dist/components/playground-badge.js +2 -2
- package/dist/components/playground-badge.js.map +1 -1
- package/dist/components/project-list.js +3 -3
- package/dist/components/project-list.js.map +1 -1
- package/dist/components/project-session.d.ts +2 -2
- package/dist/components/project-session.js +5 -5
- package/dist/components/project-session.js.map +1 -1
- package/dist/components/project-switcher.js +2 -2
- package/dist/components/project-switcher.js.map +1 -1
- package/dist/components/rail-store-status.js +1 -1
- package/dist/components/rail-store-status.js.map +1 -1
- package/dist/components/rail-view-source.d.ts +6 -5
- package/dist/components/rail-view-source.js +4 -3
- package/dist/components/rail-view-source.js.map +1 -1
- package/dist/components/records/record-lens.d.ts +51 -0
- package/dist/components/records/record-lens.js +156 -0
- package/dist/components/records/record-lens.js.map +1 -0
- package/dist/components/records/record-rail.d.ts +86 -0
- package/dist/components/records/record-rail.js +241 -0
- package/dist/components/records/record-rail.js.map +1 -0
- package/dist/components/redirects/redirects-schema.js +1 -1
- package/dist/components/redirects/redirects-schema.js.map +1 -1
- package/dist/components/references/references-panel.js +2 -2
- package/dist/components/references/references-panel.js.map +1 -1
- package/dist/components/section-nav.js +27 -11
- package/dist/components/section-nav.js.map +1 -1
- package/dist/components/section-state.js +2 -2
- package/dist/components/section-state.js.map +1 -1
- package/dist/components/section-surface.js +2 -2
- package/dist/components/section-surface.js.map +1 -1
- package/dist/components/session-expired-overlay.js +3 -3
- package/dist/components/session-expired-overlay.js.map +1 -1
- package/dist/components/settings-form.js +1 -1
- package/dist/components/settings-form.js.map +1 -1
- package/dist/components/sign-in-needed-badge.js +1 -1
- package/dist/components/sign-in-needed-badge.js.map +1 -1
- package/dist/components/sign-in-view.d.ts +2 -2
- package/dist/components/sign-in-view.js +5 -5
- package/dist/components/sign-in-view.js.map +1 -1
- package/dist/components/studio-context.js +3 -3
- package/dist/components/studio-context.js.map +1 -1
- package/dist/components/studio-project-screen.js +30 -3
- package/dist/components/studio-project-screen.js.map +1 -1
- package/dist/components/studio-shell.js +6 -5
- package/dist/components/studio-shell.js.map +1 -1
- package/dist/components/theme-context.d.ts +1 -1
- package/dist/components/theme-context.js +2 -2
- package/dist/components/theme-context.js.map +1 -1
- package/dist/components/use-hydration-phase.d.ts +1 -1
- package/dist/components/use-hydration-phase.js +1 -1
- package/dist/components/use-hydration-phase.js.map +1 -1
- package/dist/components/users/invite-user-bar.d.ts +27 -0
- package/dist/components/users/invite-user-bar.js +25 -9
- package/dist/components/users/invite-user-bar.js.map +1 -1
- package/dist/components/users/role-policy-drift-issues.d.ts +38 -0
- package/dist/components/users/role-policy-drift-issues.js +71 -0
- package/dist/components/users/role-policy-drift-issues.js.map +1 -0
- package/dist/components/users/users-collection-view-source.d.ts +1 -1
- package/dist/components/users/users-collection-view-source.js +4 -2
- package/dist/components/users/users-collection-view-source.js.map +1 -1
- package/dist/components/users/users-module.js +1 -1
- package/dist/components/users/users-module.js.map +1 -1
- package/dist/components/users/users-schema.d.ts +10 -1
- package/dist/components/users/users-schema.js +45 -25
- package/dist/components/users/users-schema.js.map +1 -1
- package/dist/components/users/users-store.js +3 -3
- package/dist/components/users/users-store.js.map +1 -1
- package/dist/components/workspace-projects.d.ts +1 -1
- package/dist/components/workspace-projects.js +1 -1
- package/dist/components/workspace-projects.js.map +1 -1
- package/dist/lib/ai/apply-content.js +56 -116
- package/dist/lib/ai/apply-content.js.map +1 -1
- package/dist/lib/ai/chat-context.d.ts +8 -0
- package/dist/lib/ai/chat-context.js +37 -6
- package/dist/lib/ai/chat-context.js.map +1 -1
- package/dist/lib/ai/chat-state.d.ts +6 -0
- package/dist/lib/ai/chat-state.js.map +1 -1
- package/dist/lib/ai/constraints.d.ts +20 -5
- package/dist/lib/ai/constraints.js +125 -33
- package/dist/lib/ai/constraints.js.map +1 -1
- package/dist/lib/ai/fake-provider.d.ts +16 -3
- package/dist/lib/ai/fake-provider.js +22 -6
- package/dist/lib/ai/fake-provider.js.map +1 -1
- package/dist/lib/ai/index.d.ts +3 -1
- package/dist/lib/ai/index.js +3 -1
- package/dist/lib/ai/index.js.map +1 -1
- package/dist/lib/ai/leash.d.ts +34 -0
- package/dist/lib/ai/leash.js +28 -0
- package/dist/lib/ai/leash.js.map +1 -0
- package/dist/lib/ai/node-handler.d.ts +1 -1
- package/dist/lib/ai/node-handler.js.map +1 -1
- package/dist/lib/ai/rich-text-input.d.ts +8 -8
- package/dist/lib/ai/rich-text-input.js +2 -1
- package/dist/lib/ai/rich-text-input.js.map +1 -1
- package/dist/lib/ai/rich-text-translate.d.ts +27 -4
- package/dist/lib/ai/rich-text-translate.js +28 -10
- package/dist/lib/ai/rich-text-translate.js.map +1 -1
- package/dist/lib/ai/shape.d.ts +13 -13
- package/dist/lib/ai/shape.js +69 -93
- package/dist/lib/ai/shape.js.map +1 -1
- package/dist/lib/ai/tools.d.ts +36 -41
- package/dist/lib/ai/tools.js +83 -87
- package/dist/lib/ai/tools.js.map +1 -1
- package/dist/lib/ai/translate-document.d.ts +32 -4
- package/dist/lib/ai/translate-document.js +145 -47
- package/dist/lib/ai/translate-document.js.map +1 -1
- package/dist/lib/ai/types.d.ts +34 -23
- package/dist/lib/ai/types.js.map +1 -1
- package/dist/lib/ai/update-fields.d.ts +15 -12
- package/dist/lib/ai/update-fields.js +112 -188
- package/dist/lib/ai/update-fields.js.map +1 -1
- package/dist/lib/ai/writer-form.d.ts +116 -0
- package/dist/lib/ai/writer-form.js +429 -0
- package/dist/lib/ai/writer-form.js.map +1 -0
- package/dist/lib/assets/upload.d.ts +31 -2
- package/dist/lib/assets/upload.js +61 -6
- package/dist/lib/assets/upload.js.map +1 -1
- package/dist/lib/clipboard/os-clipboard.d.ts +10 -2
- package/dist/lib/clipboard/os-clipboard.js +8 -4
- package/dist/lib/clipboard/os-clipboard.js.map +1 -1
- package/dist/lib/clipboard/paste-issues.js +1 -1
- package/dist/lib/clipboard/paste-issues.js.map +1 -1
- package/dist/lib/clipboard/paste-target.d.ts +18 -9
- package/dist/lib/clipboard/paste-target.js +10 -9
- package/dist/lib/clipboard/paste-target.js.map +1 -1
- package/dist/lib/cms/field-patch.d.ts +0 -10
- package/dist/lib/cms/field-patch.js +4 -30
- package/dist/lib/cms/field-patch.js.map +1 -1
- package/dist/lib/cms/published-documents.js +1 -1
- package/dist/lib/cms/published-documents.js.map +1 -1
- package/dist/lib/cms-views/view-model.d.ts +9 -9
- package/dist/lib/cms-views/view-model.js +2 -2
- package/dist/lib/cms-views/view-model.js.map +1 -1
- package/dist/lib/collection-view/collection-view-source.d.ts +14 -5
- package/dist/lib/collection-view/collection-view-source.js.map +1 -1
- package/dist/lib/data/collection-content.js +3 -2
- package/dist/lib/data/collection-content.js.map +1 -1
- package/dist/lib/data/content-session-refresh.d.ts +1 -1
- package/dist/lib/data/content-session-refresh.js.map +1 -1
- package/dist/lib/data/playground-unload-guard.d.ts +1 -1
- package/dist/lib/data/playground-unload-guard.js +1 -1
- package/dist/lib/data/playground-unload-guard.js.map +1 -1
- package/dist/lib/dnd/drop-rules.d.ts +25 -31
- package/dist/lib/dnd/drop-rules.js +53 -59
- package/dist/lib/dnd/drop-rules.js.map +1 -1
- package/dist/lib/engine-version.d.ts +1 -1
- package/dist/lib/engine-version.js +1 -1
- package/dist/lib/engine-version.js.map +1 -1
- package/dist/lib/i18n/i18n.js +1 -1
- package/dist/lib/i18n/i18n.js.map +1 -1
- package/dist/lib/i18n/messages.de.js +44 -3
- package/dist/lib/i18n/messages.de.js.map +1 -1
- package/dist/lib/i18n/messages.en.d.ts +38 -2
- package/dist/lib/i18n/messages.en.js +51 -3
- package/dist/lib/i18n/messages.en.js.map +1 -1
- package/dist/lib/operations/project-runtime-config.d.ts +34 -6
- package/dist/lib/operations/project-runtime-config.js +50 -10
- package/dist/lib/operations/project-runtime-config.js.map +1 -1
- package/dist/lib/operations/publish-with-revision.d.ts +33 -1
- package/dist/lib/operations/publish-with-revision.js +32 -0
- package/dist/lib/operations/publish-with-revision.js.map +1 -1
- package/dist/lib/operations/runtime-validator.d.ts +19 -68
- package/dist/lib/operations/runtime-validator.js +12 -138
- package/dist/lib/operations/runtime-validator.js.map +1 -1
- package/dist/lib/operations/studio-operations.d.ts +17 -2
- package/dist/lib/operations/studio-operations.js +18 -11
- package/dist/lib/operations/studio-operations.js.map +1 -1
- package/dist/lib/persistence/convex-adapter.d.ts +16 -3
- package/dist/lib/persistence/convex-adapter.js +83 -47
- package/dist/lib/persistence/convex-adapter.js.map +1 -1
- package/dist/lib/persistence/index.js +14 -1
- package/dist/lib/persistence/index.js.map +1 -1
- package/dist/lib/persona/labels.js +4 -2
- package/dist/lib/persona/labels.js.map +1 -1
- package/dist/lib/query/operations-query.d.ts +1 -1
- package/dist/lib/query/operations-query.js +1 -1
- package/dist/lib/query/operations-query.js.map +1 -1
- package/dist/lib/references/reference-model.js +1 -1
- package/dist/lib/references/reference-model.js.map +1 -1
- package/dist/lib/references/references-store.js +1 -1
- package/dist/lib/references/references-store.js.map +1 -1
- package/dist/lib/workspace/index.d.ts +14 -11
- package/dist/lib/workspace/index.js +20 -24
- package/dist/lib/workspace/index.js.map +1 -1
- package/dist/mount.d.ts +28 -0
- package/dist/mount.js +27 -0
- package/dist/mount.js.map +1 -0
- package/dist/router/root.js +10 -10
- package/dist/router/root.js.map +1 -1
- package/dist/ui/badge.d.ts +1 -1
- package/dist/ui/button.d.ts +2 -2
- package/package.json +15 -11
- package/dist/components/cms/cms-composition-validators.d.ts +0 -16
- package/dist/components/cms/cms-composition-validators.js +0 -133
- package/dist/components/cms/cms-composition-validators.js.map +0 -1
- package/dist/components/cms/publish-clean.d.ts +0 -28
- package/dist/components/cms/publish-clean.js +0 -38
- package/dist/components/cms/publish-clean.js.map +0 -1
- package/dist/components/redirects/redirects-collection-view-source.d.ts +0 -53
- package/dist/components/redirects/redirects-collection-view-source.js +0 -146
- package/dist/components/redirects/redirects-collection-view-source.js.map +0 -1
- package/dist/components/redirects/redirects-module.d.ts +0 -21
- package/dist/components/redirects/redirects-module.js +0 -40
- package/dist/components/redirects/redirects-module.js.map +0 -1
- package/dist/components/redirects/redirects-store.d.ts +0 -68
- package/dist/components/redirects/redirects-store.js +0 -132
- package/dist/components/redirects/redirects-store.js.map +0 -1
- package/dist/lib/assets/asset-placements.d.ts +0 -28
- package/dist/lib/assets/asset-placements.js +0 -83
- package/dist/lib/assets/asset-placements.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { cn } from '
|
|
3
|
-
import { cmsIssueLabel, cmsValidationIssueMessage } from '
|
|
2
|
+
import { cn } from '../../lib/utils.js';
|
|
3
|
+
import { cmsIssueLabel, cmsValidationIssueMessage } from '../../lib/persona/labels.js';
|
|
4
4
|
import { Row, Stack, Text } from '@elytracms/studio/ui';
|
|
5
5
|
import { issueSeverityIcon, issueSeverityStyle, orderBySeverity } from '../issue-severity.js';
|
|
6
6
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proposal-issues.js","sourceRoot":"","sources":["../../../src/components/ai/proposal-issues.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,EAAE,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"proposal-issues.js","sourceRoot":"","sources":["../../../src/components/ai/proposal-issues.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,EAAE,EAAE,MAAM,iBAAiB,CAAA;AACpC,OAAO,EAAE,aAAa,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAA;AACnF,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAA;AACvD,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAE1F;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,qBAAqB,CACnC,MAAqC;IAErC,OAAO,eAAe,CAAC,MAAM,CAAC,CAAA;AAChC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,EAC7B,MAAM,EACN,SAAS,GAIV;IACC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IACpC,OAAO,CACL,KAAC,KAAK,IAAC,GAAG,EAAC,GAAG,EAAC,SAAS,EAAE,SAAS,iBAAc,iBAAiB,YAC/D,qBAAqB,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CACnD,KAAC,iBAAiB,IAEhB,KAAK,EAAE,KAAK,IADP,GAAG,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,KAAK,EAAE,CAErD,CACH,CAAC,GACI,CACT,CAAA;AACH,CAAC;AAED,qEAAqE;AACrE,SAAS,iBAAiB,CAAC,EAAE,KAAK,EAAiC;IACjE,MAAM,KAAK,GAAG,kBAAkB,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAA;IAChD,MAAM,IAAI,GAAG,iBAAiB,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAA;IAC9C,OAAO,CACL,MAAC,GAAG,IACF,GAAG,EAAC,GAAG,EACP,KAAK,EAAC,OAAO,EACb,SAAS,EAAE,EAAE,CAAC,+BAA+B,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,kCACxC,KAAK,CAAC,QAAQ,aAE5C,KAAC,IAAI,IAAC,SAAS,EAAC,0BAA0B,wBAAe,EACzD,MAAC,KAAK,IAAC,GAAG,EAAC,GAAG,EAAC,SAAS,EAAC,SAAS,aAChC,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,MAAM,EAAC,UAAU,EAAC,KAAK,EAAE,KAAK,CAAC,IAAI,YAChD,aAAa,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,QAAQ,CAAC,GACrC,EACP,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,SAAS,EAAC,sBAAsB,YAC7C,yBAAyB,CAAC,KAAK,CAAC,GAC5B,IACD,IACJ,CACP,CAAA;AACH,CAAC","sourcesContent":["import type { CmsValidationIssue } from '@elytracms/core/cms-core'\nimport { cn } from '../../lib/utils'\nimport { cmsIssueLabel, cmsValidationIssueMessage } from '../../lib/persona/labels'\nimport { Row, Stack, Text } from '@elytracms/studio/ui'\nimport { issueSeverityIcon, issueSeverityStyle, orderBySeverity } from '../issue-severity'\n\n/**\n * EC-496 — the findings the engine attached to an AI proposal, rendered on the\n * review surface.\n *\n * A proposal carries the non-error issues its content produced\n * (`ChatProposal.issues`): the repair loop and the EC-473 gate block on errors the\n * change INTRODUCES, and everything else — an EC-472 implausible year, an EC-390\n * localized-plain-value note, an unpublished relation target, a nesting-depth\n * warning — deliberately survives into the proposal. Until this ticket nothing\n * read the field, so the one consumer of a warning-severity finding on the AI path\n * was a UI that ignored it: an editor pressed **Apply as draft** without ever being\n * told the engine had flagged the content.\n *\n * The treatment is the studio's ONE severity vocabulary, not a third one: the\n * palette and glyph come from {@link issueSeverityStyle} / {@link issueSeverityIcon}\n * in `components/issue-severity.ts` — the pure cores EC-493 first extracted from the\n * document Issues pane, which EC-507 then moved out of the section rail into a home\n * neither surface owns (importing them from `nav-issues` was how this card ended up\n * depending on a `nav`-prefixed core). The card copy follows the pane too: the plain-language\n * {@link cmsIssueLabel} as the title with the raw `code` in `title=` for engineers\n * (the EC-192 persona boundary), and the localized body via\n * {@link cmsValidationIssueMessage} (EC-487's `messageRef` carriage, degrading to the\n * producer's English for a ref-less issue).\n *\n * `data-proposal-issue-severity` on each card is the machine-readable form of the\n * same distinction — what the smoke test pins, and what an agent inspecting the live\n * DOM reads without decoding Tailwind classes.\n */\n\n/**\n * The proposal's findings in render order: **errors first**, warnings after, stable\n * within each severity.\n *\n * The card's primary action is *Apply as draft*, and the reader scans top-down — so\n * the finding that should give pause must not sit below one that should merely\n * inform. That is the shared rank rule (`error` outranks `warning`), so since EC-507\n * this delegates to {@link orderBySeverity} rather than re-expressing it: the rail's\n * `highestIssueSeverity` is the same rule reduced to one aggregate glyph.\n *\n * Errors are not *expected* here — all three producers filter to non-error findings\n * (`apply-content.ts:259`, `update-fields.ts:442`, `tools.ts:764`) — but\n * `ChatProposal.issues` is typed `CmsValidationIssue[]`, so this surface renders\n * whatever it is handed, truthfully. See the apply-gate note in `proposal-card.tsx`.\n */\nexport function orderedProposalIssues(\n issues: readonly CmsValidationIssue[],\n): CmsValidationIssue[] {\n return orderBySeverity(issues)\n}\n\n/**\n * The proposal's findings, severity-tinted. Renders NOTHING for an empty list — a\n * clean proposal gets no issue affordance at all, never an empty box captioned\n * \"no issues\" (the card is a narrow sidebar surface; silence is the correct\n * negative).\n */\nexport function ProposalIssues({\n issues,\n className,\n}: {\n issues: readonly CmsValidationIssue[]\n className?: string\n}) {\n if (issues.length === 0) return null\n return (\n <Stack gap=\"1\" className={className} data-testid=\"proposal-issues\">\n {orderedProposalIssues(issues).map((issue, index) => (\n <ProposalIssueCard\n key={`${issue.code}:${issue.path.join('/')}:${index}`}\n issue={issue}\n />\n ))}\n </Stack>\n )\n}\n\n/** One finding on the proposal, tinted + glyphed by its severity. */\nfunction ProposalIssueCard({ issue }: { issue: CmsValidationIssue }) {\n const style = issueSeverityStyle(issue.severity)\n const Icon = issueSeverityIcon(issue.severity)\n return (\n <Row\n gap=\"2\"\n align=\"start\"\n className={cn('rounded-md border px-2.5 py-2', style.card, style.text)}\n data-proposal-issue-severity={issue.severity}\n >\n <Icon className=\"mt-0.5 size-3.5 shrink-0\" aria-hidden />\n <Stack gap=\"1\" className=\"min-w-0\">\n <Text size=\"xs\" weight=\"semibold\" title={issue.code}>\n {cmsIssueLabel(issue.code, issue.severity)}\n </Text>\n <Text size=\"xs\" className=\"min-w-0 leading-snug\">\n {cmsValidationIssueMessage(issue)}\n </Text>\n </Stack>\n </Row>\n )\n}\n"]}
|
|
@@ -3,8 +3,8 @@ import { Suspense, lazy, useMemo, useState } from 'react';
|
|
|
3
3
|
import { Languages } from 'lucide-react';
|
|
4
4
|
import { isFieldLocalized } from '@elytracms/core/cms-core';
|
|
5
5
|
import { Button } from '@elytracms/studio/ui';
|
|
6
|
-
import { translate } from '
|
|
7
|
-
import { resolveDeploymentAi } from '
|
|
6
|
+
import { translate } from '../../lib/i18n/index.js';
|
|
7
|
+
import { resolveDeploymentAi } from '../../lib/workspace/index.js';
|
|
8
8
|
/**
|
|
9
9
|
* The "Translate document" header action (EC-332), gate side.
|
|
10
10
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"translate-document-action.js","sourceRoot":"","sources":["../../../src/components/ai/translate-document-action.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AACzD,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AACxC,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AAE3D,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAA;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"translate-document-action.js","sourceRoot":"","sources":["../../../src/components/ai/translate-document-action.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AACzD,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AACxC,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AAE3D,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAA;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AAEzD;;;;;;;;;;GAUG;AACH,MAAM,uBAAuB,GAAG,IAAI,CAAC,GAAG,EAAE,CACxC,MAAM,CAAC,6BAA6B,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,uBAAuB,EAAE,CAAC,CAAC,CAC5F,CAAA;AAqBD,MAAM,UAAU,uBAAuB,CAAC,KAAmC;IACzE,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IACvC,MAAM,EAAE,UAAU,EAAE,YAAY,EAAE,cAAc,EAAE,SAAS,EAAE,GAAG,KAAK,CAAA;IAErE,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,EAAE;QAC5B,MAAM,EAAE,GAAG,mBAAmB,EAAE,CAAA;QAChC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,OAAO,KAAK,KAAK;YAAE,OAAO,KAAK,CAAA;QAC7C,IAAI,CAAC,cAAc,IAAI,SAAS,KAAK,SAAS;YAAE,OAAO,KAAK,CAAA;QAC5D,MAAM,IAAI,GAAG,YAAY,CAAC,OAAO,CAAA;QACjC,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC;YAAE,OAAO,KAAK,CAAA;QAC/D,OAAO,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,gBAAgB,CAAC,UAAU,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAA;IAC5E,CAAC,EAAE,CAAC,UAAU,EAAE,YAAY,EAAE,cAAc,EAAE,SAAS,CAAC,CAAC,CAAA;IAEzD,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAA;IAE1B,OAAO,CACL,8BACE,MAAC,MAAM,IACL,IAAI,EAAC,IAAI,EACT,OAAO,EAAC,SAAS,EACjB,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,iBAChB,eAAe,EAC3B,KAAK,EAAE,SAAS,CAAC,sBAAsB,CAAC,aAExC,KAAC,SAAS,KAAG,OAAE,SAAS,CAAC,qBAAqB,CAAC,IACxC,EACR,IAAI,CAAC,CAAC,CAAC,CACN,KAAC,QAAQ,IAAC,QAAQ,EAAE,IAAI,YACtB,KAAC,uBAAuB,OAAK,KAAK,EAAE,IAAI,QAAC,YAAY,EAAE,OAAO,GAAI,GACzD,CACZ,CAAC,CAAC,CAAC,IAAI,IACP,CACJ,CAAA;AACH,CAAC","sourcesContent":["import { Suspense, lazy, useMemo, useState } from 'react'\nimport { Languages } from 'lucide-react'\nimport { isFieldLocalized } from '@elytracms/core/cms-core'\nimport type { CmsDocument, CollectionDef, DocumentRef, LocaleConfig } from '@elytracms/core/cms-core'\nimport { Button } from '@elytracms/studio/ui'\nimport { translate } from '../../lib/i18n'\nimport { resolveDeploymentAi } from '../../lib/workspace'\n\n/**\n * The \"Translate document\" header action (EC-332), gate side.\n *\n * Rendered ALWAYS in the detail-editor header but returns null unless the studio is\n * `ai`-configured (EC-331 presence-enable), the collection is localized with a real\n * localized field, and there is a target locale to fill. Like the AI panel toggle, the\n * gate reads only the config object + pure core helpers — it never statically imports\n * the AI engine (`@elytracms/studio/lib/ai`) or the AI SDK. The heavy dialog (which runs\n * the translation engine + operations write) loads ONLY when the editor opens it, via a\n * dynamic `import()`, so an unconfigured studio pulls none of it onto the critical path.\n */\nconst TranslateDocumentDialog = lazy(() =>\n import('./translate-document-dialog').then((m) => ({ default: m.TranslateDocumentDialog })),\n)\n\nexport interface TranslateDocumentActionProps {\n projectId: string | undefined\n collection: CollectionDef\n docRef: DocumentRef\n /** The live editor draft — the translation source + the target for the fills. */\n document: CmsDocument\n localeConfig: LocaleConfig\n /** Whether the collection view exposes per-locale editing (`source.capabilities.locales`). */\n localesEnabled: boolean\n /**\n * EC-595 A2 — the FORCED FLUSH before the AI reads the draft (the ruling: \"a forced\n * flush before … an AI proposal\"): the editor commits its uncommitted fields and the\n * persister lands them, so the translation's patches are made on the committed draft.\n * The translated buckets reach the open editor through the session (it rebases), so\n * there is no draft to hand back.\n */\n flushDraft: () => Promise<void>\n}\n\nexport function TranslateDocumentAction(props: TranslateDocumentActionProps) {\n const [open, setOpen] = useState(false)\n const { collection, localeConfig, localesEnabled, projectId } = props\n\n const eligible = useMemo(() => {\n const ai = resolveDeploymentAi()\n if (!ai || ai.enabled === false) return false\n if (!localesEnabled || projectId === undefined) return false\n const base = localeConfig.default\n if (!localeConfig.locales.some((l) => l !== base)) return false\n return collection.fields.some((f) => isFieldLocalized(collection, f.name))\n }, [collection, localeConfig, localesEnabled, projectId])\n\n if (!eligible) return null\n\n return (\n <>\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={() => setOpen(true)}\n data-testid=\"doc-translate\"\n title={translate('ai.translate.tooltip')}\n >\n <Languages /> {translate('ai.translate.action')}\n </Button>\n {open ? (\n <Suspense fallback={null}>\n <TranslateDocumentDialog {...props} open onOpenChange={setOpen} />\n </Suspense>\n ) : null}\n </>\n )\n}\n"]}
|
|
@@ -3,13 +3,14 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
|
3
3
|
import { createCollectionRegistry } from '@elytracms/core/cms-core';
|
|
4
4
|
import { Check, Languages, Loader2, X } from 'lucide-react';
|
|
5
5
|
import { Badge, Button, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, Row, Stack, Text, } from '@elytracms/studio/ui';
|
|
6
|
-
import { translate } from '
|
|
7
|
-
import { localeChipLabel } from '
|
|
8
|
-
import { getStudioOperations } from '
|
|
9
|
-
import { resolveDeploymentAi } from '
|
|
10
|
-
import { resolveAiConfig, resolveKnowledge, translateDocument } from '
|
|
11
|
-
import { cmsValidationIssueMessage } from '
|
|
6
|
+
import { translate } from '../../lib/i18n/index.js';
|
|
7
|
+
import { localeChipLabel } from '../../lib/locale/index.js';
|
|
8
|
+
import { getStudioOperations } from '../../lib/operations/index.js';
|
|
9
|
+
import { resolveDeploymentAi } from '../../lib/workspace/index.js';
|
|
10
|
+
import { resolveAiConfig, resolveKnowledge, translateDocument } from '../../lib/ai/index.js';
|
|
11
|
+
import { cmsValidationIssueMessage } from '../../lib/persona/labels.js';
|
|
12
12
|
import { useCms } from '../cms/cms-store.js';
|
|
13
|
+
import { TranslateSkipped } from './translate-skipped.js';
|
|
13
14
|
export function TranslateDocumentDialog({ projectId, collection, docRef, document, localeConfig, flushDraft, open, onOpenChange, }) {
|
|
14
15
|
const [phase, setPhase] = useState('confirm');
|
|
15
16
|
const [progress, setProgress] = useState(null);
|
|
@@ -106,7 +107,8 @@ export function TranslateDocumentDialog({ projectId, collection, docRef, documen
|
|
|
106
107
|
slugs: result.slugsDerived,
|
|
107
108
|
locales: Object.keys(result.perLocale).length,
|
|
108
109
|
cancelled: result.cancelled,
|
|
109
|
-
nothing: result.total === 0,
|
|
110
|
+
nothing: result.total === 0 && result.skipped.length === 0,
|
|
111
|
+
skipped: result.skipped,
|
|
110
112
|
});
|
|
111
113
|
setPhase('done');
|
|
112
114
|
}
|
|
@@ -131,6 +133,6 @@ export function TranslateDocumentDialog({ projectId, collection, docRef, documen
|
|
|
131
133
|
};
|
|
132
134
|
return (_jsx(Dialog, { open: open, onOpenChange: (next) => (!next ? close() : undefined), children: _jsxs(DialogContent, { children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: _jsxs(Row, { gap: "2", align: "center", children: [_jsx(Languages, { className: "size-4" }), " ", translate('ai.translate.dialogTitle')] }) }), _jsx(DialogDescription, { children: translate('ai.translate.dialogDescription', { base: localeChipLabel(base) }) })] }), phase === 'confirm' ? (_jsxs(Stack, { gap: "4", className: "py-2", children: [_jsxs(Stack, { gap: "1", children: [_jsx(Text, { size: "xs", weight: "medium", tone: "muted", children: translate('ai.translate.targetsLabel') }), _jsx(Row, { gap: "1", wrap: true, children: targets.map((l) => (_jsx(Badge, { variant: "outline", children: localeChipLabel(l) }, l))) })] }), _jsxs(Stack, { gap: "1", children: [_jsx(Text, { size: "xs", weight: "medium", tone: "muted", children: translate('ai.translate.scopeLabel') }), _jsxs(Row, { gap: "2", align: "center", children: [_jsx(Badge, { variant: "default", children: translate('ai.translate.fillMissing') }), _jsx(Button, { size: "sm", variant: "ghost", disabled: true, title: translate('ai.translate.retranslateSoon'), children: translate('ai.translate.retranslateAll') }), _jsx(Text, { size: "xs", tone: "muted", children: translate('ai.translate.retranslateSoon') })] })] })] })) : null, phase === 'running' ? (_jsxs(Stack, { gap: "3", align: "center", className: "py-6", children: [_jsx(Loader2, { className: "size-6 animate-spin text-primary" }), _jsx(Text, { size: "sm", tone: "muted", "data-testid": "translate-progress", children: progress && progress.total > 0
|
|
133
135
|
? translate('ai.translate.progress', { completed: progress.completed, total: progress.total })
|
|
134
|
-
: translate('ai.translate.preparing') }), progress && progress.total > 0 ? (_jsx("div", { className: "h-1.5 w-56 overflow-hidden rounded-full bg-muted", children: _jsx("div", { className: "h-full rounded-full bg-primary transition-all", style: { width: `${Math.round((progress.completed / progress.total) * 100)}%` } }) })) : null] })) : null, phase === 'done' && summary ? (_jsxs(Stack, { gap: "2", align: "center", className: "py-6", children: [_jsx(Check, { className: "size-6 text-success" }), _jsx(Text, { size: "sm", tone: "muted", className: "text-center", "data-testid": "translate-summary", children: doneMessage(summary) })] })) : null, phase === 'error' ? (_jsxs(Stack, { gap: "2", align: "center", className: "py-6", children: [_jsx(X, { className: "size-6 text-destructive" }), _jsx(Text, { size: "sm", tone: "muted", className: "text-center", "data-testid": "translate-error", children: refusal ?? translate('ai.translate.failed') })] })) : null, _jsx(DialogFooter, { className: "mt-2", children: phase === 'confirm' ? (_jsxs(_Fragment, { children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: close, children: translate('common.cancel') }), _jsxs(Button, { size: "sm", onClick: () => void run(), "data-testid": "translate-confirm", children: [_jsx(Languages, {}), " ", translate('ai.translate.confirm')] })] })) : phase === 'running' ? (_jsx(Button, { variant: "outline", size: "sm", onClick: () => abortRef.current?.abort(), "data-testid": "translate-cancel", children: translate('common.cancel') })) : (_jsx(Button, { size: "sm", onClick: close, "data-testid": "translate-close", children: translate('ai.translate.close') })) })] }) }));
|
|
136
|
+
: translate('ai.translate.preparing') }), progress && progress.total > 0 ? (_jsx("div", { className: "h-1.5 w-56 overflow-hidden rounded-full bg-muted", children: _jsx("div", { className: "h-full rounded-full bg-primary transition-all", style: { width: `${Math.round((progress.completed / progress.total) * 100)}%` } }) })) : null] })) : null, phase === 'done' && summary ? (_jsxs(Stack, { gap: "2", align: "center", className: "py-6", children: [_jsx(Check, { className: "size-6 text-success" }), _jsx(Text, { size: "sm", tone: "muted", className: "text-center", "data-testid": "translate-summary", children: doneMessage(summary) }), _jsx(TranslateSkipped, { skipped: summary.skipped, collection: collection })] })) : null, phase === 'error' ? (_jsxs(Stack, { gap: "2", align: "center", className: "py-6", children: [_jsx(X, { className: "size-6 text-destructive" }), _jsx(Text, { size: "sm", tone: "muted", className: "text-center", "data-testid": "translate-error", children: refusal ?? translate('ai.translate.failed') })] })) : null, _jsx(DialogFooter, { className: "mt-2", children: phase === 'confirm' ? (_jsxs(_Fragment, { children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: close, children: translate('common.cancel') }), _jsxs(Button, { size: "sm", onClick: () => void run(), "data-testid": "translate-confirm", children: [_jsx(Languages, {}), " ", translate('ai.translate.confirm')] })] })) : phase === 'running' ? (_jsx(Button, { variant: "outline", size: "sm", onClick: () => abortRef.current?.abort(), "data-testid": "translate-cancel", children: translate('common.cancel') })) : (_jsx(Button, { size: "sm", onClick: close, "data-testid": "translate-close", children: translate('ai.translate.close') })) })] }) }));
|
|
135
137
|
}
|
|
136
138
|
//# sourceMappingURL=translate-document-dialog.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"translate-document-dialog.js","sourceRoot":"","sources":["../../../src/components/ai/translate-document-dialog.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport { createCollectionRegistry } from '@elytracms/core/cms-core'\nimport { Check, Languages, Loader2, X } from 'lucide-react'\nimport {\n Badge,\n Button,\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n Row,\n Stack,\n Text,\n} from '@elytracms/studio/ui'\nimport { translate } from '@elytracms/studio/lib/i18n'\nimport { localeChipLabel } from '@elytracms/studio/lib/locale'\nimport { getStudioOperations } from '@elytracms/studio/lib/operations'\nimport { resolveDeploymentAi } from '@elytracms/studio/lib/workspace'\nimport { resolveAiConfig, resolveKnowledge, translateDocument } from '@elytracms/studio/lib/ai'\nimport type { TranslateProgress } from '@elytracms/studio/lib/ai'\nimport { cmsValidationIssueMessage } from '@elytracms/studio/lib/persona/labels'\nimport { useCms } from '../cms/cms-store'\nimport type { TranslateDocumentActionProps } from './translate-document-action'\n\n/**\n * The \"Translate document\" confirm + progress dialog (EC-332), heavy side — loaded lazily\n * by the gate so the AI engine stays off the critical path (EC-331 bundle discipline). A\n * modal is allowed here (a picker/confirmation, per the house rule — property forms are\n * not). It resolves the EC-331 provider + knowledge, runs the ONE `translateDocument`\n * engine over the live draft (fill-empty-only on EC-318 connection state), streams\n * progress, then writes the result to the DRAFT via the operations layer with a\n * provenance-tagged snapshot (mirroring the EC-199 apply) and reflects it into the open\n * editor. Cancel aborts cleanly — any already-translated units stay on the draft, visible\n * in Compare (EC-317) and rejectable via Discard (EC-316).\n */\ntype Phase = 'confirm' | 'running' | 'done' | 'error'\n\ninterface RunSummary {\n translated: number\n slugs: number\n locales: number\n cancelled: boolean\n nothing: boolean\n}\n\nexport function TranslateDocumentDialog({\n projectId,\n collection,\n docRef,\n document,\n localeConfig,\n flushDraft,\n open,\n onOpenChange,\n}: TranslateDocumentActionProps & { open: boolean; onOpenChange: (open: boolean) => void }) {\n const [phase, setPhase] = useState<Phase>('confirm')\n const [progress, setProgress] = useState<TranslateProgress | null>(null)\n const [summary, setSummary] = useState<RunSummary | null>(null)\n /**\n * EC-497 — why the refusal was blocked, when the gate refused. Reuses the\n * EC-473 `ai.error.applyInvalid` copy (\"the proposal no longer fits the current\n * document: …\"), which is exactly this situation: no new catalog string, and the\n * editor is told WHICH rules the translation would have broken rather than a bare\n * \"failed\".\n */\n const [refusal, setRefusal] = useState<string | null>(null)\n const abortRef = useRef<AbortController | null>(null)\n const cms = useCms()\n // The session registry the produced draft is judged against. Built here rather\n // than added to the CMS store's public surface for one consumer; the memo means\n // once per open, not once per render.\n const registry = useMemo(() => createCollectionRegistry(cms.collections), [cms.collections])\n\n useEffect(() => () => abortRef.current?.abort(), [])\n\n const base = localeConfig.default\n const targets = localeConfig.locales.filter((l) => l !== base)\n\n const run = useCallback(async () => {\n if (projectId === undefined) {\n setPhase('error')\n return\n }\n const resolved = resolveAiConfig(resolveDeploymentAi())\n const provider = resolved.resolved?.provider\n if (!provider || !resolved.ok) {\n setPhase('error')\n return\n }\n const controller = new AbortController()\n abortRef.current = controller\n setRefusal(null)\n setPhase('running')\n await flushDraft()\n setProgress({ completed: 0, total: 0 })\n try {\n const knowledge = await resolveKnowledge(resolved.resolved?.knowledge ?? [])\n const result = await translateDocument({\n provider,\n collection,\n document,\n localeConfig,\n knowledge,\n signal: controller.signal,\n onProgress: setProgress,\n // EC-497 — this action used to hand `translateDocument`'s output STRAIGHT to\n // `documents.upsert` with nothing in between: no gate, no proposal card, no\n // re-validation, so model-generated content reached the document completely\n // unvalidated while the chat path validated twice. It now validates with the\n // session's canonical bag (`cms.validateOptions`) — the editor's own rules —\n // and refuses below on what the translation INTRODUCED.\n registry,\n validateOptions: cms.validateOptions,\n })\n if (result.introducedErrors.length > 0) {\n setRefusal(\n translate('ai.error.applyInvalid', {\n issues: result.introducedErrors\n .slice(0, 3)\n .map((issue) => cmsValidationIssueMessage(issue))\n .join('; '),\n }),\n )\n setPhase('error')\n return\n }\n if (result.translated > 0 || result.slugsDerived > 0) {\n // EC-595 A2: the translation persists as the patches of the buckets it filled\n // (never the whole document) — through the session's queue, at the revision\n // the editor's view is at; a bucket a colleague changed meanwhile is refused on\n // that field and shown there.\n const outcome = await cms.commitPatches(docRef, result.patches)\n if (outcome?.status === 'failed') {\n setPhase('error')\n return\n }\n const { operations } = await getStudioOperations()\n // Provenance: an AI-tagged version snapshot (best-effort — the draft already landed).\n try {\n await operations.documents.snapshot({\n projectId,\n ref: docRef,\n label: translate('cms.version.aiAuthoredTitle'),\n origin: 'assistant',\n })\n } catch {\n // ignore — provenance is additive\n }\n }\n setSummary({\n translated: result.translated,\n slugs: result.slugsDerived,\n locales: Object.keys(result.perLocale).length,\n cancelled: result.cancelled,\n nothing: result.total === 0,\n })\n setPhase('done')\n } catch {\n setPhase('error')\n } finally {\n abortRef.current = null\n }\n }, [projectId, collection, document, localeConfig, docRef, flushDraft, registry, cms])\n\n const close = () => {\n if (phase === 'running') return // never close mid-run — Cancel aborts instead\n onOpenChange(false)\n }\n\n const doneMessage = (s: RunSummary): string => {\n if (s.nothing) return translate('ai.translate.nothing')\n if (s.cancelled) return translate('ai.translate.cancelled')\n return translate('ai.translate.applied', { count: s.translated, locales: s.locales })\n }\n\n return (\n <Dialog open={open} onOpenChange={(next) => (!next ? close() : undefined)}>\n <DialogContent>\n <DialogHeader>\n <DialogTitle>\n <Row gap=\"2\" align=\"center\">\n <Languages className=\"size-4\" /> {translate('ai.translate.dialogTitle')}\n </Row>\n </DialogTitle>\n <DialogDescription>\n {translate('ai.translate.dialogDescription', { base: localeChipLabel(base) })}\n </DialogDescription>\n </DialogHeader>\n\n {phase === 'confirm' ? (\n <Stack gap=\"4\" className=\"py-2\">\n <Stack gap=\"1\">\n <Text size=\"xs\" weight=\"medium\" tone=\"muted\">\n {translate('ai.translate.targetsLabel')}\n </Text>\n <Row gap=\"1\" wrap>\n {targets.map((l) => (\n <Badge key={l} variant=\"outline\">\n {localeChipLabel(l)}\n </Badge>\n ))}\n </Row>\n </Stack>\n <Stack gap=\"1\">\n <Text size=\"xs\" weight=\"medium\" tone=\"muted\">\n {translate('ai.translate.scopeLabel')}\n </Text>\n <Row gap=\"2\" align=\"center\">\n <Badge variant=\"default\">{translate('ai.translate.fillMissing')}</Badge>\n <Button size=\"sm\" variant=\"ghost\" disabled title={translate('ai.translate.retranslateSoon')}>\n {translate('ai.translate.retranslateAll')}\n </Button>\n <Text size=\"xs\" tone=\"muted\">\n {translate('ai.translate.retranslateSoon')}\n </Text>\n </Row>\n </Stack>\n </Stack>\n ) : null}\n\n {phase === 'running' ? (\n <Stack gap=\"3\" align=\"center\" className=\"py-6\">\n <Loader2 className=\"size-6 animate-spin text-primary\" />\n <Text size=\"sm\" tone=\"muted\" data-testid=\"translate-progress\">\n {progress && progress.total > 0\n ? translate('ai.translate.progress', { completed: progress.completed, total: progress.total })\n : translate('ai.translate.preparing')}\n </Text>\n {progress && progress.total > 0 ? (\n <div className=\"h-1.5 w-56 overflow-hidden rounded-full bg-muted\">\n <div\n className=\"h-full rounded-full bg-primary transition-all\"\n style={{ width: `${Math.round((progress.completed / progress.total) * 100)}%` }}\n />\n </div>\n ) : null}\n </Stack>\n ) : null}\n\n {phase === 'done' && summary ? (\n <Stack gap=\"2\" align=\"center\" className=\"py-6\">\n <Check className=\"size-6 text-success\" />\n <Text size=\"sm\" tone=\"muted\" className=\"text-center\" data-testid=\"translate-summary\">\n {doneMessage(summary)}\n </Text>\n </Stack>\n ) : null}\n\n {phase === 'error' ? (\n <Stack gap=\"2\" align=\"center\" className=\"py-6\">\n <X className=\"size-6 text-destructive\" />\n <Text size=\"sm\" tone=\"muted\" className=\"text-center\" data-testid=\"translate-error\">\n {refusal ?? translate('ai.translate.failed')}\n </Text>\n </Stack>\n ) : null}\n\n <DialogFooter className=\"mt-2\">\n {phase === 'confirm' ? (\n <>\n <Button variant=\"ghost\" size=\"sm\" onClick={close}>\n {translate('common.cancel')}\n </Button>\n <Button size=\"sm\" onClick={() => void run()} data-testid=\"translate-confirm\">\n <Languages /> {translate('ai.translate.confirm')}\n </Button>\n </>\n ) : phase === 'running' ? (\n <Button variant=\"outline\" size=\"sm\" onClick={() => abortRef.current?.abort()} data-testid=\"translate-cancel\">\n {translate('common.cancel')}\n </Button>\n ) : (\n <Button size=\"sm\" onClick={close} data-testid=\"translate-close\">\n {translate('ai.translate.close')}\n </Button>\n )}\n </DialogFooter>\n </DialogContent>\n </Dialog>\n )\n}\n"]}
|
|
1
|
+
{"version":3,"file":"translate-document-dialog.js","sourceRoot":"","sources":["../../../src/components/ai/translate-document-dialog.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport { createCollectionRegistry } from '@elytracms/core/cms-core'\nimport { Check, Languages, Loader2, X } from 'lucide-react'\nimport {\n Badge,\n Button,\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n Row,\n Stack,\n Text,\n} from '@elytracms/studio/ui'\nimport { translate } from '../../lib/i18n'\nimport { localeChipLabel } from '../../lib/locale'\nimport { getStudioOperations } from '../../lib/operations'\nimport { resolveDeploymentAi } from '../../lib/workspace'\nimport { resolveAiConfig, resolveKnowledge, translateDocument } from '../../lib/ai'\nimport type { TranslateProgress, TranslateSkip } from '../../lib/ai'\nimport { cmsValidationIssueMessage } from '../../lib/persona/labels'\nimport { useCms } from '../cms/cms-store'\nimport type { TranslateDocumentActionProps } from './translate-document-action'\nimport { TranslateSkipped } from './translate-skipped'\n\n/**\n * The \"Translate document\" confirm + progress dialog (EC-332), heavy side — loaded lazily\n * by the gate so the AI engine stays off the critical path (EC-331 bundle discipline). A\n * modal is allowed here (a picker/confirmation, per the house rule — property forms are\n * not). It resolves the EC-331 provider + knowledge, runs the ONE `translateDocument`\n * engine over the live draft (fill-empty-only on EC-318 connection state), streams\n * progress, then writes the result to the DRAFT via the operations layer with a\n * provenance-tagged snapshot (mirroring the EC-199 apply) and reflects it into the open\n * editor. Cancel aborts cleanly — any already-translated units stay on the draft, visible\n * in Compare (EC-317) and rejectable via Discard (EC-316).\n */\ntype Phase = 'confirm' | 'running' | 'done' | 'error'\n\ninterface RunSummary {\n translated: number\n slugs: number\n locales: number\n cancelled: boolean\n nothing: boolean\n /** EC-593: every text position the run could not translate — listed, never silent. */\n skipped: readonly TranslateSkip[]\n}\n\nexport function TranslateDocumentDialog({\n projectId,\n collection,\n docRef,\n document,\n localeConfig,\n flushDraft,\n open,\n onOpenChange,\n}: TranslateDocumentActionProps & { open: boolean; onOpenChange: (open: boolean) => void }) {\n const [phase, setPhase] = useState<Phase>('confirm')\n const [progress, setProgress] = useState<TranslateProgress | null>(null)\n const [summary, setSummary] = useState<RunSummary | null>(null)\n /**\n * EC-497 — why the refusal was blocked, when the gate refused. Reuses the\n * EC-473 `ai.error.applyInvalid` copy (\"the proposal no longer fits the current\n * document: …\"), which is exactly this situation: no new catalog string, and the\n * editor is told WHICH rules the translation would have broken rather than a bare\n * \"failed\".\n */\n const [refusal, setRefusal] = useState<string | null>(null)\n const abortRef = useRef<AbortController | null>(null)\n const cms = useCms()\n // The session registry the produced draft is judged against. Built here rather\n // than added to the CMS store's public surface for one consumer; the memo means\n // once per open, not once per render.\n const registry = useMemo(() => createCollectionRegistry(cms.collections), [cms.collections])\n\n useEffect(() => () => abortRef.current?.abort(), [])\n\n const base = localeConfig.default\n const targets = localeConfig.locales.filter((l) => l !== base)\n\n const run = useCallback(async () => {\n if (projectId === undefined) {\n setPhase('error')\n return\n }\n const resolved = resolveAiConfig(resolveDeploymentAi())\n const provider = resolved.resolved?.provider\n if (!provider || !resolved.ok) {\n setPhase('error')\n return\n }\n const controller = new AbortController()\n abortRef.current = controller\n setRefusal(null)\n setPhase('running')\n await flushDraft()\n setProgress({ completed: 0, total: 0 })\n try {\n const knowledge = await resolveKnowledge(resolved.resolved?.knowledge ?? [])\n const result = await translateDocument({\n provider,\n collection,\n document,\n localeConfig,\n knowledge,\n signal: controller.signal,\n onProgress: setProgress,\n // EC-497 — this action used to hand `translateDocument`'s output STRAIGHT to\n // `documents.upsert` with nothing in between: no gate, no proposal card, no\n // re-validation, so model-generated content reached the document completely\n // unvalidated while the chat path validated twice. It now validates with the\n // session's canonical bag (`cms.validateOptions`) — the editor's own rules —\n // and refuses below on what the translation INTRODUCED.\n registry,\n validateOptions: cms.validateOptions,\n })\n if (result.introducedErrors.length > 0) {\n setRefusal(\n translate('ai.error.applyInvalid', {\n issues: result.introducedErrors\n .slice(0, 3)\n .map((issue) => cmsValidationIssueMessage(issue))\n .join('; '),\n }),\n )\n setPhase('error')\n return\n }\n if (result.translated > 0 || result.slugsDerived > 0) {\n // EC-595 A2: the translation persists as the patches of the buckets it filled\n // (never the whole document) — through the session's queue, at the revision\n // the editor's view is at; a bucket a colleague changed meanwhile is refused on\n // that field and shown there.\n const outcome = await cms.commitPatches(docRef, result.patches)\n if (outcome?.status === 'failed') {\n setPhase('error')\n return\n }\n const { operations } = await getStudioOperations()\n // Provenance: an AI-tagged version snapshot (best-effort — the draft already landed).\n try {\n await operations.documents.snapshot({\n projectId,\n ref: docRef,\n label: translate('cms.version.aiAuthoredTitle'),\n origin: 'assistant',\n })\n } catch {\n // ignore — provenance is additive\n }\n }\n setSummary({\n translated: result.translated,\n slugs: result.slugsDerived,\n locales: Object.keys(result.perLocale).length,\n cancelled: result.cancelled,\n nothing: result.total === 0 && result.skipped.length === 0,\n skipped: result.skipped,\n })\n setPhase('done')\n } catch {\n setPhase('error')\n } finally {\n abortRef.current = null\n }\n }, [projectId, collection, document, localeConfig, docRef, flushDraft, registry, cms])\n\n const close = () => {\n if (phase === 'running') return // never close mid-run — Cancel aborts instead\n onOpenChange(false)\n }\n\n const doneMessage = (s: RunSummary): string => {\n if (s.nothing) return translate('ai.translate.nothing')\n if (s.cancelled) return translate('ai.translate.cancelled')\n return translate('ai.translate.applied', { count: s.translated, locales: s.locales })\n }\n\n return (\n <Dialog open={open} onOpenChange={(next) => (!next ? close() : undefined)}>\n <DialogContent>\n <DialogHeader>\n <DialogTitle>\n <Row gap=\"2\" align=\"center\">\n <Languages className=\"size-4\" /> {translate('ai.translate.dialogTitle')}\n </Row>\n </DialogTitle>\n <DialogDescription>\n {translate('ai.translate.dialogDescription', { base: localeChipLabel(base) })}\n </DialogDescription>\n </DialogHeader>\n\n {phase === 'confirm' ? (\n <Stack gap=\"4\" className=\"py-2\">\n <Stack gap=\"1\">\n <Text size=\"xs\" weight=\"medium\" tone=\"muted\">\n {translate('ai.translate.targetsLabel')}\n </Text>\n <Row gap=\"1\" wrap>\n {targets.map((l) => (\n <Badge key={l} variant=\"outline\">\n {localeChipLabel(l)}\n </Badge>\n ))}\n </Row>\n </Stack>\n <Stack gap=\"1\">\n <Text size=\"xs\" weight=\"medium\" tone=\"muted\">\n {translate('ai.translate.scopeLabel')}\n </Text>\n <Row gap=\"2\" align=\"center\">\n <Badge variant=\"default\">{translate('ai.translate.fillMissing')}</Badge>\n <Button size=\"sm\" variant=\"ghost\" disabled title={translate('ai.translate.retranslateSoon')}>\n {translate('ai.translate.retranslateAll')}\n </Button>\n <Text size=\"xs\" tone=\"muted\">\n {translate('ai.translate.retranslateSoon')}\n </Text>\n </Row>\n </Stack>\n </Stack>\n ) : null}\n\n {phase === 'running' ? (\n <Stack gap=\"3\" align=\"center\" className=\"py-6\">\n <Loader2 className=\"size-6 animate-spin text-primary\" />\n <Text size=\"sm\" tone=\"muted\" data-testid=\"translate-progress\">\n {progress && progress.total > 0\n ? translate('ai.translate.progress', { completed: progress.completed, total: progress.total })\n : translate('ai.translate.preparing')}\n </Text>\n {progress && progress.total > 0 ? (\n <div className=\"h-1.5 w-56 overflow-hidden rounded-full bg-muted\">\n <div\n className=\"h-full rounded-full bg-primary transition-all\"\n style={{ width: `${Math.round((progress.completed / progress.total) * 100)}%` }}\n />\n </div>\n ) : null}\n </Stack>\n ) : null}\n\n {phase === 'done' && summary ? (\n <Stack gap=\"2\" align=\"center\" className=\"py-6\">\n <Check className=\"size-6 text-success\" />\n <Text size=\"sm\" tone=\"muted\" className=\"text-center\" data-testid=\"translate-summary\">\n {doneMessage(summary)}\n </Text>\n <TranslateSkipped skipped={summary.skipped} collection={collection} />\n </Stack>\n ) : null}\n\n {phase === 'error' ? (\n <Stack gap=\"2\" align=\"center\" className=\"py-6\">\n <X className=\"size-6 text-destructive\" />\n <Text size=\"sm\" tone=\"muted\" className=\"text-center\" data-testid=\"translate-error\">\n {refusal ?? translate('ai.translate.failed')}\n </Text>\n </Stack>\n ) : null}\n\n <DialogFooter className=\"mt-2\">\n {phase === 'confirm' ? (\n <>\n <Button variant=\"ghost\" size=\"sm\" onClick={close}>\n {translate('common.cancel')}\n </Button>\n <Button size=\"sm\" onClick={() => void run()} data-testid=\"translate-confirm\">\n <Languages /> {translate('ai.translate.confirm')}\n </Button>\n </>\n ) : phase === 'running' ? (\n <Button variant=\"outline\" size=\"sm\" onClick={() => abortRef.current?.abort()} data-testid=\"translate-cancel\">\n {translate('common.cancel')}\n </Button>\n ) : (\n <Button size=\"sm\" onClick={close} data-testid=\"translate-close\">\n {translate('ai.translate.close')}\n </Button>\n )}\n </DialogFooter>\n </DialogContent>\n </Dialog>\n )\n}\n"]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { CollectionDef } from '@elytracms/core/cms-core';
|
|
2
|
+
import type { TranslateSkip } from '../../lib/ai/index.js';
|
|
3
|
+
/** One skip as the editor reads it: where (field label › component › position), which language, why. */
|
|
4
|
+
export interface TranslateSkipLine {
|
|
5
|
+
where: string;
|
|
6
|
+
locale: string;
|
|
7
|
+
reason: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* The where-label of a skipped position: the field's label, then — inside a component —
|
|
11
|
+
* the component and its prop path, else the position below the field (an item counted
|
|
12
|
+
* from 1). Index-bearing segments the editor does not see (`content`, `props`) are left out.
|
|
13
|
+
*/
|
|
14
|
+
export declare function translateSkipLines(skipped: readonly TranslateSkip[], collection: CollectionDef | undefined): TranslateSkipLine[];
|
|
15
|
+
/** The list; renders nothing when everything was translated. */
|
|
16
|
+
export declare function TranslateSkipped({ skipped, collection, }: {
|
|
17
|
+
skipped: readonly TranslateSkip[];
|
|
18
|
+
collection: CollectionDef | undefined;
|
|
19
|
+
}): import("react").JSX.Element | null;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { resolveLabel } from '@elytracms/core/cms-core';
|
|
3
|
+
import { AlertTriangle } from 'lucide-react';
|
|
4
|
+
import { Row, Stack, Text } from '@elytracms/studio/ui';
|
|
5
|
+
import { translate } from '../../lib/i18n/index.js';
|
|
6
|
+
import { localeChipLabel } from '../../lib/locale/index.js';
|
|
7
|
+
/**
|
|
8
|
+
* EC-593 (ratified 4e) — what a translation run could NOT translate, shown where the
|
|
9
|
+
* editor reads the outcome: the "Translate document" dialog and the chat's translation
|
|
10
|
+
* proposal card. Before, a localized object or composition was skipped with no word,
|
|
11
|
+
* and a garbled rich-text block silently kept its original.
|
|
12
|
+
*/
|
|
13
|
+
const REASON_KEY = {
|
|
14
|
+
unreadable: 'ai.translate.skipped.unreadable',
|
|
15
|
+
'not-returned': 'ai.translate.skipped.notReturned',
|
|
16
|
+
'structure-changed': 'ai.translate.skipped.structureChanged',
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* The where-label of a skipped position: the field's label, then — inside a component —
|
|
20
|
+
* the component and its prop path, else the position below the field (an item counted
|
|
21
|
+
* from 1). Index-bearing segments the editor does not see (`content`, `props`) are left out.
|
|
22
|
+
*/
|
|
23
|
+
export function translateSkipLines(skipped, collection) {
|
|
24
|
+
return skipped.map((skip) => {
|
|
25
|
+
const field = collection?.fields.find((f) => f.name === skip.field);
|
|
26
|
+
const label = resolveLabel(field?.form?.label) ?? skip.field;
|
|
27
|
+
const below = skip.component
|
|
28
|
+
? [skip.component.componentId, ...skip.at.slice(skip.at.lastIndexOf('props') + 1)]
|
|
29
|
+
: skip.at;
|
|
30
|
+
const segments = below
|
|
31
|
+
.filter((segment) => segment !== 'content' && segment !== 'props')
|
|
32
|
+
.map((segment) => (typeof segment === 'number' ? `#${segment + 1}` : segment));
|
|
33
|
+
return {
|
|
34
|
+
where: [label, ...segments].join(' › '),
|
|
35
|
+
locale: localeChipLabel(skip.locale),
|
|
36
|
+
reason: translate(REASON_KEY[skip.reason]),
|
|
37
|
+
};
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
/** The list; renders nothing when everything was translated. */
|
|
41
|
+
export function TranslateSkipped({ skipped, collection, }) {
|
|
42
|
+
if (skipped.length === 0)
|
|
43
|
+
return null;
|
|
44
|
+
return (_jsxs(Stack, { gap: "1", "data-testid": "translate-skipped", children: [_jsxs(Row, { gap: "1", align: "center", children: [_jsx(AlertTriangle, { className: "size-3.5 text-warning" }), _jsx(Text, { size: "xs", weight: "medium", children: translate('ai.translate.skippedTitle', { count: skipped.length }) })] }), translateSkipLines(skipped, collection).map((line, index) => (_jsxs(Text, { size: "xs", tone: "muted", "data-translate-skip": true, children: [line.where, " (", line.locale, ") \u2014 ", line.reason] }, index)))] }));
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=translate-skipped.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"translate-skipped.js","sourceRoot":"","sources":["../../../src/components/ai/translate-skipped.tsx"],"names":[],"mappings":"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","sourcesContent":["import { resolveLabel } from '@elytracms/core/cms-core'\nimport type { CollectionDef } from '@elytracms/core/cms-core'\nimport { AlertTriangle } from 'lucide-react'\nimport { Row, Stack, Text } from '@elytracms/studio/ui'\nimport { translate } from '../../lib/i18n'\nimport { localeChipLabel } from '../../lib/locale'\nimport type { TranslateSkip, TranslateSkipReason } from '../../lib/ai'\n\n/**\n * EC-593 (ratified 4e) — what a translation run could NOT translate, shown where the\n * editor reads the outcome: the \"Translate document\" dialog and the chat's translation\n * proposal card. Before, a localized object or composition was skipped with no word,\n * and a garbled rich-text block silently kept its original.\n */\n\nconst REASON_KEY: Record<TranslateSkipReason, string> = {\n unreadable: 'ai.translate.skipped.unreadable',\n 'not-returned': 'ai.translate.skipped.notReturned',\n 'structure-changed': 'ai.translate.skipped.structureChanged',\n}\n\n/** One skip as the editor reads it: where (field label › component › position), which language, why. */\nexport interface TranslateSkipLine {\n where: string\n locale: string\n reason: string\n}\n\n/**\n * The where-label of a skipped position: the field's label, then — inside a component —\n * the component and its prop path, else the position below the field (an item counted\n * from 1). Index-bearing segments the editor does not see (`content`, `props`) are left out.\n */\nexport function translateSkipLines(\n skipped: readonly TranslateSkip[],\n collection: CollectionDef | undefined,\n): TranslateSkipLine[] {\n return skipped.map((skip) => {\n const field = collection?.fields.find((f) => f.name === skip.field)\n const label = resolveLabel(field?.form?.label) ?? skip.field\n const below = skip.component\n ? [skip.component.componentId, ...skip.at.slice(skip.at.lastIndexOf('props') + 1)]\n : skip.at\n const segments = below\n .filter((segment) => segment !== 'content' && segment !== 'props')\n .map((segment) => (typeof segment === 'number' ? `#${segment + 1}` : segment))\n return {\n where: [label, ...segments].join(' › '),\n locale: localeChipLabel(skip.locale),\n reason: translate(REASON_KEY[skip.reason]),\n }\n })\n}\n\n/** The list; renders nothing when everything was translated. */\nexport function TranslateSkipped({\n skipped,\n collection,\n}: {\n skipped: readonly TranslateSkip[]\n collection: CollectionDef | undefined\n}) {\n if (skipped.length === 0) return null\n return (\n <Stack gap=\"1\" data-testid=\"translate-skipped\">\n <Row gap=\"1\" align=\"center\">\n <AlertTriangle className=\"size-3.5 text-warning\" />\n <Text size=\"xs\" weight=\"medium\">\n {translate('ai.translate.skippedTitle', { count: skipped.length })}\n </Text>\n </Row>\n {translateSkipLines(skipped, collection).map((line, index) => (\n <Text key={index} size=\"xs\" tone=\"muted\" data-translate-skip>\n {line.where} ({line.locale}) — {line.reason}\n </Text>\n ))}\n </Stack>\n )\n}\n"]}
|
|
@@ -2,8 +2,10 @@ import type { ReactNode } from 'react';
|
|
|
2
2
|
import type { LucideIcon } from 'lucide-react';
|
|
3
3
|
import type { CmsDocument, CollectionDef, CollectionFieldOverride, CollectionFormMeta, LocaleConfig } from '@elytracms/core/cms-core';
|
|
4
4
|
import type { CustomizationIssue, Project, SectionId } from '@elytracms/core/studio-core';
|
|
5
|
-
import type { HydratedCms } from '
|
|
6
|
-
import { type AppViewContext, type AppViewDeepLink, type AppViewModule, type NavPermission, type SidebarViewNode } from './module.js';
|
|
5
|
+
import type { HydratedCms } from '../../lib/operations/index.js';
|
|
6
|
+
import { type AppViewContext, type AppViewDeepLink, type AppViewModule, type NavIssuesHook, type NavPermission, type SidebarViewNode } from './module.js';
|
|
7
|
+
import type { AnyRecordKind } from '@elytracms/core/persistence';
|
|
8
|
+
import type { CollectionViewCapabilities } from '../../lib/collection-view/collection-view-source.js';
|
|
7
9
|
/**
|
|
8
10
|
* EC-237/239 — the studio's BUILT-IN views + the consumer-facing `nav` namespace,
|
|
9
11
|
* an instance of the EC-236 customization seam.
|
|
@@ -106,6 +108,8 @@ export interface CustomViewConfig {
|
|
|
106
108
|
availabilitySection?: SectionId;
|
|
107
109
|
/** OPTIONAL, default-open nav-permission predicate (EC-240 / AD-18). See {@link BuiltinViewConfig.visible}. */
|
|
108
110
|
visible?: NavPermission;
|
|
111
|
+
/** Always-on rail-foot findings for this view (EC-616, see `NavIssuesHook`). */
|
|
112
|
+
useIssues?: NavIssuesHook;
|
|
109
113
|
}
|
|
110
114
|
/**
|
|
111
115
|
* Derive a stable, deterministic id from the CMS focus (overridable via `config.id`).
|
|
@@ -159,6 +163,43 @@ export declare function componentsView(config?: BuiltinViewConfig): AppViewModul
|
|
|
159
163
|
* routing concern). `[]` is the list; `[redirectId]` opens that redirect's detail.
|
|
160
164
|
*/
|
|
161
165
|
export declare function redirectsView(config?: RedirectsViewConfig): AppViewModule;
|
|
166
|
+
/**
|
|
167
|
+
* Config for a RECORD RAIL (`nav.rail(kind, config)`, EC-616): a record kind on the
|
|
168
|
+
* portable list/edit surfaces. Everything is optional — `nav.rail(kind)` alone derives
|
|
169
|
+
* the lens from the kind's columns (`recordLensDef`). `form`/`fields` override the lens by
|
|
170
|
+
* name exactly as `nav.redirects` does (EC-311); a net-new field is additive and persists
|
|
171
|
+
* in the record's open `meta` bag.
|
|
172
|
+
*/
|
|
173
|
+
export interface RailViewConfig extends BuiltinViewConfig {
|
|
174
|
+
/** The route segment (default: the kind's `module`, e.g. `submissions`). */
|
|
175
|
+
id?: string;
|
|
176
|
+
description?: string;
|
|
177
|
+
/** The lens to start from (default: derived from the kind). */
|
|
178
|
+
schema?: CollectionDef;
|
|
179
|
+
/** Rename the rail / override lens fields by name; a net-new field lives in `meta`. */
|
|
180
|
+
form?: Partial<CollectionFormMeta>;
|
|
181
|
+
fields?: readonly CollectionFieldOverride[];
|
|
182
|
+
/** The column a record's title reads (default: the first text column). */
|
|
183
|
+
titleField?: string;
|
|
184
|
+
/** Narrow the default capabilities (create, edit, remove, bulk delete). */
|
|
185
|
+
capabilities?: Partial<CollectionViewCapabilities>;
|
|
186
|
+
availabilitySection?: SectionId;
|
|
187
|
+
/** Always-on rail-foot findings for this rail (EC-616, see `NavIssuesHook`). */
|
|
188
|
+
useIssues?: NavIssuesHook;
|
|
189
|
+
/** Copy for the loading / failure states (default: generic catalog copy naming the label). */
|
|
190
|
+
messages?: {
|
|
191
|
+
loading?: () => string;
|
|
192
|
+
loadFailed?: () => string;
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* A record rail view (EC-616): the generic surface `nav.rail(kind)` mounts, and the
|
|
197
|
+
* engine's redirects rail ({@link redirectsView}). `[]` is the list; `[id]` opens that
|
|
198
|
+
* record (the deep link is keyed by the kind's `idKey`).
|
|
199
|
+
*/
|
|
200
|
+
export declare function railView(kind: AnyRecordKind, config?: RailViewConfig): AppViewModule & {
|
|
201
|
+
schema: CollectionDef;
|
|
202
|
+
};
|
|
162
203
|
/**
|
|
163
204
|
* Resolve the assets rail's EFFECTIVE schema (EC-319/EC-311): the deployment's
|
|
164
205
|
* OWN `kind:'asset'` collection — hydrated session → playground config,
|
|
@@ -281,6 +322,13 @@ export declare const nav: {
|
|
|
281
322
|
readonly settings: (config?: BuiltinViewConfig) => SidebarViewNode;
|
|
282
323
|
/** The admin users roster (EC-240). */
|
|
283
324
|
readonly users: (config?: BuiltinViewConfig) => SidebarViewNode;
|
|
325
|
+
/**
|
|
326
|
+
* A RECORD RAIL (EC-616): a declared record kind (`defineRecordKind`) on the portable
|
|
327
|
+
* list/edit surfaces — `nav.rail(submissionRecordKind, { label: 'Submissions' })`. The
|
|
328
|
+
* lens derives from the kind; `fields` refine it by name (EC-311), a net-new field
|
|
329
|
+
* persists in the record's `meta`. Reads and writes go through `operations.records(kind)`.
|
|
330
|
+
*/
|
|
331
|
+
readonly rail: (kind: AnyRecordKind, config?: RailViewConfig) => SidebarViewNode;
|
|
284
332
|
/** A fully custom view — pass your own `render` (and optional `parsePath`). */
|
|
285
333
|
readonly view: (config: CustomViewConfig) => SidebarViewNode;
|
|
286
334
|
/** A labelled group of views (a header with sub-entries). */
|
|
@@ -4,15 +4,17 @@ import { Blocks, Database, FileCog, Image, LayoutGrid, Settings as SettingsIcon,
|
|
|
4
4
|
import { defaultAssetCollection, extendCollectionDef, isSingleton, parseLocaleConfig, resolveSingletonDocument, } from '@elytracms/core/cms-core';
|
|
5
5
|
import { NavigateOnce, STUDIO_PROJECT_ROUTE, studioPath } from '@elytracms/studio/router';
|
|
6
6
|
import { Text } from '@elytracms/studio/ui';
|
|
7
|
-
import { localizedLabel, translate } from '
|
|
8
|
-
import { resolveDeploymentCollections, resolveDeploymentLocaleConfig, resolveDeploymentPageCollections, } from '
|
|
9
|
-
import { cmsDeepLink, componentsDeepLink, usersDeepLink,
|
|
7
|
+
import { localizedLabel, translate } from '../../lib/i18n/index.js';
|
|
8
|
+
import { resolveDeploymentCollections, resolveDeploymentLocaleConfig, resolveDeploymentPageCollections, } from '../../lib/workspace/index.js';
|
|
9
|
+
import { cmsDeepLink, componentsDeepLink, usersDeepLink, recordDeepLink, assetsDeepLink, groupNode, separatorNode, viewNode, } from './module.js';
|
|
10
10
|
import { SectionSurface } from '../section-surface.js';
|
|
11
11
|
import { SectionStateView } from '../section-state.js';
|
|
12
12
|
import { SettingsForm } from '../settings-form.js';
|
|
13
13
|
import { AssetsModule } from '../assets/assets-module.js';
|
|
14
14
|
import { UsersModule } from '../users/users-module.js';
|
|
15
|
-
import {
|
|
15
|
+
import { RECORD_RAIL_CAPABILITIES, RecordRailModule } from '../records/record-rail.js';
|
|
16
|
+
import { recordLensDef } from '../records/record-lens.js';
|
|
17
|
+
import { redirectRecordKind } from '@elytracms/core/persistence';
|
|
16
18
|
import { redirectsCollectionDef } from '../redirects/redirects-schema.js';
|
|
17
19
|
import { RegistryBrowser } from '../components/registry-browser.js';
|
|
18
20
|
import { ComponentsIndex } from '../components/components-index.js';
|
|
@@ -181,29 +183,64 @@ export function componentsView(config = {}) {
|
|
|
181
183
|
* routing concern). `[]` is the list; `[redirectId]` opens that redirect's detail.
|
|
182
184
|
*/
|
|
183
185
|
export function redirectsView(config = {}) {
|
|
184
|
-
// EC-
|
|
185
|
-
//
|
|
186
|
-
//
|
|
187
|
-
//
|
|
188
|
-
const
|
|
189
|
-
|
|
186
|
+
// EC-616: the redirects rail IS a record rail over the engine's redirect kind — its lens
|
|
187
|
+
// (`redirectsCollectionDef`: catalog labels and help) is the presentation, the kind the
|
|
188
|
+
// storage. EC-311 overrides merge over the lens ONCE, at config build (a locked
|
|
189
|
+
// violation throws here, at `defineStudio` mount); a net-new field persists in `meta`.
|
|
190
|
+
const view = railView(redirectRecordKind, {
|
|
191
|
+
...config,
|
|
190
192
|
id: 'redirects',
|
|
191
|
-
|
|
193
|
+
schema: redirectsCollectionDef,
|
|
194
|
+
icon: config.icon ?? Signpost,
|
|
195
|
+
availabilitySection: 'cms',
|
|
196
|
+
messages: {
|
|
197
|
+
loading: () => translate('redirects.loading'),
|
|
198
|
+
loadFailed: () => translate('redirects.loadFailed'),
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
const builtin = () => localizedLabel(redirectsCollectionDef.form?.label);
|
|
202
|
+
return Object.defineProperties({}, {
|
|
203
|
+
...Object.getOwnPropertyDescriptors(view),
|
|
204
|
+
label: {
|
|
192
205
|
// Prefer an explicit rail label, then a consumer-*customized* collection label,
|
|
193
206
|
// else the localized catalog nav label. Never let the built-in English schema
|
|
194
207
|
// label ('Redirects') preempt translation — compare against the built-in default.
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
208
|
+
get: () => {
|
|
209
|
+
const customized = localizedLabel(view.schema?.form?.label);
|
|
210
|
+
return config.label ?? (customized && customized !== builtin() ? customized : translate('nav.redirects.label'));
|
|
211
|
+
},
|
|
212
|
+
enumerable: true,
|
|
213
|
+
configurable: true,
|
|
214
|
+
},
|
|
215
|
+
description: { get: () => translate('nav.redirects.description'), enumerable: true, configurable: true },
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* A record rail view (EC-616): the generic surface `nav.rail(kind)` mounts, and the
|
|
220
|
+
* engine's redirects rail ({@link redirectsView}). `[]` is the list; `[id]` opens that
|
|
221
|
+
* record (the deep link is keyed by the kind's `idKey`).
|
|
222
|
+
*/
|
|
223
|
+
export function railView(kind, config = {}) {
|
|
224
|
+
const base = config.schema ?? recordLensDef(kind, config.titleField ? { titleField: config.titleField } : {});
|
|
225
|
+
const schema = extendCollectionDef(base, config, { additiveFields: 'allow' });
|
|
226
|
+
const id = config.id ?? kind.module;
|
|
227
|
+
const paths = { listPath: id, detailPath: (recordId) => `${id}/${recordId}` };
|
|
228
|
+
const capabilities = { ...RECORD_RAIL_CAPABILITIES, ...config.capabilities };
|
|
229
|
+
const label = () => config.label ?? localizedLabel(schema.form?.label) ?? kind.kind;
|
|
230
|
+
return {
|
|
231
|
+
id,
|
|
232
|
+
schema,
|
|
233
|
+
get label() {
|
|
234
|
+
return label();
|
|
199
235
|
},
|
|
200
236
|
get description() {
|
|
201
|
-
return
|
|
237
|
+
return config.description ?? label();
|
|
202
238
|
},
|
|
203
|
-
icon: config.icon ??
|
|
204
|
-
availabilitySection:
|
|
205
|
-
|
|
206
|
-
|
|
239
|
+
icon: config.icon ?? Database,
|
|
240
|
+
...(config.availabilitySection ? { availabilitySection: config.availabilitySection } : {}),
|
|
241
|
+
...(config.useIssues ? { useIssues: config.useIssues } : {}),
|
|
242
|
+
parsePath: recordDeepLink(kind.idKey),
|
|
243
|
+
render: (ctx) => ctx.search.state && ctx.search.state !== 'working' ? (_jsx(SectionStateView, { state: ctx.search.state, title: label() })) : (_jsx(RecordRailModule, { kind: kind, projectId: ctx.projectId, recordId: ctx.deepLink[kind.idKey] ?? null, schema: schema, paths: paths, capabilities: capabilities, label: label(), ...(config.messages ? { messages: config.messages } : {}) })),
|
|
207
244
|
};
|
|
208
245
|
}
|
|
209
246
|
/**
|
|
@@ -333,6 +370,7 @@ export function customView(config) {
|
|
|
333
370
|
render: config.render,
|
|
334
371
|
...(config.parsePath ? { parsePath: config.parsePath } : {}),
|
|
335
372
|
...(config.availabilitySection ? { availabilitySection: config.availabilitySection } : {}),
|
|
373
|
+
...(config.useIssues ? { useIssues: config.useIssues } : {}),
|
|
336
374
|
};
|
|
337
375
|
}
|
|
338
376
|
/**
|
|
@@ -498,6 +536,13 @@ export const nav = {
|
|
|
498
536
|
settings: (config = {}) => viewNode(withNavPermission(settingsView(config), config)),
|
|
499
537
|
/** The admin users roster (EC-240). */
|
|
500
538
|
users: (config = {}) => viewNode(withNavPermission(usersView(config), config)),
|
|
539
|
+
/**
|
|
540
|
+
* A RECORD RAIL (EC-616): a declared record kind (`defineRecordKind`) on the portable
|
|
541
|
+
* list/edit surfaces — `nav.rail(submissionRecordKind, { label: 'Submissions' })`. The
|
|
542
|
+
* lens derives from the kind; `fields` refine it by name (EC-311), a net-new field
|
|
543
|
+
* persists in the record's `meta`. Reads and writes go through `operations.records(kind)`.
|
|
544
|
+
*/
|
|
545
|
+
rail: (kind, config = {}) => viewNode(withNavPermission(railView(kind, config), config)),
|
|
501
546
|
/** A fully custom view — pass your own `render` (and optional `parsePath`). */
|
|
502
547
|
view: (config) => viewNode(withNavPermission(customView(config), config)),
|
|
503
548
|
/** A labelled group of views (a header with sub-entries). */
|