@elytracms/studio 0.0.13 → 0.0.15

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.
Files changed (94) hide show
  1. package/convex/cms.ts +8 -22
  2. package/convex/content.ts +3 -44
  3. package/convex/delivery.ts +8 -42
  4. package/convex/functions.ts +17 -2
  5. package/convex/members.ts +2 -2
  6. package/convex/referenceMaintenance.ts +4 -73
  7. package/convex/references.ts +4 -2
  8. package/convex/revalidate.ts +42 -0
  9. package/convex/schema.ts +17 -37
  10. package/convex/sync.ts +10 -42
  11. package/convex/triggers.ts +57 -0
  12. package/convex/webhooks.ts +3 -2
  13. package/dist/components/builder/builder-store.d.ts +85 -39
  14. package/dist/components/builder/builder-store.js +29 -125
  15. package/dist/components/builder/builder-store.js.map +1 -1
  16. package/dist/components/builder/canvas.js +23 -55
  17. package/dist/components/builder/canvas.js.map +1 -1
  18. package/dist/components/builder/clipboard-actions.js +3 -3
  19. package/dist/components/builder/clipboard-actions.js.map +1 -1
  20. package/dist/components/builder/composition-field-editor.js +13 -23
  21. package/dist/components/builder/composition-field-editor.js.map +1 -1
  22. package/dist/components/builder/insert-context.js +1 -1
  23. package/dist/components/builder/insert-context.js.map +1 -1
  24. package/dist/components/builder/page-tree.js +3 -4
  25. package/dist/components/builder/page-tree.js.map +1 -1
  26. package/dist/components/builder/property-inspector.js +4 -64
  27. package/dist/components/builder/property-inspector.js.map +1 -1
  28. package/dist/components/cms/cell.d.ts +0 -4
  29. package/dist/components/cms/cell.js +1 -5
  30. package/dist/components/cms/cell.js.map +1 -1
  31. package/dist/components/cms/cms-store.d.ts +1 -1
  32. package/dist/components/cms/cms-store.js +4 -9
  33. package/dist/components/cms/cms-store.js.map +1 -1
  34. package/dist/components/cms/collection-table.js +1 -1
  35. package/dist/components/cms/collection-table.js.map +1 -1
  36. package/dist/components/cms/detail-editor.js +2 -2
  37. package/dist/components/cms/detail-editor.js.map +1 -1
  38. package/dist/components/cms/doc-state.d.ts +0 -5
  39. package/dist/components/cms/doc-state.js +1 -7
  40. package/dist/components/cms/doc-state.js.map +1 -1
  41. package/dist/components/cms/editable-cell.js +1 -1
  42. package/dist/components/cms/editable-cell.js.map +1 -1
  43. package/dist/components/collab/collab-context.d.ts +6 -14
  44. package/dist/components/collab/collab-context.js +3 -12
  45. package/dist/components/collab/collab-context.js.map +1 -1
  46. package/dist/components/collab/collab-seed.d.ts +7 -19
  47. package/dist/components/collab/collab-seed.js +9 -31
  48. package/dist/components/collab/collab-seed.js.map +1 -1
  49. package/dist/components/components/registry-browser.js +6 -12
  50. package/dist/components/components/registry-browser.js.map +1 -1
  51. package/dist/components/guards.d.ts +0 -9
  52. package/dist/components/guards.js +0 -12
  53. package/dist/components/guards.js.map +1 -1
  54. package/dist/components/section-state.d.ts +0 -2
  55. package/dist/components/section-state.js +0 -9
  56. package/dist/components/section-state.js.map +1 -1
  57. package/dist/components/section-surface.d.ts +3 -7
  58. package/dist/components/section-surface.js +5 -12
  59. package/dist/components/section-surface.js.map +1 -1
  60. package/dist/components/sections.d.ts +0 -1
  61. package/dist/components/sections.js +0 -10
  62. package/dist/components/sections.js.map +1 -1
  63. package/dist/components/studio-project-screen.js +13 -14
  64. package/dist/components/studio-project-screen.js.map +1 -1
  65. package/dist/components/studio-shell.d.ts +0 -4
  66. package/dist/components/studio-shell.js +1 -5
  67. package/dist/components/studio-shell.js.map +1 -1
  68. package/dist/lib/operations/graph-commands.d.ts +2 -6
  69. package/dist/lib/operations/graph-commands.js +1 -2
  70. package/dist/lib/operations/graph-commands.js.map +1 -1
  71. package/dist/lib/operations/index.d.ts +0 -2
  72. package/dist/lib/operations/index.js +0 -2
  73. package/dist/lib/operations/index.js.map +1 -1
  74. package/dist/lib/operations/session-hydration.d.ts +14 -24
  75. package/dist/lib/operations/session-hydration.js +15 -34
  76. package/dist/lib/operations/session-hydration.js.map +1 -1
  77. package/dist/lib/persistence/convex-adapter.d.ts +1 -3
  78. package/dist/lib/persistence/convex-adapter.js +1 -154
  79. package/dist/lib/persistence/convex-adapter.js.map +1 -1
  80. package/dist/lib/persona/labels.js +0 -2
  81. package/dist/lib/persona/labels.js.map +1 -1
  82. package/dist/lib/references/reference-model.d.ts +0 -16
  83. package/dist/lib/references/reference-model.js +2 -54
  84. package/dist/lib/references/reference-model.js.map +1 -1
  85. package/dist/lib/references/references-store.js +3 -4
  86. package/dist/lib/references/references-store.js.map +1 -1
  87. package/dist/lib/registry/index.d.ts +0 -16
  88. package/dist/lib/registry/index.js +0 -15
  89. package/dist/lib/registry/index.js.map +1 -1
  90. package/dist/lib/webhooks/tags.js +2 -16
  91. package/dist/lib/webhooks/tags.js.map +1 -1
  92. package/package.json +3 -2
  93. package/convex/graphs.ts +0 -55
  94. package/convex/publishing.ts +0 -108
@@ -1 +1 @@
1
- {"version":3,"file":"sections.js","sourceRoot":"","sources":["../../src/components/sections.ts"],"names":[],"mappings":"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","sourcesContent":["import { Blocks, Database, Image, Puzzle, Settings, Signpost } from 'lucide-react'\nimport type { LucideIcon } from 'lucide-react'\nimport { SECTIONS, SECTION_IDS, availabilityOf, sectionIdSchema } from '@elytracms/core/studio-core'\nimport type {\n Project,\n SectionAvailability,\n SectionAvailabilityMap,\n SectionId,\n} from '@elytracms/core/studio-core'\n\n/**\n * App-level navigation ids: the studio-core sections plus surfaces registered\n * by the app itself. `routes` (EC-170) is the first app-registered surface —\n * it lives next to the CMS in the rail and rides on the CMS availability,\n * since routes/redirects are CMS routing concerns (`@elytracms/core/cms-core`).\n */\nexport type StudioNavId = SectionId | 'routes'\n\n/** Which Studio phase delivers each section's live surface (for placeholders). */\nconst SECTION_PHASE: Record<StudioNavId, string> = {\n components: 'Phase 4',\n cms: 'Phase 6',\n routes: 'Milestone B',\n assets: 'Phase 6',\n extensions: 'Phase 11',\n settings: 'Phase 2',\n}\n\nexport interface SectionNavItem {\n id: StudioNavId\n label: string\n description: string\n icon: LucideIcon\n phase: string\n}\n\nconst ICONS: Record<StudioNavId, LucideIcon> = {\n components: Blocks,\n cms: Database,\n routes: Signpost,\n assets: Image,\n extensions: Puzzle,\n settings: Settings,\n}\n\n/** Descriptor for the app-registered Routes surface (EC-170). */\nconst ROUTES_DESCRIPTOR = {\n id: 'routes',\n label: 'Routes',\n description: 'View the resolved URL routes & redirects (defined in elytra.config.ts).',\n} as const\n\n/** Canonical nav order: studio-core order with Routes slotted after CMS. */\nconst NAV_IDS: readonly StudioNavId[] = SECTION_IDS.flatMap((id) =>\n id === 'cms' ? ([id, 'routes'] as const) : [id],\n)\n\nfunction descriptorOf(id: StudioNavId): { label: string; description: string } {\n return id === 'routes' ? ROUTES_DESCRIPTOR : SECTIONS[id]\n}\n\n/** Navigation items in canonical order (brief §8.1 + EC-170). */\nexport const SECTION_NAV_ITEMS: readonly SectionNavItem[] = NAV_IDS.map((id) => ({\n id,\n label: descriptorOf(id).label,\n description: descriptorOf(id).description,\n icon: ICONS[id],\n phase: SECTION_PHASE[id],\n}))\n\nexport function sectionNavItem(id: StudioNavId): SectionNavItem {\n return SECTION_NAV_ITEMS.find((item) => item.id === id)!\n}\n\n/**\n * Availability for a nav id. App-registered surfaces map onto the section\n * whose data they manage: Routes follows the CMS availability.\n */\nexport function navAvailabilityOf(\n map: SectionAvailabilityMap,\n id: StudioNavId,\n): SectionAvailability {\n return availabilityOf(map, id === 'routes' ? 'cms' : id)\n}\n\n/**\n * The section a project should open to: the first non-`unavailable` section in\n * canonical order. Settings is always available, so this never returns null.\n */\nexport function firstAvailableSection(project: Project): SectionId {\n const found = SECTION_IDS.find(\n (id) => availabilityOf(project.settings.sectionAvailability, id) !== 'unavailable',\n )\n return found ?? 'settings'\n}\n\n/**\n * Parse the active nav id out of a studio pathname `/$projectId/<section>[/...]`\n * (incl. the app-registered Routes surface, EC-170). Returns null for the bare\n * project URL or an unknown segment. The section is the SECOND path segment —\n * directly after the projectId; there is no `/p` prefix (EC-220). The single\n * source of truth for active-section detection: the shell breadcrumb and the\n * nav rail both read it, so the segment index lives in exactly one place.\n */\nexport function activeSectionFromPath(pathname: string): StudioNavId | null {\n const segment = pathname.split('/')[2] ?? ''\n if (segment === 'routes') return 'routes'\n const parsed = sectionIdSchema.safeParse(segment)\n return parsed.success ? parsed.data : null\n}\n"]}
1
+ {"version":3,"file":"sections.js","sourceRoot":"","sources":["../../src/components/sections.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAElF,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAA;AAuBpG,MAAM,KAAK,GAAoC;IAC7C,UAAU,EAAE,MAAM;IAClB,GAAG,EAAE,QAAQ;IACb,MAAM,EAAE,QAAQ;IAChB,MAAM,EAAE,KAAK;IACb,UAAU,EAAE,MAAM;IAClB,QAAQ,EAAE,QAAQ;CACnB,CAAA;AAED,iEAAiE;AACjE,MAAM,iBAAiB,GAAG;IACxB,EAAE,EAAE,QAAQ;IACZ,KAAK,EAAE,QAAQ;IACf,WAAW,EAAE,yEAAyE;CAC9E,CAAA;AAEV,4EAA4E;AAC5E,MAAM,OAAO,GAA2B,WAAW,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,CACjE,EAAE,KAAK,KAAK,CAAC,CAAC,CAAE,CAAC,EAAE,EAAE,QAAQ,CAAW,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAChD,CAAA;AAED,SAAS,YAAY,CAAC,EAAe;IACnC,OAAO,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA;AAC3D,CAAC;AAED,iEAAiE;AACjE,MAAM,CAAC,MAAM,iBAAiB,GAA8B,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;IAC/E,EAAE;IACF,KAAK,EAAE,YAAY,CAAC,EAAE,CAAC,CAAC,KAAK;IAC7B,WAAW,EAAE,YAAY,CAAC,EAAE,CAAC,CAAC,WAAW;IACzC,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC;CAChB,CAAC,CAAC,CAAA;AAEH,MAAM,UAAU,cAAc,CAAC,EAAe;IAC5C,OAAO,iBAAiB,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAE,CAAA;AAC1D,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAC/B,GAA2B,EAC3B,EAAe;IAEf,OAAO,cAAc,CAAC,GAAG,EAAE,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;AAC1D,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAAgB;IACpD,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAC5B,CAAC,EAAE,EAAE,EAAE,CAAC,cAAc,CAAC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,EAAE,EAAE,CAAC,KAAK,aAAa,CACnF,CAAA;IACD,OAAO,KAAK,IAAI,UAAU,CAAA;AAC5B,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB,CAAC,QAAgB;IACpD,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;IAC5C,IAAI,OAAO,KAAK,QAAQ;QAAE,OAAO,QAAQ,CAAA;IACzC,MAAM,MAAM,GAAG,eAAe,CAAC,SAAS,CAAC,OAAO,CAAC,CAAA;IACjD,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAA;AAC5C,CAAC","sourcesContent":["import { Blocks, Database, Image, Puzzle, Settings, Signpost } from 'lucide-react'\nimport type { LucideIcon } from 'lucide-react'\nimport { SECTIONS, SECTION_IDS, availabilityOf, sectionIdSchema } from '@elytracms/core/studio-core'\nimport type {\n Project,\n SectionAvailability,\n SectionAvailabilityMap,\n SectionId,\n} from '@elytracms/core/studio-core'\n\n/**\n * App-level navigation ids: the studio-core sections plus surfaces registered\n * by the app itself. `routes` (EC-170) is the first app-registered surface —\n * it lives next to the CMS in the rail and rides on the CMS availability,\n * since routes/redirects are CMS routing concerns (`@elytracms/core/cms-core`).\n */\nexport type StudioNavId = SectionId | 'routes'\n\nexport interface SectionNavItem {\n id: StudioNavId\n label: string\n description: string\n icon: LucideIcon\n}\n\nconst ICONS: Record<StudioNavId, LucideIcon> = {\n components: Blocks,\n cms: Database,\n routes: Signpost,\n assets: Image,\n extensions: Puzzle,\n settings: Settings,\n}\n\n/** Descriptor for the app-registered Routes surface (EC-170). */\nconst ROUTES_DESCRIPTOR = {\n id: 'routes',\n label: 'Routes',\n description: 'View the resolved URL routes & redirects (defined in elytra.config.ts).',\n} as const\n\n/** Canonical nav order: studio-core order with Routes slotted after CMS. */\nconst NAV_IDS: readonly StudioNavId[] = SECTION_IDS.flatMap((id) =>\n id === 'cms' ? ([id, 'routes'] as const) : [id],\n)\n\nfunction descriptorOf(id: StudioNavId): { label: string; description: string } {\n return id === 'routes' ? ROUTES_DESCRIPTOR : SECTIONS[id]\n}\n\n/** Navigation items in canonical order (brief §8.1 + EC-170). */\nexport const SECTION_NAV_ITEMS: readonly SectionNavItem[] = NAV_IDS.map((id) => ({\n id,\n label: descriptorOf(id).label,\n description: descriptorOf(id).description,\n icon: ICONS[id],\n}))\n\nexport function sectionNavItem(id: StudioNavId): SectionNavItem {\n return SECTION_NAV_ITEMS.find((item) => item.id === id)!\n}\n\n/**\n * Availability for a nav id. App-registered surfaces map onto the section\n * whose data they manage: Routes follows the CMS availability.\n */\nexport function navAvailabilityOf(\n map: SectionAvailabilityMap,\n id: StudioNavId,\n): SectionAvailability {\n return availabilityOf(map, id === 'routes' ? 'cms' : id)\n}\n\n/**\n * The section a project should open to: the first non-`unavailable` section in\n * canonical order. Settings is always available, so this never returns null.\n */\nexport function firstAvailableSection(project: Project): SectionId {\n const found = SECTION_IDS.find(\n (id) => availabilityOf(project.settings.sectionAvailability, id) !== 'unavailable',\n )\n return found ?? 'settings'\n}\n\n/**\n * Parse the active nav id out of a studio pathname `/$projectId/<section>[/...]`\n * (incl. the app-registered Routes surface, EC-170). Returns null for the bare\n * project URL or an unknown segment. The section is the SECOND path segment —\n * directly after the projectId; there is no `/p` prefix (EC-220). The single\n * source of truth for active-section detection: the shell breadcrumb and the\n * nav rail both read it, so the segment index lives in exactly one place.\n */\nexport function activeSectionFromPath(pathname: string): StudioNavId | null {\n const segment = pathname.split('/')[2] ?? ''\n if (segment === 'routes') return 'routes'\n const parsed = sectionIdSchema.safeParse(segment)\n return parsed.success ? parsed.data : null\n}\n"]}
@@ -1,7 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useEffect } from 'react';
3
3
  import { isSingleton, resolveSingletonDocument } from '@elytracms/core/cms-core';
4
- import { validSampleGraph } from '@elytracms/core/project-graph';
5
4
  import { Link, Navigate, STUDIO_PROJECT_ROUTE, studioPath, useParams, useSearch, } from '@elytracms/studio/router';
6
5
  import { matchStudioRoute } from '@elytracms/studio/router/match';
7
6
  import { studioSearch } from '@elytracms/studio/router/search';
@@ -16,14 +15,13 @@ import { SectionStateView } from './section-state';
16
15
  import { SettingsForm } from './settings-form';
17
16
  import { AssetLibrary } from './assets/asset-library';
18
17
  import { AssetsProvider } from './assets/assets-store';
19
- import { BuilderProvider } from './builder/builder-store';
20
18
  import { CollabProvider } from './collab/collab-context';
21
19
  import { RoutesShell } from './routes/routes-shell';
22
20
  import { ExtensionsView } from './extensions/extensions-view';
23
21
  import { RegistryBrowser } from './components/registry-browser';
24
22
  import { ComponentsIndex } from './components/components-index';
25
23
  import { ComponentDetail } from './components/component-detail';
26
- import { CmsSessionGate, ProjectSessionProvider, useProjectSession, } from './project-session';
24
+ import { CmsSessionGate, ProjectSessionProvider } from './project-session';
27
25
  import { CmsProvider, useCms } from './cms/cms-store';
28
26
  import { CollectionsSidebar } from './cms/collections-sidebar';
29
27
  import { CmsIndex } from './cms/cms-index';
@@ -71,19 +69,20 @@ export function StudioProjectScreen() {
71
69
  const search = studioSearch(rawSearch);
72
70
  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) }));
73
71
  }
74
- /** The per-project provider stack, mounted once the session source is known. */
72
+ /**
73
+ * The per-project provider stack, mounted once the session source is known.
74
+ *
75
+ * EC-287: there is no project-level builder graph anymore — layouts are code and
76
+ * the DB holds content only. The block-field canvas mounts its OWN scoped
77
+ * `BuilderProvider` per document (`composition-field-editor`), so no graph is
78
+ * threaded here. `CollabProvider` stays as the canvas's session-scoped
79
+ * presence/perspective store, seeded graph-lessly.
80
+ */
75
81
  function ProjectProviders({ project, children }) {
76
- const { session } = useProjectSession();
77
- // A project created from a starter kit (EC-119) opens on the kit's graph —
78
- // but only until something is persisted: the hydrated revision wins.
79
- const kit = getInstantiatedKit(project.id);
80
- const graph = session?.graph ?? kit?.graph ?? validSampleGraph;
81
82
  return (
82
- // Key by project id so the session data sources + graph reset when switching projects.
83
- // One canonical graph per project session: the Builder canvas and its Content tab
84
- // (bindings, repeating sections, template pages) read the same live graph. The data
85
- // store hydrates/persists this project's `cms-data-sources` records.
86
- _jsx(DataProvider, { projectId: project.id, children: _jsx(BuilderProvider, { graph: graph, projectId: project.id, children: _jsx(AssetsProvider, { projectId: project.id, children: _jsx(CollabProvider, { projectId: project.id, graph: graph, publish: session?.publish, children: children }, project.id) }, project.id) }, project.id) }, project.id));
83
+ // Key by project id so the session data sources reset when switching projects.
84
+ // The data store hydrates/persists this project's `cms-data-sources` records.
85
+ _jsx(DataProvider, { projectId: project.id, children: _jsx(AssetsProvider, { projectId: project.id, children: _jsx(CollabProvider, { projectId: project.id, children: children }, project.id) }, project.id) }, project.id));
87
86
  }
88
87
  /**
89
88
  * The URL→screen switch (EC-223 Stage 6) — what the per-section `<Outlet />`s
@@ -1 +1 @@
1
- {"version":3,"file":"studio-project-screen.js","sourceRoot":"","sources":["../../src/components/studio-project-screen.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useEffect } from 'react'\nimport type { ReactNode } from 'react'\nimport { isSingleton, resolveSingletonDocument } from '@elytracms/core/cms-core'\nimport { validSampleGraph } from '@elytracms/core/project-graph'\nimport type { Project, SectionState } from '@elytracms/core/studio-core'\nimport {\n Link,\n Navigate,\n STUDIO_PROJECT_ROUTE,\n studioPath,\n useParams,\n useSearch,\n} from '@elytracms/studio/router'\nimport { matchStudioRoute } from '@elytracms/studio/router/match'\nimport type { StudioRouteMatch } from '@elytracms/studio/router/match'\nimport { studioSearch } from '@elytracms/studio/router/search'\nimport type { StudioSearch } from '@elytracms/studio/router/search'\nimport { Button, Heading, Stack, Text } from '@elytracms/studio/ui'\nimport { DataProvider } from '@elytracms/studio/lib/data'\nimport { getInstantiatedKit } from '@elytracms/studio/lib/starters/kit-session'\nimport { useStudio } from './studio-context'\nimport { StudioShell } from './studio-shell'\nimport { firstAvailableSection } from './sections'\nimport { SectionSurface } from './section-surface'\nimport { SectionStateView } from './section-state'\nimport { SettingsForm } from './settings-form'\nimport { AssetLibrary } from './assets/asset-library'\nimport { AssetsProvider } from './assets/assets-store'\nimport { BuilderProvider } from './builder/builder-store'\nimport { CollabProvider } from './collab/collab-context'\nimport { RoutesShell } from './routes/routes-shell'\nimport { ExtensionsView } from './extensions/extensions-view'\nimport { RegistryBrowser } from './components/registry-browser'\nimport { ComponentsIndex } from './components/components-index'\nimport { ComponentDetail } from './components/component-detail'\nimport {\n CmsSessionGate,\n ProjectSessionProvider,\n useProjectSession,\n} from './project-session'\nimport { CmsProvider, useCms } from './cms/cms-store'\nimport { CollectionsSidebar } from './cms/collections-sidebar'\nimport { CmsIndex } from './cms/cms-index'\nimport { CollectionTable } from './cms/collection-table'\nimport { DetailEditor } from './cms/detail-editor'\nimport { OperationErrorBanner } from './operation-error'\n\n/**\n * EC-223 Stage 6 (catch-all routing): the studio's per-project route component.\n *\n * The consumer mounts ONE splat route (`/$projectId/$`) whose component is this.\n * Everything that was a per-project file route — the provider stack, the section\n * rail shell, and every section screen — now lives inside the package and is\n * resolved here from the URL. The consumer's route tree shrinks to a single\n * ~10-line mount, which is the whole point of Stage 6: the engine owns its\n * navigation, so an external repo consumes the studio without rebuilding a route\n * file per screen.\n *\n * `projectId` stays a real route param (the `$projectId` segment); the trailing\n * pathname (`_splat`) is matched against the engine route table\n * (`matchStudioRoute`) and the deeper ids (collection / document / component)\n * flow down as props. Every screen already takes its ids as props, so this\n * collapse is mechanical — the package never read deep params through the router;\n * the disappearing route-file wrappers did.\n */\nexport function StudioProjectScreen() {\n const params = useParams({ strict: false }) as { projectId?: string; _splat?: string }\n // The route's VALIDATED search (the splat's `validateSearch: studioSearch`).\n // Read it through `useSearch`, not `useLocation().search`: the validated search\n // is resolved at route-match time and stable from the component's first render,\n // whereas `location.search` can lag a tick during hydration — long enough for a\n // child `<Navigate>` (the builder redirect) to fire on a momentarily-empty\n // search and drop its `?page=` deep-link target.\n const rawSearch = useSearch({ strict: false }) as Record<string, unknown>\n const studio = useStudio()\n const projectId = params.projectId ?? ''\n const project = studio.getProject(projectId)\n\n // Record the active project once it resolves (preserves recents across sections).\n useEffect(() => {\n if (project) studio.activate(project.id)\n }, [project, studio])\n\n if (!project) return <ProjectNotFound />\n\n const match = matchStudioRoute(params._splat ?? '')\n const search = studioSearch(rawSearch)\n\n return (\n <StudioShell project={project}>\n {/* EC-177 gap 1: in Convex mode the session HYDRATES from persistence\n before any provider mounts — the builder boots from the latest\n persisted revision, publishing reflects the persisted state, and the\n preview content engine serves live documents. Playground renders\n synchronously from fixtures (session: null), exactly as before. */}\n <ProjectSessionProvider key={project.id} projectId={project.id}>\n <ProjectProviders project={project}>\n <StudioSectionResolver project={project} match={match} search={search} />\n </ProjectProviders>\n </ProjectSessionProvider>\n </StudioShell>\n )\n}\n\n/** The per-project provider stack, mounted once the session source is known. */\nfunction ProjectProviders({ project, children }: { project: Project; children: ReactNode }) {\n const { session } = useProjectSession()\n // A project created from a starter kit (EC-119) opens on the kit's graph —\n // but only until something is persisted: the hydrated revision wins.\n const kit = getInstantiatedKit(project.id)\n const graph = session?.graph ?? kit?.graph ?? validSampleGraph\n\n return (\n // Key by project id so the session data sources + graph reset when switching projects.\n // One canonical graph per project session: the Builder canvas and its Content tab\n // (bindings, repeating sections, template pages) read the same live graph. The data\n // store hydrates/persists this project's `cms-data-sources` records.\n <DataProvider key={project.id} projectId={project.id}>\n <BuilderProvider key={project.id} graph={graph} projectId={project.id}>\n {/* EC-195: project-level so the canvas resolves `asset`-typed Image props\n and the inspector picks from the live library — not just the Assets\n section. Asset METADATA only (small); blobs stay lazy. */}\n <AssetsProvider key={project.id} projectId={project.id}>\n <CollabProvider\n key={project.id}\n projectId={project.id}\n graph={graph}\n publish={session?.publish}\n >\n {children}\n </CollabProvider>\n </AssetsProvider>\n </BuilderProvider>\n </DataProvider>\n )\n}\n\n/**\n * The URL→screen switch (EC-223 Stage 6) — what the per-section `<Outlet />`s\n * used to resolve. Renders inside the shell + provider stack, so every screen\n * sees the same live graph, CMS store, and breadcrumb context the file routes\n * gave it. `index` and `builder` are redirect-only screens, kept here (rather\n * than short-circuited before the shell) because the retired routes nested under\n * the project layout too — they mounted the stack and then navigated, exact\n * parity.\n */\nfunction StudioSectionResolver({\n project,\n match,\n search,\n}: {\n project: Project\n match: StudioRouteMatch\n search: StudioSearch\n}) {\n const projectId = project.id\n\n switch (match.section) {\n case 'index':\n // Bare `/$projectId` → the project's first available section.\n return (\n <Navigate\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId, _splat: firstAvailableSection(project) }}\n replace\n />\n )\n\n case 'settings':\n return (\n <SectionSurface project={project} section=\"settings\" forcedState={search.state}>\n <SettingsForm project={project} />\n </SectionSurface>\n )\n\n case 'assets':\n return (\n <SectionSurface project={project} section=\"assets\" forcedState={search.state}>\n {/* The AssetsProvider is mounted at the project level (EC-195). */}\n <AssetLibrary projectId={projectId} focusAssetId={search.asset} />\n </SectionSurface>\n )\n\n case 'routes':\n // Deterministic forced states (?state=) for the surface (EC-036).\n return search.state && search.state !== 'working' ? (\n <SectionStateView state={search.state} title=\"Routes\" />\n ) : (\n <RoutesShell project={project} />\n )\n\n case 'extensions':\n return search.state && search.state !== 'working' ? (\n <SectionStateView state={search.state} title=\"Extensions\" />\n ) : (\n <ExtensionsView />\n )\n\n case 'builder':\n // Retired surface (EC-187 slice 6): a page is a document in the `page`\n // collection. A `?page=<id>` deep link lands on that document's editor;\n // otherwise the page-collection list. The page-graph-only params have no\n // home here and are dropped.\n return (\n <Navigate\n to={STUDIO_PROJECT_ROUTE}\n params={{\n projectId,\n _splat: search.page\n ? studioPath.cmsDocument('page', search.page)\n : studioPath.cmsCollection('page'),\n }}\n replace\n />\n )\n\n case 'components':\n return (\n <ComponentsScreen\n projectId={projectId}\n componentId={match.componentId}\n state={search.state}\n />\n )\n\n case 'cms':\n return (\n <CmsScreen\n project={project}\n collectionId={match.collectionId}\n documentId={match.documentId}\n state={search.state}\n field={search.field}\n />\n )\n\n case 'not-found':\n default:\n return <StudioRouteNotFound />\n }\n}\n\n/**\n * Components section (EC-050): a registry-browser sidebar beside the active\n * screen — the index (no selection) or a component's detail. Forced states\n * (?state=) replace the whole surface.\n */\nfunction ComponentsScreen({\n projectId,\n componentId,\n state,\n}: {\n projectId: string\n componentId: string | null\n state?: SectionState\n}) {\n if (state && state !== 'working') {\n return <SectionStateView state={state} title=\"Components\" />\n }\n return (\n <div className=\"flex h-full min-h-0\">\n <aside className=\"hidden h-full min-h-0 w-[300px] shrink-0 border-r border-border md:block\">\n <RegistryBrowser projectId={projectId} activeComponentId={componentId ?? undefined} />\n </aside>\n <div className=\"min-h-0 flex-1 overflow-auto\">\n {componentId === null ? (\n <ComponentsIndex projectId={projectId} />\n ) : (\n <ComponentDetail projectId={projectId} componentId={componentId} />\n )}\n </div>\n </div>\n )\n}\n\n/**\n * CMS section: hydrates the persisted (or kit-seeded) collections + documents,\n * then renders the collections sidebar beside the active screen (index, a\n * collection table, or a document editor). Forced states (?state=) replace the\n * whole surface before the provider mounts.\n */\nfunction CmsScreen({\n project,\n collectionId,\n documentId,\n state,\n field,\n}: {\n project: Project\n collectionId: string | null\n documentId: string | null\n state?: SectionState\n field?: string\n}) {\n const projectId = project.id\n if (state && state !== 'working') {\n return <SectionStateView state={state} title=\"CMS\" />\n }\n\n // A project created from a starter kit (EC-119) opens the CMS on the kit's schemas + content.\n const kit = getInstantiatedKit(projectId)\n\n return (\n // EC-177 gap 1: in Convex mode every CMS mount loads the CURRENT persisted\n // schema + documents (fixtures only when nothing is persisted — first run).\n // Playground renders synchronously with `null` → fixture seeds, as before.\n <CmsSessionGate>\n {(cms) => (\n <CmsProvider\n key={projectId}\n projectId={projectId}\n initialCollections={cms?.collections ?? kit?.collections}\n initialDocuments={cms?.documents ?? kit?.documents}\n initialPublishedKeys={cms?.publishedKeys}\n initialLocaleConfig={\n cms\n ? // Hydrated sessions follow the project's persisted locale settings.\n { default: project.settings.defaultLocale, locales: [...project.settings.locales] }\n : kit?.localeConfig\n }\n >\n <div className=\"flex h-full min-h-0 flex-col\">\n <CmsPersistErrorBanner />\n <div className=\"flex min-h-0 flex-1\">\n <aside className=\"hidden h-full min-h-0 w-[240px] shrink-0 border-r border-border md:block\">\n <CollectionsSidebar\n projectId={projectId}\n activeCollectionId={collectionId ?? undefined}\n />\n </aside>\n <div className=\"min-h-0 flex-1 overflow-hidden\">\n <CmsContent\n projectId={projectId}\n collectionId={collectionId}\n documentId={documentId}\n field={field}\n />\n </div>\n </div>\n </div>\n </CmsProvider>\n )}\n </CmsSessionGate>\n )\n}\n\n/** Resolves the active CMS screen inside the provider (needs the live store). */\nfunction CmsContent({\n projectId,\n collectionId,\n documentId,\n field,\n}: {\n projectId: string\n collectionId: string | null\n documentId: string | null\n field?: string\n}) {\n const cms = useCms()\n\n if (collectionId === null) return <CmsIndex projectId={projectId} />\n\n if (documentId === null) {\n const collection = cms.getCollection(collectionId)\n // EC-274: a singleton is a UI lens — at the collection URL it opens its one\n // document directly *while in its expected shape*, but degrades to the list\n // (with a \"for review\" lane) on drift. SingletonGate decides per the data.\n if (collection && isSingleton(collection)) {\n return <SingletonGate projectId={projectId} collectionId={collectionId} />\n }\n return <CollectionTable projectId={projectId} collectionId={collectionId} />\n }\n\n return (\n <DetailEditor\n projectId={projectId}\n collectionId={collectionId}\n documentId={documentId}\n focusPath={field}\n />\n )\n}\n\n/**\n * EC-274 — the singleton lens gate. At a singleton collection's URL, resolve its\n * lone document and open it directly; on drift (> 1 document) fall back to the\n * list with the \"for review\" lane so an editor can resolve the conflict. The one\n * document keeps a normal id — nothing here names a literal document id.\n */\nfunction SingletonGate({ projectId, collectionId }: { projectId: string; collectionId: string }) {\n const cms = useCms()\n const collection = cms.getCollection(collectionId)\n const docs = cms.documentsOf(collectionId)\n const resolution = collection\n ? resolveSingletonDocument(collection, docs)\n : ({ kind: 'empty' } as const)\n\n // Materialize the lone document on first visit (an empty draft, persisted like\n // any create). Idempotent in the store; only fires while genuinely empty.\n useEffect(() => {\n if (resolution.kind === 'empty') cms.ensureSingletonDocument(collectionId)\n }, [resolution.kind, cms, collectionId])\n\n if (resolution.kind === 'ready') {\n return (\n <DetailEditor projectId={projectId} collectionId={collectionId} documentId={resolution.doc.id} />\n )\n }\n if (resolution.kind === 'drift') {\n return <CollectionTable projectId={projectId} collectionId={collectionId} />\n }\n return (\n <div className=\"grid h-full place-items-center p-8\">\n <Text size=\"sm\" tone=\"muted\">\n Opening {collection?.form?.label ?? collectionId}…\n </Text>\n </div>\n )\n}\n\n/** Failed/denied persisted CMS mutations are always visible (EC-141). */\nfunction CmsPersistErrorBanner() {\n const cms = useCms()\n if (!cms.persistError) return null\n return (\n <div className=\"shrink-0 border-b border-border p-2\">\n <OperationErrorBanner error={cms.persistError} onDismiss={cms.clearPersistError} />\n </div>\n )\n}\n\nfunction ProjectNotFound() {\n return (\n <div className=\"grid min-h-dvh place-items-center p-8\">\n <Stack gap=\"3\" align=\"center\" className=\"max-w-sm text-center\">\n <Heading size=\"display\">Project not found</Heading>\n <Text tone=\"muted\">\n This project doesn’t exist or was deleted. It may only have existed in this session.\n </Text>\n <Button asChild>\n <Link to=\"/\">Back to projects</Link>\n </Button>\n </Stack>\n </div>\n )\n}\n\nfunction StudioRouteNotFound() {\n return (\n <div className=\"grid h-full place-items-center p-8\">\n <Stack gap=\"2\" align=\"center\" className=\"max-w-sm text-center\">\n <Heading level={2} size=\"h2\">\n Page not found\n </Heading>\n <Text tone=\"muted\">This studio URL doesn’t resolve to a section.</Text>\n </Stack>\n </div>\n )\n}\n"]}
1
+ {"version":3,"file":"studio-project-screen.js","sourceRoot":"","sources":["../../src/components/studio-project-screen.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useEffect } from 'react'\nimport type { ReactNode } from 'react'\nimport { isSingleton, resolveSingletonDocument } from '@elytracms/core/cms-core'\nimport type { Project, SectionState } from '@elytracms/core/studio-core'\nimport {\n Link,\n Navigate,\n STUDIO_PROJECT_ROUTE,\n studioPath,\n useParams,\n useSearch,\n} from '@elytracms/studio/router'\nimport { matchStudioRoute } from '@elytracms/studio/router/match'\nimport type { StudioRouteMatch } from '@elytracms/studio/router/match'\nimport { studioSearch } from '@elytracms/studio/router/search'\nimport type { StudioSearch } from '@elytracms/studio/router/search'\nimport { Button, Heading, Stack, Text } from '@elytracms/studio/ui'\nimport { DataProvider } from '@elytracms/studio/lib/data'\nimport { getInstantiatedKit } from '@elytracms/studio/lib/starters/kit-session'\nimport { useStudio } from './studio-context'\nimport { StudioShell } from './studio-shell'\nimport { firstAvailableSection } from './sections'\nimport { SectionSurface } from './section-surface'\nimport { SectionStateView } from './section-state'\nimport { SettingsForm } from './settings-form'\nimport { AssetLibrary } from './assets/asset-library'\nimport { AssetsProvider } from './assets/assets-store'\nimport { CollabProvider } from './collab/collab-context'\nimport { RoutesShell } from './routes/routes-shell'\nimport { ExtensionsView } from './extensions/extensions-view'\nimport { RegistryBrowser } from './components/registry-browser'\nimport { ComponentsIndex } from './components/components-index'\nimport { ComponentDetail } from './components/component-detail'\nimport { CmsSessionGate, ProjectSessionProvider } from './project-session'\nimport { CmsProvider, useCms } from './cms/cms-store'\nimport { CollectionsSidebar } from './cms/collections-sidebar'\nimport { CmsIndex } from './cms/cms-index'\nimport { CollectionTable } from './cms/collection-table'\nimport { DetailEditor } from './cms/detail-editor'\nimport { OperationErrorBanner } from './operation-error'\n\n/**\n * EC-223 Stage 6 (catch-all routing): the studio's per-project route component.\n *\n * The consumer mounts ONE splat route (`/$projectId/$`) whose component is this.\n * Everything that was a per-project file route — the provider stack, the section\n * rail shell, and every section screen — now lives inside the package and is\n * resolved here from the URL. The consumer's route tree shrinks to a single\n * ~10-line mount, which is the whole point of Stage 6: the engine owns its\n * navigation, so an external repo consumes the studio without rebuilding a route\n * file per screen.\n *\n * `projectId` stays a real route param (the `$projectId` segment); the trailing\n * pathname (`_splat`) is matched against the engine route table\n * (`matchStudioRoute`) and the deeper ids (collection / document / component)\n * flow down as props. Every screen already takes its ids as props, so this\n * collapse is mechanical — the package never read deep params through the router;\n * the disappearing route-file wrappers did.\n */\nexport function StudioProjectScreen() {\n const params = useParams({ strict: false }) as { projectId?: string; _splat?: string }\n // The route's VALIDATED search (the splat's `validateSearch: studioSearch`).\n // Read it through `useSearch`, not `useLocation().search`: the validated search\n // is resolved at route-match time and stable from the component's first render,\n // whereas `location.search` can lag a tick during hydration — long enough for a\n // child `<Navigate>` (the builder redirect) to fire on a momentarily-empty\n // search and drop its `?page=` deep-link target.\n const rawSearch = useSearch({ strict: false }) as Record<string, unknown>\n const studio = useStudio()\n const projectId = params.projectId ?? ''\n const project = studio.getProject(projectId)\n\n // Record the active project once it resolves (preserves recents across sections).\n useEffect(() => {\n if (project) studio.activate(project.id)\n }, [project, studio])\n\n if (!project) return <ProjectNotFound />\n\n const match = matchStudioRoute(params._splat ?? '')\n const search = studioSearch(rawSearch)\n\n return (\n <StudioShell project={project}>\n {/* EC-177 gap 1: in Convex mode the session HYDRATES from persistence\n before any provider mounts — the builder boots from the latest\n persisted revision, publishing reflects the persisted state, and the\n preview content engine serves live documents. Playground renders\n synchronously from fixtures (session: null), exactly as before. */}\n <ProjectSessionProvider key={project.id} projectId={project.id}>\n <ProjectProviders project={project}>\n <StudioSectionResolver project={project} match={match} search={search} />\n </ProjectProviders>\n </ProjectSessionProvider>\n </StudioShell>\n )\n}\n\n/**\n * The per-project provider stack, mounted once the session source is known.\n *\n * EC-287: there is no project-level builder graph anymore — layouts are code and\n * the DB holds content only. The block-field canvas mounts its OWN scoped\n * `BuilderProvider` per document (`composition-field-editor`), so no graph is\n * threaded here. `CollabProvider` stays as the canvas's session-scoped\n * presence/perspective store, seeded graph-lessly.\n */\nfunction ProjectProviders({ project, children }: { project: Project; children: ReactNode }) {\n return (\n // Key by project id so the session data sources reset when switching projects.\n // The data store hydrates/persists this project's `cms-data-sources` records.\n <DataProvider key={project.id} projectId={project.id}>\n {/* EC-195: project-level so the canvas resolves `asset`-typed Image props\n and the inspector picks from the live library — not just the Assets\n section. Asset METADATA only (small); blobs stay lazy. */}\n <AssetsProvider key={project.id} projectId={project.id}>\n <CollabProvider key={project.id} projectId={project.id}>\n {children}\n </CollabProvider>\n </AssetsProvider>\n </DataProvider>\n )\n}\n\n/**\n * The URL→screen switch (EC-223 Stage 6) — what the per-section `<Outlet />`s\n * used to resolve. Renders inside the shell + provider stack, so every screen\n * sees the same live graph, CMS store, and breadcrumb context the file routes\n * gave it. `index` and `builder` are redirect-only screens, kept here (rather\n * than short-circuited before the shell) because the retired routes nested under\n * the project layout too — they mounted the stack and then navigated, exact\n * parity.\n */\nfunction StudioSectionResolver({\n project,\n match,\n search,\n}: {\n project: Project\n match: StudioRouteMatch\n search: StudioSearch\n}) {\n const projectId = project.id\n\n switch (match.section) {\n case 'index':\n // Bare `/$projectId` → the project's first available section.\n return (\n <Navigate\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId, _splat: firstAvailableSection(project) }}\n replace\n />\n )\n\n case 'settings':\n return (\n <SectionSurface project={project} section=\"settings\" forcedState={search.state}>\n <SettingsForm project={project} />\n </SectionSurface>\n )\n\n case 'assets':\n return (\n <SectionSurface project={project} section=\"assets\" forcedState={search.state}>\n {/* The AssetsProvider is mounted at the project level (EC-195). */}\n <AssetLibrary projectId={projectId} focusAssetId={search.asset} />\n </SectionSurface>\n )\n\n case 'routes':\n // Deterministic forced states (?state=) for the surface (EC-036).\n return search.state && search.state !== 'working' ? (\n <SectionStateView state={search.state} title=\"Routes\" />\n ) : (\n <RoutesShell project={project} />\n )\n\n case 'extensions':\n return search.state && search.state !== 'working' ? (\n <SectionStateView state={search.state} title=\"Extensions\" />\n ) : (\n <ExtensionsView />\n )\n\n case 'builder':\n // Retired surface (EC-187 slice 6): a page is a document in the `page`\n // collection. A `?page=<id>` deep link lands on that document's editor;\n // otherwise the page-collection list. The page-graph-only params have no\n // home here and are dropped.\n return (\n <Navigate\n to={STUDIO_PROJECT_ROUTE}\n params={{\n projectId,\n _splat: search.page\n ? studioPath.cmsDocument('page', search.page)\n : studioPath.cmsCollection('page'),\n }}\n replace\n />\n )\n\n case 'components':\n return (\n <ComponentsScreen\n projectId={projectId}\n componentId={match.componentId}\n state={search.state}\n />\n )\n\n case 'cms':\n return (\n <CmsScreen\n project={project}\n collectionId={match.collectionId}\n documentId={match.documentId}\n state={search.state}\n field={search.field}\n />\n )\n\n case 'not-found':\n default:\n return <StudioRouteNotFound />\n }\n}\n\n/**\n * Components section (EC-050): a registry-browser sidebar beside the active\n * screen — the index (no selection) or a component's detail. Forced states\n * (?state=) replace the whole surface.\n */\nfunction ComponentsScreen({\n projectId,\n componentId,\n state,\n}: {\n projectId: string\n componentId: string | null\n state?: SectionState\n}) {\n if (state && state !== 'working') {\n return <SectionStateView state={state} title=\"Components\" />\n }\n return (\n <div className=\"flex h-full min-h-0\">\n <aside className=\"hidden h-full min-h-0 w-[300px] shrink-0 border-r border-border md:block\">\n <RegistryBrowser projectId={projectId} activeComponentId={componentId ?? undefined} />\n </aside>\n <div className=\"min-h-0 flex-1 overflow-auto\">\n {componentId === null ? (\n <ComponentsIndex projectId={projectId} />\n ) : (\n <ComponentDetail projectId={projectId} componentId={componentId} />\n )}\n </div>\n </div>\n )\n}\n\n/**\n * CMS section: hydrates the persisted (or kit-seeded) collections + documents,\n * then renders the collections sidebar beside the active screen (index, a\n * collection table, or a document editor). Forced states (?state=) replace the\n * whole surface before the provider mounts.\n */\nfunction CmsScreen({\n project,\n collectionId,\n documentId,\n state,\n field,\n}: {\n project: Project\n collectionId: string | null\n documentId: string | null\n state?: SectionState\n field?: string\n}) {\n const projectId = project.id\n if (state && state !== 'working') {\n return <SectionStateView state={state} title=\"CMS\" />\n }\n\n // A project created from a starter kit (EC-119) opens the CMS on the kit's schemas + content.\n const kit = getInstantiatedKit(projectId)\n\n return (\n // EC-177 gap 1: in Convex mode every CMS mount loads the CURRENT persisted\n // schema + documents (fixtures only when nothing is persisted — first run).\n // Playground renders synchronously with `null` → fixture seeds, as before.\n <CmsSessionGate>\n {(cms) => (\n <CmsProvider\n key={projectId}\n projectId={projectId}\n initialCollections={cms?.collections ?? kit?.collections}\n initialDocuments={cms?.documents ?? kit?.documents}\n initialPublishedKeys={cms?.publishedKeys}\n initialLocaleConfig={\n cms\n ? // Hydrated sessions follow the project's persisted locale settings.\n { default: project.settings.defaultLocale, locales: [...project.settings.locales] }\n : kit?.localeConfig\n }\n >\n <div className=\"flex h-full min-h-0 flex-col\">\n <CmsPersistErrorBanner />\n <div className=\"flex min-h-0 flex-1\">\n <aside className=\"hidden h-full min-h-0 w-[240px] shrink-0 border-r border-border md:block\">\n <CollectionsSidebar\n projectId={projectId}\n activeCollectionId={collectionId ?? undefined}\n />\n </aside>\n <div className=\"min-h-0 flex-1 overflow-hidden\">\n <CmsContent\n projectId={projectId}\n collectionId={collectionId}\n documentId={documentId}\n field={field}\n />\n </div>\n </div>\n </div>\n </CmsProvider>\n )}\n </CmsSessionGate>\n )\n}\n\n/** Resolves the active CMS screen inside the provider (needs the live store). */\nfunction CmsContent({\n projectId,\n collectionId,\n documentId,\n field,\n}: {\n projectId: string\n collectionId: string | null\n documentId: string | null\n field?: string\n}) {\n const cms = useCms()\n\n if (collectionId === null) return <CmsIndex projectId={projectId} />\n\n if (documentId === null) {\n const collection = cms.getCollection(collectionId)\n // EC-274: a singleton is a UI lens — at the collection URL it opens its one\n // document directly *while in its expected shape*, but degrades to the list\n // (with a \"for review\" lane) on drift. SingletonGate decides per the data.\n if (collection && isSingleton(collection)) {\n return <SingletonGate projectId={projectId} collectionId={collectionId} />\n }\n return <CollectionTable projectId={projectId} collectionId={collectionId} />\n }\n\n return (\n <DetailEditor\n projectId={projectId}\n collectionId={collectionId}\n documentId={documentId}\n focusPath={field}\n />\n )\n}\n\n/**\n * EC-274 — the singleton lens gate. At a singleton collection's URL, resolve its\n * lone document and open it directly; on drift (> 1 document) fall back to the\n * list with the \"for review\" lane so an editor can resolve the conflict. The one\n * document keeps a normal id — nothing here names a literal document id.\n */\nfunction SingletonGate({ projectId, collectionId }: { projectId: string; collectionId: string }) {\n const cms = useCms()\n const collection = cms.getCollection(collectionId)\n const docs = cms.documentsOf(collectionId)\n const resolution = collection\n ? resolveSingletonDocument(collection, docs)\n : ({ kind: 'empty' } as const)\n\n // Materialize the lone document on first visit (an empty draft, persisted like\n // any create). Idempotent in the store; only fires while genuinely empty.\n useEffect(() => {\n if (resolution.kind === 'empty') cms.ensureSingletonDocument(collectionId)\n }, [resolution.kind, cms, collectionId])\n\n if (resolution.kind === 'ready') {\n return (\n <DetailEditor projectId={projectId} collectionId={collectionId} documentId={resolution.doc.id} />\n )\n }\n if (resolution.kind === 'drift') {\n return <CollectionTable projectId={projectId} collectionId={collectionId} />\n }\n return (\n <div className=\"grid h-full place-items-center p-8\">\n <Text size=\"sm\" tone=\"muted\">\n Opening {collection?.form?.label ?? collectionId}…\n </Text>\n </div>\n )\n}\n\n/** Failed/denied persisted CMS mutations are always visible (EC-141). */\nfunction CmsPersistErrorBanner() {\n const cms = useCms()\n if (!cms.persistError) return null\n return (\n <div className=\"shrink-0 border-b border-border p-2\">\n <OperationErrorBanner error={cms.persistError} onDismiss={cms.clearPersistError} />\n </div>\n )\n}\n\nfunction ProjectNotFound() {\n return (\n <div className=\"grid min-h-dvh place-items-center p-8\">\n <Stack gap=\"3\" align=\"center\" className=\"max-w-sm text-center\">\n <Heading size=\"display\">Project not found</Heading>\n <Text tone=\"muted\">\n This project doesn’t exist or was deleted. It may only have existed in this session.\n </Text>\n <Button asChild>\n <Link to=\"/\">Back to projects</Link>\n </Button>\n </Stack>\n </div>\n )\n}\n\nfunction StudioRouteNotFound() {\n return (\n <div className=\"grid h-full place-items-center p-8\">\n <Stack gap=\"2\" align=\"center\" className=\"max-w-sm text-center\">\n <Heading level={2} size=\"h2\">\n Page not found\n </Heading>\n <Text tone=\"muted\">This studio URL doesn’t resolve to a section.</Text>\n </Stack>\n </div>\n )\n}\n"]}
@@ -12,7 +12,3 @@ export interface StudioShellProps {
12
12
  * overflow-safe.
13
13
  */
14
14
  export declare function StudioShell({ project, children }: StudioShellProps): import("react").JSX.Element;
15
- /** Small inline helper used by section surfaces that want a consistent empty hint. */
16
- export declare function SurfaceHint({ children }: {
17
- children: ReactNode;
18
- }): import("react").JSX.Element;
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Link, STUDIO_PROJECT_ROUTE, useLocation } from '@elytracms/studio/router';
3
3
  import { ChevronRight } from 'lucide-react';
4
4
  import { useCallback, useEffect, useState } from 'react';
5
- import { Row, Spacer, Text, Toaster } from '@elytracms/studio/ui';
5
+ import { Row, Spacer, Toaster } from '@elytracms/studio/ui';
6
6
  import { readRailCollapsed, storeRailCollapsed } from '@elytracms/studio/lib/rail';
7
7
  import { CurrentUserMenu } from './current-user-menu';
8
8
  import { DeliveryHealthBanner } from './delivery-health-banner';
@@ -58,8 +58,4 @@ function HeaderBreadcrumb({ projectId, activeSection, }) {
58
58
  _jsx(Link, { to: item.to, params: item.params, className: CRUMB_LINK, children: item.label })) : (_jsx("span", { className: isLast ? CRUMB_CURRENT : CRUMB_LINK, children: item.label }))] }, `${item.label}-${index}`));
59
59
  })] }));
60
60
  }
61
- /** Small inline helper used by section surfaces that want a consistent empty hint. */
62
- export function SurfaceHint({ children }) {
63
- return (_jsx(Text, { size: "sm", tone: "muted", className: "px-1", children: children }));
64
- }
65
61
  //# sourceMappingURL=studio-shell.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"studio-shell.js","sourceRoot":"","sources":["../../src/components/studio-shell.tsx"],"names":[],"mappings":"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","sourcesContent":["import { Link, STUDIO_PROJECT_ROUTE, useLocation } from '@elytracms/studio/router'\nimport { ChevronRight } from 'lucide-react'\nimport { useCallback, useEffect, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport type { Project, ProjectId } from '@elytracms/core/studio-core'\nimport { Row, Spacer, Text, Toaster } from '@elytracms/studio/ui'\nimport { readRailCollapsed, storeRailCollapsed } from '@elytracms/studio/lib/rail'\nimport { CurrentUserMenu } from './current-user-menu'\nimport { DeliveryHealthBanner } from './delivery-health-banner'\nimport { PlaygroundBadge } from './playground-badge'\nimport { DevLoginBadge } from './dev-login-badge'\nimport { ProjectSwitcher } from './project-switcher'\nimport { SectionNav } from './section-nav'\nimport { activeSectionFromPath, sectionNavItem } from './sections'\nimport type { StudioNavId } from './sections'\nimport { BreadcrumbProvider, useBreadcrumbTrail } from './breadcrumb-context'\n\nexport interface StudioShellProps {\n project: Project\n children: ReactNode\n}\n\n/**\n * The Studio shell (brief §4.3, §8.1): a persistent top bar + section rail that\n * preserve the active project while the user moves between surfaces. The top bar\n * carries the single global breadcrumb reaching the active record (EC-214) — no\n * secondary band, no per-view title repeating the section. Responsive and\n * overflow-safe.\n */\nexport function StudioShell({ project, children }: StudioShellProps) {\n const { pathname } = useLocation()\n const activeSection = activeSectionFromPath(pathname)\n\n // User-toggled, persisted rail collapse (EC-214). Defaults to expanded; the\n // stored preference is read after mount (no flash script needed for a rail).\n const [railCollapsed, setRailCollapsed] = useState(false)\n useEffect(() => {\n setRailCollapsed(readRailCollapsed())\n }, [])\n const toggleRail = useCallback(() => {\n setRailCollapsed((prev) => {\n const next = !prev\n storeRailCollapsed(next)\n return next\n })\n }, [])\n\n return (\n <BreadcrumbProvider>\n <div className=\"flex h-dvh flex-col bg-background\">\n {/* Top bar — global controls + the one breadcrumb to the active record. */}\n <header className=\"flex h-12 shrink-0 items-center gap-2 border-b border-border px-3\">\n <Link to=\"/\" className=\"grid size-6 shrink-0 place-items-center\" aria-label=\"All projects\">\n <span className=\"size-5 rounded-[5px] bg-primary shadow-sm\" />\n </Link>\n <ProjectSwitcher activeProject={project} />\n <HeaderBreadcrumb projectId={project.id} activeSection={activeSection} />\n <Spacer />\n {/* EC-153: explicit playground-mode marker (renders nothing on Convex). */}\n <PlaygroundBadge />\n {/* Dev-only unattended-auth marker (renders nothing in prod / when unset). */}\n <DevLoginBadge />\n {/* EC-150: signed-in user + their role on this project + sign-out. */}\n <CurrentUserMenu projectId={project.id} />\n </header>\n\n {/* EC-268: warns when instant-publish env is incomplete (connected mode only). */}\n <DeliveryHealthBanner />\n\n <div className=\"flex min-h-0 flex-1\">\n <SectionNav\n projectId={project.id}\n activeSection={activeSection}\n availability={project.settings.sectionAvailability}\n collapsed={railCollapsed}\n onToggleCollapsed={toggleRail}\n />\n\n <main className=\"flex min-w-0 flex-1 flex-col overflow-hidden\">\n <div className=\"min-h-0 flex-1 overflow-auto\">{children}</div>\n </main>\n </div>\n </div>\n {/* EC-268: transient action feedback (e.g. a publish that fails at transport). */}\n <Toaster />\n </BreadcrumbProvider>\n )\n}\n\nconst CRUMB_LINK =\n 'max-w-[12rem] truncate text-muted-foreground transition-colors hover:text-foreground'\nconst CRUMB_CURRENT = 'max-w-[16rem] truncate font-medium text-foreground'\n\n/**\n * The single global breadcrumb (EC-214): project (the switcher) › section › …the\n * active record. The section crumb is shell-owned; deeper crumbs are contributed\n * by the active surface via {@link useBreadcrumbTrail}. The last crumb is the\n * current page (plain text); earlier crumbs link back.\n */\nfunction HeaderBreadcrumb({\n projectId,\n activeSection,\n}: {\n projectId: ProjectId\n activeSection: StudioNavId | null\n}) {\n const extra = useBreadcrumbTrail()\n if (!activeSection) return null\n const sectionIsLast = extra.length === 0\n\n return (\n <Row gap=\"1\" className=\"min-w-0 text-sm\" aria-label=\"Breadcrumb\">\n <ChevronRight className=\"size-3.5 shrink-0 text-muted-foreground/50\" aria-hidden />\n {sectionIsLast ? (\n <span className={CRUMB_CURRENT}>{sectionNavItem(activeSection).label}</span>\n ) : (\n <Link\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId, _splat: activeSection }}\n className={CRUMB_LINK}\n >\n {sectionNavItem(activeSection).label}\n </Link>\n )}\n {extra.map((item, index) => {\n const isLast = index === extra.length - 1\n return (\n <Row gap=\"1\" key={`${item.label}-${index}`} className=\"min-w-0\">\n <ChevronRight className=\"size-3.5 shrink-0 text-muted-foreground/50\" aria-hidden />\n {!isLast && item.to ? (\n // `to` and `params` are always contributed as a unit (the type\n // pairs them); the assertion satisfies the route's required params.\n <Link to={item.to} params={item.params!} className={CRUMB_LINK}>\n {item.label}\n </Link>\n ) : (\n <span className={isLast ? CRUMB_CURRENT : CRUMB_LINK}>{item.label}</span>\n )}\n </Row>\n )\n })}\n </Row>\n )\n}\n\n/** Small inline helper used by section surfaces that want a consistent empty hint. */\nexport function SurfaceHint({ children }: { children: ReactNode }) {\n return (\n <Text size=\"sm\" tone=\"muted\" className=\"px-1\">\n {children}\n </Text>\n )\n}\n"]}
1
+ {"version":3,"file":"studio-shell.js","sourceRoot":"","sources":["../../src/components/studio-shell.tsx"],"names":[],"mappings":"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","sourcesContent":["import { Link, STUDIO_PROJECT_ROUTE, useLocation } from '@elytracms/studio/router'\nimport { ChevronRight } from 'lucide-react'\nimport { useCallback, useEffect, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport type { Project, ProjectId } from '@elytracms/core/studio-core'\nimport { Row, Spacer, Toaster } from '@elytracms/studio/ui'\nimport { readRailCollapsed, storeRailCollapsed } from '@elytracms/studio/lib/rail'\nimport { CurrentUserMenu } from './current-user-menu'\nimport { DeliveryHealthBanner } from './delivery-health-banner'\nimport { PlaygroundBadge } from './playground-badge'\nimport { DevLoginBadge } from './dev-login-badge'\nimport { ProjectSwitcher } from './project-switcher'\nimport { SectionNav } from './section-nav'\nimport { activeSectionFromPath, sectionNavItem } from './sections'\nimport type { StudioNavId } from './sections'\nimport { BreadcrumbProvider, useBreadcrumbTrail } from './breadcrumb-context'\n\nexport interface StudioShellProps {\n project: Project\n children: ReactNode\n}\n\n/**\n * The Studio shell (brief §4.3, §8.1): a persistent top bar + section rail that\n * preserve the active project while the user moves between surfaces. The top bar\n * carries the single global breadcrumb reaching the active record (EC-214) — no\n * secondary band, no per-view title repeating the section. Responsive and\n * overflow-safe.\n */\nexport function StudioShell({ project, children }: StudioShellProps) {\n const { pathname } = useLocation()\n const activeSection = activeSectionFromPath(pathname)\n\n // User-toggled, persisted rail collapse (EC-214). Defaults to expanded; the\n // stored preference is read after mount (no flash script needed for a rail).\n const [railCollapsed, setRailCollapsed] = useState(false)\n useEffect(() => {\n setRailCollapsed(readRailCollapsed())\n }, [])\n const toggleRail = useCallback(() => {\n setRailCollapsed((prev) => {\n const next = !prev\n storeRailCollapsed(next)\n return next\n })\n }, [])\n\n return (\n <BreadcrumbProvider>\n <div className=\"flex h-dvh flex-col bg-background\">\n {/* Top bar — global controls + the one breadcrumb to the active record. */}\n <header className=\"flex h-12 shrink-0 items-center gap-2 border-b border-border px-3\">\n <Link to=\"/\" className=\"grid size-6 shrink-0 place-items-center\" aria-label=\"All projects\">\n <span className=\"size-5 rounded-[5px] bg-primary shadow-sm\" />\n </Link>\n <ProjectSwitcher activeProject={project} />\n <HeaderBreadcrumb projectId={project.id} activeSection={activeSection} />\n <Spacer />\n {/* EC-153: explicit playground-mode marker (renders nothing on Convex). */}\n <PlaygroundBadge />\n {/* Dev-only unattended-auth marker (renders nothing in prod / when unset). */}\n <DevLoginBadge />\n {/* EC-150: signed-in user + their role on this project + sign-out. */}\n <CurrentUserMenu projectId={project.id} />\n </header>\n\n {/* EC-268: warns when instant-publish env is incomplete (connected mode only). */}\n <DeliveryHealthBanner />\n\n <div className=\"flex min-h-0 flex-1\">\n <SectionNav\n projectId={project.id}\n activeSection={activeSection}\n availability={project.settings.sectionAvailability}\n collapsed={railCollapsed}\n onToggleCollapsed={toggleRail}\n />\n\n <main className=\"flex min-w-0 flex-1 flex-col overflow-hidden\">\n <div className=\"min-h-0 flex-1 overflow-auto\">{children}</div>\n </main>\n </div>\n </div>\n {/* EC-268: transient action feedback (e.g. a publish that fails at transport). */}\n <Toaster />\n </BreadcrumbProvider>\n )\n}\n\nconst CRUMB_LINK =\n 'max-w-[12rem] truncate text-muted-foreground transition-colors hover:text-foreground'\nconst CRUMB_CURRENT = 'max-w-[16rem] truncate font-medium text-foreground'\n\n/**\n * The single global breadcrumb (EC-214): project (the switcher) › section › …the\n * active record. The section crumb is shell-owned; deeper crumbs are contributed\n * by the active surface via {@link useBreadcrumbTrail}. The last crumb is the\n * current page (plain text); earlier crumbs link back.\n */\nfunction HeaderBreadcrumb({\n projectId,\n activeSection,\n}: {\n projectId: ProjectId\n activeSection: StudioNavId | null\n}) {\n const extra = useBreadcrumbTrail()\n if (!activeSection) return null\n const sectionIsLast = extra.length === 0\n\n return (\n <Row gap=\"1\" className=\"min-w-0 text-sm\" aria-label=\"Breadcrumb\">\n <ChevronRight className=\"size-3.5 shrink-0 text-muted-foreground/50\" aria-hidden />\n {sectionIsLast ? (\n <span className={CRUMB_CURRENT}>{sectionNavItem(activeSection).label}</span>\n ) : (\n <Link\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId, _splat: activeSection }}\n className={CRUMB_LINK}\n >\n {sectionNavItem(activeSection).label}\n </Link>\n )}\n {extra.map((item, index) => {\n const isLast = index === extra.length - 1\n return (\n <Row gap=\"1\" key={`${item.label}-${index}`} className=\"min-w-0\">\n <ChevronRight className=\"size-3.5 shrink-0 text-muted-foreground/50\" aria-hidden />\n {!isLast && item.to ? (\n // `to` and `params` are always contributed as a unit (the type\n // pairs them); the assertion satisfies the route's required params.\n <Link to={item.to} params={item.params!} className={CRUMB_LINK}>\n {item.label}\n </Link>\n ) : (\n <span className={isLast ? CRUMB_CURRENT : CRUMB_LINK}>{item.label}</span>\n )}\n </Row>\n )\n })}\n </Row>\n )\n}\n"]}
@@ -1,4 +1,4 @@
1
- import type { ComponentNode, Condition, InsertTarget, ProjectGraph, PropValue } from '@elytracms/core/project-graph';
1
+ import type { ComponentNode, InsertTarget, ProjectGraph, PropValue } from '@elytracms/core/project-graph';
2
2
  import type { WorkingGraph, WorkingPageSeo } from '../working-graph';
3
3
  import type { OperationResult, OperationsClient } from './operations-package';
4
4
  /**
@@ -79,10 +79,6 @@ export type BuilderAction =
79
79
  type: 'removeProp';
80
80
  id: string;
81
81
  name: string;
82
- } | {
83
- type: 'setCondition';
84
- id: string;
85
- condition: Condition | undefined;
86
82
  }
87
83
  /**
88
84
  * EC-171 legacy page SEO (in-memory only): set a working page's `seo` block.
@@ -114,7 +110,7 @@ export interface GraphInvocation {
114
110
  * After EC-187 only the whole-graph operations survive: `loadGraph`/`undo`/
115
111
  * `redo` persist a layouts-only `ProjectGraph` via `graph.save`. Every
116
112
  * page-tree edit (insert/remove/duplicate/move/reorder/wrap/setProp/
117
- * removeProp/setCondition) returns `null` — page-node graph commands are gone;
113
+ * removeProp) returns `null` — page-node graph commands are gone;
118
114
  * those edits stay in-memory until the page is saved as a CMS document.
119
115
  */
120
116
  export declare function graphInvocationFor(projectId: string, action: BuilderAction, prev: BuilderSnapshot, next: {
@@ -5,7 +5,7 @@ import { toProjectGraph } from '../working-graph';
5
5
  * After EC-187 only the whole-graph operations survive: `loadGraph`/`undo`/
6
6
  * `redo` persist a layouts-only `ProjectGraph` via `graph.save`. Every
7
7
  * page-tree edit (insert/remove/duplicate/move/reorder/wrap/setProp/
8
- * removeProp/setCondition) returns `null` — page-node graph commands are gone;
8
+ * removeProp) returns `null` — page-node graph commands are gone;
9
9
  * those edits stay in-memory until the page is saved as a CMS document.
10
10
  */
11
11
  export function graphInvocationFor(projectId, action, prev, next) {
@@ -43,7 +43,6 @@ export function graphInvocationFor(projectId, action, prev, next) {
43
43
  case 'wrap':
44
44
  case 'setProp':
45
45
  case 'removeProp':
46
- case 'setCondition':
47
46
  case 'setPageSeo':
48
47
  return null;
49
48
  default:
@@ -1 +1 @@
1
- {"version":3,"file":"graph-commands.js","sourceRoot":"","sources":["../../../src/lib/operations/graph-commands.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAoEjD;;;;;;;;GAQG;AACH,MAAM,UAAU,kBAAkB,CAChC,SAAiB,EACjB,MAAqB,EACrB,IAAqB,EACrB,IAA+B;IAE/B,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;QACpB,KAAK,MAAM,CAAC;QACZ,KAAK,MAAM,CAAC,CAAC,CAAC;YACZ,IAAI,IAAI,CAAC,OAAO,KAAK,IAAI,CAAC,OAAO;gBAAE,OAAO,IAAI,CAAA;YAC9C,OAAO;gBACL,SAAS,EAAE,YAAY;gBACvB,KAAK,EAAE;oBACL,SAAS;oBACT,wDAAwD;oBACxD,KAAK,EAAE,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC;oBACnC,OAAO,EAAE,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM;iBAClD;aACF,CAAA;QACH,CAAC;QAED,KAAK,WAAW;YACd,OAAO;gBACL,SAAS,EAAE,YAAY;gBACvB,uEAAuE;gBACvE,KAAK,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE;aACjE,CAAA;QAEH,uEAAuE;QACvE,oEAAoE;QACpE,KAAK,QAAQ,CAAC;QACd,KAAK,OAAO,CAAC;QACb,KAAK,UAAU,CAAC;QAChB,KAAK,QAAQ,CAAC;QACd,KAAK,QAAQ,CAAC;QACd,KAAK,WAAW,CAAC;QACjB,KAAK,SAAS,CAAC;QACf,KAAK,MAAM,CAAC;QACZ,KAAK,MAAM,CAAC;QACZ,KAAK,SAAS,CAAC;QACf,KAAK,YAAY,CAAC;QAClB,KAAK,cAAc,CAAC;QACpB,KAAK,YAAY;YACf,OAAO,IAAI,CAAA;QAEb;YACE,OAAO,IAAI,CAAA;IACf,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,UAA4B,EAC5B,SAAiB,EACjB,KAAkC;IAElC,mEAAmE;IACnE,kEAAkE;IAClE,MAAM,SAAS,GAAG,OAAO,IAAI,KAAK,CAAC,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAA;IAClE,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,CAAC,CAAA;IAC9D,IAAI,MAAM,CAAC,EAAE,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC;QAAE,OAAO,MAAM,CAAA;IAChG,OAAO,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,0BAA0B,EAAE,CAAC,CAAA;AACpG,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,UAA4B,EAC5B,SAAiB,EACjB,YAAyC;IAEzC,MAAM,QAAQ,GAAG,MAAM,mBAAmB,CAAC,UAAU,EAAE,SAAS,EAAE,YAAY,CAAC,CAAA;IAC/E,IAAI,CAAC,QAAQ,CAAC,EAAE;QAAE,OAAO,QAAQ,CAAA;IACjC,OAAO,UAAU,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,SAAS,EAAE,CAAC,CAAA;AACrD,CAAC;AAED,4DAA4D;AAC5D,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,UAA4B,EAC5B,SAAiB;IAEjB,OAAO,UAAU,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,CAAC,CAAA;AACvD,CAAC","sourcesContent":["import type {\n ComponentNode,\n Condition,\n InsertTarget,\n ProjectGraph,\n PropValue,\n} from '@elytracms/core/project-graph'\nimport { toProjectGraph } from '../working-graph'\nimport type { WorkingGraph, WorkingPageSeo } from '../working-graph'\nimport type { OperationResult, OperationsClient } from './operations-package'\n\n/**\n * Builder action → graph operation mapping (EC-141, reshaped by EC-187).\n *\n * The canonical `ProjectGraph` is now LAYOUTS-ONLY, and the page-node graph\n * commands (`graph.insertNode`/`removeNode`/`duplicateNode`/`moveNode`/\n * `reorderNode`/`setNodeProp`/`removeNodeProp`/`setNodeCondition`) were DELETED\n * from the operations layer — a page is a CMS document now, edited through the\n * composition-field path, not mirrored as node ops. So every page-tree edit\n * here returns `null`: the legacy `/builder` page editor is in-memory-only for\n * page edits (it is retired in a later slice).\n *\n * What still persists: `loadGraph` (restoring a saved revision), `undo`, and\n * `redo` each save the whole graph via `graph.save` — but a `graph.save`\n * persists a CANONICAL layouts-only `ProjectGraph`, so the WorkingGraph\n * `present` is converted via `toProjectGraph` before it is sent.\n */\n\n/** The builder store's action union (owned here so the mapping stays React-free). */\nexport type BuilderAction =\n /** `pageId` additionally switches the active page (reference deep links, EC-172). */\n | { type: 'select'; id: string | null; pageId?: string }\n | { type: 'hover'; id: string | null }\n | { type: 'viewport'; viewport: 'desktop' | 'tablet' | 'mobile' }\n | { type: 'undo' }\n | { type: 'redo' }\n | { type: 'insert'; target: InsertTarget; node: ComponentNode; select?: boolean }\n | { type: 'remove'; id: string }\n | { type: 'duplicate'; id: string }\n | { type: 'reorder'; id: string; delta: -1 | 1 }\n | { type: 'move'; id: string; target: InsertTarget }\n /**\n * EC-162 layout gesture: wrap the reference node and the dragged subject in\n * a two-column Grid container as ONE atomic structural edit (one undo step).\n */\n | {\n type: 'wrap'\n referenceId: string\n side: 'left' | 'right'\n subject: { kind: 'node'; nodeId: string } | { kind: 'component'; node: ComponentNode }\n }\n | { type: 'setProp'; id: string; name: string; value: PropValue; coalesceKey?: string }\n | { type: 'removeProp'; id: string; name: string }\n | { type: 'setCondition'; id: string; condition: Condition | undefined }\n /**\n * EC-171 legacy page SEO (in-memory only): set a working page's `seo` block.\n * Page SEO persists via the `page` CMS document now, never as a node op — so\n * this action does not mirror to a `graph.*` command.\n */\n | { type: 'setPageSeo'; pageId: string; seo: WorkingPageSeo | undefined }\n | { type: 'loadGraph'; graph: ProjectGraph }\n\n/** The slice of builder state the mapping needs (pre-dispatch snapshot). */\nexport interface BuilderSnapshot {\n present: WorkingGraph\n pageId: string\n nodeSeq: number\n}\n\n/** A graph operation invocation, dispatchable via `operations.execute`. */\nexport interface GraphInvocation {\n operation: string\n input: unknown\n}\n\n/**\n * Map a dispatched builder action onto the `graph.*` command that persists it.\n *\n * After EC-187 only the whole-graph operations survive: `loadGraph`/`undo`/\n * `redo` persist a layouts-only `ProjectGraph` via `graph.save`. Every\n * page-tree edit (insert/remove/duplicate/move/reorder/wrap/setProp/\n * removeProp/setCondition) returns `null` — page-node graph commands are gone;\n * those edits stay in-memory until the page is saved as a CMS document.\n */\nexport function graphInvocationFor(\n projectId: string,\n action: BuilderAction,\n prev: BuilderSnapshot,\n next: { present: WorkingGraph },\n): GraphInvocation | null {\n switch (action.type) {\n case 'undo':\n case 'redo': {\n if (prev.present === next.present) return null\n return {\n operation: 'graph.save',\n input: {\n projectId,\n // graph.save persists the canonical layouts-only graph.\n graph: toProjectGraph(next.present),\n message: action.type === 'undo' ? 'Undo' : 'Redo',\n },\n }\n }\n\n case 'loadGraph':\n return {\n operation: 'graph.save',\n // `loadGraph` carries a canonical (layouts-only) ProjectGraph restore.\n input: { projectId, graph: action.graph, message: 'Load graph' },\n }\n\n // Page-tree edits no longer mirror to node-level graph commands (those\n // operations were deleted) — the page editor is in-memory-only now.\n case 'select':\n case 'hover':\n case 'viewport':\n case 'insert':\n case 'remove':\n case 'duplicate':\n case 'reorder':\n case 'move':\n case 'wrap':\n case 'setProp':\n case 'removeProp':\n case 'setCondition':\n case 'setPageSeo':\n return null\n\n default:\n return null\n }\n}\n\n/**\n * Make the persisted latest revision equal `graph` (the session's working\n * graph) so node-level `graph.*` commands apply onto identical state. Saves a\n * baseline revision when the project has no revision yet or the persisted\n * latest diverged from the session (the builder session is the source of\n * truth for the draft timeline — see the builder-store design note).\n */\nexport async function ensureGraphBaseline(\n operations: OperationsClient,\n projectId: string,\n graph: ProjectGraph | WorkingGraph,\n): Promise<OperationResult<unknown>> {\n // A working graph (the live session) is persisted as the canonical\n // layouts-only graph; a canonical graph passes through unchanged.\n const canonical = 'pages' in graph ? toProjectGraph(graph) : graph\n const latest = await operations.graph.getLatest({ projectId })\n if (latest.ok && JSON.stringify(latest.value.graph) === JSON.stringify(canonical)) return latest\n return operations.graph.save({ projectId, graph: canonical, message: 'Builder session baseline' })\n}\n\n/**\n * Persist \"publish the session's latest state\" (EC-141): ensure the working\n * graph is the persisted latest revision (as the canonical layouts-only\n * graph), then pin it live via `publishing.publish`. Single-environment\n * (EC-179): a deployment IS one environment, so there is no target environment\n * and no promote.\n */\nexport async function persistPublish(\n operations: OperationsClient,\n projectId: string,\n workingGraph: ProjectGraph | WorkingGraph,\n): Promise<OperationResult<unknown>> {\n const baseline = await ensureGraphBaseline(operations, projectId, workingGraph)\n if (!baseline.ok) return baseline\n return operations.publishing.publish({ projectId })\n}\n\n/** Persist an unpublish: revert the deployment to draft. */\nexport async function persistUnpublish(\n operations: OperationsClient,\n projectId: string,\n): Promise<OperationResult<unknown>> {\n return operations.publishing.unpublish({ projectId })\n}\n"]}
1
+ {"version":3,"file":"graph-commands.js","sourceRoot":"","sources":["../../../src/lib/operations/graph-commands.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAmEjD;;;;;;;;GAQG;AACH,MAAM,UAAU,kBAAkB,CAChC,SAAiB,EACjB,MAAqB,EACrB,IAAqB,EACrB,IAA+B;IAE/B,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;QACpB,KAAK,MAAM,CAAC;QACZ,KAAK,MAAM,CAAC,CAAC,CAAC;YACZ,IAAI,IAAI,CAAC,OAAO,KAAK,IAAI,CAAC,OAAO;gBAAE,OAAO,IAAI,CAAA;YAC9C,OAAO;gBACL,SAAS,EAAE,YAAY;gBACvB,KAAK,EAAE;oBACL,SAAS;oBACT,wDAAwD;oBACxD,KAAK,EAAE,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC;oBACnC,OAAO,EAAE,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM;iBAClD;aACF,CAAA;QACH,CAAC;QAED,KAAK,WAAW;YACd,OAAO;gBACL,SAAS,EAAE,YAAY;gBACvB,uEAAuE;gBACvE,KAAK,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE;aACjE,CAAA;QAEH,uEAAuE;QACvE,oEAAoE;QACpE,KAAK,QAAQ,CAAC;QACd,KAAK,OAAO,CAAC;QACb,KAAK,UAAU,CAAC;QAChB,KAAK,QAAQ,CAAC;QACd,KAAK,QAAQ,CAAC;QACd,KAAK,WAAW,CAAC;QACjB,KAAK,SAAS,CAAC;QACf,KAAK,MAAM,CAAC;QACZ,KAAK,MAAM,CAAC;QACZ,KAAK,SAAS,CAAC;QACf,KAAK,YAAY,CAAC;QAClB,KAAK,YAAY;YACf,OAAO,IAAI,CAAA;QAEb;YACE,OAAO,IAAI,CAAA;IACf,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,UAA4B,EAC5B,SAAiB,EACjB,KAAkC;IAElC,mEAAmE;IACnE,kEAAkE;IAClE,MAAM,SAAS,GAAG,OAAO,IAAI,KAAK,CAAC,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAA;IAClE,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,CAAC,CAAA;IAC9D,IAAI,MAAM,CAAC,EAAE,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC;QAAE,OAAO,MAAM,CAAA;IAChG,OAAO,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,0BAA0B,EAAE,CAAC,CAAA;AACpG,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,UAA4B,EAC5B,SAAiB,EACjB,YAAyC;IAEzC,MAAM,QAAQ,GAAG,MAAM,mBAAmB,CAAC,UAAU,EAAE,SAAS,EAAE,YAAY,CAAC,CAAA;IAC/E,IAAI,CAAC,QAAQ,CAAC,EAAE;QAAE,OAAO,QAAQ,CAAA;IACjC,OAAO,UAAU,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,SAAS,EAAE,CAAC,CAAA;AACrD,CAAC;AAED,4DAA4D;AAC5D,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,UAA4B,EAC5B,SAAiB;IAEjB,OAAO,UAAU,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,CAAC,CAAA;AACvD,CAAC","sourcesContent":["import type {\n ComponentNode,\n InsertTarget,\n ProjectGraph,\n PropValue,\n} from '@elytracms/core/project-graph'\nimport { toProjectGraph } from '../working-graph'\nimport type { WorkingGraph, WorkingPageSeo } from '../working-graph'\nimport type { OperationResult, OperationsClient } from './operations-package'\n\n/**\n * Builder action → graph operation mapping (EC-141, reshaped by EC-187).\n *\n * The canonical `ProjectGraph` is now LAYOUTS-ONLY, and the page-node graph\n * commands (`graph.insertNode`/`removeNode`/`duplicateNode`/`moveNode`/\n * `reorderNode`/`setNodeProp`/`removeNodeProp`/`setNodeCondition`) were DELETED\n * from the operations layer — a page is a CMS document now, edited through the\n * composition-field path, not mirrored as node ops. So every page-tree edit\n * here returns `null`: the legacy `/builder` page editor is in-memory-only for\n * page edits (it is retired in a later slice).\n *\n * What still persists: `loadGraph` (restoring a saved revision), `undo`, and\n * `redo` each save the whole graph via `graph.save` — but a `graph.save`\n * persists a CANONICAL layouts-only `ProjectGraph`, so the WorkingGraph\n * `present` is converted via `toProjectGraph` before it is sent.\n */\n\n/** The builder store's action union (owned here so the mapping stays React-free). */\nexport type BuilderAction =\n /** `pageId` additionally switches the active page (reference deep links, EC-172). */\n | { type: 'select'; id: string | null; pageId?: string }\n | { type: 'hover'; id: string | null }\n | { type: 'viewport'; viewport: 'desktop' | 'tablet' | 'mobile' }\n | { type: 'undo' }\n | { type: 'redo' }\n | { type: 'insert'; target: InsertTarget; node: ComponentNode; select?: boolean }\n | { type: 'remove'; id: string }\n | { type: 'duplicate'; id: string }\n | { type: 'reorder'; id: string; delta: -1 | 1 }\n | { type: 'move'; id: string; target: InsertTarget }\n /**\n * EC-162 layout gesture: wrap the reference node and the dragged subject in\n * a two-column Grid container as ONE atomic structural edit (one undo step).\n */\n | {\n type: 'wrap'\n referenceId: string\n side: 'left' | 'right'\n subject: { kind: 'node'; nodeId: string } | { kind: 'component'; node: ComponentNode }\n }\n | { type: 'setProp'; id: string; name: string; value: PropValue; coalesceKey?: string }\n | { type: 'removeProp'; id: string; name: string }\n /**\n * EC-171 legacy page SEO (in-memory only): set a working page's `seo` block.\n * Page SEO persists via the `page` CMS document now, never as a node op — so\n * this action does not mirror to a `graph.*` command.\n */\n | { type: 'setPageSeo'; pageId: string; seo: WorkingPageSeo | undefined }\n | { type: 'loadGraph'; graph: ProjectGraph }\n\n/** The slice of builder state the mapping needs (pre-dispatch snapshot). */\nexport interface BuilderSnapshot {\n present: WorkingGraph\n pageId: string\n nodeSeq: number\n}\n\n/** A graph operation invocation, dispatchable via `operations.execute`. */\nexport interface GraphInvocation {\n operation: string\n input: unknown\n}\n\n/**\n * Map a dispatched builder action onto the `graph.*` command that persists it.\n *\n * After EC-187 only the whole-graph operations survive: `loadGraph`/`undo`/\n * `redo` persist a layouts-only `ProjectGraph` via `graph.save`. Every\n * page-tree edit (insert/remove/duplicate/move/reorder/wrap/setProp/\n * removeProp) returns `null` — page-node graph commands are gone;\n * those edits stay in-memory until the page is saved as a CMS document.\n */\nexport function graphInvocationFor(\n projectId: string,\n action: BuilderAction,\n prev: BuilderSnapshot,\n next: { present: WorkingGraph },\n): GraphInvocation | null {\n switch (action.type) {\n case 'undo':\n case 'redo': {\n if (prev.present === next.present) return null\n return {\n operation: 'graph.save',\n input: {\n projectId,\n // graph.save persists the canonical layouts-only graph.\n graph: toProjectGraph(next.present),\n message: action.type === 'undo' ? 'Undo' : 'Redo',\n },\n }\n }\n\n case 'loadGraph':\n return {\n operation: 'graph.save',\n // `loadGraph` carries a canonical (layouts-only) ProjectGraph restore.\n input: { projectId, graph: action.graph, message: 'Load graph' },\n }\n\n // Page-tree edits no longer mirror to node-level graph commands (those\n // operations were deleted) — the page editor is in-memory-only now.\n case 'select':\n case 'hover':\n case 'viewport':\n case 'insert':\n case 'remove':\n case 'duplicate':\n case 'reorder':\n case 'move':\n case 'wrap':\n case 'setProp':\n case 'removeProp':\n case 'setPageSeo':\n return null\n\n default:\n return null\n }\n}\n\n/**\n * Make the persisted latest revision equal `graph` (the session's working\n * graph) so node-level `graph.*` commands apply onto identical state. Saves a\n * baseline revision when the project has no revision yet or the persisted\n * latest diverged from the session (the builder session is the source of\n * truth for the draft timeline — see the builder-store design note).\n */\nexport async function ensureGraphBaseline(\n operations: OperationsClient,\n projectId: string,\n graph: ProjectGraph | WorkingGraph,\n): Promise<OperationResult<unknown>> {\n // A working graph (the live session) is persisted as the canonical\n // layouts-only graph; a canonical graph passes through unchanged.\n const canonical = 'pages' in graph ? toProjectGraph(graph) : graph\n const latest = await operations.graph.getLatest({ projectId })\n if (latest.ok && JSON.stringify(latest.value.graph) === JSON.stringify(canonical)) return latest\n return operations.graph.save({ projectId, graph: canonical, message: 'Builder session baseline' })\n}\n\n/**\n * Persist \"publish the session's latest state\" (EC-141): ensure the working\n * graph is the persisted latest revision (as the canonical layouts-only\n * graph), then pin it live via `publishing.publish`. Single-environment\n * (EC-179): a deployment IS one environment, so there is no target environment\n * and no promote.\n */\nexport async function persistPublish(\n operations: OperationsClient,\n projectId: string,\n workingGraph: ProjectGraph | WorkingGraph,\n): Promise<OperationResult<unknown>> {\n const baseline = await ensureGraphBaseline(operations, projectId, workingGraph)\n if (!baseline.ok) return baseline\n return operations.publishing.publish({ projectId })\n}\n\n/** Persist an unpublish: revert the deployment to draft. */\nexport async function persistUnpublish(\n operations: OperationsClient,\n projectId: string,\n): Promise<OperationResult<unknown>> {\n return operations.publishing.unpublish({ projectId })\n}\n"]}
@@ -6,8 +6,6 @@
6
6
  export * from './operations-package';
7
7
  export * from './studio-operations';
8
8
  export * from './persist-queue';
9
- export * from './graph-commands';
10
- export * from './graph-persistence';
11
9
  export * from './cms-persistence';
12
10
  export * from './asset-repository';
13
11
  export * from './session-hydration';
@@ -6,8 +6,6 @@
6
6
  export * from './operations-package';
7
7
  export * from './studio-operations';
8
8
  export * from './persist-queue';
9
- export * from './graph-commands';
10
- export * from './graph-persistence';
11
9
  export * from './cms-persistence';
12
10
  export * from './asset-repository';
13
11
  export * from './session-hydration';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/operations/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,cAAc,sBAAsB,CAAA;AACpC,cAAc,qBAAqB,CAAA;AACnC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,kBAAkB,CAAA;AAChC,cAAc,qBAAqB,CAAA;AACnC,cAAc,mBAAmB,CAAA;AACjC,cAAc,oBAAoB,CAAA;AAClC,cAAc,qBAAqB,CAAA","sourcesContent":["/**\n * Studio operations layer (EC-141). The single seam every studio mutation\n * goes through — see `studio-operations.ts` for the bootstrap and\n * `operations-package.ts` for why the package is bridged by path.\n */\nexport * from './operations-package'\nexport * from './studio-operations'\nexport * from './persist-queue'\nexport * from './graph-commands'\nexport * from './graph-persistence'\nexport * from './cms-persistence'\nexport * from './asset-repository'\nexport * from './session-hydration'\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/operations/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,cAAc,sBAAsB,CAAA;AACpC,cAAc,qBAAqB,CAAA;AACnC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,mBAAmB,CAAA;AACjC,cAAc,oBAAoB,CAAA;AAClC,cAAc,qBAAqB,CAAA","sourcesContent":["/**\n * Studio operations layer (EC-141). The single seam every studio mutation\n * goes through — see `studio-operations.ts` for the bootstrap and\n * `operations-package.ts` for why the package is bridged by path.\n */\nexport * from './operations-package'\nexport * from './studio-operations'\nexport * from './persist-queue'\nexport * from './cms-persistence'\nexport * from './asset-repository'\nexport * from './session-hydration'\n"]}
@@ -1,4 +1,3 @@
1
- import type { ProjectGraph } from '@elytracms/core/project-graph';
2
1
  import type { CmsDocument, CollectionDef } from '@elytracms/core/cms-core';
3
2
  import type { AssetRecord } from '@elytracms/core/persistence';
4
3
  import type { OperationResult, OperationsClient } from './operations-package';
@@ -6,12 +5,16 @@ import type { OperationResult, OperationsClient } from './operations-package';
6
5
  * Persisted-session hydration (Milestone B sign-off fix, EC-177 gap 1).
7
6
  *
8
7
  * Against a persistent backend (Convex mode) a studio session must BOOT from
9
- * what is persisted — the latest graph revision, the saved CMS schema +
10
- * documents, and the per-environment publishing state — and fall back to the
11
- * deterministic fixtures only when the project has nothing persisted yet
12
- * (first run). Before this, every session seeded from fixtures, so reloads
13
- * hid the editor's work and the next publish pinned fixture-derived state
14
- * over production.
8
+ * what is persisted — the saved CMS schema + documents (+ asset records) — and
9
+ * fall back to the deterministic fixtures only when the project has nothing
10
+ * persisted yet (first run). Before this, every session seeded from fixtures,
11
+ * so reloads hid the editor's work.
12
+ *
13
+ * EC-287: the DB-resident project graph and its publish path are retired —
14
+ * layouts/config/schema/components are CODE, the DB holds content only. A
15
+ * session no longer hydrates a graph revision or graph-publishing state; CMS
16
+ * content (with per-document `publishedVersion`, EC-224) is the only persisted
17
+ * surface a session boots from.
15
18
  *
16
19
  * This module is the pure load half: read-only `@elytracms/core/operations` queries
17
20
  * composed into one hydration result. `not-found` is a *normal* answer (the
@@ -48,25 +51,10 @@ export interface HydratedCms {
48
51
  */
49
52
  assets: AssetRecord[];
50
53
  }
51
- /** The deployment's persisted publish, with the pinned revision's graph snapshot. */
52
- export interface HydratedPublish {
53
- /** The pinned graph revision number. */
54
- revision: number;
55
- /** Serialized graph of the pinned revision (collab version snapshot shape). */
56
- serializedGraph: string;
57
- publishedAt?: string;
58
- }
59
54
  /** Everything a Convex-mode session boots from. `null` fields mean "nothing persisted" (first run). */
60
55
  export interface HydratedProjectSession {
61
- /** The latest persisted graph revision's graph, or `null` when no revision exists. */
62
- graph: ProjectGraph | null;
63
56
  /** Persisted CMS schema + documents, or `null` when no content schema was ever saved. */
64
57
  cms: HydratedCms | null;
65
- /**
66
- * The currently-published revision snapshot, or `null` when nothing is
67
- * published. Single-environment (EC-179): a deployment IS one environment.
68
- */
69
- publish: HydratedPublish | null;
70
58
  }
71
59
  /**
72
60
  * Load the persisted CMS content state. `null` when the project has no
@@ -78,7 +66,9 @@ export interface HydratedProjectSession {
78
66
  */
79
67
  export declare function loadHydratedCms(operations: OperationsClient, projectId: string): Promise<OperationResult<HydratedCms | null>>;
80
68
  /**
81
- * Load everything a project session hydrates from: latest graph, CMS content,
82
- * and the currently-published revision snapshot (single-environment, EC-179).
69
+ * Load everything a project session hydrates from. Post-EC-287 that is just the
70
+ * persisted CMS content (schema + documents + assets): the project graph and its
71
+ * publish path are gone, so there is no graph revision or graph-publishing state
72
+ * to load. Per-document `publishedVersion` (EC-224) is the only published surface.
83
73
  */
84
74
  export declare function loadProjectSession(operations: OperationsClient, projectId: string): Promise<OperationResult<HydratedProjectSession>>;
@@ -1,4 +1,3 @@
1
- import { serializeProjectGraph } from '@elytracms/core/project-graph';
2
1
  import { documentKey } from '@elytracms/core/cms-core';
3
2
  import { DATA_SOURCES_COLLECTION } from '../data/sources-store';
4
3
  import { VIEWS_COLLECTION } from '../cms-views/views-store';
@@ -6,12 +5,16 @@ import { VIEWS_COLLECTION } from '../cms-views/views-store';
6
5
  * Persisted-session hydration (Milestone B sign-off fix, EC-177 gap 1).
7
6
  *
8
7
  * Against a persistent backend (Convex mode) a studio session must BOOT from
9
- * what is persisted — the latest graph revision, the saved CMS schema +
10
- * documents, and the per-environment publishing state — and fall back to the
11
- * deterministic fixtures only when the project has nothing persisted yet
12
- * (first run). Before this, every session seeded from fixtures, so reloads
13
- * hid the editor's work and the next publish pinned fixture-derived state
14
- * over production.
8
+ * what is persisted — the saved CMS schema + documents (+ asset records) — and
9
+ * fall back to the deterministic fixtures only when the project has nothing
10
+ * persisted yet (first run). Before this, every session seeded from fixtures,
11
+ * so reloads hid the editor's work.
12
+ *
13
+ * EC-287: the DB-resident project graph and its publish path are retired —
14
+ * layouts/config/schema/components are CODE, the DB holds content only. A
15
+ * session no longer hydrates a graph revision or graph-publishing state; CMS
16
+ * content (with per-document `publishedVersion`, EC-224) is the only persisted
17
+ * surface a session boots from.
15
18
  *
16
19
  * This module is the pure load half: read-only `@elytracms/core/operations` queries
17
20
  * composed into one hydration result. `not-found` is a *normal* answer (the
@@ -76,37 +79,15 @@ export async function loadHydratedCms(operations, projectId) {
76
79
  };
77
80
  }
78
81
  /**
79
- * Load everything a project session hydrates from: latest graph, CMS content,
80
- * and the currently-published revision snapshot (single-environment, EC-179).
82
+ * Load everything a project session hydrates from. Post-EC-287 that is just the
83
+ * persisted CMS content (schema + documents + assets): the project graph and its
84
+ * publish path are gone, so there is no graph revision or graph-publishing state
85
+ * to load. Per-document `publishedVersion` (EC-224) is the only published surface.
81
86
  */
82
87
  export async function loadProjectSession(operations, projectId) {
83
- const latest = await operations.graph.getLatest({ projectId });
84
- if (!latest.ok && latest.error.code !== 'not-found')
85
- return latest;
86
- const graph = latest.ok ? latest.value.graph : null;
87
88
  const cms = await loadHydratedCms(operations, projectId);
88
89
  if (!cms.ok)
89
90
  return cms;
90
- const state = await operations.publishing.getState({ projectId });
91
- if (!state.ok)
92
- return state;
93
- let publish = null;
94
- if (state.value &&
95
- state.value.status === 'published' &&
96
- state.value.publishedRevision !== undefined) {
97
- const publishedRevision = state.value.publishedRevision;
98
- const revision = await operations.graph.getRevision({ projectId, revision: publishedRevision });
99
- if (!revision.ok && revision.error.code !== 'not-found')
100
- return revision;
101
- // A dangling pin (revision record gone) leaves `publish` null, not fatal.
102
- if (revision.ok) {
103
- publish = {
104
- revision: publishedRevision,
105
- serializedGraph: serializeProjectGraph(revision.value.graph),
106
- ...(state.value.publishedAt !== undefined ? { publishedAt: state.value.publishedAt } : {}),
107
- };
108
- }
109
- }
110
- return { ok: true, value: { graph, cms: cms.value, publish } };
91
+ return { ok: true, value: { cms: cms.value } };
111
92
  }
112
93
  //# sourceMappingURL=session-hydration.js.map