@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
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
import { getStudioOperations } from '../../lib/operations/index.js';
|
|
3
|
+
import { useOperationCache, useOperationQuery } from '../../lib/query/index.js';
|
|
4
|
+
import { useStudioLocale } from '../../lib/locale/index.js';
|
|
5
|
+
import { resolveDeploymentDocumentPanes } from '../../lib/workspace/index.js';
|
|
6
|
+
/** Typed identity helper for a contributed pane. */
|
|
7
|
+
export function documentPane(pane) {
|
|
8
|
+
return pane;
|
|
9
|
+
}
|
|
10
|
+
/** The contributed tab id — namespaced, so it can never equal a frame pane's (`details`, `versions`, `issues`). */
|
|
11
|
+
export function contributedPaneId(index, pane) {
|
|
12
|
+
return `contributed:${index}:${pane.id}`;
|
|
13
|
+
}
|
|
14
|
+
/** PURE: the inspector panes the contributions yield on one document. */
|
|
15
|
+
export function contributedInspectorPanes(panes, ctx) {
|
|
16
|
+
return (panes ?? []).flatMap((pane, index) => pane.collections && !pane.collections.includes(ctx.collection)
|
|
17
|
+
? []
|
|
18
|
+
: [
|
|
19
|
+
{
|
|
20
|
+
id: contributedPaneId(index, pane),
|
|
21
|
+
label: pane.label,
|
|
22
|
+
...(pane.icon !== undefined ? { icon: pane.icon } : {}),
|
|
23
|
+
render: () => pane.render(ctx),
|
|
24
|
+
},
|
|
25
|
+
]);
|
|
26
|
+
}
|
|
27
|
+
const PANE_QUERY_HOOKS = { useOperationQuery, useOperationCache };
|
|
28
|
+
const paneOperations = async () => (await getStudioOperations()).operations;
|
|
29
|
+
/** The contributed panes of one CMS document (the project's `defineStudio({ documentPanes })`). */
|
|
30
|
+
export function useContributedDocumentPanes(projectId, collection, documentId) {
|
|
31
|
+
const { locale } = useStudioLocale();
|
|
32
|
+
return useMemo(() => contributedInspectorPanes(resolveDeploymentDocumentPanes(), {
|
|
33
|
+
projectId,
|
|
34
|
+
collection,
|
|
35
|
+
documentId,
|
|
36
|
+
operations: paneOperations,
|
|
37
|
+
query: PANE_QUERY_HOOKS,
|
|
38
|
+
locale,
|
|
39
|
+
}), [projectId, collection, documentId, locale]);
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=document-panes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"document-panes.js","sourceRoot":"","sources":["../../../src/components/inspector/document-panes.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAA;AAI/B,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAA;AAC1D,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AACtE,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAClD,OAAO,EAAE,8BAA8B,EAAE,MAAM,qBAAqB,CAAA;AAgDpE,oDAAoD;AACpD,MAAM,UAAU,YAAY,CAAC,IAAkB;IAC7C,OAAO,IAAI,CAAA;AACb,CAAC;AAED,mHAAmH;AACnH,MAAM,UAAU,iBAAiB,CAAC,KAAa,EAAE,IAAkB;IACjE,OAAO,eAAe,KAAK,IAAI,IAAI,CAAC,EAAE,EAAE,CAAA;AAC1C,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,yBAAyB,CACvC,KAA0C,EAC1C,GAAwB;IAExB,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAC3C,IAAI,CAAC,WAAW,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC;QAC5D,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC;YACE;gBACE,EAAE,EAAE,iBAAiB,CAAC,KAAK,EAAE,IAAI,CAAC;gBAClC,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,GAAG,CAAC,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACvD,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC;aAC/B;SACF,CACN,CAAA;AACH,CAAC;AAED,MAAM,gBAAgB,GAAsB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,CAAA;AACpF,MAAM,cAAc,GAAG,KAAK,IAAI,EAAE,CAAC,CAAC,MAAM,mBAAmB,EAAE,CAAC,CAAC,UAAU,CAAA;AAE3E,mGAAmG;AACnG,MAAM,UAAU,2BAA2B,CAAC,SAAiB,EAAE,UAAkB,EAAE,UAAkB;IACnG,MAAM,EAAE,MAAM,EAAE,GAAG,eAAe,EAAE,CAAA;IACpC,OAAO,OAAO,CACZ,GAAG,EAAE,CACH,yBAAyB,CAAC,8BAA8B,EAAE,EAAE;QAC1D,SAAS;QACT,UAAU;QACV,UAAU;QACV,UAAU,EAAE,cAAc;QAC1B,KAAK,EAAE,gBAAgB;QACvB,MAAM;KACP,CAAC,EACJ,CAAC,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,CAAC,CAC5C,CAAA;AACH,CAAC","sourcesContent":["import { useMemo } from 'react'\nimport type { ReactNode } from 'react'\nimport type { Locale } from '@elytracms/core/cms-core'\nimport type { OperationsClient } from '@elytracms/core/operations'\nimport { getStudioOperations } from '../../lib/operations'\nimport { useOperationCache, useOperationQuery } from '../../lib/query'\nimport { useStudioLocale } from '../../lib/locale'\nimport { resolveDeploymentDocumentPanes } from '../../lib/workspace'\nimport type { AppViewQueryHooks } from '../app-views/module'\nimport type { InspectorPane } from './inspector-sidebar'\n\n/**\n * EC-618 — the DOCUMENT-PANE contribution seam (AD-19's edit-shell slot): a project — or\n * an addon it installs — adds a pane to the document editor's inspector beside the\n * frame's own Details / Versions / Issues, through ONE direct-value key:\n *\n * ```tsx\n * // studio.config.tsx\n * import { analyticsDocumentPane } from '@elytracms/analytics/studio'\n * export default defineStudio({ sidebar: [ … ], documentPanes: [analyticsDocumentPane] })\n * ```\n *\n * A pane is told which document it is on and reaches the studio the way a view does\n * (the operations client, the query hooks over the one cache, the content locale). It\n * appears on CMS documents only — a record rail (users, redirects, an addon's kind) has no\n * document to report on — and, with `collections`, only where it says. Its tab id is\n * namespaced (`contributed:<index>:<id>`), so a contribution can never shadow or collide\n * with the frame's own panes or with another contribution.\n */\n\n/** What a contributed pane is told about the document it is on. */\nexport interface DocumentPaneContext {\n projectId: string\n /** The document's collection id. */\n collection: string\n documentId: string\n /** The studio's operations client — every read and write, authorized at the chokepoint. */\n operations(): Promise<OperationsClient>\n /** The query hooks over the studio's one operations-backed cache. */\n query: AppViewQueryHooks\n /** The ambient content locale (`null` until the project's locale config is installed). */\n locale: Locale | null\n}\n\n/** A pane contributed to the document editor's inspector (`defineStudio({ documentPanes })`). */\nexport interface DocumentPane {\n /** Stable within the contributions (the tab id is namespaced — see the module header). */\n id: string\n label: string\n icon?: ReactNode\n /** The collections it appears on; absent → every CMS collection. */\n collections?: readonly string[]\n render: (ctx: DocumentPaneContext) => ReactNode\n}\n\n/** Typed identity helper for a contributed pane. */\nexport function documentPane(pane: DocumentPane): DocumentPane {\n return pane\n}\n\n/** The contributed tab id — namespaced, so it can never equal a frame pane's (`details`, `versions`, `issues`). */\nexport function contributedPaneId(index: number, pane: DocumentPane): string {\n return `contributed:${index}:${pane.id}`\n}\n\n/** PURE: the inspector panes the contributions yield on one document. */\nexport function contributedInspectorPanes(\n panes: readonly DocumentPane[] | undefined,\n ctx: DocumentPaneContext,\n): InspectorPane[] {\n return (panes ?? []).flatMap((pane, index) =>\n pane.collections && !pane.collections.includes(ctx.collection)\n ? []\n : [\n {\n id: contributedPaneId(index, pane),\n label: pane.label,\n ...(pane.icon !== undefined ? { icon: pane.icon } : {}),\n render: () => pane.render(ctx),\n },\n ],\n )\n}\n\nconst PANE_QUERY_HOOKS: AppViewQueryHooks = { useOperationQuery, useOperationCache }\nconst paneOperations = async () => (await getStudioOperations()).operations\n\n/** The contributed panes of one CMS document (the project's `defineStudio({ documentPanes })`). */\nexport function useContributedDocumentPanes(projectId: string, collection: string, documentId: string): InspectorPane[] {\n const { locale } = useStudioLocale()\n return useMemo(\n () =>\n contributedInspectorPanes(resolveDeploymentDocumentPanes(), {\n projectId,\n collection,\n documentId,\n operations: paneOperations,\n query: PANE_QUERY_HOOKS,\n locale,\n }),\n [projectId, collection, documentId, locale],\n )\n}\n"]}
|
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { PanelRight } from 'lucide-react';
|
|
3
3
|
import { useState } from 'react';
|
|
4
4
|
import { Row, Text } from '@elytracms/studio/ui';
|
|
5
|
-
import { cn } from '
|
|
5
|
+
import { cn } from '../../lib/utils.js';
|
|
6
6
|
/**
|
|
7
7
|
* When the sidebar carries this many panes or more, inactive tabs collapse to their
|
|
8
8
|
* icon alone (label in a tooltip + `aria-label`) so the tab strip stops overflowing
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inspector-sidebar.js","sourceRoot":"","sources":["../../../src/components/inspector/inspector-sidebar.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAEhC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAA;AAChD,OAAO,EAAE,EAAE,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"inspector-sidebar.js","sourceRoot":"","sources":["../../../src/components/inspector/inspector-sidebar.tsx"],"names":[],"mappings":"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","sourcesContent":["import { PanelRight } from 'lucide-react'\nimport { useState } from 'react'\nimport type { ReactNode } from 'react'\nimport { Row, Text } from '@elytracms/studio/ui'\nimport { cn } from '../../lib/utils'\n\n/**\n * When the sidebar carries this many panes or more, inactive tabs collapse to their\n * icon alone (label in a tooltip + `aria-label`) so the tab strip stops overflowing\n * (0.0.42, round 3). 1–2 panes keep their full labels.\n */\nconst ICON_COLLAPSE_THRESHOLD = 3\n\n/** Fallback icon for a pane that declares none — so an icon-collapsed tab is never blank. */\nfunction paneIcon(pane: InspectorPane): ReactNode {\n return pane.icon ?? <PanelRight className=\"size-3.5\" />\n}\n\n/**\n * EC-224 — the shared, view-supplied inspector sidebar.\n *\n * A generic tabbed frame: it renders whatever {@link InspectorPane}s it is given\n * and owns nothing domain-specific. The OWNER composes the pane list — by\n * convention `panes = [...view.suppliedPanes, versionsPane]`, so a view (the\n * canvas) contributes its context pane (`Properties`) and the frame-owner (the\n * document editor) appends the document-level `Versions` pane. A future rich\n * field editor contributes its own pane the same way, without touching this\n * frame (AD-12 — the canvas is itself just a field editor).\n *\n * Each pane's `render` runs where the frame is mounted, so a pane that needs a\n * scoped context (e.g. the canvas `BuilderProvider` behind `PropertyInspector`)\n * must be supplied by a view that mounts the frame inside that provider.\n */\nexport interface InspectorPane {\n id: string\n label: string\n icon?: ReactNode\n /** Optional trailing adornment on the tab (e.g. a count badge for `Issues`). */\n badge?: ReactNode\n render: () => ReactNode\n}\n\nexport function InspectorSidebar({\n panes,\n className,\n activeId: controlledId,\n onActiveChange,\n}: {\n panes: readonly InspectorPane[]\n className?: string\n /**\n * Optionally control which pane is shown (EC-285): the document editor drives\n * this to open the `Details` pane when a jump-to-field targets a sidebar field.\n * Uncontrolled (internal state) when omitted.\n */\n activeId?: string\n onActiveChange?: (id: string) => void\n}) {\n const [internalId, setInternalId] = useState<string | undefined>(() => panes[0]?.id)\n const activeId = controlledId ?? internalId\n const select = (id: string) => {\n if (controlledId === undefined) setInternalId(id)\n onActiveChange?.(id)\n }\n // Derive the active pane defensively so a changed pane list (switching views)\n // can never render a stale/absent tab — fall back to the first pane.\n const active = panes.find((pane) => pane.id === activeId) ?? panes[0]\n if (!active) return null\n\n // 0.0.42: with a crowded tab strip (3+ panes) the inactive tabs collapse to icons so\n // the labels stop overflowing; the active tab always keeps its icon + label.\n const collapseInactive = panes.length >= ICON_COLLAPSE_THRESHOLD\n\n return (\n <div className={cn('flex h-full min-h-0 flex-col bg-card', className)}>\n <Row gap=\"1\" className=\"shrink-0 border-b border-border p-1.5\">\n {panes.map((pane) => {\n const isActive = pane.id === active.id\n return (\n <PaneTab\n key={pane.id}\n pane={pane}\n active={isActive}\n showLabel={isActive || !collapseInactive}\n onClick={() => select(pane.id)}\n />\n )\n })}\n </Row>\n <div className=\"min-h-0 flex-1 overflow-auto\">{active.render()}</div>\n </div>\n )\n}\n\nfunction PaneTab({\n pane,\n active,\n showLabel,\n onClick,\n}: {\n pane: InspectorPane\n active: boolean\n /** When false, the tab renders icon-only — the label moves to the tooltip + aria-label. */\n showLabel: boolean\n onClick: () => void\n}) {\n return (\n <button\n type=\"button\"\n aria-pressed={active}\n aria-label={showLabel ? undefined : pane.label}\n title={showLabel ? undefined : pane.label}\n onClick={onClick}\n className={cn(\n 'inline-flex items-center gap-1.5 rounded-md px-2.5 py-1 transition-colors',\n active ? 'bg-accent text-accent-foreground' : 'text-muted-foreground hover:bg-muted',\n )}\n >\n {paneIcon(pane)}\n {showLabel ? (\n <Text as=\"span\" size=\"xs\" weight=\"medium\" className=\"leading-none\">\n {pane.label}\n </Text>\n ) : null}\n {pane.badge}\n </button>\n )\n}\n"]}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { IssueSeverity } from './issue-severity.js';
|
|
3
|
+
import type { NavIssueItem } from './nav-issues.js';
|
|
4
|
+
/**
|
|
5
|
+
* EC-616 — the rail foot's CONTRIBUTION seam (`useNavIssues`, ruling § 6b item 1).
|
|
6
|
+
*
|
|
7
|
+
* The issues foot (`SectionNav`) is on every studio surface, so it is where a finding the
|
|
8
|
+
* developer must not miss belongs — but its sources were a fixed list (config, nav,
|
|
9
|
+
* renderers, reuse, routes, orphans), and a custom view or an addon had no way in. Now any
|
|
10
|
+
* component under the studio shell publishes findings with {@link useNavIssues}; the foot
|
|
11
|
+
* renders them beside its own. While the publishing component is mounted its findings
|
|
12
|
+
* show; on unmount they go. A rail item that must ALWAYS report (not only while its view
|
|
13
|
+
* is open) declares `useIssues` on its view (`nav.view({ useIssues })` /
|
|
14
|
+
* `nav.rail(kind, { useIssues })`) — the rail mounts that hook for it.
|
|
15
|
+
*
|
|
16
|
+
* A finding is plain data: a stable `key` (unique within its source), the localized
|
|
17
|
+
* `message`, and a `severity` (`'error' | 'warning'`, default `'warning'`).
|
|
18
|
+
*/
|
|
19
|
+
/** One finding a view or addon puts in the rail foot. */
|
|
20
|
+
export interface NavIssue {
|
|
21
|
+
/** Stable within its source (re-publishing the same key replaces, never duplicates). */
|
|
22
|
+
key: string;
|
|
23
|
+
/** The localized message, shown as written. */
|
|
24
|
+
message: string;
|
|
25
|
+
/** Default `'warning'`. */
|
|
26
|
+
severity?: IssueSeverity;
|
|
27
|
+
}
|
|
28
|
+
type Listener = () => void;
|
|
29
|
+
/** The shell-scoped store of contributed findings, keyed by source. */
|
|
30
|
+
export declare class NavIssuesRegistry {
|
|
31
|
+
private readonly bySource;
|
|
32
|
+
private readonly listeners;
|
|
33
|
+
private snapshot;
|
|
34
|
+
subscribe: (listener: Listener) => (() => void);
|
|
35
|
+
getSnapshot: () => readonly NavIssueItem[];
|
|
36
|
+
/** Publish (or replace) one source's findings; an empty list removes the source. */
|
|
37
|
+
publish(source: string, issues: readonly NavIssue[]): void;
|
|
38
|
+
retract(source: string): void;
|
|
39
|
+
private recompute;
|
|
40
|
+
}
|
|
41
|
+
/** Mounted once by the studio project screen, around the shell (rail + content). */
|
|
42
|
+
export declare function NavIssuesProvider({ children }: {
|
|
43
|
+
children: ReactNode;
|
|
44
|
+
}): import("react").JSX.Element;
|
|
45
|
+
/**
|
|
46
|
+
* Publish findings to the rail foot while this component is mounted. `source` names the
|
|
47
|
+
* publisher (`'forms'`, `'analytics:beacon'`); pass the same `issues` array identity
|
|
48
|
+
* between renders (memoize it) or the foot re-renders on every render.
|
|
49
|
+
*/
|
|
50
|
+
export declare function useNavIssues(source: string, issues: readonly NavIssue[]): void;
|
|
51
|
+
/** The contributed findings, for the rail foot. */
|
|
52
|
+
export declare function useContributedNavIssues(): readonly NavIssueItem[];
|
|
53
|
+
export {};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useContext, useEffect, useMemo, useSyncExternalStore } from 'react';
|
|
3
|
+
/** The shell-scoped store of contributed findings, keyed by source. */
|
|
4
|
+
export class NavIssuesRegistry {
|
|
5
|
+
bySource = new Map();
|
|
6
|
+
listeners = new Set();
|
|
7
|
+
snapshot = [];
|
|
8
|
+
subscribe = (listener) => {
|
|
9
|
+
this.listeners.add(listener);
|
|
10
|
+
return () => {
|
|
11
|
+
this.listeners.delete(listener);
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
getSnapshot = () => this.snapshot;
|
|
15
|
+
/** Publish (or replace) one source's findings; an empty list removes the source. */
|
|
16
|
+
publish(source, issues) {
|
|
17
|
+
if (issues.length === 0)
|
|
18
|
+
this.bySource.delete(source);
|
|
19
|
+
else
|
|
20
|
+
this.bySource.set(source, issues);
|
|
21
|
+
this.recompute();
|
|
22
|
+
}
|
|
23
|
+
retract(source) {
|
|
24
|
+
if (this.bySource.delete(source))
|
|
25
|
+
this.recompute();
|
|
26
|
+
}
|
|
27
|
+
recompute() {
|
|
28
|
+
const items = [];
|
|
29
|
+
for (const [source, issues] of this.bySource) {
|
|
30
|
+
for (const issue of issues) {
|
|
31
|
+
items.push({ key: `contributed:${source}:${issue.key}`, message: issue.message, severity: issue.severity ?? 'warning' });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
this.snapshot = items;
|
|
35
|
+
for (const listener of [...this.listeners])
|
|
36
|
+
listener();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const NavIssuesContext = createContext(null);
|
|
40
|
+
/** Mounted once by the studio project screen, around the shell (rail + content). */
|
|
41
|
+
export function NavIssuesProvider({ children }) {
|
|
42
|
+
const registry = useMemo(() => new NavIssuesRegistry(), []);
|
|
43
|
+
return _jsx(NavIssuesContext.Provider, { value: registry, children: children });
|
|
44
|
+
}
|
|
45
|
+
function useRegistry(caller) {
|
|
46
|
+
const registry = useContext(NavIssuesContext);
|
|
47
|
+
if (!registry) {
|
|
48
|
+
throw new Error(`${caller} must be used inside the studio shell (no NavIssuesProvider above this component).`);
|
|
49
|
+
}
|
|
50
|
+
return registry;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Publish findings to the rail foot while this component is mounted. `source` names the
|
|
54
|
+
* publisher (`'forms'`, `'analytics:beacon'`); pass the same `issues` array identity
|
|
55
|
+
* between renders (memoize it) or the foot re-renders on every render.
|
|
56
|
+
*/
|
|
57
|
+
export function useNavIssues(source, issues) {
|
|
58
|
+
const registry = useRegistry('useNavIssues');
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
registry.publish(source, issues);
|
|
61
|
+
}, [registry, source, issues]);
|
|
62
|
+
useEffect(() => () => registry.retract(source), [registry, source]);
|
|
63
|
+
}
|
|
64
|
+
/** The contributed findings, for the rail foot. */
|
|
65
|
+
export function useContributedNavIssues() {
|
|
66
|
+
const registry = useRegistry('useContributedNavIssues');
|
|
67
|
+
return useSyncExternalStore(registry.subscribe, registry.getSnapshot, registry.getSnapshot);
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=nav-issues-registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nav-issues-registry.js","sourceRoot":"","sources":["../../src/components/nav-issues-registry.tsx"],"names":[],"mappings":"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","sourcesContent":["import { createContext, useContext, useEffect, useMemo, useSyncExternalStore } from 'react'\nimport type { ReactNode } from 'react'\nimport type { IssueSeverity } from './issue-severity'\nimport type { NavIssueItem } from './nav-issues'\n\n/**\n * EC-616 — the rail foot's CONTRIBUTION seam (`useNavIssues`, ruling § 6b item 1).\n *\n * The issues foot (`SectionNav`) is on every studio surface, so it is where a finding the\n * developer must not miss belongs — but its sources were a fixed list (config, nav,\n * renderers, reuse, routes, orphans), and a custom view or an addon had no way in. Now any\n * component under the studio shell publishes findings with {@link useNavIssues}; the foot\n * renders them beside its own. While the publishing component is mounted its findings\n * show; on unmount they go. A rail item that must ALWAYS report (not only while its view\n * is open) declares `useIssues` on its view (`nav.view({ useIssues })` /\n * `nav.rail(kind, { useIssues })`) — the rail mounts that hook for it.\n *\n * A finding is plain data: a stable `key` (unique within its source), the localized\n * `message`, and a `severity` (`'error' | 'warning'`, default `'warning'`).\n */\n\n/** One finding a view or addon puts in the rail foot. */\nexport interface NavIssue {\n /** Stable within its source (re-publishing the same key replaces, never duplicates). */\n key: string\n /** The localized message, shown as written. */\n message: string\n /** Default `'warning'`. */\n severity?: IssueSeverity\n}\n\ntype Listener = () => void\n\n/** The shell-scoped store of contributed findings, keyed by source. */\nexport class NavIssuesRegistry {\n private readonly bySource = new Map<string, readonly NavIssue[]>()\n private readonly listeners = new Set<Listener>()\n private snapshot: readonly NavIssueItem[] = []\n\n subscribe = (listener: Listener): (() => void) => {\n this.listeners.add(listener)\n return () => {\n this.listeners.delete(listener)\n }\n }\n\n getSnapshot = (): readonly NavIssueItem[] => this.snapshot\n\n /** Publish (or replace) one source's findings; an empty list removes the source. */\n publish(source: string, issues: readonly NavIssue[]): void {\n if (issues.length === 0) this.bySource.delete(source)\n else this.bySource.set(source, issues)\n this.recompute()\n }\n\n retract(source: string): void {\n if (this.bySource.delete(source)) this.recompute()\n }\n\n private recompute(): void {\n const items: NavIssueItem[] = []\n for (const [source, issues] of this.bySource) {\n for (const issue of issues) {\n items.push({ key: `contributed:${source}:${issue.key}`, message: issue.message, severity: issue.severity ?? 'warning' })\n }\n }\n this.snapshot = items\n for (const listener of [...this.listeners]) listener()\n }\n}\n\nconst NavIssuesContext = createContext<NavIssuesRegistry | null>(null)\n\n/** Mounted once by the studio project screen, around the shell (rail + content). */\nexport function NavIssuesProvider({ children }: { children: ReactNode }) {\n const registry = useMemo(() => new NavIssuesRegistry(), [])\n return <NavIssuesContext.Provider value={registry}>{children}</NavIssuesContext.Provider>\n}\n\nfunction useRegistry(caller: string): NavIssuesRegistry {\n const registry = useContext(NavIssuesContext)\n if (!registry) {\n throw new Error(`${caller} must be used inside the studio shell (no NavIssuesProvider above this component).`)\n }\n return registry\n}\n\n/**\n * Publish findings to the rail foot while this component is mounted. `source` names the\n * publisher (`'forms'`, `'analytics:beacon'`); pass the same `issues` array identity\n * between renders (memoize it) or the foot re-renders on every render.\n */\nexport function useNavIssues(source: string, issues: readonly NavIssue[]): void {\n const registry = useRegistry('useNavIssues')\n useEffect(() => {\n registry.publish(source, issues)\n }, [registry, source, issues])\n useEffect(() => () => registry.retract(source), [registry, source])\n}\n\n/** The contributed findings, for the rail foot. */\nexport function useContributedNavIssues(): readonly NavIssueItem[] {\n const registry = useRegistry('useContributedNavIssues')\n return useSyncExternalStore(registry.subscribe, registry.getSnapshot, registry.getSnapshot)\n}\n"]}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { cn } from '
|
|
3
|
-
import { translate } from '
|
|
4
|
-
import { PROJECT_CONFIG_FILE } from '
|
|
5
|
-
import { cmsIssueLabel, cmsValidationIssueMessage, customizationIssueMessage, } from '
|
|
2
|
+
import { cn } from '../lib/utils.js';
|
|
3
|
+
import { translate } from '../lib/i18n/index.js';
|
|
4
|
+
import { PROJECT_CONFIG_FILE } from '../lib/workspace/index.js';
|
|
5
|
+
import { cmsIssueLabel, cmsValidationIssueMessage, customizationIssueMessage, } from '../lib/persona/labels.js';
|
|
6
6
|
import { Row, Stack, Text } from '@elytracms/studio/ui';
|
|
7
7
|
import { highestIssueSeverity, issueSeverityIcon, issueSeverityStyle } from './issue-severity.js';
|
|
8
8
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nav-issues.js","sourceRoot":"","sources":["../../src/components/nav-issues.tsx"],"names":[],"mappings":"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","sourcesContent":["import type { CmsValidationIssue, OrphanedCollection } from '@elytracms/core/cms-core'\nimport type { CustomizationIssue } from '@elytracms/core/studio-core'\nimport { cn } from '@elytracms/studio/lib/utils'\nimport { translate } from '@elytracms/studio/lib/i18n'\nimport { PROJECT_CONFIG_FILE } from '@elytracms/studio/lib/workspace'\nimport {\n cmsIssueLabel,\n cmsValidationIssueMessage,\n customizationIssueMessage,\n} from '@elytracms/studio/lib/persona/labels'\nimport { Row, Stack, Text } from '@elytracms/studio/ui'\nimport { highestIssueSeverity, issueSeverityIcon, issueSeverityStyle } from './issue-severity'\nimport type { IssueSeverity } from './issue-severity'\n\n/**\n * What the rail foot needs to render one finding: a stable key, a message, and the\n * finding's severity. The least common shape across its contributing sources —\n * customization issues (`CustomizationIssue`) and URL-map validation\n * (`CmsValidationIssue`, EC-469) — so a new source joins by mapping to this, not by\n * pretending to be a customization issue.\n *\n * `severity` was added by EC-493: both mappers used to drop it and the widget painted\n * every finding amber, which stopped being a style nit once EC-488 re-derived\n * `shadowedMountSpaces` from `error` to `warning` ON THE REASONING that the two mean\n * different things, and EC-482 shipped one producer (`hierarchyMountConflicts`)\n * emitting both into this very list.\n */\nexport interface NavIssueItem {\n key: string\n message: string\n severity: IssueSeverity\n}\n\n/**\n * Customization issues, as rail-foot items — localized (EC-478). The producers\n * (`studio-core/customization.ts`, `default-modules.tsx`, `builder/reuse.ts`) emit a\n * catalog key + identifier values rather than a sentence, because the EC-236 seam\n * lives in `packages/core` and may not import the studio catalog;\n * {@link customizationIssueMessage} resolves them here, so this list renders wholly in\n * the active locale instead of half of it.\n *\n * EC-493: `severity` rides along. It used to be dropped here, which is half of why an\n * error and a warning were indistinguishable at the foot — `CustomizationIssue` has\n * carried the field since EC-236, the mapper simply did not read it.\n */\nexport function customizationItems(issues: readonly CustomizationIssue[]): NavIssueItem[] {\n return issues.map((issue, index) => ({\n key: `${issue.point}:${issue.id ?? index}`,\n message: customizationIssueMessage(issue),\n severity: issue.severity,\n }))\n}\n\n/**\n * URL-map validation issues (EC-469), as rail-foot items.\n *\n * Copy follows the issues-pane convention (EC-192): the localized plain-language label,\n * then the detail — both localized since EC-487, which gave `CmsValidationIssue` the\n * same catalog-reference carriage EC-478 gave `CustomizationIssue`.\n *\n * EC-493: `severity` rides along, the other half of the fix. It matters most here:\n * EC-482's `hierarchyMountConflicts` emits `error` for two mounts on the same path and\n * `warning` for a nested pair FROM ONE FUNCTION, and EC-488 re-derived\n * `shadowedMountSpaces` to `warning` precisely because the fall-through made it\n * conditional. Both land in this list.\n */\nexport function routeIssueItems(issues: readonly CmsValidationIssue[]): NavIssueItem[] {\n return issues.map((issue, index) => ({\n key: `route-issue:${issue.code}:${issue.path.join('/') || index}`,\n message: `${cmsIssueLabel(issue.code, issue.severity)} — ${cmsValidationIssueMessage(issue)}`,\n severity: issue.severity,\n }))\n}\n\n/** The stable key of the one orphaned-collections finding (EC-606). */\nexport const ORPHANED_COLLECTIONS_ISSUE_KEY = 'orphaned-collections'\n\n/**\n * EC-606 — stored documents whose collection the project does not declare, as ONE\n * rail-foot item (or none).\n *\n * Session hydration keeps only the declared collections' documents and counts the\n * rest (`HydratedCms.orphans`, via core's `orphanedCollections`). Before EC-606 those\n * rows were dropped with no trace, so renaming a collection id in config made its\n * documents vanish from the studio. One item for the whole list: it is one condition\n * with one remedy, and N cards would crowd out the rail.\n *\n * `warning`, not `error`: nothing is lost (the rows are stored, untouched) and nothing\n * the editor does is blocked. The message names only one remedy, declaring the\n * collection again or restoring its old id (B6). `documents.clear` plus a re-seed\n * would also remove the warning, but for a renamed collection on a live site that is\n * data loss, so the message does not suggest it. A published count rides each entry\n * that has one: those rows are still on the public delivery snapshot.\n */\nexport function orphanedCollectionItems(orphans: readonly OrphanedCollection[]): NavIssueItem[] {\n if (orphans.length === 0) return []\n const collections = orphans\n .map(({ collection, count, published }) =>\n published > 0\n ? translate('shell.nav.orphanedCollections.itemPublished', { collection, count, published })\n : translate('shell.nav.orphanedCollections.item', { collection, count }),\n )\n .join(', ')\n return [\n {\n key: ORPHANED_COLLECTIONS_ISSUE_KEY,\n message: translate('shell.nav.orphanedCollections', { collections, file: PROJECT_CONFIG_FILE }),\n severity: 'warning',\n },\n ]\n}\n\n/**\n * Visible project-level issues at the rail foot — the seam's \"never silent\" (EC-236,\n * extended by EC-467's reuse issues, EC-469's URL-map issues, EC-479's config\n * issues and EC-606's orphaned collections).\n *\n * EC-493: an `error` and a `warning` render distinguishably here, in the treatment the\n * document Issues pane already uses. Each finding is its own tinted card (as in the\n * pane) rather than one shared amber box, because a shared box cannot express a mixed\n * list; collapsed, the single glyph follows {@link highestIssueSeverity}. EC-507 moved\n * that treatment to `components/issue-severity.ts`, the one home all three surfaces\n * import from.\n *\n * `data-nav-issue-severity` is on each card (and on the collapsed glyph) as the\n * machine-readable form of the same distinction — what the smoke test pins, and what an\n * agent inspecting the DOM reads without decoding Tailwind classes.\n */\nexport function NavIssues({ issues, collapsed }: { issues: NavIssueItem[]; collapsed: boolean }) {\n if (collapsed) {\n const severity = highestIssueSeverity(issues)\n const Icon = issueSeverityIcon(severity)\n return (\n <div\n className={cn('grid h-9 place-items-center', issueSeverityStyle(severity).text)}\n data-nav-issue-severity={severity}\n title={issues.map((issue) => issue.message).join('\\n')}\n >\n <Icon\n className=\"size-4\"\n aria-label={translate('shell.nav.issuesCount', { count: issues.length })}\n />\n </div>\n )\n }\n return (\n <Stack gap=\"1\" className=\"mb-1\">\n {issues.map((issue) => (\n <NavIssueCard key={issue.key} issue={issue} />\n ))}\n </Stack>\n )\n}\n\n/** One finding at the rail foot, tinted + glyphed by its severity. */\nfunction NavIssueCard({ issue }: { issue: NavIssueItem }) {\n const style = issueSeverityStyle(issue.severity)\n const Icon = issueSeverityIcon(issue.severity)\n return (\n <Row\n gap=\"1\"\n align=\"start\"\n // `gap-1.5` (the pre-EC-493 icon/text spacing) merged over the primitive's `gap-1`.\n className={cn('gap-1.5 rounded-md border px-2 py-1.5', style.card, style.text)}\n data-nav-issue-severity={issue.severity}\n title={issue.message}\n >\n <Icon className=\"mt-0.5 size-3.5 shrink-0\" aria-hidden />\n <Text size=\"xs\" className=\"min-w-0 leading-snug\">\n {issue.message}\n </Text>\n </Row>\n )\n}\n"]}
|
|
1
|
+
{"version":3,"file":"nav-issues.js","sourceRoot":"","sources":["../../src/components/nav-issues.tsx"],"names":[],"mappings":"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","sourcesContent":["import type { CmsValidationIssue, OrphanedCollection } from '@elytracms/core/cms-core'\nimport type { CustomizationIssue } from '@elytracms/core/studio-core'\nimport { cn } from '../lib/utils'\nimport { translate } from '../lib/i18n'\nimport { PROJECT_CONFIG_FILE } from '../lib/workspace'\nimport {\n cmsIssueLabel,\n cmsValidationIssueMessage,\n customizationIssueMessage,\n} from '../lib/persona/labels'\nimport { Row, Stack, Text } from '@elytracms/studio/ui'\nimport { highestIssueSeverity, issueSeverityIcon, issueSeverityStyle } from './issue-severity'\nimport type { IssueSeverity } from './issue-severity'\n\n/**\n * What the rail foot needs to render one finding: a stable key, a message, and the\n * finding's severity. The least common shape across its contributing sources —\n * customization issues (`CustomizationIssue`) and URL-map validation\n * (`CmsValidationIssue`, EC-469) — so a new source joins by mapping to this, not by\n * pretending to be a customization issue.\n *\n * `severity` was added by EC-493: both mappers used to drop it and the widget painted\n * every finding amber, which stopped being a style nit once EC-488 re-derived\n * `shadowedMountSpaces` from `error` to `warning` ON THE REASONING that the two mean\n * different things, and EC-482 shipped one producer (`hierarchyMountConflicts`)\n * emitting both into this very list.\n */\nexport interface NavIssueItem {\n key: string\n message: string\n severity: IssueSeverity\n}\n\n/**\n * Customization issues, as rail-foot items — localized (EC-478). The producers\n * (`studio-core/customization.ts`, `default-modules.tsx`, `builder/reuse.ts`) emit a\n * catalog key + identifier values rather than a sentence, because the EC-236 seam\n * lives in `packages/core` and may not import the studio catalog;\n * {@link customizationIssueMessage} resolves them here, so this list renders wholly in\n * the active locale instead of half of it.\n *\n * EC-493: `severity` rides along. It used to be dropped here, which is half of why an\n * error and a warning were indistinguishable at the foot — `CustomizationIssue` has\n * carried the field since EC-236, the mapper simply did not read it.\n */\nexport function customizationItems(issues: readonly CustomizationIssue[]): NavIssueItem[] {\n return issues.map((issue, index) => ({\n key: `${issue.point}:${issue.id ?? index}`,\n message: customizationIssueMessage(issue),\n severity: issue.severity,\n }))\n}\n\n/**\n * URL-map validation issues (EC-469), as rail-foot items.\n *\n * Copy follows the issues-pane convention (EC-192): the localized plain-language label,\n * then the detail — both localized since EC-487, which gave `CmsValidationIssue` the\n * same catalog-reference carriage EC-478 gave `CustomizationIssue`.\n *\n * EC-493: `severity` rides along, the other half of the fix. It matters most here:\n * EC-482's `hierarchyMountConflicts` emits `error` for two mounts on the same path and\n * `warning` for a nested pair FROM ONE FUNCTION, and EC-488 re-derived\n * `shadowedMountSpaces` to `warning` precisely because the fall-through made it\n * conditional. Both land in this list.\n */\nexport function routeIssueItems(issues: readonly CmsValidationIssue[]): NavIssueItem[] {\n return issues.map((issue, index) => ({\n key: `route-issue:${issue.code}:${issue.path.join('/') || index}`,\n message: `${cmsIssueLabel(issue.code, issue.severity)} — ${cmsValidationIssueMessage(issue)}`,\n severity: issue.severity,\n }))\n}\n\n/** The stable key of the one orphaned-collections finding (EC-606). */\nexport const ORPHANED_COLLECTIONS_ISSUE_KEY = 'orphaned-collections'\n\n/**\n * EC-606 — stored documents whose collection the project does not declare, as ONE\n * rail-foot item (or none).\n *\n * Session hydration keeps only the declared collections' documents and counts the\n * rest (`HydratedCms.orphans`, via core's `orphanedCollections`). Before EC-606 those\n * rows were dropped with no trace, so renaming a collection id in config made its\n * documents vanish from the studio. One item for the whole list: it is one condition\n * with one remedy, and N cards would crowd out the rail.\n *\n * `warning`, not `error`: nothing is lost (the rows are stored, untouched) and nothing\n * the editor does is blocked. The message names only one remedy, declaring the\n * collection again or restoring its old id (B6). `documents.clear` plus a re-seed\n * would also remove the warning, but for a renamed collection on a live site that is\n * data loss, so the message does not suggest it. A published count rides each entry\n * that has one: those rows are still on the public delivery snapshot.\n */\nexport function orphanedCollectionItems(orphans: readonly OrphanedCollection[]): NavIssueItem[] {\n if (orphans.length === 0) return []\n const collections = orphans\n .map(({ collection, count, published }) =>\n published > 0\n ? translate('shell.nav.orphanedCollections.itemPublished', { collection, count, published })\n : translate('shell.nav.orphanedCollections.item', { collection, count }),\n )\n .join(', ')\n return [\n {\n key: ORPHANED_COLLECTIONS_ISSUE_KEY,\n message: translate('shell.nav.orphanedCollections', { collections, file: PROJECT_CONFIG_FILE }),\n severity: 'warning',\n },\n ]\n}\n\n/**\n * Visible project-level issues at the rail foot — the seam's \"never silent\" (EC-236,\n * extended by EC-467's reuse issues, EC-469's URL-map issues, EC-479's config\n * issues and EC-606's orphaned collections).\n *\n * EC-493: an `error` and a `warning` render distinguishably here, in the treatment the\n * document Issues pane already uses. Each finding is its own tinted card (as in the\n * pane) rather than one shared amber box, because a shared box cannot express a mixed\n * list; collapsed, the single glyph follows {@link highestIssueSeverity}. EC-507 moved\n * that treatment to `components/issue-severity.ts`, the one home all three surfaces\n * import from.\n *\n * `data-nav-issue-severity` is on each card (and on the collapsed glyph) as the\n * machine-readable form of the same distinction — what the smoke test pins, and what an\n * agent inspecting the DOM reads without decoding Tailwind classes.\n */\nexport function NavIssues({ issues, collapsed }: { issues: NavIssueItem[]; collapsed: boolean }) {\n if (collapsed) {\n const severity = highestIssueSeverity(issues)\n const Icon = issueSeverityIcon(severity)\n return (\n <div\n className={cn('grid h-9 place-items-center', issueSeverityStyle(severity).text)}\n data-nav-issue-severity={severity}\n title={issues.map((issue) => issue.message).join('\\n')}\n >\n <Icon\n className=\"size-4\"\n aria-label={translate('shell.nav.issuesCount', { count: issues.length })}\n />\n </div>\n )\n }\n return (\n <Stack gap=\"1\" className=\"mb-1\">\n {issues.map((issue) => (\n <NavIssueCard key={issue.key} issue={issue} />\n ))}\n </Stack>\n )\n}\n\n/** One finding at the rail foot, tinted + glyphed by its severity. */\nfunction NavIssueCard({ issue }: { issue: NavIssueItem }) {\n const style = issueSeverityStyle(issue.severity)\n const Icon = issueSeverityIcon(issue.severity)\n return (\n <Row\n gap=\"1\"\n align=\"start\"\n // `gap-1.5` (the pre-EC-493 icon/text spacing) merged over the primitive's `gap-1`.\n className={cn('gap-1.5 rounded-md border px-2 py-1.5', style.card, style.text)}\n data-nav-issue-severity={issue.severity}\n title={issue.message}\n >\n <Icon className=\"mt-0.5 size-3.5 shrink-0\" aria-hidden />\n <Text size=\"xs\" className=\"min-w-0 leading-snug\">\n {issue.message}\n </Text>\n </Row>\n )\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { OperationError } from '
|
|
1
|
+
import type { OperationError } from '../lib/operations/index.js';
|
|
2
2
|
/**
|
|
3
3
|
* Shared error surface for failed/denied operations (EC-141). Every studio
|
|
4
4
|
* surface renders rejected commands through this banner so a structured
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect } from 'react';
|
|
3
3
|
import { CircleAlert, X } from 'lucide-react';
|
|
4
|
-
import { isSessionExpiredError } from '
|
|
5
|
-
import { reportSessionExpiredError } from '
|
|
4
|
+
import { isSessionExpiredError } from '../lib/auth-errors.js';
|
|
5
|
+
import { reportSessionExpiredError } from '../lib/session-expiry.js';
|
|
6
6
|
import { Badge, Button, Row, Stack, Text } from '@elytracms/studio/ui';
|
|
7
|
-
import { operationErrorLabel, operationErrorMessage, operationIssueMessage, } from '
|
|
8
|
-
import { translate } from '
|
|
7
|
+
import { operationErrorLabel, operationErrorMessage, operationIssueMessage, } from '../lib/persona/labels.js';
|
|
8
|
+
import { translate } from '../lib/i18n/index.js';
|
|
9
9
|
/**
|
|
10
10
|
* Shared error surface for failed/denied operations (EC-141). Every studio
|
|
11
11
|
* surface renders rejected commands through this banner so a structured
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"operation-error.js","sourceRoot":"","sources":["../../src/components/operation-error.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACjC,OAAO,EAAE,WAAW,EAAE,CAAC,EAAE,MAAM,cAAc,CAAA;AAE7C,OAAO,EAAE,qBAAqB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"operation-error.js","sourceRoot":"","sources":["../../src/components/operation-error.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACjC,OAAO,EAAE,WAAW,EAAE,CAAC,EAAE,MAAM,cAAc,CAAA;AAE7C,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAA;AAC1D,OAAO,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAA;AACjE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAA;AACtE,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAEvC;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,oBAAoB,CAAC,EACnC,KAAK,EACL,SAAS,EACT,SAAS,GAKV;IACC,uEAAuE;IACvE,yEAAyE;IACzE,kEAAkE;IAClE,0DAA0D;IAC1D,SAAS,CAAC,GAAG,EAAE;QACb,yBAAyB,CAAC,KAAK,CAAC,CAAA;IAClC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAA;IAEX,IAAI,qBAAqB,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IAE7C,OAAO,CACL,cACE,IAAI,EAAC,OAAO,EACZ,SAAS,EACP,sEAAsE,GAAG,CAAC,SAAS,IAAI,EAAE,CAAC,YAG5F,MAAC,GAAG,IAAC,GAAG,EAAC,GAAG,EAAC,KAAK,EAAC,OAAO,aACxB,KAAC,WAAW,IAAC,SAAS,EAAC,yCAAyC,GAAG,EACnE,MAAC,KAAK,IAAC,GAAG,EAAC,GAAG,EAAC,SAAS,EAAC,gBAAgB,aACvC,MAAC,GAAG,IAAC,GAAG,EAAC,GAAG,EAAC,KAAK,EAAC,QAAQ,aACzB,KAAC,KAAK,IAAC,OAAO,EAAC,OAAO,EAAC,KAAK,EAAE,KAAK,CAAC,IAAI,eAAa,KAAK,CAAC,IAAI,YAC5D,mBAAmB,CAAC,KAAK,CAAC,IAAI,CAAC,GAC1B,EACR,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,SAAS,EAAC,kBAAkB,YAKzC,qBAAqB,CAAC,KAAK,CAAC,GACxB,IACH,EACL,KAAK,CAAC,IAAI,KAAK,YAAY,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CACxD,KAAC,KAAK,IAAC,GAAG,EAAC,MAAM,YACd,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,CAC9B,KAAC,IAAI,IAEH,IAAI,EAAC,IAAI,EACT,IAAI,EAAC,OAAO;gCACZ,2DAA2D;gCAC3D,8DAA8D;gCAC9D,sDAAsD;gCACtD,KAAK,EAAE,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,YAE5E,qBAAqB,CAAC,KAAK,CAAC,IARxB,GAAG,KAAK,CAAC,IAAI,IAAI,CAAC,EAAE,CASpB,CACR,CAAC,GACI,CACT,CAAC,CAAC,CAAC,IAAI,IACF,EACP,SAAS,CAAC,CAAC,CAAC,CACX,KAAC,MAAM,IACL,IAAI,EAAC,SAAS,EACd,OAAO,EAAC,OAAO,gBACH,SAAS,CAAC,eAAe,CAAC,EACtC,OAAO,EAAE,SAAS,EAClB,SAAS,EAAC,UAAU,YAEpB,KAAC,CAAC,KAAG,GACE,CACV,CAAC,CAAC,CAAC,IAAI,IACJ,GACF,CACP,CAAA;AACH,CAAC","sourcesContent":["import { useEffect } from 'react'\nimport { CircleAlert, X } from 'lucide-react'\nimport type { OperationError } from '../lib/operations'\nimport { isSessionExpiredError } from '../lib/auth-errors'\nimport { reportSessionExpiredError } from '../lib/session-expiry'\nimport { Badge, Button, Row, Stack, Text } from '@elytracms/studio/ui'\nimport {\n operationErrorLabel,\n operationErrorMessage,\n operationIssueMessage,\n} from '../lib/persona/labels'\nimport { translate } from '../lib/i18n'\n\n/**\n * Shared error surface for failed/denied operations (EC-141). Every studio\n * surface renders rejected commands through this banner so a structured\n * `OperationError` — including its validation issues — is always *visible*,\n * never a silent failure.\n *\n * Session expiry (EC-177 gap 9, reworked by EC-403) is routed, not rendered:\n * an expired session is an app-level state, so the banner funnels the error\n * into the global session-expiry chokepoint (which raises the blocking\n * in-place re-auth overlay) and renders nothing itself — the per-surface\n * banner path remains only for non-expiry failures. The raw OIDC blob is\n * never shown either way.\n */\nexport function OperationErrorBanner({\n error,\n onDismiss,\n className,\n}: {\n error: OperationError\n onDismiss?: () => void\n className?: string\n}) {\n // Funnel every error into the EC-403 chokepoint (belt-and-braces for a\n // failure that reached this surface without passing the operations event\n // bus). Deduped per error instance there, so re-rendering a stale\n // persistError after re-auth never re-raises the overlay.\n useEffect(() => {\n reportSessionExpiredError(error)\n }, [error])\n\n if (isSessionExpiredError(error)) return null\n\n return (\n <div\n role=\"alert\"\n className={\n 'rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 ' + (className ?? '')\n }\n >\n <Row gap=\"2\" align=\"start\">\n <CircleAlert className=\"mt-0.5 size-4 shrink-0 text-destructive\" />\n <Stack gap=\"1\" className=\"min-w-0 flex-1\">\n <Row gap=\"2\" align=\"center\">\n <Badge variant=\"error\" title={error.code} data-code={error.code}>\n {operationErrorLabel(error.code)}\n </Badge>\n <Text size=\"sm\" className=\"text-destructive\">\n {/* EC-494: the envelope carries its own catalog ref across the wire,\n so this sentence localizes instead of sitting in English under a\n localized badge. Ref-less (a persistence-layer or hook-authored\n message) falls back to that English, visibly. */}\n {operationErrorMessage(error)}\n </Text>\n </Row>\n {error.code === 'validation' && error.issues.length > 0 ? (\n <Stack gap=\"none\">\n {error.issues.map((issue, i) => (\n <Text\n key={`${issue.code}-${i}`}\n size=\"xs\"\n tone=\"muted\"\n // The raw graph path (`pages.0.root.slots…`) is structural\n // machine addressing — kept inspectable on hover, never shown\n // as visible copy to a non-technical editor (EC-192).\n title={issue.path && issue.path.length > 0 ? issue.path.join('.') : undefined}\n >\n {operationIssueMessage(issue)}\n </Text>\n ))}\n </Stack>\n ) : null}\n </Stack>\n {onDismiss ? (\n <Button\n size=\"icon-sm\"\n variant=\"ghost\"\n aria-label={translate('error.dismiss')}\n onClick={onDismiss}\n className=\"shrink-0\"\n >\n <X />\n </Button>\n ) : null}\n </Row>\n </div>\n )\n}\n"]}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { FlaskConical } from 'lucide-react';
|
|
3
3
|
import { Badge } from '@elytracms/studio/ui';
|
|
4
|
-
import { isPlaygroundMode } from '
|
|
5
|
-
import { translate } from '
|
|
4
|
+
import { isPlaygroundMode } from '../lib/persistence/index.js';
|
|
5
|
+
import { translate } from '../lib/i18n/index.js';
|
|
6
6
|
/**
|
|
7
7
|
* EC-153: the explicit playground-mode marker. When the studio runs on the
|
|
8
8
|
* seeded in-memory adapter (`VITE_ELYTRA_MODE=playground`) this badge is
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"playground-badge.js","sourceRoot":"","sources":["../../src/components/playground-badge.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAC3C,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAA;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"playground-badge.js","sourceRoot":"","sources":["../../src/components/playground-badge.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAC3C,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAA;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAEvC;;;;;;GAMG;AACH,MAAM,UAAU,eAAe;IAC7B,IAAI,CAAC,gBAAgB,EAAE;QAAE,OAAO,IAAI,CAAA;IACpC,OAAO,CACL,MAAC,KAAK,IAAC,OAAO,EAAC,SAAS,EAAC,KAAK,EAAE,SAAS,CAAC,uBAAuB,CAAC,iBAAc,kBAAkB,aAChG,KAAC,YAAY,0BAAe,EAC3B,SAAS,CAAC,wBAAwB,CAAC,IAC9B,CACT,CAAA;AACH,CAAC","sourcesContent":["import { FlaskConical } from 'lucide-react'\nimport { Badge } from '@elytracms/studio/ui'\nimport { isPlaygroundMode } from '../lib/persistence'\nimport { translate } from '../lib/i18n'\n\n/**\n * EC-153: the explicit playground-mode marker. When the studio runs on the\n * seeded in-memory adapter (`VITE_ELYTRA_MODE=playground`) this badge is\n * always visible in the shell header; in Convex mode it renders nothing.\n * Deterministic: mode is resolved from build-time env, identical on SSR and\n * client.\n */\nexport function PlaygroundBadge() {\n if (!isPlaygroundMode()) return null\n return (\n <Badge variant=\"warning\" title={translate('shell.playground.hint')} data-testid=\"playground-badge\">\n <FlaskConical aria-hidden />\n {translate('shell.playground.badge')}\n </Badge>\n )\n}\n"]}
|
|
@@ -2,9 +2,9 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
import { isExplicitLauncherVisit, Link, NavigateOnce, STUDIO_PROJECT_ROUTE, studioPath, useLocation, } from '@elytracms/studio/router';
|
|
3
3
|
import { Globe, LogIn, TerminalSquare } from 'lucide-react';
|
|
4
4
|
import { Badge, Button, Card, Code, Heading, Row, Spacer, Stack, Text } from '@elytracms/studio/ui';
|
|
5
|
-
import { hasProjectSession } from '
|
|
6
|
-
import { launcherAutoForwardTarget } from '
|
|
7
|
-
import { translate } from '
|
|
5
|
+
import { hasProjectSession } from '../lib/auth/index.js';
|
|
6
|
+
import { launcherAutoForwardTarget } from '../lib/workspace/index.js';
|
|
7
|
+
import { translate } from '../lib/i18n/index.js';
|
|
8
8
|
import { PlaygroundBadge } from './playground-badge.js';
|
|
9
9
|
import { SignInNeededBadge } from './sign-in-needed-badge.js';
|
|
10
10
|
import { SectionStateView } from './section-state.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-list.js","sourceRoot":"","sources":["../../src/components/project-list.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n isExplicitLauncherVisit,\n Link,\n NavigateOnce,\n STUDIO_PROJECT_ROUTE,\n studioPath,\n useLocation,\n} from '@elytracms/studio/router'\nimport { Globe, LogIn, TerminalSquare } from 'lucide-react'\nimport type { Project, ResolvedProject, SectionState } from '@elytracms/core/studio-core'\nimport { Badge, Button, Card, Code, Heading, Row, Spacer, Stack, Text } from '@elytracms/studio/ui'\nimport { hasProjectSession } from '@elytracms/studio/lib/auth'\nimport { launcherAutoForwardTarget } from '@elytracms/studio/lib/workspace'\nimport { translate } from '@elytracms/studio/lib/i18n'\nimport { PlaygroundBadge } from './playground-badge'\nimport { SignInNeededBadge } from './sign-in-needed-badge'\nimport { SectionStateView } from './section-state'\nimport { useStudio } from './studio-context'\nimport { useRecentProjects, useSwitcherEntries } from './workspace-projects'\n\nexport interface ProjectLauncherProps {\n /** Force a deterministic list state for browser verification (?state=…). */\n forcedState?: SectionState | null\n}\n\n/**\n * Studio launcher / project list (brief §4.3, §8.1; reframed by EC-154). The\n * Studio's front door: lists the workspace's projects and routes into a\n * project's shell. With a workspace config the list is config-driven\n * (configured projects ∩ grants, plus explicit \"not provisioned\" states);\n * project *creation* is code-first — declare it in `elytra.config.ts`, then\n * provision the backend with `elytra init` — so there is no in-UI create flow.\n * Without a config the adapter-backed list (and the starter-kit gallery)\n * behaves as before.\n *\n * EC-470: this used to instruct `builder init --project <slug>`. No `builder`\n * bin was ever declared, so that was an unrunnable command shown as product UI.\n * The real CLI is `elytra` (`init` provisions Convex + auth keys; `doctor`\n * reports what is still missing).\n */\nexport function ProjectLauncher({ forcedState }: ProjectLauncherProps) {\n const studio = useStudio()\n const { entries, workspace } = useSwitcherEntries()\n\n const hasConfig = workspace.kind === 'config'\n const visible = entries\n const recents = useRecentProjects(entries)\n const location = useLocation()\n\n const forced = forcedState && forcedState !== 'working' ? forcedState : null\n\n // EC-401: a single-project workspace skips the launcher hop — the bare `/`\n // hit forwards straight into the sole project (replace, so Back never loops\n // through the launcher). Explicit \"all projects\" navigations carry the\n // history-state marker and keep the launcher visible, as do forced `?state=`\n // verification states and a lone UNPROVISIONED entry (its setup gate lives\n // here). Activation/recents are recorded by the project screen on arrival.\n const autoForward = launcherAutoForwardTarget({\n entries,\n forcedState: forcedState != null,\n explicitVisit: isExplicitLauncherVisit(location.state),\n })\n if (autoForward) {\n // EC-441: `NavigateOnce`, not `<Navigate>` — on a slow boot the launcher\n // stays mounted while the project splat's modules load, re-rendering on\n // every router-store tick; a per-render re-navigate compounded into the\n // \"Maximum update depth exceeded\" rejection (104 re-fires on one cold load).\n return (\n <NavigateOnce\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId: autoForward.id, _splat: studioPath.index() }}\n replace\n />\n )\n }\n\n return (\n <div className=\"mx-auto w-full max-w-6xl px-6 py-8\">\n <Row gap=\"3\" className=\"mb-6\">\n <Stack gap=\"1\">\n <Row gap=\"2\">\n <Heading size=\"display\">{translate('shell.projects')}</Heading>\n {/* EC-153: explicit playground-mode marker (renders nothing on Convex). */}\n <PlaygroundBadge />\n {hasConfig ? (\n // EC-154: this workspace is defined in code.\n <Badge variant=\"outline\">\n <TerminalSquare className=\"size-3\" /> {workspace.source}\n </Badge>\n ) : null}\n </Row>\n <Text tone=\"muted\">\n {hasConfig\n ? 'Projects are defined in code. Add entries to elytra.config.ts and provision them with the CLI.'\n : 'Open and manage your Elytra projects.'}\n </Text>\n </Stack>\n <Spacer />\n </Row>\n\n {recents.length > 0 && !forced ? (\n <Stack gap=\"2\" className=\"mb-6\">\n <Text size=\"xs\" tone=\"muted\" mono className=\"uppercase\">\n {translate('shell.projectList.recent')}\n </Text>\n <Row gap=\"2\" wrap>\n {recents.map((p) => (\n <Link\n key={p.id}\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId: p.id, _splat: studioPath.index() }}\n onClick={() => studio.activate(p.id)}\n className=\"inline-flex items-center gap-2 rounded-md border border-border bg-card px-3 py-1.5 text-sm transition-colors hover:border-primary/40 hover:bg-muted\"\n >\n <span className=\"grid size-4 place-items-center rounded-[4px] bg-primary text-[0.6rem] font-bold text-primary-foreground\">\n {p.name.charAt(0).toUpperCase()}\n </span>\n {p.name}\n </Link>\n ))}\n </Row>\n </Stack>\n ) : null}\n\n {forced ? (\n <SectionStateView\n state={forced}\n title={forced === 'empty' ? translate('shell.projectList.empty.title') : undefined}\n />\n ) : visible.length === 0 ? (\n <SectionStateView\n state=\"empty\"\n title={translate('shell.projectList.empty.title')}\n description={\n hasConfig\n ? 'The workspace config declares no visible projects. Add an entry to elytra.config.ts, or ask an admin to grant you access.'\n : 'No active projects. Projects are code: scaffold a new one with `npm create elytra@latest`, or declare it in elytra.config.ts, then run `elytra init` to provision its backend.'\n }\n />\n ) : (\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {visible.map((entry) =>\n entry.kind === 'project' ? (\n <ProjectCard key={entry.project.id} project={entry.project} />\n ) : (\n <UnprovisionedCard key={`config:${entry.config.slug}`} config={entry.config} />\n ),\n )}\n </div>\n )}\n </div>\n )\n}\n\nfunction ProjectCard({ project }: { project: Project }) {\n const studio = useStudio()\n // Per-backend sign-in state (EC-179): one project per backend, so a project on\n // a deployment the user hasn't authenticated into shows \"Sign in\", not \"Open\".\n const authed = hasProjectSession(project.slug)\n return (\n <Card className=\"flex flex-col gap-3 p-4 transition-colors hover:border-primary/30\">\n <Row gap=\"2\">\n <span className=\"grid size-8 shrink-0 place-items-center rounded-lg bg-primary text-sm font-bold text-primary-foreground\">\n {project.name.charAt(0).toUpperCase()}\n </span>\n <Stack gap=\"1\" className=\"min-w-0\">\n <Text weight=\"semibold\" className=\"truncate\">\n {project.name}\n </Text>\n <Code tone=\"muted\" className=\"w-fit max-w-full truncate\">\n {project.slug}\n </Code>\n </Stack>\n <Spacer />\n {authed ? null : <SignInNeededBadge />}\n </Row>\n\n <Row gap=\"2\" className=\"text-muted-foreground\">\n <Badge variant=\"outline\">\n <Globe className=\"size-3\" /> {project.settings.locales.length}\n </Badge>\n <Spacer />\n </Row>\n\n <Button variant={authed ? 'secondary' : 'default'} size=\"sm\" asChild className=\"mt-1\">\n <Link\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId: project.id, _splat: studioPath.index() }}\n onClick={() => studio.activate(project.id)}\n >\n {authed ? translate('shell.projectCard.open') : (\n <>\n <LogIn /> {translate('auth.signIn')}\n </>\n )}\n </Link>\n </Button>\n </Card>\n )\n}\n\n/**\n * A configured project that has no backend project yet (EC-154): an explicit,\n * deterministic state pointing at the CLI provisioning flow — never an error.\n */\nfunction UnprovisionedCard({ config }: { config: ResolvedProject }) {\n return (\n <Card className=\"flex flex-col gap-3 border-dashed p-4\">\n <Row gap=\"2\">\n <span className=\"grid size-8 shrink-0 place-items-center rounded-lg border border-border bg-muted text-sm font-bold text-muted-foreground\">\n {config.name.charAt(0).toUpperCase()}\n </span>\n <Stack gap=\"1\" className=\"min-w-0\">\n <Text weight=\"semibold\" className=\"truncate\">\n {config.name}\n </Text>\n <Code tone=\"muted\" className=\"w-fit max-w-full truncate\">\n {config.slug}\n </Code>\n </Stack>\n <Spacer />\n <Badge variant=\"outline\">{translate('shell.project.notProvisioned')}</Badge>\n </Row>\n\n <Text size=\"sm\" tone=\"muted\">\n Declared in the workspace config, but no backend project exists yet. Point{' '}\n <Code tone=\"muted\">studio/.env</Code> at this project’s Convex deployment, then provision it\n from that project’s repo:\n </Text>\n <Code className=\"block w-fit max-w-full truncate text-xs\">elytra init</Code>\n <Text size=\"xs\" tone=\"muted\">\n <Code tone=\"muted\">elytra doctor</Code> reports what is still missing.\n </Text>\n </Card>\n )\n}\n"]}
|
|
1
|
+
{"version":3,"file":"project-list.js","sourceRoot":"","sources":["../../src/components/project-list.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n isExplicitLauncherVisit,\n Link,\n NavigateOnce,\n STUDIO_PROJECT_ROUTE,\n studioPath,\n useLocation,\n} from '@elytracms/studio/router'\nimport { Globe, LogIn, TerminalSquare } from 'lucide-react'\nimport type { Project, ResolvedProject, SectionState } from '@elytracms/core/studio-core'\nimport { Badge, Button, Card, Code, Heading, Row, Spacer, Stack, Text } from '@elytracms/studio/ui'\nimport { hasProjectSession } from '../lib/auth'\nimport { launcherAutoForwardTarget } from '../lib/workspace'\nimport { translate } from '../lib/i18n'\nimport { PlaygroundBadge } from './playground-badge'\nimport { SignInNeededBadge } from './sign-in-needed-badge'\nimport { SectionStateView } from './section-state'\nimport { useStudio } from './studio-context'\nimport { useRecentProjects, useSwitcherEntries } from './workspace-projects'\n\nexport interface ProjectLauncherProps {\n /** Force a deterministic list state for browser verification (?state=…). */\n forcedState?: SectionState | null\n}\n\n/**\n * Studio launcher / project list (brief §4.3, §8.1; reframed by EC-154). The\n * Studio's front door: lists the workspace's projects and routes into a\n * project's shell. With a workspace config the list is config-driven\n * (configured projects ∩ grants, plus explicit \"not provisioned\" states);\n * project *creation* is code-first — declare it in `elytra.config.ts`, then\n * provision the backend with `elytra init` — so there is no in-UI create flow.\n * Without a config the adapter-backed list (and the starter-kit gallery)\n * behaves as before.\n *\n * EC-470: this used to instruct `builder init --project <slug>`. No `builder`\n * bin was ever declared, so that was an unrunnable command shown as product UI.\n * The real CLI is `elytra` (`init` provisions Convex + auth keys; `doctor`\n * reports what is still missing).\n */\nexport function ProjectLauncher({ forcedState }: ProjectLauncherProps) {\n const studio = useStudio()\n const { entries, workspace } = useSwitcherEntries()\n\n const hasConfig = workspace.kind === 'config'\n const visible = entries\n const recents = useRecentProjects(entries)\n const location = useLocation()\n\n const forced = forcedState && forcedState !== 'working' ? forcedState : null\n\n // EC-401: a single-project workspace skips the launcher hop — the bare `/`\n // hit forwards straight into the sole project (replace, so Back never loops\n // through the launcher). Explicit \"all projects\" navigations carry the\n // history-state marker and keep the launcher visible, as do forced `?state=`\n // verification states and a lone UNPROVISIONED entry (its setup gate lives\n // here). Activation/recents are recorded by the project screen on arrival.\n const autoForward = launcherAutoForwardTarget({\n entries,\n forcedState: forcedState != null,\n explicitVisit: isExplicitLauncherVisit(location.state),\n })\n if (autoForward) {\n // EC-441: `NavigateOnce`, not `<Navigate>` — on a slow boot the launcher\n // stays mounted while the project splat's modules load, re-rendering on\n // every router-store tick; a per-render re-navigate compounded into the\n // \"Maximum update depth exceeded\" rejection (104 re-fires on one cold load).\n return (\n <NavigateOnce\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId: autoForward.id, _splat: studioPath.index() }}\n replace\n />\n )\n }\n\n return (\n <div className=\"mx-auto w-full max-w-6xl px-6 py-8\">\n <Row gap=\"3\" className=\"mb-6\">\n <Stack gap=\"1\">\n <Row gap=\"2\">\n <Heading size=\"display\">{translate('shell.projects')}</Heading>\n {/* EC-153: explicit playground-mode marker (renders nothing on Convex). */}\n <PlaygroundBadge />\n {hasConfig ? (\n // EC-154: this workspace is defined in code.\n <Badge variant=\"outline\">\n <TerminalSquare className=\"size-3\" /> {workspace.source}\n </Badge>\n ) : null}\n </Row>\n <Text tone=\"muted\">\n {hasConfig\n ? 'Projects are defined in code. Add entries to elytra.config.ts and provision them with the CLI.'\n : 'Open and manage your Elytra projects.'}\n </Text>\n </Stack>\n <Spacer />\n </Row>\n\n {recents.length > 0 && !forced ? (\n <Stack gap=\"2\" className=\"mb-6\">\n <Text size=\"xs\" tone=\"muted\" mono className=\"uppercase\">\n {translate('shell.projectList.recent')}\n </Text>\n <Row gap=\"2\" wrap>\n {recents.map((p) => (\n <Link\n key={p.id}\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId: p.id, _splat: studioPath.index() }}\n onClick={() => studio.activate(p.id)}\n className=\"inline-flex items-center gap-2 rounded-md border border-border bg-card px-3 py-1.5 text-sm transition-colors hover:border-primary/40 hover:bg-muted\"\n >\n <span className=\"grid size-4 place-items-center rounded-[4px] bg-primary text-[0.6rem] font-bold text-primary-foreground\">\n {p.name.charAt(0).toUpperCase()}\n </span>\n {p.name}\n </Link>\n ))}\n </Row>\n </Stack>\n ) : null}\n\n {forced ? (\n <SectionStateView\n state={forced}\n title={forced === 'empty' ? translate('shell.projectList.empty.title') : undefined}\n />\n ) : visible.length === 0 ? (\n <SectionStateView\n state=\"empty\"\n title={translate('shell.projectList.empty.title')}\n description={\n hasConfig\n ? 'The workspace config declares no visible projects. Add an entry to elytra.config.ts, or ask an admin to grant you access.'\n : 'No active projects. Projects are code: scaffold a new one with `npm create elytra@latest`, or declare it in elytra.config.ts, then run `elytra init` to provision its backend.'\n }\n />\n ) : (\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {visible.map((entry) =>\n entry.kind === 'project' ? (\n <ProjectCard key={entry.project.id} project={entry.project} />\n ) : (\n <UnprovisionedCard key={`config:${entry.config.slug}`} config={entry.config} />\n ),\n )}\n </div>\n )}\n </div>\n )\n}\n\nfunction ProjectCard({ project }: { project: Project }) {\n const studio = useStudio()\n // Per-backend sign-in state (EC-179): one project per backend, so a project on\n // a deployment the user hasn't authenticated into shows \"Sign in\", not \"Open\".\n const authed = hasProjectSession(project.slug)\n return (\n <Card className=\"flex flex-col gap-3 p-4 transition-colors hover:border-primary/30\">\n <Row gap=\"2\">\n <span className=\"grid size-8 shrink-0 place-items-center rounded-lg bg-primary text-sm font-bold text-primary-foreground\">\n {project.name.charAt(0).toUpperCase()}\n </span>\n <Stack gap=\"1\" className=\"min-w-0\">\n <Text weight=\"semibold\" className=\"truncate\">\n {project.name}\n </Text>\n <Code tone=\"muted\" className=\"w-fit max-w-full truncate\">\n {project.slug}\n </Code>\n </Stack>\n <Spacer />\n {authed ? null : <SignInNeededBadge />}\n </Row>\n\n <Row gap=\"2\" className=\"text-muted-foreground\">\n <Badge variant=\"outline\">\n <Globe className=\"size-3\" /> {project.settings.locales.length}\n </Badge>\n <Spacer />\n </Row>\n\n <Button variant={authed ? 'secondary' : 'default'} size=\"sm\" asChild className=\"mt-1\">\n <Link\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId: project.id, _splat: studioPath.index() }}\n onClick={() => studio.activate(project.id)}\n >\n {authed ? translate('shell.projectCard.open') : (\n <>\n <LogIn /> {translate('auth.signIn')}\n </>\n )}\n </Link>\n </Button>\n </Card>\n )\n}\n\n/**\n * A configured project that has no backend project yet (EC-154): an explicit,\n * deterministic state pointing at the CLI provisioning flow — never an error.\n */\nfunction UnprovisionedCard({ config }: { config: ResolvedProject }) {\n return (\n <Card className=\"flex flex-col gap-3 border-dashed p-4\">\n <Row gap=\"2\">\n <span className=\"grid size-8 shrink-0 place-items-center rounded-lg border border-border bg-muted text-sm font-bold text-muted-foreground\">\n {config.name.charAt(0).toUpperCase()}\n </span>\n <Stack gap=\"1\" className=\"min-w-0\">\n <Text weight=\"semibold\" className=\"truncate\">\n {config.name}\n </Text>\n <Code tone=\"muted\" className=\"w-fit max-w-full truncate\">\n {config.slug}\n </Code>\n </Stack>\n <Spacer />\n <Badge variant=\"outline\">{translate('shell.project.notProvisioned')}</Badge>\n </Row>\n\n <Text size=\"sm\" tone=\"muted\">\n Declared in the workspace config, but no backend project exists yet. Point{' '}\n <Code tone=\"muted\">studio/.env</Code> at this project’s Convex deployment, then provision it\n from that project’s repo:\n </Text>\n <Code className=\"block w-fit max-w-full truncate text-xs\">elytra init</Code>\n <Text size=\"xs\" tone=\"muted\">\n <Code tone=\"muted\">elytra doctor</Code> reports what is still missing.\n </Text>\n </Card>\n )\n}\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
2
|
import type { CollectionDef, LocaleConfig, RouteRecord } from '@elytracms/core/cms-core';
|
|
3
|
-
import type { HydratedCms, HydratedProjectSession, OperationResult, OperationsClient } from '
|
|
4
|
-
import type { ContentSessionState } from '
|
|
3
|
+
import type { HydratedCms, HydratedProjectSession, OperationResult, OperationsClient } from '../lib/operations/index.js';
|
|
4
|
+
import type { ContentSessionState } from '../lib/data/index.js';
|
|
5
5
|
/**
|
|
6
6
|
* Project-session hydration gate (Milestone B sign-off fix, EC-177 gap 1).
|
|
7
7
|
*
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useEffect } from 'react';
|
|
3
|
-
import { getStudioOperations, loadHydratedCms, loadProjectSession } from '
|
|
4
|
-
import { isPlaygroundMode } from '
|
|
5
|
-
import { configureContentSession, isContentSessionWrite, markContentSessionStale, notePlaygroundWriteForUnloadGuard, registerContentSessionRefresher, } from '
|
|
6
|
-
import { resolveDeploymentCollections, resolveDeploymentLocaleConfig, resolveDeploymentRoutes, } from '
|
|
7
|
-
import { createRequiredContext } from '
|
|
3
|
+
import { getStudioOperations, loadHydratedCms, loadProjectSession } from '../lib/operations/index.js';
|
|
4
|
+
import { isPlaygroundMode } from '../lib/persistence/index.js';
|
|
5
|
+
import { configureContentSession, isContentSessionWrite, markContentSessionStale, notePlaygroundWriteForUnloadGuard, registerContentSessionRefresher, } from '../lib/data/index.js';
|
|
6
|
+
import { resolveDeploymentCollections, resolveDeploymentLocaleConfig, resolveDeploymentRoutes, } from '../lib/workspace/index.js';
|
|
7
|
+
import { createRequiredContext } from '../lib/required-context.js';
|
|
8
8
|
import { HydrationBoundary, useHydrationPhase } from './use-hydration-phase.js';
|
|
9
9
|
const { Context: ProjectSessionContext, useRequired: useProjectSession } = createRequiredContext('ProjectSession');
|
|
10
10
|
/**
|