@elytracms/studio 0.0.42 → 0.0.44
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/dist/components/app-views/studio-config.d.ts +12 -0
- package/dist/components/app-views/studio-config.js.map +1 -1
- package/dist/components/assets/asset-detail.js +1 -1
- package/dist/components/assets/asset-detail.js.map +1 -1
- package/dist/components/assets/assets-collection-view-source.d.ts +13 -1
- package/dist/components/assets/assets-collection-view-source.js +42 -51
- package/dist/components/assets/assets-collection-view-source.js.map +1 -1
- package/dist/components/assets/assets-module.js +19 -6
- package/dist/components/assets/assets-module.js.map +1 -1
- package/dist/components/assets/assets-store.d.ts +2 -1
- package/dist/components/assets/assets-store.js +4 -8
- package/dist/components/assets/assets-store.js.map +1 -1
- package/dist/components/assets/assets-view-preference.d.ts +0 -8
- package/dist/components/assets/assets-view-preference.js +12 -13
- package/dist/components/assets/assets-view-preference.js.map +1 -1
- package/dist/components/auth-context.d.ts +2 -1
- package/dist/components/auth-context.js +6 -8
- package/dist/components/auth-context.js.map +1 -1
- package/dist/components/builder/builder-store.d.ts +2 -2
- package/dist/components/builder/builder-store.js +4 -8
- package/dist/components/builder/builder-store.js.map +1 -1
- package/dist/components/cms/bulk-actions.js +2 -1
- package/dist/components/cms/bulk-actions.js.map +1 -1
- package/dist/components/cms/cell-edit.js +2 -1
- package/dist/components/cms/cell-edit.js.map +1 -1
- package/dist/components/cms/cms-composition-validators.d.ts +25 -0
- package/dist/components/cms/cms-composition-validators.js +75 -0
- package/dist/components/cms/cms-composition-validators.js.map +1 -0
- package/dist/components/cms/cms-labels.d.ts +15 -0
- package/dist/components/cms/cms-labels.js +27 -0
- package/dist/components/cms/cms-labels.js.map +1 -0
- package/dist/components/cms/cms-store.d.ts +23 -10
- package/dist/components/cms/cms-store.js +87 -165
- package/dist/components/cms/cms-store.js.map +1 -1
- package/dist/components/cms/collection-table.js +1 -1
- package/dist/components/cms/collection-table.js.map +1 -1
- package/dist/components/cms/detail-editor.js +111 -348
- package/dist/components/cms/detail-editor.js.map +1 -1
- package/dist/components/cms/discard-draft.d.ts +18 -0
- package/dist/components/cms/discard-draft.js +18 -0
- package/dist/components/cms/discard-draft.js.map +1 -1
- package/dist/components/cms/document-editor-context.d.ts +2 -1
- package/dist/components/cms/document-editor-context.js +4 -9
- package/dist/components/cms/document-editor-context.js.map +1 -1
- package/dist/components/cms/document-ref-utils.d.ts +33 -0
- package/dist/components/cms/document-ref-utils.js +47 -0
- package/dist/components/cms/document-ref-utils.js.map +1 -0
- package/dist/components/cms/document-staging.d.ts +8 -3
- package/dist/components/cms/document-staging.js +81 -21
- package/dist/components/cms/document-staging.js.map +1 -1
- package/dist/components/cms/field-control.js +1 -1
- package/dist/components/cms/field-control.js.map +1 -1
- package/dist/components/cms/issues-pane.js +1 -1
- package/dist/components/cms/issues-pane.js.map +1 -1
- package/dist/components/cms/rich-text/block-commands.d.ts +36 -0
- package/dist/components/cms/rich-text/block-commands.js +47 -0
- package/dist/components/cms/rich-text/block-commands.js.map +1 -0
- package/dist/components/cms/rich-text/slash-command.js +25 -8
- package/dist/components/cms/rich-text/slash-command.js.map +1 -1
- package/dist/components/cms/rich-text/toolbar.js +3 -2
- package/dist/components/cms/rich-text/toolbar.js.map +1 -1
- package/dist/components/cms/use-compare-selection.d.ts +27 -0
- package/dist/components/cms/use-compare-selection.js +29 -0
- package/dist/components/cms/use-compare-selection.js.map +1 -0
- package/dist/components/cms/use-discard-controller.d.ts +44 -0
- package/dist/components/cms/use-discard-controller.js +51 -0
- package/dist/components/cms/use-discard-controller.js.map +1 -0
- package/dist/components/cms/use-embed-inspector-host.d.ts +12 -0
- package/dist/components/cms/use-embed-inspector-host.js +17 -0
- package/dist/components/cms/use-embed-inspector-host.js.map +1 -0
- package/dist/components/cms/use-field-focus.d.ts +38 -0
- package/dist/components/cms/use-field-focus.js +70 -0
- package/dist/components/cms/use-field-focus.js.map +1 -0
- package/dist/components/cms/use-live-url.d.ts +41 -0
- package/dist/components/cms/use-live-url.js +55 -0
- package/dist/components/cms/use-live-url.js.map +1 -0
- package/dist/components/cms/use-publish-controller.d.ts +49 -0
- package/dist/components/cms/use-publish-controller.js +102 -0
- package/dist/components/cms/use-publish-controller.js.map +1 -0
- package/dist/components/cms/use-published-perspective.d.ts +67 -0
- package/dist/components/cms/use-published-perspective.js +67 -0
- package/dist/components/cms/use-published-perspective.js.map +1 -0
- package/dist/components/cms/use-restore-version.d.ts +57 -0
- package/dist/components/cms/use-restore-version.js +66 -0
- package/dist/components/cms/use-restore-version.js.map +1 -0
- package/dist/components/cms/use-save-mode.d.ts +35 -0
- package/dist/components/cms/use-save-mode.js +54 -0
- package/dist/components/cms/use-save-mode.js.map +1 -0
- package/dist/components/cms/version-compare.js +14 -17
- package/dist/components/cms/version-compare.js.map +1 -1
- package/dist/components/cms/versions-model.d.ts +31 -0
- package/dist/components/cms/versions-model.js +30 -0
- package/dist/components/cms/versions-model.js.map +1 -1
- package/dist/components/cms/versions-pane.js +21 -19
- package/dist/components/cms/versions-pane.js.map +1 -1
- package/dist/components/cms/view-bar.js +9 -6
- package/dist/components/cms/view-bar.js.map +1 -1
- package/dist/components/collab/collab-context.d.ts +2 -1
- package/dist/components/collab/collab-context.js +4 -8
- package/dist/components/collab/collab-context.js.map +1 -1
- package/dist/components/collection-view/collection-view-context.d.ts +9 -1
- package/dist/components/collection-view/collection-view-context.js +4 -9
- package/dist/components/collection-view/collection-view-context.js.map +1 -1
- package/dist/components/components/component-detail.js +2 -5
- package/dist/components/components/component-detail.js.map +1 -1
- package/dist/components/components/component-preview.js +8 -4
- package/dist/components/components/component-preview.js.map +1 -1
- package/dist/components/components/registry-browser.js +24 -5
- package/dist/components/components/registry-browser.js.map +1 -1
- package/dist/components/components/transfer-dialog.d.ts +21 -6
- package/dist/components/components/transfer-dialog.js +120 -31
- package/dist/components/components/transfer-dialog.js.map +1 -1
- package/dist/components/components/transfer.d.ts +74 -8
- package/dist/components/components/transfer.js +87 -2
- package/dist/components/components/transfer.js.map +1 -1
- package/dist/components/delivery-health-banner.d.ts +12 -6
- package/dist/components/delivery-health-banner.js +27 -29
- package/dist/components/delivery-health-banner.js.map +1 -1
- package/dist/components/dev-login-badge.d.ts +0 -2
- package/dist/components/dev-login-badge.js +1 -3
- package/dist/components/dev-login-badge.js.map +1 -1
- package/dist/components/playground-badge.d.ts +0 -2
- package/dist/components/playground-badge.js +0 -2
- package/dist/components/playground-badge.js.map +1 -1
- package/dist/components/project-list.js +2 -6
- package/dist/components/project-list.js.map +1 -1
- package/dist/components/project-session.d.ts +6 -2
- package/dist/components/project-session.js +32 -83
- package/dist/components/project-session.js.map +1 -1
- package/dist/components/project-switcher.js +2 -6
- package/dist/components/project-switcher.js.map +1 -1
- package/dist/components/rail-store-status.d.ts +7 -5
- package/dist/components/rail-store-status.js +12 -9
- package/dist/components/rail-store-status.js.map +1 -1
- package/dist/components/rail-view-source.d.ts +111 -0
- package/dist/components/rail-view-source.js +139 -0
- package/dist/components/rail-view-source.js.map +1 -0
- package/dist/components/redirects/redirects-collection-view-source.d.ts +13 -1
- package/dist/components/redirects/redirects-collection-view-source.js +42 -70
- package/dist/components/redirects/redirects-collection-view-source.js.map +1 -1
- package/dist/components/redirects/redirects-store.d.ts +2 -1
- package/dist/components/redirects/redirects-store.js +4 -8
- package/dist/components/redirects/redirects-store.js.map +1 -1
- package/dist/components/section-state.d.ts +12 -3
- package/dist/components/section-state.js +8 -4
- package/dist/components/section-state.js.map +1 -1
- package/dist/components/studio-context.d.ts +2 -1
- package/dist/components/studio-context.js +4 -8
- package/dist/components/studio-context.js.map +1 -1
- package/dist/components/studio-shell.js +1 -1
- package/dist/components/studio-shell.js.map +1 -1
- package/dist/components/theme-context.d.ts +2 -1
- package/dist/components/theme-context.js +4 -8
- package/dist/components/theme-context.js.map +1 -1
- package/dist/components/use-hydration-phase.d.ts +65 -0
- package/dist/components/use-hydration-phase.js +64 -0
- package/dist/components/use-hydration-phase.js.map +1 -0
- package/dist/components/users/users-collection-view-source.d.ts +13 -1
- package/dist/components/users/users-collection-view-source.js +60 -79
- package/dist/components/users/users-collection-view-source.js.map +1 -1
- package/dist/components/users/users-store.d.ts +2 -1
- package/dist/components/users/users-store.js +4 -8
- package/dist/components/users/users-store.js.map +1 -1
- package/dist/components/workspace-projects.d.ts +14 -0
- package/dist/components/workspace-projects.js +21 -0
- package/dist/components/workspace-projects.js.map +1 -1
- package/dist/lib/ai/anthropic-provider.d.ts +29 -0
- package/dist/lib/ai/anthropic-provider.js +59 -0
- package/dist/lib/ai/anthropic-provider.js.map +1 -0
- package/dist/lib/ai/apply-content.d.ts +9 -0
- package/dist/lib/ai/apply-content.js +170 -0
- package/dist/lib/ai/apply-content.js.map +1 -0
- package/dist/lib/ai/config.d.ts +58 -0
- package/dist/lib/ai/config.js +134 -0
- package/dist/lib/ai/config.js.map +1 -0
- package/dist/lib/ai/fake-provider.d.ts +34 -0
- package/dist/lib/ai/fake-provider.js +31 -0
- package/dist/lib/ai/fake-provider.js.map +1 -0
- package/dist/lib/ai/index.d.ts +22 -0
- package/dist/lib/ai/index.js +23 -0
- package/dist/lib/ai/index.js.map +1 -0
- package/dist/lib/ai/knowledge.d.ts +39 -0
- package/dist/lib/ai/knowledge.js +60 -0
- package/dist/lib/ai/knowledge.js.map +1 -0
- package/dist/lib/ai/provenance.d.ts +20 -0
- package/dist/lib/ai/provenance.js +15 -0
- package/dist/lib/ai/provenance.js.map +1 -0
- package/dist/lib/ai/route.d.ts +24 -0
- package/dist/lib/ai/route.js +90 -0
- package/dist/lib/ai/route.js.map +1 -0
- package/dist/lib/ai/shape.d.ts +35 -0
- package/dist/lib/ai/shape.js +113 -0
- package/dist/lib/ai/shape.js.map +1 -0
- package/dist/lib/ai/types.d.ts +227 -0
- package/dist/lib/ai/types.js +2 -0
- package/dist/lib/ai/types.js.map +1 -0
- package/dist/lib/data/data-store.d.ts +2 -1
- package/dist/lib/data/data-store.js +4 -8
- package/dist/lib/data/data-store.js.map +1 -1
- package/dist/lib/delivery/instant-publish.d.ts +9 -13
- package/dist/lib/delivery/instant-publish.js +0 -7
- package/dist/lib/delivery/instant-publish.js.map +1 -1
- package/dist/lib/diff-view-preference.js +4 -4
- package/dist/lib/diff-view-preference.js.map +1 -1
- package/dist/lib/i18n/messages.en.js +5 -2
- package/dist/lib/i18n/messages.en.js.map +1 -1
- package/dist/lib/local-preference.d.ts +37 -0
- package/dist/lib/local-preference.js +36 -0
- package/dist/lib/local-preference.js.map +1 -1
- package/dist/lib/persistence/convex-adapter.d.ts +3 -1
- package/dist/lib/persistence/convex-adapter.js +20 -0
- package/dist/lib/persistence/convex-adapter.js.map +1 -1
- package/dist/lib/rail.js +7 -6
- package/dist/lib/rail.js.map +1 -1
- package/dist/lib/required-context.d.ts +24 -0
- package/dist/lib/required-context.js +28 -0
- package/dist/lib/required-context.js.map +1 -0
- package/dist/lib/theme.js +11 -4
- package/dist/lib/theme.js.map +1 -1
- package/dist/lib/workspace/index.d.ts +9 -0
- package/dist/lib/workspace/index.js +11 -0
- package/dist/lib/workspace/index.js.map +1 -1
- package/dist/ui/index.d.ts +1 -0
- package/dist/ui/index.js +1 -0
- package/dist/ui/index.js.map +1 -1
- package/dist/ui/segmented-control.d.ts +46 -0
- package/dist/ui/segmented-control.js +13 -0
- package/dist/ui/segmented-control.js.map +1 -0
- package/package.json +8 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-list.js","sourceRoot":"","sources":["../../src/components/project-list.tsx"],"names":[],"mappings":"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","sourcesContent":["import { Link, STUDIO_PROJECT_ROUTE, studioPath } from '@elytracms/studio/router'\nimport { Globe, LogIn, TerminalSquare } from 'lucide-react'\nimport type { Project, ResolvedWorkspaceProject, SectionState } from '@elytracms/core/studio-core'\nimport { Badge, Button, Card, Code, Heading, Row, Spacer, Stack, Text } from '@elytracms/studio/ui'\nimport { hasProjectSession } from '@elytracms/studio/lib/auth'\nimport { translate } from '@elytracms/studio/lib/i18n'\nimport { PlaygroundBadge } from './playground-badge'\nimport { SignInNeededBadge } from './sign-in-needed-badge'\nimport { SectionStateView } from './section-state'\nimport { useStudio } from './studio-context'\nimport { useSwitcherEntries } from './workspace-projects'\n\nexport interface ProjectLauncherProps {\n /** Force a deterministic list state for browser verification (?state=…). */\n forcedState?: SectionState | null\n}\n\n/**\n * Studio launcher / project list (brief §4.3, §8.1; reframed by EC-154). The\n * Studio's front door: lists the workspace's projects and routes into a\n * project's shell. With a workspace config the list is config-driven\n * (configured projects ∩ grants, plus explicit \"not provisioned\" states);\n * project *creation* is code-first — `elytra.config.ts` + `builder init\n * --project <slug>` — so there is no in-UI create flow. Without a config the\n * adapter-backed list (and the starter-kit gallery) behaves as before.\n */\nexport function ProjectLauncher({ forcedState }: ProjectLauncherProps) {\n const studio = useStudio()\n const { entries, workspace } = useSwitcherEntries()\n\n const hasConfig = workspace.kind === 'config'\n const visible = entries\n const recents = studio.context.recentProjectIds\n .map((id) => studio.getProject(id))\n .filter((p): p is Project => Boolean(p))\n .filter((p) => entries.some((e) => e.kind === 'project' && e.project.id === p.id))\n .slice(0, 4)\n\n const forced = forcedState && forcedState !== 'working' ? forcedState : null\n\n return (\n <div className=\"mx-auto w-full max-w-6xl px-6 py-8\">\n <Row gap=\"3\" className=\"mb-6\">\n <Stack gap=\"1\">\n <Row gap=\"2\">\n <Heading size=\"display\">{translate('shell.projects')}</Heading>\n {/* EC-153: explicit playground-mode marker (renders nothing on Convex). */}\n <PlaygroundBadge />\n {hasConfig ? (\n // EC-154: this workspace is defined in code.\n <Badge variant=\"outline\">\n <TerminalSquare className=\"size-3\" /> {workspace.source}\n </Badge>\n ) : null}\n </Row>\n <Text tone=\"muted\">\n {hasConfig\n ? 'Projects are defined in code. Add entries to elytra.config.ts and provision them with the CLI.'\n : 'Open and manage your Elytra projects.'}\n </Text>\n </Stack>\n <Spacer />\n </Row>\n\n {recents.length > 0 && !forced ? (\n <Stack gap=\"2\" className=\"mb-6\">\n <Text size=\"xs\" tone=\"muted\" mono className=\"uppercase\">\n {translate('shell.projectList.recent')}\n </Text>\n <Row gap=\"2\" wrap>\n {recents.map((p) => (\n <Link\n key={p.id}\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId: p.id, _splat: studioPath.index() }}\n onClick={() => studio.activate(p.id)}\n className=\"inline-flex items-center gap-2 rounded-md border border-border bg-card px-3 py-1.5 text-sm transition-colors hover:border-primary/40 hover:bg-muted\"\n >\n <span className=\"grid size-4 place-items-center rounded-[4px] bg-primary text-[0.6rem] font-bold text-primary-foreground\">\n {p.name.charAt(0).toUpperCase()}\n </span>\n {p.name}\n </Link>\n ))}\n </Row>\n </Stack>\n ) : null}\n\n {forced ? (\n <SectionStateView\n state={forced}\n title={forced === 'empty' ? translate('shell.projectList.empty.title') : undefined}\n />\n ) : visible.length === 0 ? (\n <SectionStateView\n state=\"empty\"\n title={translate('shell.projectList.empty.title')}\n description={\n hasConfig\n ? 'The workspace config declares no visible projects. Add an entry to elytra.config.ts, or ask an admin to grant you access.'\n : 'No active projects. Create one from the CLI: run `builder init` in your repo to scaffold elytra.config.ts, then `builder init --project <slug>`.'\n }\n />\n ) : (\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {visible.map((entry) =>\n entry.kind === 'project' ? (\n <ProjectCard key={entry.project.id} project={entry.project} />\n ) : (\n <UnprovisionedCard key={`config:${entry.config.slug}`} config={entry.config} />\n ),\n )}\n </div>\n )}\n </div>\n )\n}\n\nfunction ProjectCard({ project }: { project: Project }) {\n const studio = useStudio()\n // Per-backend sign-in state (EC-179): one project per backend, so a project on\n // a deployment the user hasn't authenticated into shows \"Sign in\", not \"Open\".\n const authed = hasProjectSession(project.slug)\n return (\n <Card className=\"flex flex-col gap-3 p-4 transition-colors hover:border-primary/30\">\n <Row gap=\"2\">\n <span className=\"grid size-8 shrink-0 place-items-center rounded-lg bg-primary text-sm font-bold text-primary-foreground\">\n {project.name.charAt(0).toUpperCase()}\n </span>\n <Stack gap=\"1\" className=\"min-w-0\">\n <Text weight=\"semibold\" className=\"truncate\">\n {project.name}\n </Text>\n <Code tone=\"muted\" className=\"w-fit max-w-full truncate\">\n {project.slug}\n </Code>\n </Stack>\n <Spacer />\n {authed ? null : <SignInNeededBadge />}\n </Row>\n\n <Row gap=\"2\" className=\"text-muted-foreground\">\n <Badge variant=\"outline\">\n <Globe className=\"size-3\" /> {project.settings.locales.length}\n </Badge>\n <Spacer />\n </Row>\n\n <Button variant={authed ? 'secondary' : 'default'} size=\"sm\" asChild className=\"mt-1\">\n <Link\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId: project.id, _splat: studioPath.index() }}\n onClick={() => studio.activate(project.id)}\n >\n {authed ? translate('shell.projectCard.open') : (\n <>\n <LogIn /> {translate('auth.signIn')}\n </>\n )}\n </Link>\n </Button>\n </Card>\n )\n}\n\n/**\n * A configured project that has no backend project yet (EC-154): an explicit,\n * deterministic state pointing at the CLI provisioning flow — never an error.\n */\nfunction UnprovisionedCard({ config }: { config: ResolvedWorkspaceProject }) {\n return (\n <Card className=\"flex flex-col gap-3 border-dashed p-4\">\n <Row gap=\"2\">\n <span className=\"grid size-8 shrink-0 place-items-center rounded-lg border border-border bg-muted text-sm font-bold text-muted-foreground\">\n {config.name.charAt(0).toUpperCase()}\n </span>\n <Stack gap=\"1\" className=\"min-w-0\">\n <Text weight=\"semibold\" className=\"truncate\">\n {config.name}\n </Text>\n <Code tone=\"muted\" className=\"w-fit max-w-full truncate\">\n {config.slug}\n </Code>\n </Stack>\n <Spacer />\n <Badge variant=\"outline\">{translate('shell.project.notProvisioned')}</Badge>\n </Row>\n\n <Text size=\"sm\" tone=\"muted\">\n Declared in the workspace config, but no backend project exists yet. Provision it from your\n repo:\n </Text>\n <Code className=\"block w-fit max-w-full truncate text-xs\">\n builder init --project {config.slug}\n </Code>\n </Card>\n )\n}\n"]}
|
|
1
|
+
{"version":3,"file":"project-list.js","sourceRoot":"","sources":["../../src/components/project-list.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,IAAI,EAAE,oBAAoB,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAA;AACjF,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAE3D,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAA;AACnG,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAA;AAC9D,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAA;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AACpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAC5C,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AAO5E;;;;;;;;GAQG;AACH,MAAM,UAAU,eAAe,CAAC,EAAE,WAAW,EAAwB;IACnE,MAAM,MAAM,GAAG,SAAS,EAAE,CAAA;IAC1B,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,kBAAkB,EAAE,CAAA;IAEnD,MAAM,SAAS,GAAG,SAAS,CAAC,IAAI,KAAK,QAAQ,CAAA;IAC7C,MAAM,OAAO,GAAG,OAAO,CAAA;IACvB,MAAM,OAAO,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAA;IAE1C,MAAM,MAAM,GAAG,WAAW,IAAI,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAA;IAE5E,OAAO,CACL,eAAK,SAAS,EAAC,oCAAoC,aACjD,MAAC,GAAG,IAAC,GAAG,EAAC,GAAG,EAAC,SAAS,EAAC,MAAM,aAC3B,MAAC,KAAK,IAAC,GAAG,EAAC,GAAG,aACZ,MAAC,GAAG,IAAC,GAAG,EAAC,GAAG,aACV,KAAC,OAAO,IAAC,IAAI,EAAC,SAAS,YAAE,SAAS,CAAC,gBAAgB,CAAC,GAAW,EAE/D,KAAC,eAAe,KAAG,EAClB,SAAS,CAAC,CAAC,CAAC,CAEX,MAAC,KAAK,IAAC,OAAO,EAAC,SAAS,aACtB,KAAC,cAAc,IAAC,SAAS,EAAC,QAAQ,GAAG,OAAE,SAAS,CAAC,MAAM,IACjD,CACT,CAAC,CAAC,CAAC,IAAI,IACJ,EACN,KAAC,IAAI,IAAC,IAAI,EAAC,OAAO,YACf,SAAS;oCACR,CAAC,CAAC,gGAAgG;oCAClG,CAAC,CAAC,uCAAuC,GACtC,IACD,EACR,KAAC,MAAM,KAAG,IACN,EAEL,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAC/B,MAAC,KAAK,IAAC,GAAG,EAAC,GAAG,EAAC,SAAS,EAAC,MAAM,aAC7B,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,OAAO,EAAC,IAAI,QAAC,SAAS,EAAC,WAAW,YACpD,SAAS,CAAC,0BAA0B,CAAC,GACjC,EACP,KAAC,GAAG,IAAC,GAAG,EAAC,GAAG,EAAC,IAAI,kBACd,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAClB,MAAC,IAAI,IAEH,EAAE,EAAE,oBAAoB,EACxB,MAAM,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,KAAK,EAAE,EAAE,EACvD,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,EACpC,SAAS,EAAC,qJAAqJ,aAE/J,eAAM,SAAS,EAAC,yGAAyG,YACtH,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAC1B,EACN,CAAC,CAAC,IAAI,KATF,CAAC,CAAC,EAAE,CAUJ,CACR,CAAC,GACE,IACA,CACT,CAAC,CAAC,CAAC,IAAI,EAEP,MAAM,CAAC,CAAC,CAAC,CACR,KAAC,gBAAgB,IACf,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,+BAA+B,CAAC,CAAC,CAAC,CAAC,SAAS,GAClF,CACH,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CACzB,KAAC,gBAAgB,IACf,KAAK,EAAC,OAAO,EACb,KAAK,EAAE,SAAS,CAAC,+BAA+B,CAAC,EACjD,WAAW,EACT,SAAS;oBACP,CAAC,CAAC,2HAA2H;oBAC7H,CAAC,CAAC,kJAAkJ,GAExJ,CACH,CAAC,CAAC,CAAC,CACF,cAAK,SAAS,EAAC,0CAA0C,YACtD,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CACrB,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,CACzB,KAAC,WAAW,IAAwB,OAAO,EAAE,KAAK,CAAC,OAAO,IAAxC,KAAK,CAAC,OAAO,CAAC,EAAE,CAA4B,CAC/D,CAAC,CAAC,CAAC,CACF,KAAC,iBAAiB,IAAqC,MAAM,EAAE,KAAK,CAAC,MAAM,IAAnD,UAAU,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,CAA0B,CAChF,CACF,GACG,CACP,IACG,CACP,CAAA;AACH,CAAC;AAED,SAAS,WAAW,CAAC,EAAE,OAAO,EAAwB;IACpD,MAAM,MAAM,GAAG,SAAS,EAAE,CAAA;IAC1B,+EAA+E;IAC/E,+EAA+E;IAC/E,MAAM,MAAM,GAAG,iBAAiB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;IAC9C,OAAO,CACL,MAAC,IAAI,IAAC,SAAS,EAAC,mEAAmE,aACjF,MAAC,GAAG,IAAC,GAAG,EAAC,GAAG,aACV,eAAM,SAAS,EAAC,yGAAyG,YACtH,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAChC,EACP,MAAC,KAAK,IAAC,GAAG,EAAC,GAAG,EAAC,SAAS,EAAC,SAAS,aAChC,KAAC,IAAI,IAAC,MAAM,EAAC,UAAU,EAAC,SAAS,EAAC,UAAU,YACzC,OAAO,CAAC,IAAI,GACR,EACP,KAAC,IAAI,IAAC,IAAI,EAAC,OAAO,EAAC,SAAS,EAAC,2BAA2B,YACrD,OAAO,CAAC,IAAI,GACR,IACD,EACR,KAAC,MAAM,KAAG,EACT,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAC,iBAAiB,KAAG,IAClC,EAEN,MAAC,GAAG,IAAC,GAAG,EAAC,GAAG,EAAC,SAAS,EAAC,uBAAuB,aAC5C,MAAC,KAAK,IAAC,OAAO,EAAC,SAAS,aACtB,KAAC,KAAK,IAAC,SAAS,EAAC,QAAQ,GAAG,OAAE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,IACvD,EACR,KAAC,MAAM,KAAG,IACN,EAEN,KAAC,MAAM,IAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,EAAE,IAAI,EAAC,IAAI,EAAC,OAAO,QAAC,SAAS,EAAC,MAAM,YACnF,KAAC,IAAI,IACH,EAAE,EAAE,oBAAoB,EACxB,MAAM,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,KAAK,EAAE,EAAE,EAC7D,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,YAEzC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,wBAAwB,CAAC,CAAC,CAAC,CAAC,CAC9C,8BACE,KAAC,KAAK,KAAG,OAAE,SAAS,CAAC,aAAa,CAAC,IAClC,CACJ,GACI,GACA,IACJ,CACR,CAAA;AACH,CAAC;AAED;;;GAGG;AACH,SAAS,iBAAiB,CAAC,EAAE,MAAM,EAAwC;IACzE,OAAO,CACL,MAAC,IAAI,IAAC,SAAS,EAAC,uCAAuC,aACrD,MAAC,GAAG,IAAC,GAAG,EAAC,GAAG,aACV,eAAM,SAAS,EAAC,0HAA0H,YACvI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAC/B,EACP,MAAC,KAAK,IAAC,GAAG,EAAC,GAAG,EAAC,SAAS,EAAC,SAAS,aAChC,KAAC,IAAI,IAAC,MAAM,EAAC,UAAU,EAAC,SAAS,EAAC,UAAU,YACzC,MAAM,CAAC,IAAI,GACP,EACP,KAAC,IAAI,IAAC,IAAI,EAAC,OAAO,EAAC,SAAS,EAAC,2BAA2B,YACrD,MAAM,CAAC,IAAI,GACP,IACD,EACR,KAAC,MAAM,KAAG,EACV,KAAC,KAAK,IAAC,OAAO,EAAC,SAAS,YAAE,SAAS,CAAC,8BAA8B,CAAC,GAAS,IACxE,EAEN,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,OAAO,kHAGrB,EACP,MAAC,IAAI,IAAC,SAAS,EAAC,yCAAyC,wCAC/B,MAAM,CAAC,IAAI,IAC9B,IACF,CACR,CAAA;AACH,CAAC","sourcesContent":["import { Link, STUDIO_PROJECT_ROUTE, studioPath } from '@elytracms/studio/router'\nimport { Globe, LogIn, TerminalSquare } from 'lucide-react'\nimport type { Project, ResolvedWorkspaceProject, SectionState } from '@elytracms/core/studio-core'\nimport { Badge, Button, Card, Code, Heading, Row, Spacer, Stack, Text } from '@elytracms/studio/ui'\nimport { hasProjectSession } from '@elytracms/studio/lib/auth'\nimport { translate } from '@elytracms/studio/lib/i18n'\nimport { PlaygroundBadge } from './playground-badge'\nimport { SignInNeededBadge } from './sign-in-needed-badge'\nimport { SectionStateView } from './section-state'\nimport { useStudio } from './studio-context'\nimport { useRecentProjects, useSwitcherEntries } from './workspace-projects'\n\nexport interface ProjectLauncherProps {\n /** Force a deterministic list state for browser verification (?state=…). */\n forcedState?: SectionState | null\n}\n\n/**\n * Studio launcher / project list (brief §4.3, §8.1; reframed by EC-154). The\n * Studio's front door: lists the workspace's projects and routes into a\n * project's shell. With a workspace config the list is config-driven\n * (configured projects ∩ grants, plus explicit \"not provisioned\" states);\n * project *creation* is code-first — `elytra.config.ts` + `builder init\n * --project <slug>` — so there is no in-UI create flow. Without a config the\n * adapter-backed list (and the starter-kit gallery) behaves as before.\n */\nexport function ProjectLauncher({ forcedState }: ProjectLauncherProps) {\n const studio = useStudio()\n const { entries, workspace } = useSwitcherEntries()\n\n const hasConfig = workspace.kind === 'config'\n const visible = entries\n const recents = useRecentProjects(entries)\n\n const forced = forcedState && forcedState !== 'working' ? forcedState : null\n\n return (\n <div className=\"mx-auto w-full max-w-6xl px-6 py-8\">\n <Row gap=\"3\" className=\"mb-6\">\n <Stack gap=\"1\">\n <Row gap=\"2\">\n <Heading size=\"display\">{translate('shell.projects')}</Heading>\n {/* EC-153: explicit playground-mode marker (renders nothing on Convex). */}\n <PlaygroundBadge />\n {hasConfig ? (\n // EC-154: this workspace is defined in code.\n <Badge variant=\"outline\">\n <TerminalSquare className=\"size-3\" /> {workspace.source}\n </Badge>\n ) : null}\n </Row>\n <Text tone=\"muted\">\n {hasConfig\n ? 'Projects are defined in code. Add entries to elytra.config.ts and provision them with the CLI.'\n : 'Open and manage your Elytra projects.'}\n </Text>\n </Stack>\n <Spacer />\n </Row>\n\n {recents.length > 0 && !forced ? (\n <Stack gap=\"2\" className=\"mb-6\">\n <Text size=\"xs\" tone=\"muted\" mono className=\"uppercase\">\n {translate('shell.projectList.recent')}\n </Text>\n <Row gap=\"2\" wrap>\n {recents.map((p) => (\n <Link\n key={p.id}\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId: p.id, _splat: studioPath.index() }}\n onClick={() => studio.activate(p.id)}\n className=\"inline-flex items-center gap-2 rounded-md border border-border bg-card px-3 py-1.5 text-sm transition-colors hover:border-primary/40 hover:bg-muted\"\n >\n <span className=\"grid size-4 place-items-center rounded-[4px] bg-primary text-[0.6rem] font-bold text-primary-foreground\">\n {p.name.charAt(0).toUpperCase()}\n </span>\n {p.name}\n </Link>\n ))}\n </Row>\n </Stack>\n ) : null}\n\n {forced ? (\n <SectionStateView\n state={forced}\n title={forced === 'empty' ? translate('shell.projectList.empty.title') : undefined}\n />\n ) : visible.length === 0 ? (\n <SectionStateView\n state=\"empty\"\n title={translate('shell.projectList.empty.title')}\n description={\n hasConfig\n ? 'The workspace config declares no visible projects. Add an entry to elytra.config.ts, or ask an admin to grant you access.'\n : 'No active projects. Create one from the CLI: run `builder init` in your repo to scaffold elytra.config.ts, then `builder init --project <slug>`.'\n }\n />\n ) : (\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {visible.map((entry) =>\n entry.kind === 'project' ? (\n <ProjectCard key={entry.project.id} project={entry.project} />\n ) : (\n <UnprovisionedCard key={`config:${entry.config.slug}`} config={entry.config} />\n ),\n )}\n </div>\n )}\n </div>\n )\n}\n\nfunction ProjectCard({ project }: { project: Project }) {\n const studio = useStudio()\n // Per-backend sign-in state (EC-179): one project per backend, so a project on\n // a deployment the user hasn't authenticated into shows \"Sign in\", not \"Open\".\n const authed = hasProjectSession(project.slug)\n return (\n <Card className=\"flex flex-col gap-3 p-4 transition-colors hover:border-primary/30\">\n <Row gap=\"2\">\n <span className=\"grid size-8 shrink-0 place-items-center rounded-lg bg-primary text-sm font-bold text-primary-foreground\">\n {project.name.charAt(0).toUpperCase()}\n </span>\n <Stack gap=\"1\" className=\"min-w-0\">\n <Text weight=\"semibold\" className=\"truncate\">\n {project.name}\n </Text>\n <Code tone=\"muted\" className=\"w-fit max-w-full truncate\">\n {project.slug}\n </Code>\n </Stack>\n <Spacer />\n {authed ? null : <SignInNeededBadge />}\n </Row>\n\n <Row gap=\"2\" className=\"text-muted-foreground\">\n <Badge variant=\"outline\">\n <Globe className=\"size-3\" /> {project.settings.locales.length}\n </Badge>\n <Spacer />\n </Row>\n\n <Button variant={authed ? 'secondary' : 'default'} size=\"sm\" asChild className=\"mt-1\">\n <Link\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId: project.id, _splat: studioPath.index() }}\n onClick={() => studio.activate(project.id)}\n >\n {authed ? translate('shell.projectCard.open') : (\n <>\n <LogIn /> {translate('auth.signIn')}\n </>\n )}\n </Link>\n </Button>\n </Card>\n )\n}\n\n/**\n * A configured project that has no backend project yet (EC-154): an explicit,\n * deterministic state pointing at the CLI provisioning flow — never an error.\n */\nfunction UnprovisionedCard({ config }: { config: ResolvedWorkspaceProject }) {\n return (\n <Card className=\"flex flex-col gap-3 border-dashed p-4\">\n <Row gap=\"2\">\n <span className=\"grid size-8 shrink-0 place-items-center rounded-lg border border-border bg-muted text-sm font-bold text-muted-foreground\">\n {config.name.charAt(0).toUpperCase()}\n </span>\n <Stack gap=\"1\" className=\"min-w-0\">\n <Text weight=\"semibold\" className=\"truncate\">\n {config.name}\n </Text>\n <Code tone=\"muted\" className=\"w-fit max-w-full truncate\">\n {config.slug}\n </Code>\n </Stack>\n <Spacer />\n <Badge variant=\"outline\">{translate('shell.project.notProvisioned')}</Badge>\n </Row>\n\n <Text size=\"sm\" tone=\"muted\">\n Declared in the workspace config, but no backend project exists yet. Provision it from your\n repo:\n </Text>\n <Code className=\"block w-fit max-w-full truncate text-xs\">\n builder init --project {config.slug}\n </Code>\n </Card>\n )\n}\n"]}
|
|
@@ -41,16 +41,20 @@ export interface ProjectSessionStore {
|
|
|
41
41
|
*/
|
|
42
42
|
loadCms(): Promise<OperationResult<HydratedCms | null>>;
|
|
43
43
|
}
|
|
44
|
+
declare const useProjectSession: () => ProjectSessionStore;
|
|
44
45
|
export declare function ProjectSessionProvider({ projectId, children, }: {
|
|
45
46
|
projectId: string;
|
|
46
47
|
children: ReactNode;
|
|
47
48
|
}): import("react").JSX.Element;
|
|
48
|
-
export
|
|
49
|
+
export { useProjectSession };
|
|
49
50
|
/**
|
|
50
51
|
* Gate one CMS surface mount on a FRESH persisted CMS load (Convex mode).
|
|
51
52
|
* Render-prop: hands the hydrated content (or `null` → fixture fallback,
|
|
52
53
|
* first-run) to the caller once it resolved. Playground renders synchronously
|
|
53
|
-
* with `null`.
|
|
54
|
+
* with `null`. Unlike `ProjectSessionProvider`, this gate owns no
|
|
55
|
+
* `configureContentSession` side effect of its own — `loadCms` (from
|
|
56
|
+
* `useProjectSession`) already calls it internally when it resolves — so no
|
|
57
|
+
* `onResolved`/`enabled` is passed here.
|
|
54
58
|
*/
|
|
55
59
|
export declare function CmsSessionGate({ children, }: {
|
|
56
60
|
children: (cms: HydratedCms | null) => ReactNode;
|
|
@@ -1,48 +1,30 @@
|
|
|
1
|
-
import { jsx as _jsx
|
|
2
|
-
import {
|
|
3
|
-
import { Button, Stack, Text } from '@elytracms/studio/ui';
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect } from 'react';
|
|
4
3
|
import { getStudioOperations, loadHydratedCms, loadProjectSession } from '@elytracms/studio/lib/operations';
|
|
5
4
|
import { isPlaygroundMode } from '@elytracms/studio/lib/persistence';
|
|
6
5
|
import { configureContentSession } from '@elytracms/studio/lib/data';
|
|
7
6
|
import { resolveDeploymentRoutes } from '@elytracms/studio/lib/workspace';
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
const ProjectSessionContext =
|
|
7
|
+
import { createRequiredContext } from '@elytracms/studio/lib/required-context';
|
|
8
|
+
import { HydrationBoundary, useHydrationPhase } from './use-hydration-phase';
|
|
9
|
+
const { Context: ProjectSessionContext, useRequired: useProjectSession } = createRequiredContext('ProjectSession');
|
|
11
10
|
export function ProjectSessionProvider({ projectId, children, }) {
|
|
12
11
|
const playground = isPlaygroundMode();
|
|
13
|
-
|
|
14
|
-
//
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
if (!result.ok) {
|
|
30
|
-
setPhase({ kind: 'error', error: result.error });
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
// Point the preview content engine at the hydrated documents before
|
|
34
|
-
// any child renders (panels read it module-level, synchronously). EC-291:
|
|
35
|
-
// include the config-sourced routes so the canvas `resolveLink` derives a
|
|
36
|
-
// rich-text link's URL exactly as delivery does.
|
|
37
|
-
configureContentSession(result.value.cms
|
|
38
|
-
? { ...result.value.cms, routes: resolveDeploymentRoutes() }
|
|
39
|
-
: null);
|
|
40
|
-
setPhase({ kind: 'ready', session: result.value });
|
|
41
|
-
});
|
|
42
|
-
return () => {
|
|
43
|
-
cancelled = true;
|
|
44
|
-
};
|
|
45
|
-
}, [projectId, playground, attempt]);
|
|
12
|
+
const loader = useCallback(() => getStudioOperations().then(({ operations }) => loadProjectSession(operations, projectId)), [projectId]);
|
|
13
|
+
// Point the preview content engine at the hydrated documents BEFORE any
|
|
14
|
+
// child renders (panels read it module-level, synchronously). EC-291:
|
|
15
|
+
// include the config-sourced routes so the canvas `resolveLink` derives a
|
|
16
|
+
// rich-text link's URL exactly as delivery does. `enabled: true` below runs
|
|
17
|
+
// this in the playground fast-path too (with `session: null`) — the one
|
|
18
|
+
// divergence from `CmsSessionGate`, which owns no such side effect.
|
|
19
|
+
const onResolved = useCallback((session) => {
|
|
20
|
+
configureContentSession(session?.cms ? { ...session.cms, routes: resolveDeploymentRoutes() } : null);
|
|
21
|
+
}, []);
|
|
22
|
+
const { phase, retry } = useHydrationPhase(loader, {
|
|
23
|
+
playground,
|
|
24
|
+
playgroundValue: null,
|
|
25
|
+
enabled: true,
|
|
26
|
+
onResolved,
|
|
27
|
+
});
|
|
46
28
|
// Leaving the project resets the preview engine to fixtures; the next
|
|
47
29
|
// project's gate reconfigures it before rendering its own children.
|
|
48
30
|
useEffect(() => () => configureContentSession(null), []);
|
|
@@ -56,58 +38,25 @@ export function ProjectSessionProvider({ projectId, children, }) {
|
|
|
56
38
|
configureContentSession({ ...result.value, routes: resolveDeploymentRoutes() });
|
|
57
39
|
return result;
|
|
58
40
|
}, [playground, projectId]);
|
|
59
|
-
|
|
60
|
-
return _jsx(SessionHydrating, {});
|
|
61
|
-
if (phase.kind === 'error') {
|
|
62
|
-
return (_jsx(SessionHydrationError, { error: phase.error, onRetry: () => setAttempt((n) => n + 1) }));
|
|
63
|
-
}
|
|
64
|
-
return (_jsx(ProjectSessionContext.Provider, { value: { session: phase.session, loadCms }, children: children }));
|
|
65
|
-
}
|
|
66
|
-
export function useProjectSession() {
|
|
67
|
-
const store = useContext(ProjectSessionContext);
|
|
68
|
-
if (!store) {
|
|
69
|
-
throw new Error('useProjectSession must be used within a <ProjectSessionProvider>.');
|
|
70
|
-
}
|
|
71
|
-
return store;
|
|
41
|
+
return (_jsx(HydrationBoundary, { phase: phase, retry: retry, children: (session) => (_jsx(ProjectSessionContext.Provider, { value: { session, loadCms }, children: children })) }));
|
|
72
42
|
}
|
|
43
|
+
export { useProjectSession };
|
|
73
44
|
/**
|
|
74
45
|
* Gate one CMS surface mount on a FRESH persisted CMS load (Convex mode).
|
|
75
46
|
* Render-prop: hands the hydrated content (or `null` → fixture fallback,
|
|
76
47
|
* first-run) to the caller once it resolved. Playground renders synchronously
|
|
77
|
-
* with `null`.
|
|
48
|
+
* with `null`. Unlike `ProjectSessionProvider`, this gate owns no
|
|
49
|
+
* `configureContentSession` side effect of its own — `loadCms` (from
|
|
50
|
+
* `useProjectSession`) already calls it internally when it resolves — so no
|
|
51
|
+
* `onResolved`/`enabled` is passed here.
|
|
78
52
|
*/
|
|
79
53
|
export function CmsSessionGate({ children, }) {
|
|
80
54
|
const { loadCms } = useProjectSession();
|
|
81
55
|
const playground = isPlaygroundMode();
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
let cancelled = false;
|
|
88
|
-
setPhase({ kind: 'loading' });
|
|
89
|
-
void loadCms().then((result) => {
|
|
90
|
-
if (cancelled)
|
|
91
|
-
return;
|
|
92
|
-
setPhase(result.ok ? { kind: 'ready', cms: result.value } : { kind: 'error', error: result.error });
|
|
93
|
-
});
|
|
94
|
-
return () => {
|
|
95
|
-
cancelled = true;
|
|
96
|
-
};
|
|
97
|
-
}, [playground, loadCms, attempt]);
|
|
98
|
-
if (phase.kind === 'loading')
|
|
99
|
-
return _jsx(SessionHydrating, {});
|
|
100
|
-
if (phase.kind === 'error') {
|
|
101
|
-
return (_jsx(SessionHydrationError, { error: phase.error, onRetry: () => setAttempt((n) => n + 1) }));
|
|
102
|
-
}
|
|
103
|
-
return _jsx(_Fragment, { children: children(phase.cms) });
|
|
104
|
-
}
|
|
105
|
-
/** Deterministic loading frame while persisted state is fetched (mirrors BackendGate). */
|
|
106
|
-
function SessionHydrating() {
|
|
107
|
-
return (_jsx("div", { className: "grid h-full min-h-64 flex-1 place-items-center p-8", "data-testid": "session-hydrating", children: _jsx(Text, { size: "sm", tone: "muted", children: translate('shell.session.loading') }) }));
|
|
108
|
-
}
|
|
109
|
-
/** A failed hydration is a visible, retryable state — never a fixture fallback. */
|
|
110
|
-
function SessionHydrationError({ error, onRetry, }) {
|
|
111
|
-
return (_jsx("div", { className: "grid h-full min-h-64 flex-1 place-items-center p-8", children: _jsxs(Stack, { gap: "3", className: "w-full max-w-xl", children: [_jsx(Text, { size: "sm", weight: "semibold", children: translate('shell.session.error.title') }), _jsx(OperationErrorBanner, { error: error }), _jsx(Text, { size: "xs", tone: "muted", children: translate('shell.session.error.body') }), _jsx(Button, { variant: "outline", size: "sm", onClick: onRetry, className: "self-start", children: translate('common.retry') })] }) }));
|
|
56
|
+
const { phase, retry } = useHydrationPhase(loadCms, {
|
|
57
|
+
playground,
|
|
58
|
+
playgroundValue: null,
|
|
59
|
+
});
|
|
60
|
+
return (_jsx(HydrationBoundary, { phase: phase, retry: retry, children: children }));
|
|
112
61
|
}
|
|
113
62
|
//# sourceMappingURL=project-session.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-session.js","sourceRoot":"","sources":["../../src/components/project-session.tsx"],"names":[],"mappings":"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","sourcesContent":["import { createContext, useCallback, useContext, useEffect, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport { Button, Stack, Text } from '@elytracms/studio/ui'\nimport { getStudioOperations, loadHydratedCms, loadProjectSession } from '@elytracms/studio/lib/operations'\nimport type {\n HydratedCms,\n HydratedProjectSession,\n OperationError,\n OperationResult,\n} from '@elytracms/studio/lib/operations'\nimport { isPlaygroundMode } from '@elytracms/studio/lib/persistence'\nimport { configureContentSession } from '@elytracms/studio/lib/data'\nimport { resolveDeploymentRoutes } from '@elytracms/studio/lib/workspace'\nimport { translate } from '@elytracms/studio/lib/i18n'\nimport { OperationErrorBanner } from './operation-error'\n\n/**\n * Project-session hydration gate (Milestone B sign-off fix, EC-177 gap 1).\n *\n * In Convex mode every project session must BOOT from persistence: the\n * builder from the latest persisted graph revision, the CMS from the saved\n * schema + documents, publishing from the persisted environment states.\n * This provider runs the `@elytracms/core/operations` hydration queries\n * (`lib/operations/session-hydration`) and — like `BackendGate` — gates its\n * children on a deterministic loading state, so fixture content never\n * flashes (and is never editable) before the persisted state arrives.\n *\n * Playground mode renders children synchronously with `session: null`\n * (fixtures), byte-identical to the pre-hydration behavior — the EC-138\n * determinism contract.\n *\n * Side effect on hydration: the content preview engine\n * (`lib/data/collection-content`) is pointed at the hydrated documents\n * BEFORE children render, so \"Preview with\" lists and section previews show\n * live entries, not fixture titles.\n *\n * EC-308/AD-17 note — this gate deliberately does NOT read through the\n * `useOperationQuery` cache seam. It is boot/session orchestration, not a\n * cacheable server read: (1) the synchronous playground first-render is the\n * EC-138 determinism contract (a pull-query is async even when disabled);\n * (2) `configureContentSession` must run synchronously BEFORE children mount,\n * which a post-render query effect cannot guarantee; (3) `CmsSessionGate` is\n * intentionally fresh-per-visit (it remounts to show edits persisted since\n * project open) — the opposite of a cached read. It still reads through the\n * operations client (no transport bypass), and uses a user-driven retry epoch,\n * not freshness machinery — so it is outside the patchwork AD-17 retires.\n */\n\nexport interface ProjectSessionStore {\n /** Hydrated persisted state (Convex mode); `null` in playground (fixtures). */\n session: HydratedProjectSession | null\n /**\n * Load the CURRENT persisted CMS state (fresh query — the CMS section\n * remounts per visit and must show edits persisted since project open).\n * Playground resolves `null` immediately (fixtures).\n */\n loadCms(): Promise<OperationResult<HydratedCms | null>>\n}\n\nconst ProjectSessionContext = createContext<ProjectSessionStore | null>(null)\n\ntype Phase =\n | { kind: 'loading' }\n | { kind: 'ready'; session: HydratedProjectSession | null }\n | { kind: 'error'; error: OperationError }\n\nexport function ProjectSessionProvider({\n projectId,\n children,\n}: {\n projectId: string\n children: ReactNode\n}) {\n const playground = isPlaygroundMode()\n // Playground renders children on the FIRST render (no effect round-trip):\n // fixture boots stay synchronous and deterministic.\n const [phase, setPhase] = useState<Phase>(() =>\n playground ? { kind: 'ready', session: null } : { kind: 'loading' },\n )\n const [attempt, setAttempt] = useState(0)\n\n useEffect(() => {\n if (playground) {\n configureContentSession(null)\n return\n }\n let cancelled = false\n setPhase({ kind: 'loading' })\n void getStudioOperations()\n .then(({ operations }) => loadProjectSession(operations, projectId))\n .then((result) => {\n if (cancelled) return\n if (!result.ok) {\n setPhase({ kind: 'error', error: result.error })\n return\n }\n // Point the preview content engine at the hydrated documents before\n // any child renders (panels read it module-level, synchronously). EC-291:\n // include the config-sourced routes so the canvas `resolveLink` derives a\n // rich-text link's URL exactly as delivery does.\n configureContentSession(\n result.value.cms\n ? { ...result.value.cms, routes: resolveDeploymentRoutes() }\n : null,\n )\n setPhase({ kind: 'ready', session: result.value })\n })\n return () => {\n cancelled = true\n }\n }, [projectId, playground, attempt])\n\n // Leaving the project resets the preview engine to fixtures; the next\n // project's gate reconfigures it before rendering its own children.\n useEffect(() => () => configureContentSession(null), [])\n\n const loadCms = useCallback(async (): Promise<OperationResult<HydratedCms | null>> => {\n if (playground) return { ok: true, value: null }\n const { operations } = await getStudioOperations()\n const result = await loadHydratedCms(operations, projectId)\n // Keep the preview engine in sync with the freshest persisted content.\n if (result.ok && result.value !== null)\n configureContentSession({ ...result.value, routes: resolveDeploymentRoutes() })\n return result\n }, [playground, projectId])\n\n if (phase.kind === 'loading') return <SessionHydrating />\n if (phase.kind === 'error') {\n return (\n <SessionHydrationError error={phase.error} onRetry={() => setAttempt((n) => n + 1)} />\n )\n }\n return (\n <ProjectSessionContext.Provider value={{ session: phase.session, loadCms }}>\n {children}\n </ProjectSessionContext.Provider>\n )\n}\n\nexport function useProjectSession(): ProjectSessionStore {\n const store = useContext(ProjectSessionContext)\n if (!store) {\n throw new Error('useProjectSession must be used within a <ProjectSessionProvider>.')\n }\n return store\n}\n\n/**\n * Gate one CMS surface mount on a FRESH persisted CMS load (Convex mode).\n * Render-prop: hands the hydrated content (or `null` → fixture fallback,\n * first-run) to the caller once it resolved. Playground renders synchronously\n * with `null`.\n */\nexport function CmsSessionGate({\n children,\n}: {\n children: (cms: HydratedCms | null) => ReactNode\n}) {\n const { loadCms } = useProjectSession()\n const playground = isPlaygroundMode()\n const [phase, setPhase] = useState<\n | { kind: 'loading' }\n | { kind: 'ready'; cms: HydratedCms | null }\n | { kind: 'error'; error: OperationError }\n >(() => (playground ? { kind: 'ready', cms: null } : { kind: 'loading' }))\n const [attempt, setAttempt] = useState(0)\n\n useEffect(() => {\n if (playground) return\n let cancelled = false\n setPhase({ kind: 'loading' })\n void loadCms().then((result) => {\n if (cancelled) return\n setPhase(result.ok ? { kind: 'ready', cms: result.value } : { kind: 'error', error: result.error })\n })\n return () => {\n cancelled = true\n }\n }, [playground, loadCms, attempt])\n\n if (phase.kind === 'loading') return <SessionHydrating />\n if (phase.kind === 'error') {\n return (\n <SessionHydrationError error={phase.error} onRetry={() => setAttempt((n) => n + 1)} />\n )\n }\n return <>{children(phase.cms)}</>\n}\n\n/** Deterministic loading frame while persisted state is fetched (mirrors BackendGate). */\nfunction SessionHydrating() {\n return (\n <div className=\"grid h-full min-h-64 flex-1 place-items-center p-8\" data-testid=\"session-hydrating\">\n <Text size=\"sm\" tone=\"muted\">\n {translate('shell.session.loading')}\n </Text>\n </div>\n )\n}\n\n/** A failed hydration is a visible, retryable state — never a fixture fallback. */\nfunction SessionHydrationError({\n error,\n onRetry,\n}: {\n error: OperationError\n onRetry: () => void\n}) {\n return (\n <div className=\"grid h-full min-h-64 flex-1 place-items-center p-8\">\n <Stack gap=\"3\" className=\"w-full max-w-xl\">\n <Text size=\"sm\" weight=\"semibold\">\n {translate('shell.session.error.title')}\n </Text>\n <OperationErrorBanner error={error} />\n <Text size=\"xs\" tone=\"muted\">\n {translate('shell.session.error.body')}\n </Text>\n <Button variant=\"outline\" size=\"sm\" onClick={onRetry} className=\"self-start\">\n {translate('common.retry')}\n </Button>\n </Stack>\n </div>\n )\n}\n"]}
|
|
1
|
+
{"version":3,"file":"project-session.js","sourceRoot":"","sources":["../../src/components/project-session.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useEffect } from 'react'\nimport type { ReactNode } from 'react'\nimport { getStudioOperations, loadHydratedCms, loadProjectSession } from '@elytracms/studio/lib/operations'\nimport type { HydratedCms, HydratedProjectSession, OperationResult } from '@elytracms/studio/lib/operations'\nimport { isPlaygroundMode } from '@elytracms/studio/lib/persistence'\nimport { configureContentSession } from '@elytracms/studio/lib/data'\nimport { resolveDeploymentRoutes } from '@elytracms/studio/lib/workspace'\nimport { createRequiredContext } from '@elytracms/studio/lib/required-context'\nimport { HydrationBoundary, useHydrationPhase } from './use-hydration-phase'\n\n/**\n * Project-session hydration gate (Milestone B sign-off fix, EC-177 gap 1).\n *\n * In Convex mode every project session must BOOT from persistence: the\n * builder from the latest persisted graph revision, the CMS from the saved\n * schema + documents, publishing from the persisted environment states.\n * This provider runs the `@elytracms/core/operations` hydration queries\n * (`lib/operations/session-hydration`) and — like `BackendGate` — gates its\n * children on a deterministic loading state, so fixture content never\n * flashes (and is never editable) before the persisted state arrives.\n *\n * Playground mode renders children synchronously with `session: null`\n * (fixtures), byte-identical to the pre-hydration behavior — the EC-138\n * determinism contract.\n *\n * Side effect on hydration: the content preview engine\n * (`lib/data/collection-content`) is pointed at the hydrated documents\n * BEFORE children render, so \"Preview with\" lists and section previews show\n * live entries, not fixture titles.\n *\n * EC-308/AD-17 note — this gate deliberately does NOT read through the\n * `useOperationQuery` cache seam. It is boot/session orchestration, not a\n * cacheable server read: (1) the synchronous playground first-render is the\n * EC-138 determinism contract (a pull-query is async even when disabled);\n * (2) `configureContentSession` must run synchronously BEFORE children mount,\n * which a post-render query effect cannot guarantee; (3) `CmsSessionGate` is\n * intentionally fresh-per-visit (it remounts to show edits persisted since\n * project open) — the opposite of a cached read. It still reads through the\n * operations client (no transport bypass), and uses a user-driven retry epoch,\n * not freshness machinery — so it is outside the patchwork AD-17 retires.\n */\n\nexport interface ProjectSessionStore {\n /** Hydrated persisted state (Convex mode); `null` in playground (fixtures). */\n session: HydratedProjectSession | null\n /**\n * Load the CURRENT persisted CMS state (fresh query — the CMS section\n * remounts per visit and must show edits persisted since project open).\n * Playground resolves `null` immediately (fixtures).\n */\n loadCms(): Promise<OperationResult<HydratedCms | null>>\n}\n\nconst { Context: ProjectSessionContext, useRequired: useProjectSession } =\n createRequiredContext<ProjectSessionStore>('ProjectSession')\n\nexport function ProjectSessionProvider({\n projectId,\n children,\n}: {\n projectId: string\n children: ReactNode\n}) {\n const playground = isPlaygroundMode()\n\n const loader = useCallback(\n () => getStudioOperations().then(({ operations }) => loadProjectSession(operations, projectId)),\n [projectId],\n )\n // Point the preview content engine at the hydrated documents BEFORE any\n // child renders (panels read it module-level, synchronously). EC-291:\n // include the config-sourced routes so the canvas `resolveLink` derives a\n // rich-text link's URL exactly as delivery does. `enabled: true` below runs\n // this in the playground fast-path too (with `session: null`) — the one\n // divergence from `CmsSessionGate`, which owns no such side effect.\n const onResolved = useCallback((session: HydratedProjectSession | null) => {\n configureContentSession(\n session?.cms ? { ...session.cms, routes: resolveDeploymentRoutes() } : null,\n )\n }, [])\n const { phase, retry } = useHydrationPhase<HydratedProjectSession | null>(loader, {\n playground,\n playgroundValue: null,\n enabled: true,\n onResolved,\n })\n\n // Leaving the project resets the preview engine to fixtures; the next\n // project's gate reconfigures it before rendering its own children.\n useEffect(() => () => configureContentSession(null), [])\n\n const loadCms = useCallback(async (): Promise<OperationResult<HydratedCms | null>> => {\n if (playground) return { ok: true, value: null }\n const { operations } = await getStudioOperations()\n const result = await loadHydratedCms(operations, projectId)\n // Keep the preview engine in sync with the freshest persisted content.\n if (result.ok && result.value !== null)\n configureContentSession({ ...result.value, routes: resolveDeploymentRoutes() })\n return result\n }, [playground, projectId])\n\n return (\n <HydrationBoundary phase={phase} retry={retry}>\n {(session) => (\n <ProjectSessionContext.Provider value={{ session, loadCms }}>\n {children}\n </ProjectSessionContext.Provider>\n )}\n </HydrationBoundary>\n )\n}\n\nexport { useProjectSession }\n\n/**\n * Gate one CMS surface mount on a FRESH persisted CMS load (Convex mode).\n * Render-prop: hands the hydrated content (or `null` → fixture fallback,\n * first-run) to the caller once it resolved. Playground renders synchronously\n * with `null`. Unlike `ProjectSessionProvider`, this gate owns no\n * `configureContentSession` side effect of its own — `loadCms` (from\n * `useProjectSession`) already calls it internally when it resolves — so no\n * `onResolved`/`enabled` is passed here.\n */\nexport function CmsSessionGate({\n children,\n}: {\n children: (cms: HydratedCms | null) => ReactNode\n}) {\n const { loadCms } = useProjectSession()\n const playground = isPlaygroundMode()\n const { phase, retry } = useHydrationPhase<HydratedCms | null>(loadCms, {\n playground,\n playgroundValue: null,\n })\n return (\n <HydrationBoundary phase={phase} retry={retry}>\n {children}\n </HydrationBoundary>\n )\n}\n"]}
|
|
@@ -5,7 +5,7 @@ import { hasProjectSession } from '@elytracms/studio/lib/auth';
|
|
|
5
5
|
import { translate } from '@elytracms/studio/lib/i18n';
|
|
6
6
|
import { Badge, Button, Code, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, Row, Spacer, Stack, Text, } from '@elytracms/studio/ui';
|
|
7
7
|
import { useStudio } from './studio-context';
|
|
8
|
-
import { useSwitcherEntries } from './workspace-projects';
|
|
8
|
+
import { useRecentProjects, useSwitcherEntries } from './workspace-projects';
|
|
9
9
|
import { SignInNeededBadge } from './sign-in-needed-badge';
|
|
10
10
|
/**
|
|
11
11
|
* The project switcher (EC-033, reframed by EC-154): with a workspace config
|
|
@@ -19,11 +19,7 @@ export function ProjectSwitcher({ activeProject }) {
|
|
|
19
19
|
const navigate = useNavigate();
|
|
20
20
|
const { entries } = useSwitcherEntries();
|
|
21
21
|
const activeEntries = entries;
|
|
22
|
-
const recents =
|
|
23
|
-
.map((id) => studio.getProject(id))
|
|
24
|
-
.filter((p) => Boolean(p) && p.id !== activeProject.id)
|
|
25
|
-
.filter((p) => activeEntries.some((entry) => entry.kind === 'project' && entry.project.id === p.id))
|
|
26
|
-
.slice(0, 4);
|
|
22
|
+
const recents = useRecentProjects(activeEntries, { exclude: activeProject.id });
|
|
27
23
|
function open(project) {
|
|
28
24
|
studio.activate(project.id);
|
|
29
25
|
navigate({ to: STUDIO_PROJECT_ROUTE, params: { projectId: project.id, _splat: studioPath.index() } });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-switcher.js","sourceRoot":"","sources":["../../src/components/project-switcher.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,oBAAoB,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AACxF,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAEhE,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAA;AAC9D,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAA;AACtD,OAAO,EACL,KAAK,EACL,MAAM,EACN,IAAI,EACJ,YAAY,EACZ,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,qBAAqB,EACrB,mBAAmB,EACnB,GAAG,EACH,MAAM,EACN,KAAK,EACL,IAAI,GACL,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAC5C,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;
|
|
1
|
+
{"version":3,"file":"project-switcher.js","sourceRoot":"","sources":["../../src/components/project-switcher.tsx"],"names":[],"mappings":"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","sourcesContent":["import { STUDIO_PROJECT_ROUTE, studioPath, useNavigate } from '@elytracms/studio/router'\nimport { Check, ChevronsUpDown, FolderOpen } from 'lucide-react'\nimport type { Project, ResolvedWorkspaceProject } from '@elytracms/core/studio-core'\nimport { hasProjectSession } from '@elytracms/studio/lib/auth'\nimport { translate } from '@elytracms/studio/lib/i18n'\nimport {\n Badge,\n Button,\n Code,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n Row,\n Spacer,\n Stack,\n Text,\n} from '@elytracms/studio/ui'\nimport { useStudio } from './studio-context'\nimport { useRecentProjects, useSwitcherEntries } from './workspace-projects'\nimport { SignInNeededBadge } from './sign-in-needed-badge'\n\n/**\n * The project switcher (EC-033, reframed by EC-154): with a workspace config\n * the list is config-driven — configured projects ∩ the user's grants, plus\n * explicit \"not provisioned\" states for config entries that have no backend\n * project yet. Without a config it lists the adapter-backed projects exactly\n * as before. Project *creation* lives in code + CLI (`builder init`), not here.\n */\nexport function ProjectSwitcher({ activeProject }: { activeProject: Project }) {\n const studio = useStudio()\n const navigate = useNavigate()\n const { entries } = useSwitcherEntries()\n\n const activeEntries = entries\n const recents = useRecentProjects(activeEntries, { exclude: activeProject.id })\n\n function open(project: Project) {\n studio.activate(project.id)\n navigate({ to: STUDIO_PROJECT_ROUTE, params: { projectId: project.id, _splat: studioPath.index() } })\n }\n\n return (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button variant=\"outline\" size=\"sm\" className=\"h-8 gap-2 px-2.5\">\n <span className=\"grid size-4 place-items-center rounded-[4px] bg-primary text-[0.6rem] font-bold text-primary-foreground\">\n {activeProject.name.charAt(0).toUpperCase()}\n </span>\n <span className=\"max-w-[12rem] truncate font-medium\">{activeProject.name}</span>\n <ChevronsUpDown className=\"size-3.5 text-muted-foreground\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent className=\"w-80\" align=\"start\">\n {recents.length > 0 ? (\n <>\n <DropdownMenuLabel>{translate('shell.projectSwitcher.recent')}</DropdownMenuLabel>\n {recents.map((p) => (\n <ProjectMenuItem key={p.id} project={p} active={false} onSelect={() => open(p)} />\n ))}\n <DropdownMenuSeparator />\n </>\n ) : null}\n <DropdownMenuLabel>{translate('shell.projects')}</DropdownMenuLabel>\n {activeEntries.map((entry) =>\n entry.kind === 'project' ? (\n <ProjectMenuItem\n key={entry.project.id}\n project={entry.project}\n active={entry.project.id === activeProject.id}\n onSelect={() => open(entry.project)}\n />\n ) : (\n <UnprovisionedMenuItem key={`config:${entry.config.slug}`} config={entry.config} />\n ),\n )}\n <DropdownMenuSeparator />\n <DropdownMenuItem onSelect={() => navigate({ to: '/' })}>\n <FolderOpen /> {translate('shell.projectSwitcher.browseAll')}\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n )\n}\n\nfunction ProjectMenuItem({\n project,\n active,\n onSelect,\n}: {\n project: Project\n active: boolean\n onSelect: () => void\n}) {\n // Per-backend sign-in state (EC-179): the active project is signed-in by\n // definition; others wear a \"Sign in\" badge when their backend has no session.\n const needsSignIn = !active && !hasProjectSession(project.slug)\n return (\n <DropdownMenuItem onSelect={onSelect} className=\"gap-2\">\n <Row gap=\"2\" className=\"min-w-0 flex-1\">\n <Text size=\"sm\" className=\"truncate\">\n {project.name}\n </Text>\n <Code tone=\"muted\" className=\"shrink-0\">\n {project.slug}\n </Code>\n <Spacer />\n {needsSignIn ? <SignInNeededBadge /> : null}\n {active ? <Check className=\"size-4 text-primary\" /> : null}\n </Row>\n </DropdownMenuItem>\n )\n}\n\n/**\n * A configured project with no backend project yet (EC-154): a clear,\n * non-navigating state pointing at the CLI provisioning flow — never an error.\n */\nfunction UnprovisionedMenuItem({ config }: { config: ResolvedWorkspaceProject }) {\n return (\n <DropdownMenuItem disabled className=\"gap-2 opacity-100\">\n <Stack gap=\"1\" className=\"min-w-0 flex-1 py-0.5\">\n <Row gap=\"2\" className=\"min-w-0\">\n <Text size=\"sm\" tone=\"muted\" className=\"truncate\">\n {config.name}\n </Text>\n <Spacer />\n <Badge variant=\"outline\">{translate('shell.project.notProvisioned')}</Badge>\n </Row>\n <Code tone=\"muted\" className=\"w-fit max-w-full truncate text-[0.7rem]\">\n builder init --project {config.slug}\n </Code>\n </Stack>\n </DropdownMenuItem>\n )\n}\n\n"]}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import type { ReactElement } from 'react';
|
|
2
2
|
/**
|
|
3
|
-
* The shared error/loading status gate for a rail module (EC-319/EC-323
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
3
|
+
* The shared error/loading status gate for a rail module (EC-319/EC-323,
|
|
4
|
+
* repointed onto `SectionStateView` in EC-327). The assets, users, and
|
|
5
|
+
* redirects rails each read a list store with the same
|
|
6
|
+
* `{ status, errorMessage, refresh }` shape; this renders the shared
|
|
7
|
+
* error/loading section state — each rail's own translated `errorLabel`/
|
|
8
|
+
* `loadingLabel` copy, not a generic one — instead of a rail re-inlining its
|
|
9
|
+
* own AlertTriangle+retry panel. Returns `null` for a `ready` store.
|
|
8
10
|
*/
|
|
9
11
|
export declare function RailStoreStatus({ store, errorLabel, loadingLabel, errorTestId, }: {
|
|
10
12
|
store: {
|
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
3
|
-
import { Button
|
|
2
|
+
import { RotateCcw } from 'lucide-react';
|
|
3
|
+
import { Button } from '@elytracms/studio/ui';
|
|
4
4
|
import { translate } from '@elytracms/studio/lib/i18n';
|
|
5
|
+
import { SectionStateView } from './section-state';
|
|
5
6
|
/**
|
|
6
|
-
* The shared error/loading status gate for a rail module (EC-319/EC-323
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* The shared error/loading status gate for a rail module (EC-319/EC-323,
|
|
8
|
+
* repointed onto `SectionStateView` in EC-327). The assets, users, and
|
|
9
|
+
* redirects rails each read a list store with the same
|
|
10
|
+
* `{ status, errorMessage, refresh }` shape; this renders the shared
|
|
11
|
+
* error/loading section state — each rail's own translated `errorLabel`/
|
|
12
|
+
* `loadingLabel` copy, not a generic one — instead of a rail re-inlining its
|
|
13
|
+
* own AlertTriangle+retry panel. Returns `null` for a `ready` store.
|
|
11
14
|
*/
|
|
12
15
|
export function RailStoreStatus({ store, errorLabel, loadingLabel, errorTestId, }) {
|
|
13
16
|
if (store.status === 'error') {
|
|
14
|
-
return (
|
|
17
|
+
return (_jsx("div", { "data-testid": errorTestId, children: _jsx(SectionStateView, { state: "error", title: errorLabel, description: store.errorMessage ?? translate('common.backendUnreachable'), action: _jsxs(Button, { variant: "outline", size: "sm", onClick: () => void store.refresh(), children: [_jsx(RotateCcw, {}), " ", translate('common.retry')] }) }) }));
|
|
15
18
|
}
|
|
16
19
|
if (store.status === 'loading') {
|
|
17
|
-
return
|
|
20
|
+
return _jsx(SectionStateView, { state: "loading", title: loadingLabel });
|
|
18
21
|
}
|
|
19
22
|
return null;
|
|
20
23
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rail-store-status.js","sourceRoot":"","sources":["../../src/components/rail-store-status.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"rail-store-status.js","sourceRoot":"","sources":["../../src/components/rail-store-status.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AACxC,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAA;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAA;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAElD;;;;;;;;GAQG;AACH,MAAM,UAAU,eAAe,CAAC,EAC9B,KAAK,EACL,UAAU,EACV,YAAY,EACZ,WAAW,GAaZ;IACC,IAAI,KAAK,CAAC,MAAM,KAAK,OAAO,EAAE,CAAC;QAC7B,OAAO,CACL,6BAAkB,WAAW,YAC3B,KAAC,gBAAgB,IACf,KAAK,EAAC,OAAO,EACb,KAAK,EAAE,UAAU,EACjB,WAAW,EAAE,KAAK,CAAC,YAAY,IAAI,SAAS,CAAC,2BAA2B,CAAC,EACzE,MAAM,EACJ,MAAC,MAAM,IAAC,OAAO,EAAC,SAAS,EAAC,IAAI,EAAC,IAAI,EAAC,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,KAAK,CAAC,OAAO,EAAE,aACrE,KAAC,SAAS,KAAG,OAAE,SAAS,CAAC,cAAc,CAAC,IACjC,GAEX,GACE,CACP,CAAA;IACH,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAC/B,OAAO,KAAC,gBAAgB,IAAC,KAAK,EAAC,SAAS,EAAC,KAAK,EAAE,YAAY,GAAI,CAAA;IAClE,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC","sourcesContent":["import type { ReactElement } from 'react'\nimport { RotateCcw } from 'lucide-react'\nimport { Button } from '@elytracms/studio/ui'\nimport { translate } from '@elytracms/studio/lib/i18n'\nimport { SectionStateView } from './section-state'\n\n/**\n * The shared error/loading status gate for a rail module (EC-319/EC-323,\n * repointed onto `SectionStateView` in EC-327). The assets, users, and\n * redirects rails each read a list store with the same\n * `{ status, errorMessage, refresh }` shape; this renders the shared\n * error/loading section state — each rail's own translated `errorLabel`/\n * `loadingLabel` copy, not a generic one — instead of a rail re-inlining its\n * own AlertTriangle+retry panel. Returns `null` for a `ready` store.\n */\nexport function RailStoreStatus({\n store,\n errorLabel,\n loadingLabel,\n errorTestId,\n}: {\n store: {\n status: 'loading' | 'ready' | 'error'\n errorMessage?: string\n refresh: () => Promise<void>\n }\n /** Localized \"…failed to load\" heading for this rail. */\n errorLabel: string\n /** Localized \"Loading …\" text for this rail. */\n loadingLabel: string\n /** data-testid on the error container (per-rail, for tests). */\n errorTestId: string\n}): ReactElement | null {\n if (store.status === 'error') {\n return (\n <div data-testid={errorTestId}>\n <SectionStateView\n state=\"error\"\n title={errorLabel}\n description={store.errorMessage ?? translate('common.backendUnreachable')}\n action={\n <Button variant=\"outline\" size=\"sm\" onClick={() => void store.refresh()}>\n <RotateCcw /> {translate('common.retry')}\n </Button>\n }\n />\n </div>\n )\n }\n if (store.status === 'loading') {\n return <SectionStateView state=\"loading\" title={loadingLabel} />\n }\n return null\n}\n"]}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import type { CmsDocument, CmsValidationIssue, CollectionDef, DocumentRef, FieldDef, LocaleConfig } from '@elytracms/core/cms-core';
|
|
2
|
+
import type { OperationError } from '@elytracms/core/operations';
|
|
3
|
+
import type { TableViewState } from '@elytracms/studio/lib/cms-views';
|
|
4
|
+
import type { CollectionViewCapabilities, CollectionViewSource, FieldAccess } from '@elytracms/studio/lib/collection-view/collection-view-source';
|
|
5
|
+
/**
|
|
6
|
+
* The shared scaffold behind the primitive-rail `CollectionViewSource` hooks
|
|
7
|
+
* (EC-327) — assets, users, redirects. All three hand-rolled identical
|
|
8
|
+
* `viewState`/`lastError` state, an identical `setViewState` updater-fn
|
|
9
|
+
* wrapper, an identical `if (!result.ok) setLastError(...)` error-mapping
|
|
10
|
+
* shape (assets' `remove` diverges — see {@link OkResult} below), and a large
|
|
11
|
+
* final `useMemo` assembling the same ~20-field {@link CollectionViewSource}.
|
|
12
|
+
* The 4th rail (`useCmsCollectionViewSource`) does NOT reduplicate this — it
|
|
13
|
+
* delegates to the ambient `CmsStore` — so this scaffold is scoped to the
|
|
14
|
+
* three self-contained rails, not a fully generic replacement for the CMS
|
|
15
|
+
* adapter.
|
|
16
|
+
*
|
|
17
|
+
* Genuinely divergent per-rail behaviour is a PARAMETER, never hidden here:
|
|
18
|
+
* users' principal-based `fieldAccess`, users' no-op vs. redirects/assets'
|
|
19
|
+
* registry-driven `validate`, redirects-only `create`, and — the one save/
|
|
20
|
+
* remove result-shape mismatch — assets' `remove()` returns an
|
|
21
|
+
* `outcome`-discriminated result where users/redirects use `{ ok }`; each rail
|
|
22
|
+
* supplies its own `normalizeSaveResult`/`normalizeRemoveResult` to reconcile
|
|
23
|
+
* whichever shape its store returns into `lastError`.
|
|
24
|
+
*/
|
|
25
|
+
/** The common `{ ok: true; … } | { ok: false; message }` store-result shape —
|
|
26
|
+
* users' and redirects' save/remove, and assets' `updateMetadata` (save). */
|
|
27
|
+
export type OkResult = {
|
|
28
|
+
ok: true;
|
|
29
|
+
} | {
|
|
30
|
+
ok: false;
|
|
31
|
+
message: string;
|
|
32
|
+
};
|
|
33
|
+
/** The default normalizer for an {@link OkResult}: a failure maps to a
|
|
34
|
+
* surfaced {@link OperationError}; a success maps to no error at all. */
|
|
35
|
+
export declare function normalizeOkResult(result: OkResult): OperationError | null;
|
|
36
|
+
/**
|
|
37
|
+
* Project a lens document's values into a meta bag (EC-311/EC-240/EC-303):
|
|
38
|
+
* every entry whose key is NOT one of the rail's canonical/identity field
|
|
39
|
+
* names round-trips through the record's open `meta` bag. Shared by the users
|
|
40
|
+
* (`IDENTITY_FIELDS`) and redirects (`CANONICAL_REDIRECT_FIELDS`) rails — the
|
|
41
|
+
* assets rail's projection is schema-FIELDS-driven inclusion (iterating the
|
|
42
|
+
* schema's declared fields, not the document's value keys), a genuinely
|
|
43
|
+
* different shape that stays bespoke in `assets-collection-view-source.ts`.
|
|
44
|
+
*/
|
|
45
|
+
export declare function metaBagProjection(values: Record<string, unknown>, canonicalNames: ReadonlySet<string>): Record<string, unknown>;
|
|
46
|
+
/** Wrap a raw store write (save/remove) so a normalized failure surfaces via
|
|
47
|
+
* `setError` — the factory's error-mapping plumbing, pulled out as a plain
|
|
48
|
+
* function (no hook) so it is unit-testable without a `renderHook` harness. */
|
|
49
|
+
export declare function createErrorMappedWriter<TArg, TResult>(write: (arg: TArg) => Promise<TResult>, normalize: (result: TResult) => OperationError | null, setError: (error: OperationError) => void): (arg: TArg) => void;
|
|
50
|
+
/** The fields shared by {@link RailAdapter} and the pure assembly input —
|
|
51
|
+
* everything the factory copies straight onto the assembled source, or
|
|
52
|
+
* defaults when a rail omits it. */
|
|
53
|
+
interface RailViewSourceCore {
|
|
54
|
+
readonly projectId: string;
|
|
55
|
+
readonly schema: CollectionDef;
|
|
56
|
+
readonly capabilities: CollectionViewCapabilities;
|
|
57
|
+
readonly localeConfig: LocaleConfig;
|
|
58
|
+
documents(): CmsDocument[];
|
|
59
|
+
get(ref: DocumentRef): CmsDocument | undefined;
|
|
60
|
+
titleOf(ref: DocumentRef): string;
|
|
61
|
+
/** Default `() => []` — none of the three rails carry cross-collection relation fields (v1). */
|
|
62
|
+
referenceCandidates?(collectionId: string): CmsDocument[];
|
|
63
|
+
/** Default `[schema]` — the rail's own schema is the only collection in scope. */
|
|
64
|
+
collections?: readonly CollectionDef[];
|
|
65
|
+
detailPath?(id: string): string;
|
|
66
|
+
listPath?: string;
|
|
67
|
+
/** Default `field => field.form?.readOnly ?? false`. */
|
|
68
|
+
isFieldReadOnly?(field: FieldDef): boolean;
|
|
69
|
+
/** Principal-based tri-state — only the users rail supplies one. */
|
|
70
|
+
fieldAccess?(field: FieldDef, record?: CmsDocument): FieldAccess;
|
|
71
|
+
validate(doc: CmsDocument): CmsValidationIssue[];
|
|
72
|
+
create(): DocumentRef;
|
|
73
|
+
}
|
|
74
|
+
/** A rail's input to {@link useRailViewSource}: its store-facing reads/writes,
|
|
75
|
+
* capability profile, and lens projections. `TSaveResult`/`TRemoveResult` are
|
|
76
|
+
* each rail's own store-result shape — reconciled to `lastError` by the
|
|
77
|
+
* rail's own `normalizeSaveResult`/`normalizeRemoveResult`. */
|
|
78
|
+
export interface RailAdapter<TSaveResult, TRemoveResult> extends RailViewSourceCore {
|
|
79
|
+
save(doc: CmsDocument): Promise<TSaveResult>;
|
|
80
|
+
remove(ref: DocumentRef): Promise<TRemoveResult>;
|
|
81
|
+
normalizeSaveResult(result: TSaveResult): OperationError | null;
|
|
82
|
+
normalizeRemoveResult(result: TRemoveResult): OperationError | null;
|
|
83
|
+
}
|
|
84
|
+
/** The already-resolved state + error-wrapped writes {@link useRailViewSource}
|
|
85
|
+
* hands to the pure assembly function below. */
|
|
86
|
+
interface RailViewSourceAssemblyInput extends RailViewSourceCore {
|
|
87
|
+
lastError: OperationError | null;
|
|
88
|
+
clearLastError(): void;
|
|
89
|
+
viewState: TableViewState;
|
|
90
|
+
setViewState: CollectionViewSource['setViewState'];
|
|
91
|
+
save(doc: CmsDocument): void;
|
|
92
|
+
remove(ref: DocumentRef): void;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Pure assembly of a {@link CollectionViewSource} from a rail's resolved
|
|
96
|
+
* fields + state. Exported (and taking plain data/functions, no hook) so it is
|
|
97
|
+
* directly unit-testable — this workspace runs vitest in `environment: 'node'`
|
|
98
|
+
* (no `renderHook`); {@link useRailViewSource} is a thin `useState`/`useMemo`
|
|
99
|
+
* wrapper around it.
|
|
100
|
+
*/
|
|
101
|
+
export declare function assembleRailViewSource(input: RailViewSourceAssemblyInput): CollectionViewSource;
|
|
102
|
+
/**
|
|
103
|
+
* The shared scaffold factory (EC-327) behind the assets/users/redirects rail
|
|
104
|
+
* `CollectionViewSource` hooks: `viewState`/`lastError` state, the
|
|
105
|
+
* `setViewState` updater-fn wrapper, the error-mapped save/remove wiring (via
|
|
106
|
+
* the rail's own result normalizer), and the final assembled source. See the
|
|
107
|
+
* module doc for what stays a per-rail parameter rather than being absorbed
|
|
108
|
+
* here.
|
|
109
|
+
*/
|
|
110
|
+
export declare function useRailViewSource<TSaveResult, TRemoveResult>(adapter: RailAdapter<TSaveResult, TRemoveResult>): CollectionViewSource;
|
|
111
|
+
export {};
|