@elytracms/studio 0.0.93 → 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/auth.ts +5 -2
- package/convex/authority.ts +35 -11
- package/convex/contract-parity.test.ts +7 -0
- package/convex/delivery.ts +12 -9
- 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/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 +30 -0
- package/convex/triggers.ts +21 -1
- package/dist/addon.d.ts +71 -0
- package/dist/addon.js +67 -0
- package/dist/addon.js.map +1 -0
- package/dist/components/account/account-settings.js +2 -2
- package/dist/components/account/account-settings.js.map +1 -1
- package/dist/components/account/credential-panel.js +2 -2
- package/dist/components/account/credential-panel.js.map +1 -1
- package/dist/components/account/self-account-schema.d.ts +6 -15
- package/dist/components/account/self-account-schema.js +52 -34
- package/dist/components/account/self-account-schema.js.map +1 -1
- package/dist/components/account/self-account-view-source.d.ts +1 -1
- package/dist/components/account/self-account-view-source.js +6 -4
- package/dist/components/account/self-account-view-source.js.map +1 -1
- package/dist/components/ai/ai-chat-panel-lazy.js +1 -1
- package/dist/components/ai/ai-chat-panel-lazy.js.map +1 -1
- package/dist/components/ai/ai-chat-panel.js +6 -6
- package/dist/components/ai/ai-chat-panel.js.map +1 -1
- package/dist/components/ai/ai-cms-panel.js +2 -2
- package/dist/components/ai/ai-cms-panel.js.map +1 -1
- package/dist/components/ai/ai-panel-toggle.js +4 -4
- package/dist/components/ai/ai-panel-toggle.js.map +1 -1
- package/dist/components/ai/chat-ui.js +2 -2
- package/dist/components/ai/chat-ui.js.map +1 -1
- package/dist/components/ai/proposal-card.d.ts +2 -2
- package/dist/components/ai/proposal-card.js +2 -2
- package/dist/components/ai/proposal-card.js.map +1 -1
- package/dist/components/ai/proposal-issues.js +2 -2
- package/dist/components/ai/proposal-issues.js.map +1 -1
- package/dist/components/ai/translate-document-action.js +2 -2
- package/dist/components/ai/translate-document-action.js.map +1 -1
- package/dist/components/ai/translate-document-dialog.js +6 -6
- package/dist/components/ai/translate-document-dialog.js.map +1 -1
- package/dist/components/ai/translate-skipped.d.ts +1 -1
- package/dist/components/ai/translate-skipped.js +2 -2
- package/dist/components/ai/translate-skipped.js.map +1 -1
- package/dist/components/app-views/default-modules.d.ts +50 -2
- package/dist/components/app-views/default-modules.js +65 -20
- package/dist/components/app-views/default-modules.js.map +1 -1
- package/dist/components/app-views/module.d.ts +53 -3
- package/dist/components/app-views/module.js +14 -7
- package/dist/components/app-views/module.js.map +1 -1
- package/dist/components/app-views/sidebar.js +1 -1
- package/dist/components/app-views/sidebar.js.map +1 -1
- package/dist/components/app-views/studio-config.d.ts +11 -2
- package/dist/components/app-views/studio-config.js.map +1 -1
- package/dist/components/assets/asset-detail.js +2 -2
- 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-thumb.js +3 -3
- package/dist/components/assets/asset-thumb.js.map +1 -1
- package/dist/components/assets/assets-collection-view-source.d.ts +2 -2
- package/dist/components/assets/assets-collection-view-source.js.map +1 -1
- package/dist/components/assets/assets-module.js +1 -1
- package/dist/components/assets/assets-module.js.map +1 -1
- package/dist/components/assets/assets-store.d.ts +2 -2
- package/dist/components/assets/assets-store.js +6 -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.js +1 -1
- package/dist/components/assets/replace-file.js.map +1 -1
- package/dist/components/auth-context.d.ts +1 -1
- package/dist/components/auth-context.js +6 -6
- package/dist/components/auth-context.js.map +1 -1
- package/dist/components/builder/blocks-model.d.ts +1 -1
- package/dist/components/builder/blocks-model.js +3 -3
- package/dist/components/builder/blocks-model.js.map +1 -1
- package/dist/components/builder/blocks-panel.d.ts +1 -1
- package/dist/components/builder/blocks-panel.js +4 -4
- package/dist/components/builder/blocks-panel.js.map +1 -1
- package/dist/components/builder/builder-store.js +2 -2
- package/dist/components/builder/builder-store.js.map +1 -1
- package/dist/components/builder/canvas-breakpoints.js +1 -1
- package/dist/components/builder/canvas-breakpoints.js.map +1 -1
- package/dist/components/builder/canvas-dnd.d.ts +2 -2
- package/dist/components/builder/canvas-dnd.js +1 -1
- package/dist/components/builder/canvas-dnd.js.map +1 -1
- package/dist/components/builder/canvas-inline-editing.d.ts +1 -1
- package/dist/components/builder/canvas-inline-editing.js.map +1 -1
- package/dist/components/builder/canvas-overlay-geometry.d.ts +1 -1
- package/dist/components/builder/canvas-overlay-geometry.js.map +1 -1
- package/dist/components/builder/canvas-render-context.d.ts +1 -1
- package/dist/components/builder/canvas-render-context.js +4 -4
- package/dist/components/builder/canvas-render-context.js.map +1 -1
- package/dist/components/builder/canvas-theme-notice.js +2 -2
- package/dist/components/builder/canvas-theme-notice.js.map +1 -1
- package/dist/components/builder/canvas.js +3 -3
- package/dist/components/builder/canvas.js.map +1 -1
- package/dist/components/builder/clipboard-actions.d.ts +2 -2
- package/dist/components/builder/clipboard-actions.js +4 -4
- package/dist/components/builder/clipboard-actions.js.map +1 -1
- package/dist/components/builder/composition-field-editor-lazy.js +1 -1
- package/dist/components/builder/composition-field-editor-lazy.js.map +1 -1
- package/dist/components/builder/composition-field-editor.d.ts +1 -1
- package/dist/components/builder/composition-field-editor.js +3 -3
- package/dist/components/builder/composition-field-editor.js.map +1 -1
- package/dist/components/builder/drag-session.d.ts +1 -1
- package/dist/components/builder/drag-session.js.map +1 -1
- package/dist/components/builder/fixture-thumbnail.js +1 -1
- package/dist/components/builder/fixture-thumbnail.js.map +1 -1
- package/dist/components/builder/insert-context.d.ts +1 -1
- package/dist/components/builder/insert-context.js +3 -3
- package/dist/components/builder/insert-context.js.map +1 -1
- package/dist/components/builder/layout-inspector.js +1 -1
- package/dist/components/builder/layout-inspector.js.map +1 -1
- package/dist/components/builder/node-title.js +1 -1
- package/dist/components/builder/node-title.js.map +1 -1
- package/dist/components/builder/page-tree.d.ts +1 -1
- package/dist/components/builder/page-tree.js +5 -5
- package/dist/components/builder/page-tree.js.map +1 -1
- package/dist/components/builder/placement-refusal.js +3 -3
- package/dist/components/builder/placement-refusal.js.map +1 -1
- package/dist/components/builder/property-inspector.js +4 -4
- package/dist/components/builder/property-inspector.js.map +1 -1
- package/dist/components/builder/reuse-context.js +4 -4
- package/dist/components/builder/reuse-context.js.map +1 -1
- package/dist/components/builder/reuse.js +1 -1
- package/dist/components/builder/reuse.js.map +1 -1
- package/dist/components/builder/rich-text-toolbar.js +1 -1
- package/dist/components/builder/rich-text-toolbar.js.map +1 -1
- package/dist/components/builder/run-inspector.js +1 -1
- package/dist/components/builder/run-inspector.js.map +1 -1
- package/dist/components/builder/selection-overlay.js +3 -3
- package/dist/components/builder/selection-overlay.js.map +1 -1
- package/dist/components/builder/spacing-handles.d.ts +2 -2
- package/dist/components/builder/spacing-handles.js +3 -3
- package/dist/components/builder/spacing-handles.js.map +1 -1
- package/dist/components/cms/asset-field.js +2 -2
- package/dist/components/cms/asset-field.js.map +1 -1
- package/dist/components/cms/asset-picker.js +2 -2
- package/dist/components/cms/asset-picker.js.map +1 -1
- package/dist/components/cms/bulk-actions.d.ts +1 -1
- package/dist/components/cms/bulk-actions.js +2 -2
- package/dist/components/cms/bulk-actions.js.map +1 -1
- package/dist/components/cms/bulk-bar.d.ts +2 -2
- package/dist/components/cms/bulk-bar.js +2 -2
- package/dist/components/cms/bulk-bar.js.map +1 -1
- package/dist/components/cms/cell-edit.d.ts +1 -1
- package/dist/components/cms/cell-edit.js +1 -1
- package/dist/components/cms/cell-edit.js.map +1 -1
- package/dist/components/cms/cell.d.ts +1 -1
- package/dist/components/cms/cell.js +3 -3
- package/dist/components/cms/cell.js.map +1 -1
- package/dist/components/cms/cms-index.js +2 -2
- package/dist/components/cms/cms-index.js.map +1 -1
- package/dist/components/cms/cms-labels.js +2 -2
- package/dist/components/cms/cms-labels.js.map +1 -1
- package/dist/components/cms/cms-store.d.ts +2 -2
- package/dist/components/cms/cms-store.js +10 -10
- package/dist/components/cms/cms-store.js.map +1 -1
- package/dist/components/cms/collection-table.js +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 -14
- package/dist/components/cms/detail-editor.js.map +1 -1
- package/dist/components/cms/details-pane.js +1 -1
- package/dist/components/cms/details-pane.js.map +1 -1
- package/dist/components/cms/doc-state.js +1 -1
- package/dist/components/cms/doc-state.js.map +1 -1
- package/dist/components/cms/document-diff-view.js +5 -5
- package/dist/components/cms/document-diff-view.js.map +1 -1
- package/dist/components/cms/document-editor-context.js +1 -1
- package/dist/components/cms/document-editor-context.js.map +1 -1
- package/dist/components/cms/document-staging.js +1 -1
- package/dist/components/cms/document-staging.js.map +1 -1
- package/dist/components/cms/document-url-path.js +2 -2
- package/dist/components/cms/document-url-path.js.map +1 -1
- package/dist/components/cms/duplicate-document.d.ts +1 -1
- package/dist/components/cms/duplicate-document.js.map +1 -1
- package/dist/components/cms/editable-cell.js +3 -3
- package/dist/components/cms/editable-cell.js.map +1 -1
- package/dist/components/cms/field-collaboration.d.ts +1 -1
- package/dist/components/cms/field-collaboration.js +1 -1
- package/dist/components/cms/field-collaboration.js.map +1 -1
- package/dist/components/cms/field-connection.d.ts +1 -1
- package/dist/components/cms/field-connection.js +2 -2
- package/dist/components/cms/field-connection.js.map +1 -1
- package/dist/components/cms/field-control-lib.js +1 -1
- package/dist/components/cms/field-control-lib.js.map +1 -1
- package/dist/components/cms/field-control.js +4 -4
- package/dist/components/cms/field-control.js.map +1 -1
- package/dist/components/cms/field-renderer.d.ts +1 -1
- package/dist/components/cms/field-renderer.js +2 -2
- package/dist/components/cms/field-renderer.js.map +1 -1
- package/dist/components/cms/filter-builder.d.ts +1 -1
- package/dist/components/cms/filter-builder.js +3 -3
- package/dist/components/cms/filter-builder.js.map +1 -1
- package/dist/components/cms/gate-validation.js +1 -1
- package/dist/components/cms/gate-validation.js.map +1 -1
- package/dist/components/cms/import-export.js +2 -2
- package/dist/components/cms/import-export.js.map +1 -1
- package/dist/components/cms/issue-copy.js +1 -1
- package/dist/components/cms/issue-copy.js.map +1 -1
- package/dist/components/cms/issues-pane.js +3 -3
- package/dist/components/cms/issues-pane.js.map +1 -1
- package/dist/components/cms/object-array-field.js +2 -2
- package/dist/components/cms/object-array-field.js.map +1 -1
- package/dist/components/cms/prop-fields.js +2 -2
- package/dist/components/cms/prop-fields.js.map +1 -1
- package/dist/components/cms/reference-confirm.d.ts +1 -1
- package/dist/components/cms/reference-confirm.js +2 -2
- package/dist/components/cms/reference-confirm.js.map +1 -1
- package/dist/components/cms/relation-picker.js +2 -2
- package/dist/components/cms/relation-picker.js.map +1 -1
- package/dist/components/cms/rich-text/block-style.js +1 -1
- package/dist/components/cms/rich-text/block-style.js.map +1 -1
- package/dist/components/cms/rich-text/component-embed.js +4 -4
- package/dist/components/cms/rich-text/component-embed.js.map +1 -1
- package/dist/components/cms/rich-text/editor.js +3 -3
- package/dist/components/cms/rich-text/editor.js.map +1 -1
- package/dist/components/cms/rich-text/embed-picker.js +3 -3
- package/dist/components/cms/rich-text/embed-picker.js.map +1 -1
- package/dist/components/cms/rich-text/embed-props.js +3 -3
- package/dist/components/cms/rich-text/embed-props.js.map +1 -1
- package/dist/components/cms/rich-text/extensions.js +1 -1
- package/dist/components/cms/rich-text/extensions.js.map +1 -1
- package/dist/components/cms/rich-text/font-size-control.d.ts +1 -1
- package/dist/components/cms/rich-text/font-size-control.js +2 -2
- package/dist/components/cms/rich-text/font-size-control.js.map +1 -1
- package/dist/components/cms/rich-text/lazy.js +1 -1
- package/dist/components/cms/rich-text/lazy.js.map +1 -1
- package/dist/components/cms/rich-text/link-control.js +2 -2
- package/dist/components/cms/rich-text/link-control.js.map +1 -1
- package/dist/components/cms/rich-text/slash-command.js +3 -3
- package/dist/components/cms/rich-text/slash-command.js.map +1 -1
- package/dist/components/cms/rich-text/toolbar.js +1 -1
- package/dist/components/cms/rich-text/toolbar.js.map +1 -1
- package/dist/components/cms/rich-text/unknown-node.js +2 -2
- package/dist/components/cms/rich-text/unknown-node.js.map +1 -1
- package/dist/components/cms/rich-text-preview.js +2 -2
- package/dist/components/cms/rich-text-preview.js.map +1 -1
- package/dist/components/cms/row-actions.d.ts +1 -1
- package/dist/components/cms/row-actions.js.map +1 -1
- package/dist/components/cms/scalar-value-editor.d.ts +1 -1
- package/dist/components/cms/scalar-value-editor.js +2 -2
- package/dist/components/cms/scalar-value-editor.js.map +1 -1
- package/dist/components/cms/use-discard-controller.d.ts +1 -1
- package/dist/components/cms/use-discard-controller.js +1 -1
- package/dist/components/cms/use-discard-controller.js.map +1 -1
- package/dist/components/cms/use-document-draft.d.ts +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 +1 -1
- package/dist/components/cms/use-publish-controller.js +1 -1
- package/dist/components/cms/use-publish-controller.js.map +1 -1
- package/dist/components/cms/use-published-perspective.d.ts +1 -1
- package/dist/components/cms/use-published-perspective.js.map +1 -1
- package/dist/components/cms/use-restore-version.d.ts +1 -1
- package/dist/components/cms/use-restore-version.js +1 -1
- package/dist/components/cms/use-restore-version.js.map +1 -1
- package/dist/components/cms/version-compare-lazy.js +1 -1
- package/dist/components/cms/version-compare-lazy.js.map +1 -1
- package/dist/components/cms/version-compare.js +1 -1
- package/dist/components/cms/version-compare.js.map +1 -1
- package/dist/components/cms/versions-pane.js +3 -3
- package/dist/components/cms/versions-pane.js.map +1 -1
- package/dist/components/cms/view-bar.js +3 -3
- package/dist/components/cms/view-bar.js.map +1 -1
- package/dist/components/collection-view/cms-collection-view-source.d.ts +1 -1
- package/dist/components/collection-view/cms-collection-view-source.js +2 -2
- package/dist/components/collection-view/cms-collection-view-source.js.map +1 -1
- package/dist/components/collection-view/collection-view-context.d.ts +1 -1
- package/dist/components/collection-view/collection-view-context.js +1 -1
- package/dist/components/collection-view/collection-view-context.js.map +1 -1
- package/dist/components/collection-view/collection-view-surface.d.ts +13 -0
- package/dist/components/collection-view/collection-view-surface.js +18 -0
- package/dist/components/collection-view/collection-view-surface.js.map +1 -0
- package/dist/components/components/component-detail.js +10 -10
- package/dist/components/components/component-detail.js.map +1 -1
- package/dist/components/components/component-preview.d.ts +2 -2
- package/dist/components/components/component-preview.js +5 -5
- package/dist/components/components/component-preview.js.map +1 -1
- package/dist/components/components/component-status.d.ts +1 -1
- package/dist/components/components/component-status.js +1 -1
- package/dist/components/components/component-status.js.map +1 -1
- package/dist/components/components/components-index.js +2 -2
- package/dist/components/components/components-index.js.map +1 -1
- package/dist/components/components/field-inspector.js +1 -1
- package/dist/components/components/field-inspector.js.map +1 -1
- package/dist/components/components/registry-browser.js +3 -3
- package/dist/components/components/registry-browser.js.map +1 -1
- package/dist/components/components/taxonomy.d.ts +1 -1
- package/dist/components/components/taxonomy.js.map +1 -1
- package/dist/components/components/transfer-dialog.js +3 -3
- package/dist/components/components/transfer-dialog.js.map +1 -1
- package/dist/components/current-user-menu.js +1 -1
- package/dist/components/current-user-menu.js.map +1 -1
- package/dist/components/delivery-health-banner.js +2 -2
- package/dist/components/delivery-health-banner.js.map +1 -1
- package/dist/components/deployment-setup-gate.js +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/index.d.ts +1 -1
- package/dist/lib/ai/index.js +1 -1
- package/dist/lib/ai/index.js.map +1 -1
- package/dist/lib/ai/node-handler.d.ts +1 -1
- package/dist/lib/ai/node-handler.js.map +1 -1
- package/dist/lib/ai/translate-document.js +1 -1
- package/dist/lib/ai/translate-document.js.map +1 -1
- package/dist/lib/clipboard/paste-issues.js +1 -1
- package/dist/lib/clipboard/paste-issues.js.map +1 -1
- package/dist/lib/clipboard/paste-target.js +2 -2
- package/dist/lib/clipboard/paste-target.js.map +1 -1
- package/dist/lib/cms/published-documents.js +1 -1
- package/dist/lib/cms/published-documents.js.map +1 -1
- package/dist/lib/cms-views/view-model.d.ts +12 -12
- 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 +7 -2
- package/dist/lib/collection-view/collection-view-source.js.map +1 -1
- package/dist/lib/data/content-session-refresh.d.ts +1 -1
- package/dist/lib/data/content-session-refresh.js.map +1 -1
- package/dist/lib/data/playground-unload-guard.d.ts +1 -1
- package/dist/lib/data/playground-unload-guard.js +1 -1
- package/dist/lib/data/playground-unload-guard.js.map +1 -1
- package/dist/lib/engine-version.d.ts +1 -1
- package/dist/lib/engine-version.js +1 -1
- package/dist/lib/engine-version.js.map +1 -1
- package/dist/lib/i18n/i18n.js +1 -1
- package/dist/lib/i18n/i18n.js.map +1 -1
- package/dist/lib/i18n/messages.de.js +4 -0
- package/dist/lib/i18n/messages.de.js.map +1 -1
- package/dist/lib/i18n/messages.en.d.ts +4 -0
- package/dist/lib/i18n/messages.en.js +8 -0
- package/dist/lib/i18n/messages.en.js.map +1 -1
- package/dist/lib/operations/project-runtime-config.d.ts +9 -0
- package/dist/lib/operations/project-runtime-config.js +13 -0
- package/dist/lib/operations/project-runtime-config.js.map +1 -1
- package/dist/lib/operations/studio-operations.d.ts +9 -1
- package/dist/lib/operations/studio-operations.js +6 -1
- package/dist/lib/operations/studio-operations.js.map +1 -1
- package/dist/lib/persistence/convex-adapter.d.ts +16 -3
- package/dist/lib/persistence/convex-adapter.js +78 -47
- package/dist/lib/persistence/convex-adapter.js.map +1 -1
- package/dist/lib/persistence/index.js +14 -1
- package/dist/lib/persistence/index.js.map +1 -1
- package/dist/lib/persona/labels.js +1 -1
- 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 +6 -0
- package/dist/lib/workspace/index.js +8 -0
- 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/redirects/redirects-collection-view-source.d.ts +0 -53
- package/dist/components/redirects/redirects-collection-view-source.js +0 -146
- package/dist/components/redirects/redirects-collection-view-source.js.map +0 -1
- package/dist/components/redirects/redirects-module.d.ts +0 -21
- package/dist/components/redirects/redirects-module.js +0 -40
- package/dist/components/redirects/redirects-module.js.map +0 -1
- package/dist/components/redirects/redirects-store.d.ts +0 -68
- package/dist/components/redirects/redirects-store.js +0 -132
- package/dist/components/redirects/redirects-store.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"section-nav.js","sourceRoot":"","sources":["../../src/components/section-nav.tsx"],"names":[],"mappings":"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","sourcesContent":["import { Fragment, useCallback, useEffect, useMemo, useState, useSyncExternalStore } from 'react'\nimport { Link, STUDIO_PROJECT_ROUTE, useLocation } from '@elytracms/studio/router'\nimport { ChevronDown, PanelLeftClose, PanelLeftOpen } from 'lucide-react'\nimport type { Project } from '@elytracms/core/studio-core'\nimport type { CustomizationIssue } from '@elytracms/core/studio-core'\nimport { cn } from '@elytracms/studio/lib/utils'\nimport { readCollapsedViews, storeCollapsedViews } from '@elytracms/studio/lib/rail'\nimport { getStudioRegistry } from '@elytracms/studio/lib/registry'\nimport {\n projectConfigIssues,\n resolveDeploymentCollections,\n resolveDeploymentReuse,\n resolveDeploymentRoutes,\n} from '@elytracms/studio/lib/workspace'\nimport { routeValidationIssues } from '@elytracms/studio/lib/routes/route-issues'\nimport {\n contentSessionOrphans,\n serverContentSessionOrphans,\n subscribeContentSession,\n} from '@elytracms/studio/lib/data'\nimport { translate } from '@elytracms/studio/lib/i18n'\nimport { Text } from '@elytracms/studio/ui'\nimport { NavIssues, customizationItems, orphanedCollectionItems, routeIssueItems } from './nav-issues'\nimport type { NavIssueItem } from './nav-issues'\nimport { activeViewIdFromPath, isNavViewVisible, useStudioNav } from './app-views/sidebar'\nimport type { ResolvedRailView } from './app-views/sidebar'\nimport { resolveReuse } from './builder/reuse'\nimport { useRedirectList } from './redirects/redirects-store'\nimport { useFieldRenderers } from './cms/field-renderer'\nimport { useProjectRole } from './auth-context'\nimport type { NavPermissionContext } from './app-views/module'\n\nexport interface SectionNavProps {\n project: Project\n /** User-toggled collapse (EC-214) — icons only when collapsed, persisted by the shell. */\n collapsed: boolean\n onToggleCollapsed: () => void\n}\n\n/**\n * Vertical section rail (brief §8.1), driven by the configurable sidebar (EC-237):\n * a consumer-extendable tree of views / groups / separators resolved over the\n * built-in views (EC-239). Availability-aware (`unavailable` items are absent per\n * EC-221); the active item is matched by the VIEW ID in the URL, so two views\n * backed by the same surface (Content vs. a focused Categories) highlight\n * independently. The preference-persisted collapse and the strong active marker\n * (EC-214) are preserved. Customization issues (duplicate view id, invalid view,\n * non-component renderers) render at the rail foot — visible, never silent.\n */\nexport function SectionNav({ project, collapsed, onToggleCollapsed }: SectionNavProps) {\n const { pathname } = useLocation()\n const activeViewId = activeViewIdFromPath(pathname)\n const { nodes, issues: navIssues } = useStudioNav(project)\n const { issues: rendererIssues } = useFieldRenderers()\n const configIssues = useProjectConfigIssues()\n const reuseIssues = useReuseIssues()\n const projectId = project.id\n const routeIssues = useRouteIssues(projectId)\n const orphanIssues = useOrphanedCollectionIssues()\n // Config issues lead: \"this project declares no collections\" explains every\n // other emptiness on the screen, so it is the first thing to read.\n const issues = [\n ...customizationItems([...configIssues, ...navIssues, ...rendererIssues, ...reuseIssues]),\n ...routeIssues,\n ...orphanIssues,\n ]\n\n // EC-240 (AD-18): per-nav-item, default-open permission gating. The signed-in\n // user's role drives an OPTIONAL `visible` predicate on any view; absent →\n // visible. UI reflection only — the server chokepoint stays the real boundary,\n // and a permission-hidden view is still reachable by URL (EC-221).\n const role = useProjectRole(projectId)\n const navCtx: NavPermissionContext = { project, role }\n const isVisible = (view: ResolvedRailView) => isNavViewVisible(view, navCtx)\n\n // EC-293: per-parent collapse of nested sub-views, persisted (read after mount,\n // like the rail-collapse pref — no flash script needed). A collapsed parent hides\n // its children but stays a navigable link.\n const [collapsedViews, setCollapsedViews] = useState<Set<string>>(() => new Set())\n useEffect(() => {\n setCollapsedViews(readCollapsedViews())\n }, [])\n const toggleViewCollapsed = useCallback((id: string) => {\n setCollapsedViews((prev) => {\n const next = new Set(prev)\n if (next.has(id)) next.delete(id)\n else next.add(id)\n storeCollapsedViews(next)\n return next\n })\n }, [])\n\n return (\n <nav\n aria-label={translate('shell.nav.label')}\n className={cn(\n 'flex shrink-0 flex-col gap-0.5 border-r border-border bg-card/40 p-2 transition-[width] duration-150',\n collapsed ? 'w-[3.5rem]' : 'w-56',\n )}\n >\n {nodes.map((node, index) => {\n if (node.kind === 'separator') {\n return <div key={`sep-${index}`} className=\"my-1.5 border-t border-border/60\" aria-hidden />\n }\n if (node.kind === 'group') {\n const views = node.views.filter(\n (view) => view.availability !== 'unavailable' && isVisible(view),\n )\n if (views.length === 0) return null\n return (\n <div key={`group-${node.label}-${index}`} className=\"mt-2 first:mt-0\">\n {collapsed ? (\n <div className=\"my-1 border-t border-border/60\" aria-hidden />\n ) : (\n <Text\n size=\"xs\"\n tone=\"subtle\"\n className=\"px-3 pb-1 pt-1 font-medium uppercase tracking-wide\"\n >\n {node.label}\n </Text>\n )}\n {views.map((view) => (\n <NavViewLink\n key={view.id}\n projectId={projectId}\n view={view}\n activeViewId={activeViewId}\n collapsed={collapsed}\n />\n ))}\n </div>\n )\n }\n // EC-221: an unavailable view is absent from the rail (not greyed) — it\n // stays reachable by URL, the rail simply doesn't advertise it. EC-240: a\n // permission-hidden view is likewise absent-but-addressable.\n if (node.availability === 'unavailable' || !isVisible(node)) return null\n // EC-293: a view may nest its sub-views (CMS collections). The parent renders\n // as a link with a disclosure chevron; its children render indented below,\n // hidden when the rail is collapsed OR this parent is collapsed. The parent\n // gets a subtle active hint when one of its children is the active view.\n const hasChildren = (node.children?.length ?? 0) > 0\n const hasActiveChild = node.children?.some((child) => child.id === activeViewId) ?? false\n const viewCollapsed = collapsedViews.has(node.id)\n return (\n <Fragment key={node.id}>\n <NavViewLink\n projectId={projectId}\n view={node}\n activeViewId={activeViewId}\n collapsed={collapsed}\n hasActiveChild={hasActiveChild}\n collapsible={hasChildren && !collapsed}\n expanded={!viewCollapsed}\n onToggleExpanded={() => toggleViewCollapsed(node.id)}\n />\n {collapsed || viewCollapsed\n ? null\n : node.children?.filter(isVisible).map((child) => (\n <NavViewLink\n key={child.id}\n projectId={projectId}\n view={child}\n activeViewId={activeViewId}\n collapsed={collapsed}\n nested\n />\n ))}\n </Fragment>\n )\n })}\n\n <div className=\"mt-auto flex flex-col gap-0.5\">\n {issues.length > 0 ? <NavIssues issues={issues} collapsed={collapsed} /> : null}\n\n {/* Collapse toggle pinned to the rail foot (EC-214). */}\n <button\n type=\"button\"\n onClick={onToggleCollapsed}\n aria-label={collapsed ? translate('shell.sidebar.expand') : translate('shell.sidebar.collapse')}\n aria-pressed={collapsed}\n title={collapsed ? translate('shell.sidebar.expand') : translate('shell.sidebar.collapse')}\n className={cn(\n 'flex h-9 items-center gap-2.5 rounded-md text-sm text-muted-foreground transition-colors hover:bg-muted hover:text-foreground',\n collapsed ? 'justify-center px-0' : 'px-3',\n )}\n >\n {collapsed ? (\n <PanelLeftOpen className=\"size-4 shrink-0\" />\n ) : (\n <>\n <PanelLeftClose className=\"size-4 shrink-0\" />\n <span className=\"min-w-0 flex-1 truncate text-left\">{translate('shell.sidebar.collapseLabel')}</span>\n </>\n )}\n </button>\n </div>\n </nav>\n )\n}\n\n/**\n * EC-479 — what the project has not declared but the studio cannot work without.\n *\n * Joins the rail foot for the same reason EC-467's and EC-469's findings do: it is a\n * project-CONFIGURATION problem, not a content problem in one document, and the rail\n * is on every studio surface. It exists at all because the engine used to hide this\n * state behind its own `cms-core` fixtures — an unconfigured studio showed sample\n * collections and sample documents as if they were the project's. Removing that\n * fallback without reporting the absence would just trade a wrong screen for a blank\n * one, which the house rule bans equally.\n *\n * Read from the config holder alone (static per studio boot, like `useReuseIssues`),\n * so it needs no CMS provider and is memoised once. The pure decision core is\n * `projectConfigIssues`.\n */\nfunction useProjectConfigIssues(): CustomizationIssue[] {\n return useMemo(() => projectConfigIssues(resolveDeploymentCollections()), [])\n}\n\n/**\n * EC-467 — the project's reusable-elements config issues: a declaration that\n * cannot resolve, or an ambiguous inference the studio refuses to guess through.\n * They join the rail foot because they are project-CONFIGURATION problems (like the\n * rail's own and the field renderers'), not content problems in one document — and\n * the rail is on every studio surface, so the developer cannot miss them. A project\n * with an unambiguous embed, or with none at all, contributes nothing here.\n *\n * Resolved from the config holders alone (registry + config collections + studio\n * config), all static per studio boot, so this needs no CMS provider and is\n * memoised once. The pure decision core is `resolveReuse`.\n */\nfunction useReuseIssues(): CustomizationIssue[] {\n return useMemo(\n () =>\n resolveReuse(\n getStudioRegistry().registry.list(),\n resolveDeploymentCollections(),\n resolveDeploymentReuse(),\n ).issues,\n [],\n )\n}\n\n/**\n * EC-469 — the project's URL-map issues: two routes that match the same URLs, or a\n * redirect chain that loops.\n *\n * They join the rail foot for the same reason EC-467's reusable-elements issues do:\n * they are project-level problems, not content problems in one document, so the\n * document-scoped issues pane has no place for them — and the rail is on every studio\n * surface, so the developer cannot miss them. This is emphatically not a revived\n * Routes view (EC-303 retired that, correctly): nothing here is editable, it is\n * validation of the code + data the studio already reads.\n *\n * The two halves come from different planes, which is why only the studio can show\n * both: routes are CODE (config, static per boot) while redirects are editorial DATA\n * (the rail, live). The redirects read shares the rail's own query key, so a loop\n * disappears from the foot the moment it is fixed — and while that query is loading or\n * failed, `data` is absent and only the code-side conflicts are reported. That is\n * honest: the rail itself surfaces its own load error.\n *\n * Copy follows the issues-pane convention (EC-192) and is mapped by\n * {@link routeIssueItems} — severity included since EC-493.\n */\nfunction useRouteIssues(projectId: string): NavIssueItem[] {\n const redirects = useRedirectList(projectId).data\n return useMemo(\n () =>\n routeIssueItems(\n routeValidationIssues(\n { routes: resolveDeploymentRoutes(), collections: resolveDeploymentCollections() },\n redirects ?? [],\n ),\n ),\n [redirects],\n )\n}\n\n/**\n * EC-606 — stored documents whose collection the project does not declare.\n *\n * Read from the content session, not the project session: this rail renders in\n * `StudioShell`, OUTSIDE `ProjectSessionProvider` (`studio-project-screen.tsx`), so\n * `useProjectSession` is unreachable here — and the provider's own `session` value is\n * `null` in the playground anyway. The content session is the channel both modes\n * already configure from the same `loadHydratedCms` result\n * (`configureContentSession(contentSessionOf(cms))`), and it is subscribable, so the\n * foot re-renders whenever it is re-pointed: at boot, on every CMS-surface mount, and\n * on the write-sync refresh while a canvas is mounted (`content-session-refresh.ts`).\n * The rail deliberately does NOT register as a refresh consumer — connected mode would\n * then pay a full hydration per autosave for a finding that changes only when config\n * changes or orphan rows are removed. The server snapshot is empty: there is no\n * session during SSR, so hydration renders no warning and the client adds it after.\n * The pure mapping is {@link orphanedCollectionItems}.\n */\nfunction useOrphanedCollectionIssues(): NavIssueItem[] {\n const orphans = useSyncExternalStore(\n subscribeContentSession,\n contentSessionOrphans,\n serverContentSessionOrphans,\n )\n return useMemo(() => orphanedCollectionItems(orphans), [orphans])\n}\n\n/** One rail view (top-level or nested) — a Link to its home route, active by view id. */\nfunction NavViewLink({\n projectId,\n view,\n activeViewId,\n collapsed,\n nested = false,\n hasActiveChild = false,\n collapsible = false,\n expanded = true,\n onToggleExpanded,\n}: {\n projectId: string\n view: ResolvedRailView\n activeViewId: string | null\n collapsed: boolean\n nested?: boolean\n /** EC-293: this parent's a child is the active view → a subtle (non-accent) hint. */\n hasActiveChild?: boolean\n /** EC-293: this parent has nested children → render a disclosure chevron. */\n collapsible?: boolean\n /** Whether the nested children are shown (chevron points down) or hidden. */\n expanded?: boolean\n /** Toggle this parent's collapse (the chevron click — does not navigate). */\n onToggleExpanded?: () => void\n}) {\n const isActive = view.id === activeViewId\n const Icon = view.icon\n const link = (\n <Link\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId, _splat: view.path }}\n title={view.label}\n aria-current={isActive ? 'page' : undefined}\n className={cn(\n 'group relative flex h-9 items-center gap-2.5 rounded-md text-sm transition-colors',\n // Collapsible parents reserve room on the right for the chevron toggle.\n collapsed ? 'justify-center px-0' : nested ? 'pl-7 pr-3' : collapsible ? 'pl-3 pr-9' : 'px-3',\n isActive\n ? 'bg-accent font-medium text-accent-foreground'\n : hasActiveChild\n ? 'text-foreground hover:bg-muted'\n : 'text-muted-foreground hover:bg-muted hover:text-foreground',\n )}\n >\n {/* Strong active marker: a leading accent bar (EC-214). */}\n {isActive ? (\n <span className=\"absolute inset-y-1.5 left-0 w-0.5 rounded-full bg-primary\" aria-hidden />\n ) : null}\n <Icon className=\"size-4 shrink-0\" />\n {collapsed ? null : <span className=\"min-w-0 flex-1 truncate\">{view.label}</span>}\n {view.availability === 'planned' && !collapsed ? (\n <span className=\"size-1.5 shrink-0 rounded-full bg-warning\" aria-hidden title={translate('shell.nav.planned')} />\n ) : null}\n </Link>\n )\n // A leaf (or the collapsed icons-only rail) is just the link.\n if (!collapsible) return link\n // A parent pairs the link with a sibling chevron toggle (so the chevron collapses\n // without navigating, and the link still navigates to the parent surface).\n return (\n <div className=\"relative\">\n {link}\n <button\n type=\"button\"\n onClick={onToggleExpanded}\n aria-label={translate(expanded ? 'shell.nav.collapseView' : 'shell.nav.expandView', {\n name: view.label,\n })}\n aria-expanded={expanded}\n className=\"absolute right-1 top-1/2 grid size-6 -translate-y-1/2 place-items-center rounded text-muted-foreground/70 transition-colors hover:bg-background hover:text-foreground\"\n >\n <ChevronDown\n className={cn('size-3.5 transition-transform', expanded ? '' : '-rotate-90')}\n aria-hidden\n />\n </button>\n </div>\n )\n}\n"]}
|
|
1
|
+
{"version":3,"file":"section-nav.js","sourceRoot":"","sources":["../../src/components/section-nav.tsx"],"names":[],"mappings":"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","sourcesContent":["import { Fragment, useCallback, useEffect, useMemo, useState, useSyncExternalStore } from 'react'\nimport { Link, STUDIO_PROJECT_ROUTE, useLocation } from '@elytracms/studio/router'\nimport { ChevronDown, PanelLeftClose, PanelLeftOpen } from 'lucide-react'\nimport type { Project } from '@elytracms/core/studio-core'\nimport type { CustomizationIssue } from '@elytracms/core/studio-core'\nimport { cn } from '../lib/utils'\nimport { readCollapsedViews, storeCollapsedViews } from '../lib/rail'\nimport { getStudioRegistry } from '../lib/registry'\nimport {\n projectConfigIssues,\n resolveDeploymentCollections,\n resolveDeploymentReuse,\n resolveDeploymentRoutes,\n} from '../lib/workspace'\nimport { routeValidationIssues } from '../lib/routes/route-issues'\nimport {\n contentSessionOrphans,\n serverContentSessionOrphans,\n subscribeContentSession,\n} from '../lib/data'\nimport { translate } from '../lib/i18n'\nimport { Text } from '@elytracms/studio/ui'\nimport { NavIssues, customizationItems, orphanedCollectionItems, routeIssueItems } from './nav-issues'\nimport type { NavIssueItem } from './nav-issues'\nimport { activeViewIdFromPath, isNavViewVisible, useStudioNav } from './app-views/sidebar'\nimport type { ResolvedRailView } from './app-views/sidebar'\nimport { resolveReuse } from './builder/reuse'\nimport { redirectRecordKind } from '@elytracms/core/persistence'\nimport { useRecordList } from './records/record-rail'\nimport { useContributedNavIssues, useNavIssues } from './nav-issues-registry'\nimport type { AppViewModule } from './app-views/module'\nimport { useFieldRenderers } from './cms/field-renderer'\nimport { useProjectRole } from './auth-context'\nimport type { NavPermissionContext } from './app-views/module'\n\nexport interface SectionNavProps {\n project: Project\n /** User-toggled collapse (EC-214) — icons only when collapsed, persisted by the shell. */\n collapsed: boolean\n onToggleCollapsed: () => void\n}\n\n/**\n * Vertical section rail (brief §8.1), driven by the configurable sidebar (EC-237):\n * a consumer-extendable tree of views / groups / separators resolved over the\n * built-in views (EC-239). Availability-aware (`unavailable` items are absent per\n * EC-221); the active item is matched by the VIEW ID in the URL, so two views\n * backed by the same surface (Content vs. a focused Categories) highlight\n * independently. The preference-persisted collapse and the strong active marker\n * (EC-214) are preserved. Customization issues (duplicate view id, invalid view,\n * non-component renderers) render at the rail foot — visible, never silent.\n */\nexport function SectionNav({ project, collapsed, onToggleCollapsed }: SectionNavProps) {\n const { pathname } = useLocation()\n const activeViewId = activeViewIdFromPath(pathname)\n const { nodes, issues: navIssues, registry } = useStudioNav(project)\n const { issues: rendererIssues } = useFieldRenderers()\n const configIssues = useProjectConfigIssues()\n const reuseIssues = useReuseIssues()\n const projectId = project.id\n const routeIssues = useRouteIssues(projectId)\n const orphanIssues = useOrphanedCollectionIssues()\n // EC-616: findings contributed by views and addons (`useNavIssues`), and the views that\n // declare always-on findings (`useIssues`) — mounted below, whether or not they are open.\n const contributedIssues = useContributedNavIssues()\n const issueViews = useMemo(() => registry.views.filter((view) => view.useIssues), [registry])\n // Config issues lead: \"this project declares no collections\" explains every\n // other emptiness on the screen, so it is the first thing to read.\n const issues = [\n ...customizationItems([...configIssues, ...navIssues, ...rendererIssues, ...reuseIssues]),\n ...routeIssues,\n ...orphanIssues,\n ...contributedIssues,\n ]\n\n // EC-240 (AD-18): per-nav-item, default-open permission gating. The signed-in\n // user's role drives an OPTIONAL `visible` predicate on any view; absent →\n // visible. UI reflection only — the server chokepoint stays the real boundary,\n // and a permission-hidden view is still reachable by URL (EC-221).\n const role = useProjectRole(projectId)\n const navCtx: NavPermissionContext = { project, role }\n const isVisible = (view: ResolvedRailView) => isNavViewVisible(view, navCtx)\n\n // EC-293: per-parent collapse of nested sub-views, persisted (read after mount,\n // like the rail-collapse pref — no flash script needed). A collapsed parent hides\n // its children but stays a navigable link.\n const [collapsedViews, setCollapsedViews] = useState<Set<string>>(() => new Set())\n useEffect(() => {\n setCollapsedViews(readCollapsedViews())\n }, [])\n const toggleViewCollapsed = useCallback((id: string) => {\n setCollapsedViews((prev) => {\n const next = new Set(prev)\n if (next.has(id)) next.delete(id)\n else next.add(id)\n storeCollapsedViews(next)\n return next\n })\n }, [])\n\n return (\n <nav\n aria-label={translate('shell.nav.label')}\n className={cn(\n 'flex shrink-0 flex-col gap-0.5 border-r border-border bg-card/40 p-2 transition-[width] duration-150',\n collapsed ? 'w-[3.5rem]' : 'w-56',\n )}\n >\n {nodes.map((node, index) => {\n if (node.kind === 'separator') {\n return <div key={`sep-${index}`} className=\"my-1.5 border-t border-border/60\" aria-hidden />\n }\n if (node.kind === 'group') {\n const views = node.views.filter(\n (view) => view.availability !== 'unavailable' && isVisible(view),\n )\n if (views.length === 0) return null\n return (\n <div key={`group-${node.label}-${index}`} className=\"mt-2 first:mt-0\">\n {collapsed ? (\n <div className=\"my-1 border-t border-border/60\" aria-hidden />\n ) : (\n <Text\n size=\"xs\"\n tone=\"subtle\"\n className=\"px-3 pb-1 pt-1 font-medium uppercase tracking-wide\"\n >\n {node.label}\n </Text>\n )}\n {views.map((view) => (\n <NavViewLink\n key={view.id}\n projectId={projectId}\n view={view}\n activeViewId={activeViewId}\n collapsed={collapsed}\n />\n ))}\n </div>\n )\n }\n // EC-221: an unavailable view is absent from the rail (not greyed) — it\n // stays reachable by URL, the rail simply doesn't advertise it. EC-240: a\n // permission-hidden view is likewise absent-but-addressable.\n if (node.availability === 'unavailable' || !isVisible(node)) return null\n // EC-293: a view may nest its sub-views (CMS collections). The parent renders\n // as a link with a disclosure chevron; its children render indented below,\n // hidden when the rail is collapsed OR this parent is collapsed. The parent\n // gets a subtle active hint when one of its children is the active view.\n const hasChildren = (node.children?.length ?? 0) > 0\n const hasActiveChild = node.children?.some((child) => child.id === activeViewId) ?? false\n const viewCollapsed = collapsedViews.has(node.id)\n return (\n <Fragment key={node.id}>\n <NavViewLink\n projectId={projectId}\n view={node}\n activeViewId={activeViewId}\n collapsed={collapsed}\n hasActiveChild={hasActiveChild}\n collapsible={hasChildren && !collapsed}\n expanded={!viewCollapsed}\n onToggleExpanded={() => toggleViewCollapsed(node.id)}\n />\n {collapsed || viewCollapsed\n ? null\n : node.children?.filter(isVisible).map((child) => (\n <NavViewLink\n key={child.id}\n projectId={projectId}\n view={child}\n activeViewId={activeViewId}\n collapsed={collapsed}\n nested\n />\n ))}\n </Fragment>\n )\n })}\n\n {issueViews.map((view) => (\n <ViewIssuesProbe key={view.id} view={view} project={project} />\n ))}\n\n <div className=\"mt-auto flex flex-col gap-0.5\">\n {issues.length > 0 ? <NavIssues issues={issues} collapsed={collapsed} /> : null}\n\n {/* Collapse toggle pinned to the rail foot (EC-214). */}\n <button\n type=\"button\"\n onClick={onToggleCollapsed}\n aria-label={collapsed ? translate('shell.sidebar.expand') : translate('shell.sidebar.collapse')}\n aria-pressed={collapsed}\n title={collapsed ? translate('shell.sidebar.expand') : translate('shell.sidebar.collapse')}\n className={cn(\n 'flex h-9 items-center gap-2.5 rounded-md text-sm text-muted-foreground transition-colors hover:bg-muted hover:text-foreground',\n collapsed ? 'justify-center px-0' : 'px-3',\n )}\n >\n {collapsed ? (\n <PanelLeftOpen className=\"size-4 shrink-0\" />\n ) : (\n <>\n <PanelLeftClose className=\"size-4 shrink-0\" />\n <span className=\"min-w-0 flex-1 truncate text-left\">{translate('shell.sidebar.collapseLabel')}</span>\n </>\n )}\n </button>\n </div>\n </nav>\n )\n}\n\n/**\n * EC-479 — what the project has not declared but the studio cannot work without.\n *\n * Joins the rail foot for the same reason EC-467's and EC-469's findings do: it is a\n * project-CONFIGURATION problem, not a content problem in one document, and the rail\n * is on every studio surface. It exists at all because the engine used to hide this\n * state behind its own `cms-core` fixtures — an unconfigured studio showed sample\n * collections and sample documents as if they were the project's. Removing that\n * fallback without reporting the absence would just trade a wrong screen for a blank\n * one, which the house rule bans equally.\n *\n * Read from the config holder alone (static per studio boot, like `useReuseIssues`),\n * so it needs no CMS provider and is memoised once. The pure decision core is\n * `projectConfigIssues`.\n */\nfunction useProjectConfigIssues(): CustomizationIssue[] {\n return useMemo(() => projectConfigIssues(resolveDeploymentCollections()), [])\n}\n\n/**\n * EC-467 — the project's reusable-elements config issues: a declaration that\n * cannot resolve, or an ambiguous inference the studio refuses to guess through.\n * They join the rail foot because they are project-CONFIGURATION problems (like the\n * rail's own and the field renderers'), not content problems in one document — and\n * the rail is on every studio surface, so the developer cannot miss them. A project\n * with an unambiguous embed, or with none at all, contributes nothing here.\n *\n * Resolved from the config holders alone (registry + config collections + studio\n * config), all static per studio boot, so this needs no CMS provider and is\n * memoised once. The pure decision core is `resolveReuse`.\n */\nfunction useReuseIssues(): CustomizationIssue[] {\n return useMemo(\n () =>\n resolveReuse(\n getStudioRegistry().registry.list(),\n resolveDeploymentCollections(),\n resolveDeploymentReuse(),\n ).issues,\n [],\n )\n}\n\n/**\n * EC-469 — the project's URL-map issues: two routes that match the same URLs, or a\n * redirect chain that loops.\n *\n * They join the rail foot for the same reason EC-467's reusable-elements issues do:\n * they are project-level problems, not content problems in one document, so the\n * document-scoped issues pane has no place for them — and the rail is on every studio\n * surface, so the developer cannot miss them. This is emphatically not a revived\n * Routes view (EC-303 retired that, correctly): nothing here is editable, it is\n * validation of the code + data the studio already reads.\n *\n * The two halves come from different planes, which is why only the studio can show\n * both: routes are CODE (config, static per boot) while redirects are editorial DATA\n * (the rail, live). The redirects read shares the rail's own query key, so a loop\n * disappears from the foot the moment it is fixed — and while that query is loading or\n * failed, `data` is absent and only the code-side conflicts are reported. That is\n * honest: the rail itself surfaces its own load error.\n *\n * Copy follows the issues-pane convention (EC-192) and is mapped by\n * {@link routeIssueItems} — severity included since EC-493.\n */\nfunction useRouteIssues(projectId: string): NavIssueItem[] {\n const redirects = useRecordList(redirectRecordKind, projectId).data\n return useMemo(\n () =>\n routeIssueItems(\n routeValidationIssues(\n { routes: resolveDeploymentRoutes(), collections: resolveDeploymentCollections() },\n redirects ?? [],\n ),\n ),\n [redirects],\n )\n}\n\n/**\n * EC-606 — stored documents whose collection the project does not declare.\n *\n * Read from the content session, not the project session: this rail renders in\n * `StudioShell`, OUTSIDE `ProjectSessionProvider` (`studio-project-screen.tsx`), so\n * `useProjectSession` is unreachable here — and the provider's own `session` value is\n * `null` in the playground anyway. The content session is the channel both modes\n * already configure from the same `loadHydratedCms` result\n * (`configureContentSession(contentSessionOf(cms))`), and it is subscribable, so the\n * foot re-renders whenever it is re-pointed: at boot, on every CMS-surface mount, and\n * on the write-sync refresh while a canvas is mounted (`content-session-refresh.ts`).\n * The rail deliberately does NOT register as a refresh consumer — connected mode would\n * then pay a full hydration per autosave for a finding that changes only when config\n * changes or orphan rows are removed. The server snapshot is empty: there is no\n * session during SSR, so hydration renders no warning and the client adds it after.\n * The pure mapping is {@link orphanedCollectionItems}.\n */\nfunction useOrphanedCollectionIssues(): NavIssueItem[] {\n const orphans = useSyncExternalStore(\n subscribeContentSession,\n contentSessionOrphans,\n serverContentSessionOrphans,\n )\n return useMemo(() => orphanedCollectionItems(orphans), [orphans])\n}\n\n/**\n * EC-616 — runs one view's always-on findings hook (`useIssues`) and publishes what it\n * returns to the foot. One component per view, so each hook keeps its own hook order.\n */\nfunction ViewIssuesProbe({ view, project }: { view: AppViewModule; project: Project }) {\n const issues = view.useIssues!({ project, projectId: project.id })\n useNavIssues(`view:`, issues)\n return null\n}\n\n/** One rail view (top-level or nested) — a Link to its home route, active by view id. */\nfunction NavViewLink({\n projectId,\n view,\n activeViewId,\n collapsed,\n nested = false,\n hasActiveChild = false,\n collapsible = false,\n expanded = true,\n onToggleExpanded,\n}: {\n projectId: string\n view: ResolvedRailView\n activeViewId: string | null\n collapsed: boolean\n nested?: boolean\n /** EC-293: this parent's a child is the active view → a subtle (non-accent) hint. */\n hasActiveChild?: boolean\n /** EC-293: this parent has nested children → render a disclosure chevron. */\n collapsible?: boolean\n /** Whether the nested children are shown (chevron points down) or hidden. */\n expanded?: boolean\n /** Toggle this parent's collapse (the chevron click — does not navigate). */\n onToggleExpanded?: () => void\n}) {\n const isActive = view.id === activeViewId\n const Icon = view.icon\n const link = (\n <Link\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId, _splat: view.path }}\n title={view.label}\n aria-current={isActive ? 'page' : undefined}\n className={cn(\n 'group relative flex h-9 items-center gap-2.5 rounded-md text-sm transition-colors',\n // Collapsible parents reserve room on the right for the chevron toggle.\n collapsed ? 'justify-center px-0' : nested ? 'pl-7 pr-3' : collapsible ? 'pl-3 pr-9' : 'px-3',\n isActive\n ? 'bg-accent font-medium text-accent-foreground'\n : hasActiveChild\n ? 'text-foreground hover:bg-muted'\n : 'text-muted-foreground hover:bg-muted hover:text-foreground',\n )}\n >\n {/* Strong active marker: a leading accent bar (EC-214). */}\n {isActive ? (\n <span className=\"absolute inset-y-1.5 left-0 w-0.5 rounded-full bg-primary\" aria-hidden />\n ) : null}\n <Icon className=\"size-4 shrink-0\" />\n {collapsed ? null : <span className=\"min-w-0 flex-1 truncate\">{view.label}</span>}\n {view.availability === 'planned' && !collapsed ? (\n <span className=\"size-1.5 shrink-0 rounded-full bg-warning\" aria-hidden title={translate('shell.nav.planned')} />\n ) : null}\n </Link>\n )\n // A leaf (or the collapsed icons-only rail) is just the link.\n if (!collapsible) return link\n // A parent pairs the link with a sibling chevron toggle (so the chevron collapses\n // without navigating, and the link still navigates to the parent surface).\n return (\n <div className=\"relative\">\n {link}\n <button\n type=\"button\"\n onClick={onToggleExpanded}\n aria-label={translate(expanded ? 'shell.nav.collapseView' : 'shell.nav.expandView', {\n name: view.label,\n })}\n aria-expanded={expanded}\n className=\"absolute right-1 top-1/2 grid size-6 -translate-y-1/2 place-items-center rounded text-muted-foreground/70 transition-colors hover:bg-background hover:text-foreground\"\n >\n <ChevronDown\n className={cn('size-3.5 transition-transform', expanded ? '' : '-rotate-90')}\n aria-hidden\n />\n </button>\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 { CircleAlert, Clock, Inbox, Loader2, Lock } from 'lucide-react';
|
|
3
3
|
import { Badge, Heading, Stack, Text } from '@elytracms/studio/ui';
|
|
4
|
-
import { cn } from '
|
|
5
|
-
import { translate } from '
|
|
4
|
+
import { cn } from '../lib/utils.js';
|
|
5
|
+
import { translate } from '../lib/i18n/index.js';
|
|
6
6
|
/**
|
|
7
7
|
* Resolve the runtime state of a section (brief §8.1, §11). A `forced` value
|
|
8
8
|
* (e.g. from a `?state=` param) wins, making every state reachable
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"section-state.js","sourceRoot":"","sources":["../../src/components/section-state.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAmB,MAAM,cAAc,CAAA;AAGxF,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAA;AAClE,OAAO,EAAE,EAAE,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"section-state.js","sourceRoot":"","sources":["../../src/components/section-state.tsx"],"names":[],"mappings":"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","sourcesContent":["import { CircleAlert, Clock, Inbox, Loader2, Lock, type LucideIcon } from 'lucide-react'\nimport type { ReactNode } from 'react'\nimport type { SectionAvailability, SectionState } from '@elytracms/core/studio-core'\nimport { Badge, Heading, Stack, Text } from '@elytracms/studio/ui'\nimport { cn } from '../lib/utils'\nimport { translate } from '../lib/i18n'\n\n/**\n * Resolve the runtime state of a section (brief §8.1, §11). A `forced` value\n * (e.g. from a `?state=` param) wins, making every state reachable\n * deterministically; otherwise availability decides.\n */\nexport function resolveSectionState(\n availability: SectionAvailability,\n forced?: SectionState | null,\n): SectionState {\n if (forced) return forced\n if (availability === 'planned') return 'planned'\n if (availability === 'unavailable') return 'unavailable'\n return 'working'\n}\n\ninterface StatePreset {\n icon: LucideIcon\n badgeVariant: 'outline' | 'warning' | 'error' | 'info'\n spin?: boolean\n}\n\n// Structural (icon/variant) only; the title/description/badge copy is resolved at\n// the render site via `translate` so it reflects the active UI locale (EC-241).\nconst PRESETS: Record<Exclude<SectionState, 'working'>, StatePreset> = {\n empty: { icon: Inbox, badgeVariant: 'outline' },\n loading: { icon: Loader2, badgeVariant: 'info', spin: true },\n error: { icon: CircleAlert, badgeVariant: 'error' },\n unavailable: { icon: Lock, badgeVariant: 'outline' },\n planned: { icon: Clock, badgeVariant: 'warning' },\n}\n\nexport interface SectionStateViewProps {\n state: Exclude<SectionState, 'working'>\n /** Section-specific heading, e.g. \"CMS\" — overrides the generic preset title. */\n title?: string\n description?: ReactNode\n action?: ReactNode\n className?: string\n}\n\n/** Loading uses a skeleton; the rest use a centered placeholder. Consistent across sections. */\nexport function SectionStateView({\n state,\n title,\n description,\n action,\n className,\n}: SectionStateViewProps) {\n // `title`/`description` thread through to the skeleton's optional label slot\n // (EC-327) — without this, a caller's section-specific loading copy (e.g. \"Loading\n // assets…\") would be silently dropped the moment `state` was 'loading'.\n if (state === 'loading') {\n return <SectionSkeleton className={className} label={title} description={description} />\n }\n\n const preset = PRESETS[state]\n const Icon = preset.icon\n // `state` is narrowed to a non-loading state here, so each has a title/description/badge key.\n const presetTitle = translate(`shell.sectionState.${state}.title`)\n const presetDescription = translate(`shell.sectionState.${state}.description`)\n const badgeLabel = translate(`shell.sectionState.badge.${state}`)\n const tone =\n state === 'error'\n ? 'border-destructive/30 bg-destructive/5'\n : state === 'planned'\n ? 'border-warning/30 bg-warning/5'\n : 'border-border bg-card/40'\n\n return (\n <div\n data-section-state={state}\n className={cn('grid min-h-[18rem] place-items-center p-8', className)}\n >\n <Stack\n gap=\"3\"\n align=\"center\"\n className={cn('max-w-sm rounded-xl border border-dashed px-8 py-10 text-center', tone)}\n >\n <span className=\"grid size-11 place-items-center rounded-lg border border-border bg-background text-muted-foreground\">\n <Icon className=\"size-5\" />\n </span>\n <Stack gap=\"1\" align=\"center\">\n <Heading level={2} size=\"h3\">\n {title ?? presetTitle}\n </Heading>\n <Text size=\"sm\" tone=\"muted\">\n {description ?? presetDescription}\n </Text>\n </Stack>\n <Badge variant={preset.badgeVariant}>{badgeLabel}</Badge>\n {action ? <div className=\"pt-1\">{action}</div> : null}\n </Stack>\n </div>\n )\n}\n\nexport interface SectionSkeletonProps {\n className?: string\n /**\n * Section-specific loading heading (e.g. \"Loading assets…\", EC-327). Renders\n * as real text instead of the generic shimmer bar; omitted keeps the bare\n * shimmer (the pre-EC-327 look, still used by callers with no rail-specific\n * copy to show).\n */\n label?: string\n description?: ReactNode\n}\n\n/** Shimmer skeleton standing in for an in-flight section load. */\nexport function SectionSkeleton({ className, label, description }: SectionSkeletonProps) {\n return (\n <div data-section-state=\"loading\" className={cn('space-y-4 p-6', className)} aria-busy>\n {label ? (\n <Stack gap=\"1\">\n <Text weight=\"medium\">{label}</Text>\n {description ? (\n <Text size=\"sm\" tone=\"muted\">\n {description}\n </Text>\n ) : null}\n </Stack>\n ) : (\n <div className=\"h-7 w-48 animate-pulse rounded-md bg-muted\" />\n )}\n <div className=\"grid gap-3 sm:grid-cols-2 lg:grid-cols-3\">\n {Array.from({ length: 6 }).map((_, i) => (\n <div key={i} className=\"h-28 animate-pulse rounded-xl bg-muted/70\" />\n ))}\n </div>\n </div>\n )\n}\n"]}
|
|
@@ -2,8 +2,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { Link, STUDIO_PROJECT_ROUTE } from '@elytracms/studio/router';
|
|
3
3
|
import { availabilityOf, sectionStateSchema } from '@elytracms/core/studio-core';
|
|
4
4
|
import { Code, Row, Stack, Text } from '@elytracms/studio/ui';
|
|
5
|
-
import { cn } from '
|
|
6
|
-
import { translate } from '
|
|
5
|
+
import { cn } from '../lib/utils.js';
|
|
6
|
+
import { translate } from '../lib/i18n/index.js';
|
|
7
7
|
import { resolveSectionState, SectionStateView } from './section-state.js';
|
|
8
8
|
/** Parse a `?state=` search param into a SectionState (undefined when absent/invalid). */
|
|
9
9
|
export function parseSectionState(raw) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"section-surface.js","sourceRoot":"","sources":["../../src/components/section-surface.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,IAAI,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAA;AAErE,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAA;AAEhF,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAA;AAC7D,OAAO,EAAE,EAAE,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"section-surface.js","sourceRoot":"","sources":["../../src/components/section-surface.tsx"],"names":[],"mappings":"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","sourcesContent":["import { Link, STUDIO_PROJECT_ROUTE } from '@elytracms/studio/router'\nimport type { ReactNode } from 'react'\nimport { availabilityOf, sectionStateSchema } from '@elytracms/core/studio-core'\nimport type { Project, ProjectId, SectionId, SectionState } from '@elytracms/core/studio-core'\nimport { Code, Row, Stack, Text } from '@elytracms/studio/ui'\nimport { cn } from '../lib/utils'\nimport { translate } from '../lib/i18n'\nimport { resolveSectionState, SectionStateView } from './section-state'\n\n/** Parse a `?state=` search param into a SectionState (undefined when absent/invalid). */\nexport function parseSectionState(raw: unknown): SectionState | undefined {\n const parsed = sectionStateSchema.safeParse(raw)\n return parsed.success ? parsed.data : undefined\n}\n\nexport interface SectionSurfaceProps {\n project: Project\n section: SectionId\n /** Forced state from `?state=`; overrides availability. */\n forcedState?: SectionState\n /** Working-state content. */\n children?: ReactNode\n}\n\n/**\n * Resolves a section's deterministic state (brief §8.1, §11) and renders it: the\n * working content when `working`, else a consistent state view.\n *\n * EC-346: the dev {@link StateSwitcher} panel used to render here, unconditionally,\n * above every section — a debug/reproducibility harness that leaked into\n * production shells (a real studio's Assets header showed a \"state · default\n * working empty loading error…\" testing strip). It no longer mounts by default.\n * The `?state=` URL contract it exercised stays fully live — `resolveSectionState`\n * below still consumes `forcedState`, so every state remains reachable by URL for\n * tests/agents. `StateSwitcher` itself stays exported and covered (nothing was\n * deleted from the state model) so a future dev-only surface — gated behind\n * `import.meta.env.DEV` or a `defineStudio({ devTools: true })` flag — can remount it.\n */\nexport function SectionSurface({ project, section, forcedState, children }: SectionSurfaceProps) {\n const availability = availabilityOf(project.settings.sectionAvailability, section)\n const state = resolveSectionState(availability, forcedState)\n\n return (\n <Stack gap=\"none\" className=\"min-h-full\">\n {/* EC-574: the title is the SAME translated label the rail shows (`nav.<section>.label`),\n not the former English `SECTIONS` table — one declaration per section label. */}\n {state === 'working' ? children : <SectionStateView state={state} title={translate(`nav.${section}.label`)} />}\n </Stack>\n )\n}\n\n/**\n * Deterministic state switcher (brief §11). Links to the same section with each\n * `?state=` value so agents/humans can reproduce every state by URL or click.\n */\nexport function StateSwitcher({\n projectId,\n section,\n current,\n}: {\n projectId: ProjectId\n section: SectionId\n current?: SectionState\n}) {\n const states: SectionState[] = ['working', 'empty', 'loading', 'error', 'unavailable', 'planned']\n const activeKey = current ?? 'default'\n return (\n <Row\n gap=\"1\"\n wrap\n className=\"shrink-0 border-b border-dashed border-border/70 px-4 py-2 sm:px-6\"\n data-testid=\"state-switcher\"\n >\n <Text size=\"xs\" tone=\"subtle\" mono className=\"mr-1\">\n state\n </Text>\n <Link\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId, _splat: section }}\n search={{}}\n className={cn(\n 'rounded-md px-2 py-0.5 font-mono text-xs transition-colors',\n activeKey === 'default'\n ? 'bg-accent text-accent-foreground'\n : 'text-muted-foreground hover:bg-muted hover:text-foreground',\n )}\n >\n default\n </Link>\n {states.map((s) => {\n const active = s === activeKey\n return (\n <Link\n key={s}\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId, _splat: section }}\n search={{ state: s }}\n className={cn(\n 'rounded-md px-2 py-0.5 font-mono text-xs transition-colors',\n active\n ? 'bg-accent text-accent-foreground'\n : 'text-muted-foreground hover:bg-muted hover:text-foreground',\n )}\n >\n {s}\n </Link>\n )\n })}\n <Code tone=\"muted\" className=\"ml-2 hidden sm:inline-flex\">\n {activeKey === 'default' ? 'auto' : `?state=${activeKey}`}\n </Code>\n </Row>\n )\n}\n"]}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { CircleAlert } from 'lucide-react';
|
|
3
|
-
import { LOCAL_DEV_CREDENTIALS, authMode } from '
|
|
4
|
-
import { useT } from '
|
|
5
|
-
import { useOperationCache } from '
|
|
3
|
+
import { LOCAL_DEV_CREDENTIALS, authMode } from '../lib/auth/index.js';
|
|
4
|
+
import { useT } from '../lib/i18n/index.js';
|
|
5
|
+
import { useOperationCache } from '../lib/query/index.js';
|
|
6
6
|
import { Dialog, DialogBlockingContent, DialogTitle, Row, Stack, Text } from '@elytracms/studio/ui';
|
|
7
7
|
import { useAuth } from './auth-context.js';
|
|
8
8
|
import { SignInScreen, activeProjectName } from './sign-in-view.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-expired-overlay.js","sourceRoot":"","sources":["../../src/components/session-expired-overlay.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,EAAE,qBAAqB,EAAE,QAAQ,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"session-expired-overlay.js","sourceRoot":"","sources":["../../src/components/session-expired-overlay.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,EAAE,qBAAqB,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAC7D,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAA;AAClC,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAChD,OAAO,EAAE,MAAM,EAAE,qBAAqB,EAAE,WAAW,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAA;AACnG,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAA;AACxC,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAEhE;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,8BAA8B,GAAG,mCAAmC,CAAA;AAEjF,MAAM,UAAU,qBAAqB;IACnC,MAAM,IAAI,GAAG,OAAO,EAAE,CAAA;IACtB,MAAM,CAAC,GAAG,IAAI,EAAE,CAAA;IAChB,MAAM,KAAK,GAAG,iBAAiB,EAAE,CAAA;IAEjC,IAAI,CAAC,IAAI,CAAC,cAAc;QAAE,OAAO,IAAI,CAAA;IAErC,OAAO,CACL,KAAC,MAAM,IAAC,IAAI,kBACV,MAAC,qBAAqB,wBACF,SAAS,EAC3B,gBAAgB,EAAE,8BAA8B,aAEhD,KAAC,WAAW,IAAC,SAAS,EAAC,SAAS,YAAE,CAAC,CAAC,2BAA2B,CAAC,GAAe,EAI/E,cAAK,SAAS,EAAC,wCAAwC,YACrD,KAAC,YAAY,IACX,MAAM,EAAC,MAAM,EACb,MAAM,EAAE,KAAC,oBAAoB,KAAG,EAChC,WAAW,EAAE,iBAAiB,EAAE,EAChC,cAAc,EAAE,KAAK,EACrB,cAAc,EAAE,QAAQ,EAAE,KAAK,YAAY,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,IAAI,EAC1E,YAAY,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,EACtE,eAAe,EAAE,GAAG,EAAE;wBACpB,kEAAkE;wBAClE,gEAAgE;wBAChE,+DAA+D;wBAC/D,0DAA0D;wBAC1D,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC,GAEtB,GACE,IACgB,GACjB,CACV,CAAA;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,oBAAoB;IAClC,MAAM,CAAC,GAAG,IAAI,EAAE,CAAA;IAChB,OAAO,CACL,cACE,IAAI,EAAC,OAAO,EACZ,SAAS,EAAC,qEAAqE,YAE/E,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,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,SAAS,EAAC,8BAA8B,YACrD,CAAC,CAAC,2BAA2B,CAAC,GAC1B,EACP,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,OAAO,YACzB,CAAC,CAAC,sBAAsB,CAAC,GACrB,IACD,IACJ,GACF,CACP,CAAA;AACH,CAAC","sourcesContent":["import { CircleAlert } from 'lucide-react'\nimport { LOCAL_DEV_CREDENTIALS, authMode } from '../lib/auth'\nimport { useT } from '../lib/i18n'\nimport { useOperationCache } from '../lib/query'\nimport { Dialog, DialogBlockingContent, DialogTitle, Row, Stack, Text } from '@elytracms/studio/ui'\nimport { useAuth } from './auth-context'\nimport { SignInScreen, activeProjectName } from './sign-in-view'\n\n/**\n * The blocking in-place re-auth overlay (EC-403). Mounted once in the studio\n * root (inside `StudioProvider`, over every route) and shown while the auth\n * store reports `sessionExpired`: the backend session died mid-flight, so all\n * editing is blocked behind a non-dismissible modal that mounts the SAME\n * `SignInScreen` composition the `/sign-in` route uses — **without any\n * navigation**. The React tree beneath stays alive, so every unsaved editor\n * state survives; a successful sign-in applies the fresh session (clearing the\n * flag in the auth store) and editing resumes exactly where it stopped.\n * Pending failed writes keep their normal retry paths (a `persistError`\n * clears on the next successful save).\n *\n * Re-auth is strictly sign-in (no create-account toggle — a brand-new account\n * cannot resume an existing editing session). On success the studio query\n * cache is invalidated wholesale so reads that failed or staled while the\n * session was dead recover without a reload.\n *\n * Stacking: `DialogBlockingContent` (EC-392 discipline) — topmost modal layer,\n * focus-trapped, Escape/outside interaction consumed, studio beneath inert.\n *\n * Presentation (EC-437): the sign-in card mounts BARE (no full-page opaque\n * panel) over the stage family's translucent blurred backdrop, so the editor\n * SEES their intact document sitting behind the prompt — showing the \"your\n * changes stay right here\" promise instead of only telling it.\n */\n\n/**\n * The re-auth backdrop (EC-437) — the stage family's visual language\n * (EC-431 AD-A, `surfaceGeometry`), borrowed as CLASSES only, never the\n * `ExpandableSurface` component: the stage is lightly dismissible, re-auth is\n * contractually non-dismissible (`DialogBlockingContent` stays the shell).\n * One blur step above the stage's `backdrop-blur-sm`: the whole dense studio\n * sits behind a small centered card here (not behind a near-full panel), and\n * the stronger step keeps the card unmistakably focal while the studio stays\n * clearly recognizable — judged live against `-sm` with screenshots.\n */\nexport const SESSION_EXPIRED_BACKDROP_CLASS = 'bg-background/70 backdrop-blur-md'\n\nexport function SessionExpiredOverlay() {\n const auth = useAuth()\n const t = useT()\n const cache = useOperationCache()\n\n if (!auth.sessionExpired) return null\n\n return (\n <Dialog open>\n <DialogBlockingContent\n aria-describedby={undefined}\n overlayClassName={SESSION_EXPIRED_BACKDROP_CLASS}\n >\n <DialogTitle className=\"sr-only\">{t('error.sessionExpiredTitle')}</DialogTitle>\n {/* min-h-full (not h-full): when the card outgrows a short viewport,\n the wrapper grows with it and the blocking content scrolls —\n centered when it fits, never clipped when it doesn't. */}\n <div className=\"grid min-h-full place-items-center p-6\">\n <SignInScreen\n layout=\"bare\"\n notice={<SessionExpiredNotice />}\n projectName={activeProjectName()}\n showModeToggle={false}\n devCredentials={authMode() === 'playground' ? LOCAL_DEV_CREDENTIALS : null}\n authenticate={(_mode, email, password) => auth.signIn(email, password)}\n onAuthenticated={() =>\n // The session was dead for an unknown span: reads may have failed\n // (retry: false) or gone stale. One wholesale invalidation lets\n // every active query refetch under the fresh session — resume,\n // not reload. (The empty key prefix matches all queries.)\n cache.invalidate([])\n }\n />\n </div>\n </DialogBlockingContent>\n </Dialog>\n )\n}\n\n/**\n * Why the sign-in screen appeared in place (EC-403): the session-expired copy\n * rendered above the form via the `SignInScreen` notice slot. Pure — exported\n * for the provider-free static-render smoke test.\n */\nexport function SessionExpiredNotice() {\n const t = useT()\n return (\n <div\n role=\"alert\"\n className=\"rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2\"\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 <Text size=\"sm\" className=\"font-medium text-destructive\">\n {t('error.sessionExpiredTitle')}\n </Text>\n <Text size=\"sm\" tone=\"muted\">\n {t('error.sessionExpired')}\n </Text>\n </Stack>\n </Row>\n </div>\n )\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Badge, Code, Panel, PanelHeader, Row, Spacer, Stack, Text } from '@elytracms/studio/ui';
|
|
3
|
-
import { translate } from '
|
|
3
|
+
import { translate } from '../lib/i18n/index.js';
|
|
4
4
|
/**
|
|
5
5
|
* Project settings — read-only (vision AD-8). Identity, locales, and
|
|
6
6
|
* environments are owned by `elytra.config.ts` and read at runtime; the studio
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings-form.js","sourceRoot":"","sources":["../../src/components/settings-form.tsx"],"names":[],"mappings":";AAEA,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAA;AAChG,OAAO,EAAE,SAAS,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"settings-form.js","sourceRoot":"","sources":["../../src/components/settings-form.tsx"],"names":[],"mappings":"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","sourcesContent":["import type { ReactNode } from 'react'\nimport type { Project } from '@elytracms/core/studio-core'\nimport { Badge, Code, Panel, PanelHeader, Row, Spacer, Stack, Text } from '@elytracms/studio/ui'\nimport { translate } from '../lib/i18n'\n\n/**\n * Project settings — read-only (vision AD-8). Identity, locales, and\n * environments are owned by `elytra.config.ts` and read at runtime; the studio\n * never writes them back. This surface displays the resolved config so editors\n * can see how the project is wired without exposing a (fake) edit path.\n */\nexport function SettingsForm({ project }: { project: Project }) {\n const { settings } = project\n return (\n <div className=\"mx-auto w-full max-w-3xl p-4 sm:p-6\">\n <Stack gap=\"6\">\n <Text size=\"sm\" tone=\"muted\">\n Project identity and settings are defined in code — <Code tone=\"muted\">elytra.config.ts</Code>\n . Edit that file to change the name, locales, or environments; the studio reads them at\n runtime.\n </Text>\n\n <Panel>\n <PanelHeader>\n <Text size=\"sm\" weight=\"semibold\">\n {translate('settings.identity.title')}\n </Text>\n </PanelHeader>\n <Stack gap=\"3\" className=\"p-4\">\n <ReadField label={translate('settings.identity.name')}>\n <Text size=\"sm\">{project.name}</Text>\n </ReadField>\n <ReadField label={translate('settings.identity.urlKey')}>\n <Code tone=\"muted\">{project.slug}</Code>\n </ReadField>\n </Stack>\n </Panel>\n\n <Panel>\n <PanelHeader>\n <Text size=\"sm\" weight=\"semibold\">\n {translate('settings.locales.title')}\n </Text>\n <Spacer />\n <Text size=\"xs\" tone=\"muted\" mono>\n {translate('settings.locales.configuredCount', { count: settings.locales.length })}\n </Text>\n </PanelHeader>\n <Stack gap=\"3\" className=\"p-4\">\n <ReadField label={translate('settings.locales.configured')}>\n <Row gap=\"2\" wrap>\n {settings.locales.map((locale) => (\n <Badge\n key={locale}\n variant={locale === settings.defaultLocale ? 'primary' : 'outline'}\n >\n {locale}\n {locale === settings.defaultLocale ? translate('settings.locales.defaultSuffix') : ''}\n </Badge>\n ))}\n </Row>\n </ReadField>\n </Stack>\n </Panel>\n\n <Panel>\n <PanelHeader>\n <Text size=\"sm\" weight=\"semibold\">\n {translate('settings.environments.title')}\n </Text>\n </PanelHeader>\n <Stack gap=\"3\" className=\"p-4\">\n <ReadField label={translate('settings.environments.label')}>\n <Stack gap=\"2\">\n {settings.environments.map((env) => (\n <Row\n key={env.key}\n gap=\"2\"\n className=\"rounded-md border border-border px-3 py-2\"\n title={env.key}\n >\n <Text size=\"sm\">{env.label}</Text>\n </Row>\n ))}\n </Stack>\n </ReadField>\n </Stack>\n </Panel>\n </Stack>\n </div>\n )\n}\n\nfunction ReadField({ label, children }: { label: string; children: ReactNode }) {\n return (\n <Stack gap=\"1\">\n <Text size=\"xs\" tone=\"muted\" weight=\"medium\">\n {label}\n </Text>\n {children}\n </Stack>\n )\n}\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { LogIn } from 'lucide-react';
|
|
3
3
|
import { Badge } from '@elytracms/studio/ui';
|
|
4
|
-
import { translate } from '
|
|
4
|
+
import { translate } from '../lib/i18n/index.js';
|
|
5
5
|
/**
|
|
6
6
|
* The per-backend "Sign in" badge (EC-179): shown on a project whose backend the
|
|
7
7
|
* user hasn't authenticated into yet. Shared by the project switcher menu item and
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sign-in-needed-badge.js","sourceRoot":"","sources":["../../src/components/sign-in-needed-badge.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAA;AACpC,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAA;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"sign-in-needed-badge.js","sourceRoot":"","sources":["../../src/components/sign-in-needed-badge.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAA;AACpC,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAA;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAEvC;;;;GAIG;AACH,MAAM,UAAU,iBAAiB;IAC/B,OAAO,CACL,MAAC,KAAK,IAAC,OAAO,EAAC,SAAS,aACtB,KAAC,KAAK,IAAC,SAAS,EAAC,QAAQ,GAAG,OAAE,SAAS,CAAC,aAAa,CAAC,IAChD,CACT,CAAA;AACH,CAAC","sourcesContent":["import { LogIn } from 'lucide-react'\nimport { Badge } from '@elytracms/studio/ui'\nimport { translate } from '../lib/i18n'\n\n/**\n * The per-backend \"Sign in\" badge (EC-179): shown on a project whose backend the\n * user hasn't authenticated into yet. Shared by the project switcher menu item and\n * the launcher card, so the two surfaces never drift.\n */\nexport function SignInNeededBadge() {\n return (\n <Badge variant=\"warning\">\n <LogIn className=\"size-3\" /> {translate('auth.signIn')}\n </Badge>\n )\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
|
-
import { LOCAL_DEV_CREDENTIALS } from '
|
|
3
|
-
import type { SignInResult } from '
|
|
2
|
+
import { LOCAL_DEV_CREDENTIALS } from '../lib/auth/index.js';
|
|
3
|
+
import type { SignInResult } from '../lib/auth/index.js';
|
|
4
4
|
type AuthMode = 'signin' | 'signup';
|
|
5
5
|
/** A seeded dev-credential entry surfaced in playground mode (fixtures, not secrets). */
|
|
6
6
|
type DevCredential = (typeof LOCAL_DEV_CREDENTIALS)[number];
|
|
@@ -2,11 +2,11 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
import { useState } from 'react';
|
|
3
3
|
import { useNavigate, useSearch } from '@tanstack/react-router';
|
|
4
4
|
import { Button, Card, CardContent, Field, Heading, Input, Stack, Text } from '@elytracms/studio/ui';
|
|
5
|
-
import { useAuth } from '
|
|
6
|
-
import { LOCAL_DEV_CREDENTIALS, authMode, postSignInTarget } from '
|
|
7
|
-
import { signInErrorMessageKey } from '
|
|
8
|
-
import { useT } from '
|
|
9
|
-
import { getActiveProjectSlug, getWorkspaceState } from '
|
|
5
|
+
import { useAuth } from './auth-context.js';
|
|
6
|
+
import { LOCAL_DEV_CREDENTIALS, authMode, postSignInTarget } from '../lib/auth/index.js';
|
|
7
|
+
import { signInErrorMessageKey } from '../lib/auth-errors.js';
|
|
8
|
+
import { useT } from '../lib/i18n/index.js';
|
|
9
|
+
import { getActiveProjectSlug, getWorkspaceState } from '../lib/workspace/index.js';
|
|
10
10
|
/**
|
|
11
11
|
* The project whose backend this sign-in authenticates against (EC-179).
|
|
12
12
|
* Exported for the session-expired re-auth overlay (EC-403), which mounts
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sign-in-view.js","sourceRoot":"","sources":["../../src/components/sign-in-view.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useState } from 'react'\nimport type { FormEvent, ReactNode } from 'react'\nimport { useNavigate, useSearch } from '@tanstack/react-router'\nimport { Button, Card, CardContent, Field, Heading, Input, Stack, Text } from '@elytracms/studio/ui'\nimport { useAuth } from '@elytracms/studio/components/auth-context'\nimport { LOCAL_DEV_CREDENTIALS, authMode, postSignInTarget } from '@elytracms/studio/lib/auth'\nimport type { SignInResult } from '@elytracms/studio/lib/auth'\nimport { signInErrorMessageKey } from '@elytracms/studio/lib/auth-errors'\nimport { useT } from '@elytracms/studio/lib/i18n'\nimport { getActiveProjectSlug, getWorkspaceState } from '@elytracms/studio/lib/workspace'\n\n/**\n * Engine-shipped `/sign-in` screen (EC-150; consolidated from the copy-pasted\n * consumer route by EC-375, EC-369 finding #8). The consumer route file keeps\n * only the route object — `createFileRoute('/sign-in')({ validateSearch,\n * component: SignInView })` — because TanStack Router requires the route object\n * to live in the consumer's route tree; the ~140-line body lives here so a\n * copy never drifts.\n *\n * Deterministic states: idle → submitting → (signed-in redirect | inline\n * structured failure). Invalid credentials are a rendered state, never a silent\n * failure or crash. In playground (in-memory) mode the seeded dev credentials\n * are listed so the whole flow works offline.\n *\n * Split into a `SignInView` container (owns the router + auth seams) and a\n * provider-free `SignInScreen` (pure local form state + injected callbacks) so\n * the screen static-renders in a node smoke test without the full provider tree.\n *\n * All copy resolves through the studio message catalog (`auth.signIn.*`,\n * EC-397), including the engine-classified auth failures — see\n * `signInErrorMessageKey`; an unrecognized failure renders `result.message`\n * raw (honest fallback).\n */\n\ntype FormPhase = 'idle' | 'submitting'\ntype AuthMode = 'signin' | 'signup'\n\n/** A seeded dev-credential entry surfaced in playground mode (fixtures, not secrets). */\ntype DevCredential = (typeof LOCAL_DEV_CREDENTIALS)[number]\n\nexport interface SignInViewProps {\n /**\n * Brand mark rendered above the heading. Defaults to the neutral studio\n * placeholder square — a future logo just passes a node here (e.g.\n * `component: () => <SignInView brandMark={<Logo />} />`), no engine change.\n */\n brandMark?: ReactNode\n /** Product name in the \"Sign in to …\" heading. Default: `'Elytra'`. */\n productName?: string\n}\n\n/**\n * The project whose backend this sign-in authenticates against (EC-179).\n * Exported for the session-expired re-auth overlay (EC-403), which mounts\n * {@link SignInScreen} with the same subtext.\n */\nexport function activeProjectName(): string | null {\n const state = getWorkspaceState()\n if (state.kind !== 'config') return null\n const slug = getActiveProjectSlug()\n const project = (slug && state.projects.find((p) => p.slug === slug)) || state.projects[0]\n return project?.name ?? null\n}\n\n/**\n * The `/sign-in` route component. Container: reads the router (post-sign-in\n * redirect target from the route search) + auth seams and hands the pure\n * presentational screen its data plus an `authenticate` callback.\n */\nexport function SignInView({ brandMark, productName }: SignInViewProps) {\n const auth = useAuth()\n const navigate = useNavigate()\n // Self-source the redirect (loose search — the consumer route's `validateSearch`\n // guarantees `{ redirect?: string }`), so the route file needs no wrapper.\n const search = useSearch({ strict: false }) as { redirect?: string }\n const redirect = search.redirect\n\n return (\n <SignInScreen\n projectName={activeProjectName()}\n showModeToggle={authMode() === 'convex'}\n devCredentials={authMode() === 'playground' ? LOCAL_DEV_CREDENTIALS : null}\n brandMark={brandMark}\n productName={productName}\n authenticate={(mode, email, password) =>\n mode === 'signup' ? auth.signUp(email, password) : auth.signIn(email, password)\n }\n onAuthenticated={() => navigate({ to: postSignInTarget(redirect) })}\n />\n )\n}\n\ninterface SignInScreenProps {\n projectName: string | null\n /** Whether to offer the sign-in ↔ create-account toggle (convex mode only). */\n showModeToggle: boolean\n /** Seeded dev accounts to list (playground mode), or null to hide the hint. */\n devCredentials: readonly DevCredential[] | null\n /**\n * Contextual notice rendered above the heading — the session-expired\n * re-auth overlay (EC-403) explains WHY the screen appeared here. The\n * normal `/sign-in` route passes none.\n */\n notice?: ReactNode\n /**\n * Presentation wrapper (EC-437). `'page'` (default) renders the\n * full-viewport opaque panel — the `/sign-in` route's shipped layout,\n * byte-identical. `'bare'` renders only the centered card column so a host\n * surface composes its own backdrop: the session-expired re-auth overlay\n * mounts it over a translucent blurred backdrop, keeping the intact studio\n * visible beneath the prompt.\n */\n layout?: 'page' | 'bare'\n brandMark?: ReactNode\n productName?: string\n /** Attempt auth for the chosen mode. The container maps this to signIn/signUp. */\n authenticate: (mode: AuthMode, email: string, password: string) => Promise<SignInResult>\n /** Called once, after a successful authentication (the container navigates). */\n onAuthenticated: () => void | Promise<void>\n}\n\n/**\n * Provider-free presentational sign-in screen. Owns only local form state and\n * calls injected callbacks — no context hooks — so it static-renders in tests.\n */\nexport function SignInScreen({\n projectName,\n showModeToggle,\n devCredentials,\n notice,\n layout = 'page',\n brandMark,\n productName = 'Elytra',\n authenticate,\n onAuthenticated,\n}: SignInScreenProps) {\n const t = useT()\n const [email, setEmail] = useState('')\n const [password, setPassword] = useState('')\n const [phase, setPhase] = useState<FormPhase>('idle')\n const [mode, setMode] = useState<AuthMode>('signin')\n const [error, setError] = useState<string | null>(null)\n\n async function handleSubmit(event: FormEvent<HTMLFormElement>) {\n event.preventDefault()\n setPhase('submitting')\n setError(null)\n const result = await authenticate(mode, email, password)\n if (result.ok) {\n await onAuthenticated()\n return\n }\n // A fresh backend has no account yet: nudge sign-in → create, and vice versa.\n if (result.reason === 'account-exists') setMode('signin')\n // EC-397: engine-classified failures (EC-362/EC-363 reasons) localize\n // through the message catalog; an unrecognized shape renders the adapter's\n // raw English message — honest, never a stale mapping.\n const errorKey = signInErrorMessageKey(result)\n setError(errorKey ? t(errorKey) : result.message)\n setPhase('idle')\n }\n\n const submitting = phase === 'submitting'\n const creating = mode === 'signup'\n\n return (\n <ScreenShell layout={layout}>\n <Stack gap=\"4\" className=\"w-full max-w-sm\">\n {notice}\n <Stack gap=\"1\" align=\"center\">\n {brandMark ?? <span className=\"size-8 rounded-lg bg-primary shadow-sm\" aria-hidden />}\n <Heading size=\"h2\">\n {creating ? t('auth.signIn.headingCreate') : t('auth.signIn.heading', { productName })}\n </Heading>\n <Text size=\"sm\" tone=\"muted\">\n {projectName\n ? t(creating ? 'auth.signIn.subtext.register' : 'auth.signIn.subtext.continue', {\n project: projectName,\n })\n : t('auth.signIn.subtext.generic')}\n </Text>\n </Stack>\n\n <Card>\n <CardContent className=\"pt-5\">\n <form onSubmit={handleSubmit} noValidate>\n <Stack gap=\"4\">\n <Field label={t('auth.signIn.emailLabel')} htmlFor=\"sign-in-email\" required>\n <Input\n id=\"sign-in-email\"\n type=\"email\"\n autoComplete=\"email\"\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n placeholder={t('auth.signIn.emailPlaceholder')}\n disabled={submitting}\n />\n </Field>\n <Field\n label={t('auth.signIn.passwordLabel')}\n htmlFor=\"sign-in-password\"\n required\n error={error}\n >\n <Input\n id=\"sign-in-password\"\n type=\"password\"\n autoComplete={creating ? 'new-password' : 'current-password'}\n value={password}\n onChange={(e) => setPassword(e.target.value)}\n disabled={submitting}\n />\n </Field>\n <Button type=\"submit\" disabled={submitting} className=\"w-full\">\n {submitting\n ? creating\n ? t('auth.signIn.submittingCreate')\n : t('auth.signIn.submitting')\n : creating\n ? t('auth.signIn.submitCreate')\n : t('auth.signIn.submit')}\n </Button>\n </Stack>\n </form>\n </CardContent>\n </Card>\n\n {showModeToggle ? (\n <Text size=\"sm\" tone=\"muted\" className=\"text-center\">\n {creating ? t('auth.signIn.toggle.alreadyHave') : t('auth.signIn.toggle.firstTime')}{' '}\n <button\n type=\"button\"\n className=\"font-medium text-primary underline-offset-2 hover:underline\"\n onClick={() => {\n setMode(creating ? 'signin' : 'signup')\n setError(null)\n }}\n >\n {creating ? t('auth.signIn.toggle.signIn') : t('auth.signIn.toggle.createAccount')}\n </button>\n </Text>\n ) : null}\n\n {devCredentials ? <DevCredentialsHint entries={devCredentials} /> : null}\n </Stack>\n </ScreenShell>\n )\n}\n\n/**\n * The screen's presentation shell (EC-437). `'page'` is the `/sign-in`\n * route's shipped full-viewport opaque panel, byte-identical; `'bare'`\n * renders the card column alone — the session-expired re-auth overlay owns\n * backdrop + centering there, so the studio stays visible beneath the prompt.\n */\nfunction ScreenShell({ layout, children }: { layout: 'page' | 'bare'; children: ReactNode }) {\n if (layout === 'bare') return <>{children}</>\n return <div className=\"grid min-h-dvh place-items-center bg-background p-6\">{children}</div>\n}\n\n/** Seeded dev accounts for the explicit playground mode — fixtures, not secrets. */\nfunction DevCredentialsHint({ entries }: { entries: readonly DevCredential[] }) {\n const t = useT()\n return (\n <Card className=\"border-dashed\">\n <CardContent className=\"pt-4\">\n <Stack gap=\"1\">\n <Text size=\"xs\" tone=\"muted\" className=\"font-medium\">\n {t('auth.signIn.devAccounts.title')}\n </Text>\n {entries.map((entry) => (\n <Text key={entry.user.id} size=\"xs\" tone=\"muted\" className=\"font-mono\">\n {entry.user.email} / {entry.password}\n </Text>\n ))}\n </Stack>\n </CardContent>\n </Card>\n )\n}\n"]}
|
|
1
|
+
{"version":3,"file":"sign-in-view.js","sourceRoot":"","sources":["../../src/components/sign-in-view.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useState } from 'react'\nimport type { FormEvent, ReactNode } from 'react'\nimport { useNavigate, useSearch } from '@tanstack/react-router'\nimport { Button, Card, CardContent, Field, Heading, Input, Stack, Text } from '@elytracms/studio/ui'\nimport { useAuth } from './auth-context'\nimport { LOCAL_DEV_CREDENTIALS, authMode, postSignInTarget } from '../lib/auth'\nimport type { SignInResult } from '../lib/auth'\nimport { signInErrorMessageKey } from '../lib/auth-errors'\nimport { useT } from '../lib/i18n'\nimport { getActiveProjectSlug, getWorkspaceState } from '../lib/workspace'\n\n/**\n * Engine-shipped `/sign-in` screen (EC-150; consolidated from the copy-pasted\n * consumer route by EC-375, EC-369 finding #8). The consumer route file keeps\n * only the route object — `createFileRoute('/sign-in')({ validateSearch,\n * component: SignInView })` — because TanStack Router requires the route object\n * to live in the consumer's route tree; the ~140-line body lives here so a\n * copy never drifts.\n *\n * Deterministic states: idle → submitting → (signed-in redirect | inline\n * structured failure). Invalid credentials are a rendered state, never a silent\n * failure or crash. In playground (in-memory) mode the seeded dev credentials\n * are listed so the whole flow works offline.\n *\n * Split into a `SignInView` container (owns the router + auth seams) and a\n * provider-free `SignInScreen` (pure local form state + injected callbacks) so\n * the screen static-renders in a node smoke test without the full provider tree.\n *\n * All copy resolves through the studio message catalog (`auth.signIn.*`,\n * EC-397), including the engine-classified auth failures — see\n * `signInErrorMessageKey`; an unrecognized failure renders `result.message`\n * raw (honest fallback).\n */\n\ntype FormPhase = 'idle' | 'submitting'\ntype AuthMode = 'signin' | 'signup'\n\n/** A seeded dev-credential entry surfaced in playground mode (fixtures, not secrets). */\ntype DevCredential = (typeof LOCAL_DEV_CREDENTIALS)[number]\n\nexport interface SignInViewProps {\n /**\n * Brand mark rendered above the heading. Defaults to the neutral studio\n * placeholder square — a future logo just passes a node here (e.g.\n * `component: () => <SignInView brandMark={<Logo />} />`), no engine change.\n */\n brandMark?: ReactNode\n /** Product name in the \"Sign in to …\" heading. Default: `'Elytra'`. */\n productName?: string\n}\n\n/**\n * The project whose backend this sign-in authenticates against (EC-179).\n * Exported for the session-expired re-auth overlay (EC-403), which mounts\n * {@link SignInScreen} with the same subtext.\n */\nexport function activeProjectName(): string | null {\n const state = getWorkspaceState()\n if (state.kind !== 'config') return null\n const slug = getActiveProjectSlug()\n const project = (slug && state.projects.find((p) => p.slug === slug)) || state.projects[0]\n return project?.name ?? null\n}\n\n/**\n * The `/sign-in` route component. Container: reads the router (post-sign-in\n * redirect target from the route search) + auth seams and hands the pure\n * presentational screen its data plus an `authenticate` callback.\n */\nexport function SignInView({ brandMark, productName }: SignInViewProps) {\n const auth = useAuth()\n const navigate = useNavigate()\n // Self-source the redirect (loose search — the consumer route's `validateSearch`\n // guarantees `{ redirect?: string }`), so the route file needs no wrapper.\n const search = useSearch({ strict: false }) as { redirect?: string }\n const redirect = search.redirect\n\n return (\n <SignInScreen\n projectName={activeProjectName()}\n showModeToggle={authMode() === 'convex'}\n devCredentials={authMode() === 'playground' ? LOCAL_DEV_CREDENTIALS : null}\n brandMark={brandMark}\n productName={productName}\n authenticate={(mode, email, password) =>\n mode === 'signup' ? auth.signUp(email, password) : auth.signIn(email, password)\n }\n onAuthenticated={() => navigate({ to: postSignInTarget(redirect) })}\n />\n )\n}\n\ninterface SignInScreenProps {\n projectName: string | null\n /** Whether to offer the sign-in ↔ create-account toggle (convex mode only). */\n showModeToggle: boolean\n /** Seeded dev accounts to list (playground mode), or null to hide the hint. */\n devCredentials: readonly DevCredential[] | null\n /**\n * Contextual notice rendered above the heading — the session-expired\n * re-auth overlay (EC-403) explains WHY the screen appeared here. The\n * normal `/sign-in` route passes none.\n */\n notice?: ReactNode\n /**\n * Presentation wrapper (EC-437). `'page'` (default) renders the\n * full-viewport opaque panel — the `/sign-in` route's shipped layout,\n * byte-identical. `'bare'` renders only the centered card column so a host\n * surface composes its own backdrop: the session-expired re-auth overlay\n * mounts it over a translucent blurred backdrop, keeping the intact studio\n * visible beneath the prompt.\n */\n layout?: 'page' | 'bare'\n brandMark?: ReactNode\n productName?: string\n /** Attempt auth for the chosen mode. The container maps this to signIn/signUp. */\n authenticate: (mode: AuthMode, email: string, password: string) => Promise<SignInResult>\n /** Called once, after a successful authentication (the container navigates). */\n onAuthenticated: () => void | Promise<void>\n}\n\n/**\n * Provider-free presentational sign-in screen. Owns only local form state and\n * calls injected callbacks — no context hooks — so it static-renders in tests.\n */\nexport function SignInScreen({\n projectName,\n showModeToggle,\n devCredentials,\n notice,\n layout = 'page',\n brandMark,\n productName = 'Elytra',\n authenticate,\n onAuthenticated,\n}: SignInScreenProps) {\n const t = useT()\n const [email, setEmail] = useState('')\n const [password, setPassword] = useState('')\n const [phase, setPhase] = useState<FormPhase>('idle')\n const [mode, setMode] = useState<AuthMode>('signin')\n const [error, setError] = useState<string | null>(null)\n\n async function handleSubmit(event: FormEvent<HTMLFormElement>) {\n event.preventDefault()\n setPhase('submitting')\n setError(null)\n const result = await authenticate(mode, email, password)\n if (result.ok) {\n await onAuthenticated()\n return\n }\n // A fresh backend has no account yet: nudge sign-in → create, and vice versa.\n if (result.reason === 'account-exists') setMode('signin')\n // EC-397: engine-classified failures (EC-362/EC-363 reasons) localize\n // through the message catalog; an unrecognized shape renders the adapter's\n // raw English message — honest, never a stale mapping.\n const errorKey = signInErrorMessageKey(result)\n setError(errorKey ? t(errorKey) : result.message)\n setPhase('idle')\n }\n\n const submitting = phase === 'submitting'\n const creating = mode === 'signup'\n\n return (\n <ScreenShell layout={layout}>\n <Stack gap=\"4\" className=\"w-full max-w-sm\">\n {notice}\n <Stack gap=\"1\" align=\"center\">\n {brandMark ?? <span className=\"size-8 rounded-lg bg-primary shadow-sm\" aria-hidden />}\n <Heading size=\"h2\">\n {creating ? t('auth.signIn.headingCreate') : t('auth.signIn.heading', { productName })}\n </Heading>\n <Text size=\"sm\" tone=\"muted\">\n {projectName\n ? t(creating ? 'auth.signIn.subtext.register' : 'auth.signIn.subtext.continue', {\n project: projectName,\n })\n : t('auth.signIn.subtext.generic')}\n </Text>\n </Stack>\n\n <Card>\n <CardContent className=\"pt-5\">\n <form onSubmit={handleSubmit} noValidate>\n <Stack gap=\"4\">\n <Field label={t('auth.signIn.emailLabel')} htmlFor=\"sign-in-email\" required>\n <Input\n id=\"sign-in-email\"\n type=\"email\"\n autoComplete=\"email\"\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n placeholder={t('auth.signIn.emailPlaceholder')}\n disabled={submitting}\n />\n </Field>\n <Field\n label={t('auth.signIn.passwordLabel')}\n htmlFor=\"sign-in-password\"\n required\n error={error}\n >\n <Input\n id=\"sign-in-password\"\n type=\"password\"\n autoComplete={creating ? 'new-password' : 'current-password'}\n value={password}\n onChange={(e) => setPassword(e.target.value)}\n disabled={submitting}\n />\n </Field>\n <Button type=\"submit\" disabled={submitting} className=\"w-full\">\n {submitting\n ? creating\n ? t('auth.signIn.submittingCreate')\n : t('auth.signIn.submitting')\n : creating\n ? t('auth.signIn.submitCreate')\n : t('auth.signIn.submit')}\n </Button>\n </Stack>\n </form>\n </CardContent>\n </Card>\n\n {showModeToggle ? (\n <Text size=\"sm\" tone=\"muted\" className=\"text-center\">\n {creating ? t('auth.signIn.toggle.alreadyHave') : t('auth.signIn.toggle.firstTime')}{' '}\n <button\n type=\"button\"\n className=\"font-medium text-primary underline-offset-2 hover:underline\"\n onClick={() => {\n setMode(creating ? 'signin' : 'signup')\n setError(null)\n }}\n >\n {creating ? t('auth.signIn.toggle.signIn') : t('auth.signIn.toggle.createAccount')}\n </button>\n </Text>\n ) : null}\n\n {devCredentials ? <DevCredentialsHint entries={devCredentials} /> : null}\n </Stack>\n </ScreenShell>\n )\n}\n\n/**\n * The screen's presentation shell (EC-437). `'page'` is the `/sign-in`\n * route's shipped full-viewport opaque panel, byte-identical; `'bare'`\n * renders the card column alone — the session-expired re-auth overlay owns\n * backdrop + centering there, so the studio stays visible beneath the prompt.\n */\nfunction ScreenShell({ layout, children }: { layout: 'page' | 'bare'; children: ReactNode }) {\n if (layout === 'bare') return <>{children}</>\n return <div className=\"grid min-h-dvh place-items-center bg-background p-6\">{children}</div>\n}\n\n/** Seeded dev accounts for the explicit playground mode — fixtures, not secrets. */\nfunction DevCredentialsHint({ entries }: { entries: readonly DevCredential[] }) {\n const t = useT()\n return (\n <Card className=\"border-dashed\">\n <CardContent className=\"pt-4\">\n <Stack gap=\"1\">\n <Text size=\"xs\" tone=\"muted\" className=\"font-medium\">\n {t('auth.signIn.devAccounts.title')}\n </Text>\n {entries.map((entry) => (\n <Text key={entry.user.id} size=\"xs\" tone=\"muted\" className=\"font-mono\">\n {entry.user.email} / {entry.password}\n </Text>\n ))}\n </Stack>\n </CardContent>\n </Card>\n )\n}\n"]}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useMemo, useState } from 'react';
|
|
3
3
|
import { activateProject, configProject, emptyContext } from '@elytracms/core/studio-core';
|
|
4
|
-
import { getWorkspaceState } from '
|
|
5
|
-
import { StudioQueryProvider } from '
|
|
6
|
-
import { createRequiredContext } from '
|
|
4
|
+
import { getWorkspaceState } from '../lib/workspace/index.js';
|
|
5
|
+
import { StudioQueryProvider } from '../lib/query/index.js';
|
|
6
|
+
import { createRequiredContext } from '../lib/required-context.js';
|
|
7
7
|
const { Context: StudioContext, useRequired: useStudio } = createRequiredContext('Studio');
|
|
8
8
|
/** Resolve the config-declared projects into studio `Project`s (slug-keyed). */
|
|
9
9
|
function configProjects() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"studio-context.js","sourceRoot":"","sources":["../../src/components/studio-context.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAEtD,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAA;AAE1F,OAAO,EAAE,iBAAiB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"studio-context.js","sourceRoot":"","sources":["../../src/components/studio-context.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAEtD,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAA;AAE1F,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AACpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAA;AAClD,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAA;AAoB/D,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,SAAS,EAAE,GAAG,qBAAqB,CAAc,QAAQ,CAAC,CAAA;AAEvG,gFAAgF;AAChF,SAAS,cAAc;IACrB,MAAM,KAAK,GAAG,iBAAiB,EAAE,CAAA;IACjC,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,EAAE,CAAA;IACtC,OAAO,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,aAAa,CAAC,CAAA;AAC1C,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,EAAE,QAAQ,EAA2B;IAClE,6EAA6E;IAC7E,2DAA2D;IAC3D,MAAM,CAAC,QAAQ,CAAC,GAAG,QAAQ,CAAqB,GAAG,EAAE,CAAC,cAAc,EAAE,CAAC,CAAA;IACvE,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAuB,YAAY,CAAC,CAAA;IAE1E,MAAM,UAAU,GAAG,WAAW,CAAC,CAAC,EAAa,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAA;IAEhG,MAAM,QAAQ,GAAG,WAAW,CAAC,CAAC,EAAa,EAAE,EAAE;QAC7C,UAAU,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,eAAe,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,eAAe,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,CAAA;IACxF,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,MAAM,KAAK,GAAG,OAAO,CACnB,GAAG,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC,EACnD,CAAC,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,CAAC,CAC1C,CAAA;IAED,0EAA0E;IAC1E,yEAAyE;IACzE,wDAAwD;IACxD,OAAO,CACL,KAAC,aAAa,CAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,YAClC,KAAC,mBAAmB,cAAE,QAAQ,GAAuB,GAC9B,CAC1B,CAAA;AACH,CAAC;AAED,OAAO,EAAE,SAAS,EAAE,CAAA","sourcesContent":["import { useCallback, useMemo, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport { activateProject, configProject, emptyContext } from '@elytracms/core/studio-core'\nimport type { ActiveProjectContext, Project, ProjectId } from '@elytracms/core/studio-core'\nimport { getWorkspaceState } from '../lib/workspace'\nimport { StudioQueryProvider } from '../lib/query'\nimport { createRequiredContext } from '../lib/required-context'\n\n/**\n * Project read store (vision AD-8). Project *identity* is owned by\n * `elytra.config.ts` — the studio reads the project list from the resolved\n * workspace config, never from the backend. The active project id is route-\n * driven; this store holds the config-derived list + the recents context.\n * Lifecycle (create/rename/archive/delete) lives in code/CLI, not the UI.\n */\n\nexport interface StudioStore {\n /** Projects declared in `elytra.config.ts`, in config order. */\n projects: readonly Project[]\n /** Active-project context (recents); the active id itself is route-driven. */\n context: ActiveProjectContext\n getProject(id: ProjectId): Project | undefined\n /** Record a project as active + most-recent (idempotent). */\n activate(id: ProjectId): void\n}\n\nconst { Context: StudioContext, useRequired: useStudio } = createRequiredContext<StudioStore>('Studio')\n\n/** Resolve the config-declared projects into studio `Project`s (slug-keyed). */\nfunction configProjects(): readonly Project[] {\n const state = getWorkspaceState()\n if (state.kind !== 'config') return []\n return state.projects.map(configProject)\n}\n\nexport function StudioProvider({ children }: { children: ReactNode }) {\n // The workspace config is static for the app's lifetime (bundled at build or\n // injected via env), so the project list is computed once.\n const [projects] = useState<readonly Project[]>(() => configProjects())\n const [context, setContext] = useState<ActiveProjectContext>(emptyContext)\n\n const getProject = useCallback((id: ProjectId) => projects.find((p) => p.id === id), [projects])\n\n const activate = useCallback((id: ProjectId) => {\n setContext((prev) => (prev.activeProjectId === id ? prev : activateProject(prev, id)))\n }, [])\n\n const store = useMemo<StudioStore>(\n () => ({ projects, context, getProject, activate }),\n [projects, context, getProject, activate],\n )\n\n // EC-308/AD-17: the single client query cache mounts here (package-owned,\n // above every per-project provider, surviving project switches) so every\n // studio server read flows through `useOperationQuery`.\n return (\n <StudioContext.Provider value={store}>\n <StudioQueryProvider>{children}</StudioQueryProvider>\n </StudioContext.Provider>\n )\n}\n\nexport { useStudio }\n"]}
|
|
@@ -4,13 +4,18 @@ import { launcherVisitState, Link, NavigateOnce, STUDIO_PROJECT_ROUTE, useParams
|
|
|
4
4
|
import { matchStudioRoute } from '@elytracms/studio/router/match';
|
|
5
5
|
import { studioSearch } from '@elytracms/studio/router/search';
|
|
6
6
|
import { Button, Heading, Stack, Text } from '@elytracms/studio/ui';
|
|
7
|
-
import { translate } from '
|
|
7
|
+
import { translate } from '../lib/i18n/index.js';
|
|
8
8
|
import { useStudio } from './studio-context.js';
|
|
9
9
|
import { StudioShell } from './studio-shell.js';
|
|
10
10
|
import { AssetsProvider } from './assets/assets-store.js';
|
|
11
11
|
import { ProjectSessionProvider } from './project-session.js';
|
|
12
12
|
import { flatPath } from './app-views/module.js';
|
|
13
13
|
import { landingViewId, useStudioNav } from './app-views/sidebar.js';
|
|
14
|
+
import { NavIssuesProvider } from './nav-issues-registry.js';
|
|
15
|
+
import { useAuth, useProjectRole } from './auth-context.js';
|
|
16
|
+
import { getStudioOperations } from '../lib/operations/index.js';
|
|
17
|
+
import { useOperationCache, useOperationQuery } from '../lib/query/index.js';
|
|
18
|
+
import { useStudioLocale } from '../lib/locale/index.js';
|
|
14
19
|
/**
|
|
15
20
|
* EC-223 Stage 6 (catch-all routing): the studio's per-project route component.
|
|
16
21
|
*
|
|
@@ -49,7 +54,7 @@ export function StudioProjectScreen() {
|
|
|
49
54
|
return _jsx(ProjectNotFound, {});
|
|
50
55
|
const match = matchStudioRoute(params._splat ?? '');
|
|
51
56
|
const search = studioSearch(rawSearch);
|
|
52
|
-
return (_jsx(StudioShell, { project: project, children: _jsx(ProjectSessionProvider, { projectId: project.id, children: _jsx(ProjectProviders, { project: project, children: _jsx(StudioSectionResolver, { project: project, match: match, search: search }) }) }, project.id) }));
|
|
57
|
+
return (_jsx(NavIssuesProvider, { children: _jsx(StudioShell, { project: project, children: _jsx(ProjectSessionProvider, { projectId: project.id, children: _jsx(ProjectProviders, { project: project, children: _jsx(StudioSectionResolver, { project: project, match: match, search: search }) }) }, project.id) }) }));
|
|
53
58
|
}
|
|
54
59
|
/**
|
|
55
60
|
* The per-project provider stack, mounted once the session source is known.
|
|
@@ -76,6 +81,7 @@ function ProjectProviders({ project, children }) {
|
|
|
76
81
|
function StudioSectionResolver({ project, match, search, }) {
|
|
77
82
|
const projectId = project.id;
|
|
78
83
|
const nav = useStudioNav(project);
|
|
84
|
+
const viewServices = useViewServices(projectId);
|
|
79
85
|
if (match.section === 'index') {
|
|
80
86
|
// Bare `/$projectId` → the project's first navigable rail view.
|
|
81
87
|
// EC-441: `NavigateOnce` — this resolver re-renders while the landing
|
|
@@ -90,7 +96,28 @@ function StudioSectionResolver({ project, match, search, }) {
|
|
|
90
96
|
const deepLink = (view.parsePath ?? flatPath)(match.segments);
|
|
91
97
|
if (deepLink === null)
|
|
92
98
|
return _jsx(StudioRouteNotFound, {});
|
|
93
|
-
return _jsx(_Fragment, { children: view.render({ project, projectId, deepLink, search }) });
|
|
99
|
+
return _jsx(_Fragment, { children: view.render({ project, projectId, deepLink, search, ...viewServices }) });
|
|
100
|
+
}
|
|
101
|
+
/** The query hooks every view receives (stable: the hooks themselves). */
|
|
102
|
+
const VIEW_QUERY_HOOKS = { useOperationQuery, useOperationCache };
|
|
103
|
+
/** EC-616: the operations client, as a view reaches it (the one chokepoint). */
|
|
104
|
+
const viewOperations = async () => (await getStudioOperations()).operations;
|
|
105
|
+
/**
|
|
106
|
+
* EC-616 — what `AppViewContext` grows by: the operations client, the query hooks, the
|
|
107
|
+
* ambient content locale, the signed-in user and their role. Read here, in the resolver
|
|
108
|
+
* (a component), so a view'''s plain `render` function receives them as values.
|
|
109
|
+
*/
|
|
110
|
+
function useViewServices(projectId) {
|
|
111
|
+
const { session } = useAuth();
|
|
112
|
+
const role = useProjectRole(projectId);
|
|
113
|
+
const { locale } = useStudioLocale();
|
|
114
|
+
return {
|
|
115
|
+
operations: viewOperations,
|
|
116
|
+
query: VIEW_QUERY_HOOKS,
|
|
117
|
+
locale,
|
|
118
|
+
user: session?.user ?? null,
|
|
119
|
+
role,
|
|
120
|
+
};
|
|
94
121
|
}
|
|
95
122
|
function ProjectNotFound() {
|
|
96
123
|
return (_jsx("div", { className: "grid min-h-dvh place-items-center p-8", children: _jsxs(Stack, { gap: "3", align: "center", className: "max-w-sm text-center", children: [_jsx(Heading, { size: "display", children: translate('shell.projectNotFound.title') }), _jsx(Text, { tone: "muted", children: translate('shell.projectNotFound.body') }), _jsx(Button, { asChild: true, children: _jsx(Link, { to: "/", state: launcherVisitState, children: translate('shell.backToProjects') }) })] }) }));
|