@elytracms/studio 0.0.14 → 0.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/convex/cms.ts +8 -31
- package/convex/content.ts +3 -44
- package/convex/delivery.ts +8 -42
- package/convex/functions.ts +17 -2
- package/convex/members.ts +2 -2
- package/convex/referenceMaintenance.ts +4 -73
- package/convex/references.ts +4 -2
- package/convex/schema.ts +17 -37
- package/convex/sync.ts +10 -42
- package/convex/triggers.ts +57 -0
- package/convex/webhooks.ts +3 -2
- package/dist/components/app-views/default-modules.d.ts +41 -0
- package/dist/components/app-views/default-modules.js +201 -0
- package/dist/components/app-views/default-modules.js.map +1 -0
- package/dist/components/app-views/module.d.ts +93 -0
- package/dist/components/app-views/module.js +75 -0
- package/dist/components/app-views/module.js.map +1 -0
- package/dist/components/app-views/sidebar.d.ts +95 -0
- package/dist/components/app-views/sidebar.js +153 -0
- package/dist/components/app-views/sidebar.js.map +1 -0
- package/dist/components/builder/builder-store.d.ts +92 -40
- package/dist/components/builder/builder-store.js +30 -126
- package/dist/components/builder/builder-store.js.map +1 -1
- package/dist/components/builder/canvas-breakpoints.d.ts +53 -0
- package/dist/components/builder/canvas-breakpoints.js +55 -0
- package/dist/components/builder/canvas-breakpoints.js.map +1 -0
- package/dist/components/builder/canvas.js +35 -61
- package/dist/components/builder/canvas.js.map +1 -1
- package/dist/components/builder/clipboard-actions.js +3 -3
- package/dist/components/builder/clipboard-actions.js.map +1 -1
- package/dist/components/builder/composition-field-editor.js +31 -40
- package/dist/components/builder/composition-field-editor.js.map +1 -1
- package/dist/components/builder/insert-context.js +1 -1
- package/dist/components/builder/insert-context.js.map +1 -1
- package/dist/components/builder/page-tree.js +3 -4
- package/dist/components/builder/page-tree.js.map +1 -1
- package/dist/components/builder/property-inspector.js +4 -64
- package/dist/components/builder/property-inspector.js.map +1 -1
- package/dist/components/cms/bulk-actions.d.ts +23 -5
- package/dist/components/cms/bulk-actions.js +7 -3
- package/dist/components/cms/bulk-actions.js.map +1 -1
- package/dist/components/cms/bulk-bar.d.ts +22 -6
- package/dist/components/cms/bulk-bar.js +46 -9
- package/dist/components/cms/bulk-bar.js.map +1 -1
- package/dist/components/cms/cell.d.ts +0 -4
- package/dist/components/cms/cell.js +1 -5
- package/dist/components/cms/cell.js.map +1 -1
- package/dist/components/cms/cms-index.d.ts +4 -2
- package/dist/components/cms/cms-index.js +6 -4
- package/dist/components/cms/cms-index.js.map +1 -1
- package/dist/components/cms/cms-store.d.ts +7 -6
- package/dist/components/cms/cms-store.js +55 -18
- package/dist/components/cms/cms-store.js.map +1 -1
- package/dist/components/cms/collection-table.js +8 -4
- package/dist/components/cms/collection-table.js.map +1 -1
- package/dist/components/cms/collections-sidebar.d.ts +10 -1
- package/dist/components/cms/collections-sidebar.js +13 -2
- package/dist/components/cms/collections-sidebar.js.map +1 -1
- package/dist/components/cms/detail-editor.js +2 -2
- package/dist/components/cms/detail-editor.js.map +1 -1
- package/dist/components/cms/doc-state.d.ts +0 -5
- package/dist/components/cms/doc-state.js +1 -7
- package/dist/components/cms/doc-state.js.map +1 -1
- package/dist/components/cms/editable-cell.js +1 -1
- package/dist/components/cms/editable-cell.js.map +1 -1
- package/dist/components/cms/field-control.d.ts +11 -2
- package/dist/components/cms/field-control.js +22 -1
- package/dist/components/cms/field-control.js.map +1 -1
- package/dist/components/cms/field-renderer.d.ts +71 -0
- package/dist/components/cms/field-renderer.js +67 -0
- package/dist/components/cms/field-renderer.js.map +1 -0
- package/dist/components/cms/filter-builder.js +6 -28
- package/dist/components/cms/filter-builder.js.map +1 -1
- package/dist/components/cms/scalar-value-editor.d.ts +35 -0
- package/dist/components/cms/scalar-value-editor.js +43 -0
- package/dist/components/cms/scalar-value-editor.js.map +1 -0
- package/dist/components/collab/collab-context.d.ts +6 -14
- package/dist/components/collab/collab-context.js +3 -12
- package/dist/components/collab/collab-context.js.map +1 -1
- package/dist/components/collab/collab-seed.d.ts +7 -19
- package/dist/components/collab/collab-seed.js +9 -31
- package/dist/components/collab/collab-seed.js.map +1 -1
- package/dist/components/components/registry-browser.js +6 -12
- package/dist/components/components/registry-browser.js.map +1 -1
- package/dist/components/guards.d.ts +0 -9
- package/dist/components/guards.js +0 -12
- package/dist/components/guards.js.map +1 -1
- package/dist/components/project-session.js +8 -3
- package/dist/components/project-session.js.map +1 -1
- package/dist/components/section-nav.d.ts +10 -10
- package/dist/components/section-nav.js +48 -25
- package/dist/components/section-nav.js.map +1 -1
- package/dist/components/section-state.d.ts +0 -2
- package/dist/components/section-state.js +0 -9
- package/dist/components/section-state.js.map +1 -1
- package/dist/components/section-surface.d.ts +3 -7
- package/dist/components/section-surface.js +6 -14
- package/dist/components/section-surface.js.map +1 -1
- package/dist/components/sections.d.ts +0 -1
- package/dist/components/sections.js +0 -10
- package/dist/components/sections.js.map +1 -1
- package/dist/components/studio-project-screen.d.ts +3 -4
- package/dist/components/studio-project-screen.js +41 -160
- package/dist/components/studio-project-screen.js.map +1 -1
- package/dist/components/studio-shell.d.ts +0 -4
- package/dist/components/studio-shell.js +14 -14
- package/dist/components/studio-shell.js.map +1 -1
- package/dist/lib/data/collection-content.d.ts +19 -1
- package/dist/lib/data/collection-content.js +16 -0
- package/dist/lib/data/collection-content.js.map +1 -1
- package/dist/lib/operations/graph-commands.d.ts +2 -6
- package/dist/lib/operations/graph-commands.js +1 -2
- package/dist/lib/operations/graph-commands.js.map +1 -1
- package/dist/lib/operations/index.d.ts +0 -2
- package/dist/lib/operations/index.js +0 -2
- package/dist/lib/operations/index.js.map +1 -1
- package/dist/lib/operations/session-hydration.d.ts +14 -24
- package/dist/lib/operations/session-hydration.js +15 -34
- package/dist/lib/operations/session-hydration.js.map +1 -1
- package/dist/lib/persistence/convex-adapter.d.ts +1 -3
- package/dist/lib/persistence/convex-adapter.js +1 -154
- package/dist/lib/persistence/convex-adapter.js.map +1 -1
- package/dist/lib/persona/labels.js +0 -2
- package/dist/lib/persona/labels.js.map +1 -1
- package/dist/lib/references/reference-model.d.ts +0 -16
- package/dist/lib/references/reference-model.js +2 -54
- package/dist/lib/references/reference-model.js.map +1 -1
- package/dist/lib/references/references-store.js +3 -4
- package/dist/lib/references/references-store.js.map +1 -1
- package/dist/lib/registry/index.d.ts +0 -16
- package/dist/lib/registry/index.js +0 -15
- package/dist/lib/registry/index.js.map +1 -1
- package/dist/lib/webhooks/tags.js +2 -16
- package/dist/lib/webhooks/tags.js.map +1 -1
- package/dist/lib/workspace/index.d.ts +27 -1
- package/dist/lib/workspace/index.js +34 -0
- package/dist/lib/workspace/index.js.map +1 -1
- package/dist/router/match.d.ts +22 -34
- package/dist/router/match.js +22 -40
- package/dist/router/match.js.map +1 -1
- package/package.json +3 -2
- package/convex/graphs.ts +0 -55
- package/convex/publishing.ts +0 -76
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"section-nav.js","sourceRoot":"","sources":["../../src/components/section-nav.tsx"],"names":[],"mappings":"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","sourcesContent":["import { Link, STUDIO_PROJECT_ROUTE, useLocation } from '@elytracms/studio/router'\nimport { PanelLeftClose, PanelLeftOpen } from 'lucide-react'\nimport type { ProjectId, SectionAvailabilityMap } from '@elytracms/core/studio-core'\nimport { cn } from '@elytracms/studio/lib/utils'\nimport { SECTION_NAV_ITEMS, activeSectionFromPath, navAvailabilityOf } from './sections'\nimport type { StudioNavId } from './sections'\n\nexport interface SectionNavProps {\n projectId: ProjectId\n activeSection: StudioNavId | null\n availability: SectionAvailabilityMap\n /** User-toggled collapse (EC-214) — icons only when collapsed, persisted by the shell. */\n collapsed: boolean\n onToggleCollapsed: () => void\n}\n\n/**\n * Vertical section rail (brief §8.1). Availability-aware: `available`/`planned`\n * are navigable; `unavailable` is rendered disabled. User-collapsible to an\n * icon rail (EC-214) — the preference persists, and the active section carries a\n * strong highlight (accent fill + a leading marker bar).\n */\nexport function SectionNav({\n projectId,\n activeSection,\n availability,\n collapsed,\n onToggleCollapsed,\n}: SectionNavProps) {\n // Recover the active id from the path if the shell didn't supply one — the\n // shared parser is the single source of truth for the section segment index.\n const { pathname } = useLocation()\n const active = activeSection ?? activeSectionFromPath(pathname)\n return (\n <nav\n aria-label=\"Sections\"\n className={cn(\n 'flex shrink-0 flex-col gap-0.5 border-r border-border bg-card/40 p-2 transition-[width] duration-150',\n collapsed ? 'w-[3.5rem]' : 'w-56',\n )}\n >\n {SECTION_NAV_ITEMS.map((item) => {\n const state = navAvailabilityOf(availability, item.id)\n const isActive = item.id === active\n const Icon = item.icon\n\n const body = (\n <>\n {/* Strong active marker: a leading accent bar (EC-214). */}\n {isActive ? (\n <span\n className=\"absolute inset-y-1.5 left-0 w-0.5 rounded-full bg-primary\"\n aria-hidden\n />\n ) : null}\n <Icon className=\"size-4 shrink-0\" />\n {collapsed ? null : (\n <span className=\"min-w-0 flex-1 truncate\">{item.label}</span>\n )}\n {state === 'planned' && !collapsed ? (\n <span\n className=\"size-1.5 shrink-0 rounded-full bg-warning\"\n aria-hidden\n title=\"Planned\"\n />\n ) : null}\n </>\n )\n\n const base = cn(\n 'group relative flex h-9 items-center gap-2.5 rounded-md text-sm transition-colors',\n collapsed ? 'justify-center px-0' : 'px-3',\n )\n\n // EC-221: an unavailable section is absent from the rail (not greyed) —\n // e.g. Extensions, cut from the v1 nav (vision §5). It stays reachable by\n // URL for Milestone E revival; the rail simply doesn't advertise it.\n if (state === 'unavailable') return null\n\n return (\n <Link\n key={item.id}\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId, _splat: item.id }}\n title={item.label}\n aria-current={isActive ? 'page' : undefined}\n className={cn(\n base,\n isActive\n ? 'bg-accent font-medium text-accent-foreground'\n : 'text-muted-foreground hover:bg-muted hover:text-foreground',\n )}\n >\n {body}\n </Link>\n )\n })}\n\n {/* Collapse toggle pinned to the rail foot (EC-214). */}\n <button\n type=\"button\"\n onClick={onToggleCollapsed}\n aria-label={collapsed ? 'Expand sidebar' : 'Collapse sidebar'}\n aria-pressed={collapsed}\n title={collapsed ? 'Expand sidebar' : 'Collapse sidebar'}\n className={cn(\n 'mt-auto flex h-9 items-center gap-2.5 rounded-md text-sm text-muted-foreground transition-colors hover:bg-muted hover:text-foreground',\n collapsed ? 'justify-center px-0' : 'px-3',\n )}\n >\n {collapsed ? (\n <PanelLeftOpen className=\"size-4 shrink-0\" />\n ) : (\n <>\n <PanelLeftClose className=\"size-4 shrink-0\" />\n <span className=\"min-w-0 flex-1 truncate text-left\">Collapse</span>\n </>\n )}\n </button>\n </nav>\n )\n}\n"]}
|
|
1
|
+
{"version":3,"file":"section-nav.js","sourceRoot":"","sources":["../../src/components/section-nav.tsx"],"names":[],"mappings":"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","sourcesContent":["import { Link, STUDIO_PROJECT_ROUTE, useLocation } from '@elytracms/studio/router'\nimport { AlertTriangle, PanelLeftClose, PanelLeftOpen } from 'lucide-react'\nimport type { Project } from '@elytracms/core/studio-core'\nimport type { CustomizationIssue } from '@elytracms/core/studio-core'\nimport { cn } from '@elytracms/studio/lib/utils'\nimport { Text } from '@elytracms/studio/ui'\nimport { useAppViews } from './app-views/default-modules'\nimport { activeModuleFromPath, useSidebar } from './app-views/sidebar'\nimport type { ResolvedSidebarItem } from './app-views/sidebar'\n\nexport interface SectionNavProps {\n project: Project\n /** User-toggled collapse (EC-214) — icons only when collapsed, persisted by the shell. */\n collapsed: boolean\n onToggleCollapsed: () => void\n}\n\n/**\n * Vertical section rail (brief §8.1), now driven by the configurable sidebar\n * (EC-237): a consumer-extendable tree of items / groups / separators resolved\n * over the app-view module registry (EC-239). Availability-aware (`unavailable`\n * items are absent per EC-221); items deep-link to a module + resource; the\n * preference-persisted collapse and the strong active marker (EC-214) are\n * preserved. Customization issues (duplicate ids, unknown modules, unresolvable\n * deep-links) render at the rail foot — visible, never silent.\n */\nexport function SectionNav({ project, collapsed, onToggleCollapsed }: SectionNavProps) {\n const { pathname } = useLocation()\n const active = activeModuleFromPath(pathname)\n const { nodes, issues: sidebarIssues } = useSidebar(project)\n const { issues: moduleIssues } = useAppViews()\n const issues = [...moduleIssues, ...sidebarIssues]\n const projectId = project.id\n\n return (\n <nav\n aria-label=\"Sections\"\n className={cn(\n 'flex shrink-0 flex-col gap-0.5 border-r border-border bg-card/40 p-2 transition-[width] duration-150',\n collapsed ? 'w-[3.5rem]' : 'w-56',\n )}\n >\n {nodes.map((node) => {\n if (node.kind === 'separator') {\n return <div key={node.id} className=\"my-1.5 border-t border-border/60\" aria-hidden />\n }\n if (node.kind === 'group') {\n const items = node.items.filter((item) => item.availability !== 'unavailable')\n if (items.length === 0) return null\n return (\n <div key={node.id} className=\"mt-2 first:mt-0\">\n {collapsed ? (\n <div className=\"my-1 border-t border-border/60\" aria-hidden />\n ) : (\n <Text\n size=\"xs\"\n tone=\"subtle\"\n className=\"px-3 pb-1 pt-1 font-medium uppercase tracking-wide\"\n >\n {node.label}\n </Text>\n )}\n {items.map((item) => (\n <NavItemLink\n key={item.id}\n projectId={projectId}\n item={item}\n active={active}\n collapsed={collapsed}\n nested\n />\n ))}\n </div>\n )\n }\n // EC-221: an unavailable item is absent from the rail (not greyed) — it\n // stays reachable by URL, the rail simply doesn't advertise it.\n if (node.availability === 'unavailable') return null\n return (\n <NavItemLink\n key={node.id}\n projectId={projectId}\n item={node}\n active={active}\n collapsed={collapsed}\n />\n )\n })}\n\n <div className=\"mt-auto flex flex-col gap-0.5\">\n {issues.length > 0 ? <NavIssues issues={issues} collapsed={collapsed} /> : null}\n\n {/* Collapse toggle pinned to the rail foot (EC-214). */}\n <button\n type=\"button\"\n onClick={onToggleCollapsed}\n aria-label={collapsed ? 'Expand sidebar' : 'Collapse sidebar'}\n aria-pressed={collapsed}\n title={collapsed ? 'Expand sidebar' : 'Collapse sidebar'}\n className={cn(\n 'flex h-9 items-center gap-2.5 rounded-md text-sm text-muted-foreground transition-colors hover:bg-muted hover:text-foreground',\n collapsed ? 'justify-center px-0' : 'px-3',\n )}\n >\n {collapsed ? (\n <PanelLeftOpen className=\"size-4 shrink-0\" />\n ) : (\n <>\n <PanelLeftClose className=\"size-4 shrink-0\" />\n <span className=\"min-w-0 flex-1 truncate text-left\">Collapse</span>\n </>\n )}\n </button>\n </div>\n </nav>\n )\n}\n\n/** One rail item (top-level or nested) — a deep-linkable Link to its module. */\nfunction NavItemLink({\n projectId,\n item,\n active,\n collapsed,\n nested = false,\n}: {\n projectId: string\n item: ResolvedSidebarItem\n active: string | null\n collapsed: boolean\n nested?: boolean\n}) {\n const isActive = item.moduleId === active\n const Icon = item.icon\n return (\n <Link\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId, _splat: item.path }}\n title={item.label}\n aria-current={isActive ? 'page' : undefined}\n className={cn(\n 'group relative flex h-9 items-center gap-2.5 rounded-md text-sm transition-colors',\n collapsed ? 'justify-center px-0' : nested ? 'pl-7 pr-3' : 'px-3',\n isActive\n ? 'bg-accent font-medium text-accent-foreground'\n : 'text-muted-foreground hover:bg-muted hover:text-foreground',\n )}\n >\n {/* Strong active marker: a leading accent bar (EC-214). */}\n {isActive ? (\n <span className=\"absolute inset-y-1.5 left-0 w-0.5 rounded-full bg-primary\" aria-hidden />\n ) : null}\n <Icon className=\"size-4 shrink-0\" />\n {collapsed ? null : <span className=\"min-w-0 flex-1 truncate\">{item.label}</span>}\n {item.availability === 'planned' && !collapsed ? (\n <span className=\"size-1.5 shrink-0 rounded-full bg-warning\" aria-hidden title=\"Planned\" />\n ) : null}\n </Link>\n )\n}\n\n/** Visible customization issues at the rail foot — the seam's \"never silent\" (EC-236). */\nfunction NavIssues({ issues, collapsed }: { issues: CustomizationIssue[]; collapsed: boolean }) {\n if (collapsed) {\n return (\n <div\n className=\"grid h-9 place-items-center text-amber-600 dark:text-amber-400\"\n title={issues.map((issue) => issue.message).join('\\n')}\n >\n <AlertTriangle className=\"size-4\" aria-label={`${issues.length} sidebar issues`} />\n </div>\n )\n }\n return (\n <div className=\"mb-1 flex flex-col gap-1 rounded-md border border-amber-500/30 bg-amber-500/5 p-2\">\n {issues.map((issue, index) => (\n <div\n key={`${issue.point}:${issue.id ?? index}`}\n className=\"flex items-start gap-1.5 text-amber-600 dark:text-amber-400\"\n title={issue.message}\n >\n <AlertTriangle className=\"mt-0.5 size-3.5 shrink-0\" aria-hidden />\n <Text size=\"xs\" className=\"min-w-0 leading-snug\">\n {issue.message}\n </Text>\n </div>\n ))}\n </div>\n )\n}\n"]}
|
|
@@ -20,5 +20,3 @@ export declare function SectionStateView({ state, title, description, action, cl
|
|
|
20
20
|
export declare function SectionSkeleton({ className }: {
|
|
21
21
|
className?: string;
|
|
22
22
|
}): import("react").JSX.Element;
|
|
23
|
-
/** The full set of forceable states, for fixtures and the state switcher. */
|
|
24
|
-
export declare const FORCEABLE_STATES: readonly SectionState[];
|
|
@@ -66,13 +66,4 @@ export function SectionStateView({ state, title, description, action, className,
|
|
|
66
66
|
export function SectionSkeleton({ className }) {
|
|
67
67
|
return (_jsxs("div", { "data-section-state": "loading", className: cn('space-y-4 p-6', className), "aria-busy": true, children: [_jsx("div", { className: "h-7 w-48 animate-pulse rounded-md bg-muted" }), _jsx("div", { className: "grid gap-3 sm:grid-cols-2 lg:grid-cols-3", children: Array.from({ length: 6 }).map((_, i) => (_jsx("div", { className: "h-28 animate-pulse rounded-xl bg-muted/70" }, i))) })] }));
|
|
68
68
|
}
|
|
69
|
-
/** The full set of forceable states, for fixtures and the state switcher. */
|
|
70
|
-
export const FORCEABLE_STATES = [
|
|
71
|
-
'working',
|
|
72
|
-
'empty',
|
|
73
|
-
'loading',
|
|
74
|
-
'error',
|
|
75
|
-
'unavailable',
|
|
76
|
-
'planned',
|
|
77
|
-
];
|
|
78
69
|
//# sourceMappingURL=section-state.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"section-state.js","sourceRoot":"","sources":["../../src/components/section-state.tsx"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"section-state.js","sourceRoot":"","sources":["../../src/components/section-state.tsx"],"names":[],"mappings":"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","sourcesContent":["import { CircleAlert, Clock, Inbox, Loader2, Lock, type LucideIcon } from 'lucide-react'\nimport type { ReactNode } from 'react'\nimport type { SectionAvailability, SectionState } from '@elytracms/core/studio-core'\nimport { Badge, Heading, Stack, Text } from '@elytracms/studio/ui'\nimport { cn } from '@elytracms/studio/lib/utils'\n\n/**\n * Resolve the runtime state of a section (brief §8.1, §11). A `forced` value\n * (e.g. from a `?state=` param) wins, making every state reachable\n * deterministically; otherwise availability decides.\n */\nexport function resolveSectionState(\n availability: SectionAvailability,\n forced?: SectionState | null,\n): SectionState {\n if (forced) return forced\n if (availability === 'planned') return 'planned'\n if (availability === 'unavailable') return 'unavailable'\n return 'working'\n}\n\ninterface StatePreset {\n icon: LucideIcon\n title: string\n description: string\n badge: { label: string; variant: 'outline' | 'warning' | 'error' | 'info' }\n spin?: boolean\n}\n\nconst PRESETS: Record<Exclude<SectionState, 'working'>, StatePreset> = {\n empty: {\n icon: Inbox,\n title: 'Nothing here yet',\n description: 'This section has no content for the active project.',\n badge: { label: 'empty', variant: 'outline' },\n },\n loading: {\n icon: Loader2,\n title: 'Loading…',\n description: 'Fetching the latest state for this section.',\n badge: { label: 'loading', variant: 'info' },\n spin: true,\n },\n error: {\n icon: CircleAlert,\n title: 'Something went wrong',\n description: 'This section could not be loaded. Try again in a moment.',\n badge: { label: 'error', variant: 'error' },\n },\n unavailable: {\n icon: Lock,\n title: 'Not available',\n description: 'This section is not available for the active project.',\n badge: { label: 'unavailable', variant: 'outline' },\n },\n planned: {\n icon: Clock,\n title: 'Planned',\n description: 'This surface is planned and not yet enabled for this project.',\n badge: { label: 'planned', variant: 'warning' },\n },\n}\n\nexport interface SectionStateViewProps {\n state: Exclude<SectionState, 'working'>\n /** Section-specific heading, e.g. \"CMS\" — overrides the generic preset title. */\n title?: string\n description?: ReactNode\n action?: ReactNode\n className?: string\n}\n\n/** Loading uses a skeleton; the rest use a centered placeholder. Consistent across sections. */\nexport function SectionStateView({\n state,\n title,\n description,\n action,\n className,\n}: SectionStateViewProps) {\n if (state === 'loading') return <SectionSkeleton className={className} />\n\n const preset = PRESETS[state]\n const Icon = preset.icon\n const tone =\n state === 'error'\n ? 'border-destructive/30 bg-destructive/5'\n : state === 'planned'\n ? 'border-warning/30 bg-warning/5'\n : 'border-border bg-card/40'\n\n return (\n <div\n data-section-state={state}\n className={cn('grid min-h-[18rem] place-items-center p-8', className)}\n >\n <Stack\n gap=\"3\"\n align=\"center\"\n className={cn('max-w-sm rounded-xl border border-dashed px-8 py-10 text-center', tone)}\n >\n <span className=\"grid size-11 place-items-center rounded-lg border border-border bg-background text-muted-foreground\">\n <Icon className=\"size-5\" />\n </span>\n <Stack gap=\"1\" align=\"center\">\n <Heading level={2} size=\"h3\">\n {title ?? preset.title}\n </Heading>\n <Text size=\"sm\" tone=\"muted\">\n {description ?? preset.description}\n </Text>\n </Stack>\n <Badge variant={preset.badge.variant}>{preset.badge.label}</Badge>\n {action ? <div className=\"pt-1\">{action}</div> : null}\n </Stack>\n </div>\n )\n}\n\n/** Shimmer skeleton standing in for an in-flight section load. */\nexport function SectionSkeleton({ className }: { className?: string }) {\n return (\n <div data-section-state=\"loading\" className={cn('space-y-4 p-6', className)} aria-busy>\n <div className=\"h-7 w-48 animate-pulse rounded-md bg-muted\" />\n <div className=\"grid gap-3 sm:grid-cols-2 lg:grid-cols-3\">\n {Array.from({ length: 6 }).map((_, i) => (\n <div key={i} className=\"h-28 animate-pulse rounded-xl bg-muted/70\" />\n ))}\n </div>\n </div>\n )\n}\n"]}
|
|
@@ -7,19 +7,15 @@ export interface SectionSurfaceProps {
|
|
|
7
7
|
section: SectionId;
|
|
8
8
|
/** Forced state from `?state=`; overrides availability. */
|
|
9
9
|
forcedState?: SectionState;
|
|
10
|
-
/** Working-state content.
|
|
10
|
+
/** Working-state content. */
|
|
11
11
|
children?: ReactNode;
|
|
12
12
|
}
|
|
13
13
|
/**
|
|
14
14
|
* Resolves a section's deterministic state (brief §8.1, §11) and renders it: the
|
|
15
|
-
* working content
|
|
16
|
-
*
|
|
15
|
+
* working content when `working`, else a consistent state view. A small state
|
|
16
|
+
* switcher makes every state reachable for inspection.
|
|
17
17
|
*/
|
|
18
18
|
export declare function SectionSurface({ project, section, forcedState, children }: SectionSurfaceProps): import("react").JSX.Element;
|
|
19
|
-
/** Placeholder for an available section whose live surface lands in a later phase. */
|
|
20
|
-
export declare function PhasePlaceholder({ section }: {
|
|
21
|
-
section: SectionId;
|
|
22
|
-
}): import("react").JSX.Element;
|
|
23
19
|
/**
|
|
24
20
|
* Deterministic state switcher (brief §11). Links to the same section with each
|
|
25
21
|
* `?state=` value so agents/humans can reproduce every state by URL or click.
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Link, STUDIO_PROJECT_ROUTE } from '@elytracms/studio/router';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { Badge, Button, Code, Row, Stack, Text } from '@elytracms/studio/ui';
|
|
3
|
+
import { availabilityOf, SECTIONS, sectionStateSchema } from '@elytracms/core/studio-core';
|
|
4
|
+
import { Code, Row, Stack, Text } from '@elytracms/studio/ui';
|
|
6
5
|
import { cn } from '@elytracms/studio/lib/utils';
|
|
7
6
|
import { resolveSectionState, SectionStateView } from './section-state';
|
|
8
|
-
import { sectionNavItem } from './sections';
|
|
9
7
|
/** Parse a `?state=` search param into a SectionState (undefined when absent/invalid). */
|
|
10
8
|
export function parseSectionState(raw) {
|
|
11
9
|
const parsed = sectionStateSchema.safeParse(raw);
|
|
@@ -13,19 +11,13 @@ export function parseSectionState(raw) {
|
|
|
13
11
|
}
|
|
14
12
|
/**
|
|
15
13
|
* Resolves a section's deterministic state (brief §8.1, §11) and renders it: the
|
|
16
|
-
* working content
|
|
17
|
-
*
|
|
14
|
+
* working content when `working`, else a consistent state view. A small state
|
|
15
|
+
* switcher makes every state reachable for inspection.
|
|
18
16
|
*/
|
|
19
17
|
export function SectionSurface({ project, section, forcedState, children }) {
|
|
20
18
|
const availability = availabilityOf(project.settings.sectionAvailability, section);
|
|
21
19
|
const state = resolveSectionState(availability, forcedState);
|
|
22
|
-
return (_jsxs(Stack, { gap: "none", className: "min-h-full", children: [_jsx(StateSwitcher, { projectId: project.id, section: section, current: forcedState }), state === 'working' ? (
|
|
23
|
-
}
|
|
24
|
-
/** Placeholder for an available section whose live surface lands in a later phase. */
|
|
25
|
-
export function PhasePlaceholder({ section }) {
|
|
26
|
-
const item = sectionNavItem(section);
|
|
27
|
-
const Icon = item.icon;
|
|
28
|
-
return (_jsx("div", { "data-section-state": "working", className: "grid flex-1 place-items-center p-8", children: _jsxs(Stack, { gap: "3", align: "center", className: "max-w-md text-center", children: [_jsx("span", { className: "grid size-12 place-items-center rounded-xl border border-border bg-card text-primary", children: _jsx(Icon, { className: "size-6" }) }), _jsxs(Stack, { gap: "1", align: "center", children: [_jsxs(Text, { weight: "semibold", children: [item.label, " is enabled"] }), _jsxs(Text, { size: "sm", tone: "muted", children: [item.description, " The full surface arrives in ", item.phase, "."] })] }), _jsx(Badge, { variant: "primary", children: item.phase })] }) }));
|
|
20
|
+
return (_jsxs(Stack, { gap: "none", className: "min-h-full", children: [_jsx(StateSwitcher, { projectId: project.id, section: section, current: forcedState }), state === 'working' ? (children) : (_jsx(SectionStateView, { state: state, title: SECTIONS[section].label }))] }));
|
|
29
21
|
}
|
|
30
22
|
/**
|
|
31
23
|
* Deterministic state switcher (brief §11). Links to the same section with each
|
|
@@ -41,6 +33,6 @@ export function StateSwitcher({ projectId, section, current, }) {
|
|
|
41
33
|
return (_jsx(Link, { to: STUDIO_PROJECT_ROUTE, params: { projectId, _splat: section }, search: { state: s }, className: cn('rounded-md px-2 py-0.5 font-mono text-xs transition-colors', active
|
|
42
34
|
? 'bg-accent text-accent-foreground'
|
|
43
35
|
: 'text-muted-foreground hover:bg-muted hover:text-foreground'), children: s }, s));
|
|
44
|
-
}), _jsx(Code, { tone: "muted", className: "ml-2 hidden sm:inline-flex", children: activeKey === 'default' ? 'auto' : `?state=${activeKey}` })
|
|
36
|
+
}), _jsx(Code, { tone: "muted", className: "ml-2 hidden sm:inline-flex", children: activeKey === 'default' ? 'auto' : `?state=${activeKey}` })] }));
|
|
45
37
|
}
|
|
46
38
|
//# sourceMappingURL=section-surface.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"section-surface.js","sourceRoot":"","sources":["../../src/components/section-surface.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,IAAI,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAA;
|
|
1
|
+
{"version":3,"file":"section-surface.js","sourceRoot":"","sources":["../../src/components/section-surface.tsx"],"names":[],"mappings":"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","sourcesContent":["import { Link, STUDIO_PROJECT_ROUTE } from '@elytracms/studio/router'\nimport type { ReactNode } from 'react'\nimport { availabilityOf, SECTIONS, sectionStateSchema } from '@elytracms/core/studio-core'\nimport type { Project, ProjectId, SectionId, SectionState } from '@elytracms/core/studio-core'\nimport { Code, Row, Stack, Text } from '@elytracms/studio/ui'\nimport { cn } from '@elytracms/studio/lib/utils'\nimport { resolveSectionState, SectionStateView } from './section-state'\n\n/** Parse a `?state=` search param into a SectionState (undefined when absent/invalid). */\nexport function parseSectionState(raw: unknown): SectionState | undefined {\n const parsed = sectionStateSchema.safeParse(raw)\n return parsed.success ? parsed.data : undefined\n}\n\nexport interface SectionSurfaceProps {\n project: Project\n section: SectionId\n /** Forced state from `?state=`; overrides availability. */\n forcedState?: SectionState\n /** Working-state content. */\n children?: ReactNode\n}\n\n/**\n * Resolves a section's deterministic state (brief §8.1, §11) and renders it: the\n * working content when `working`, else a consistent state view. A small state\n * switcher makes every state reachable for inspection.\n */\nexport function SectionSurface({ project, section, forcedState, children }: SectionSurfaceProps) {\n const availability = availabilityOf(project.settings.sectionAvailability, section)\n const state = resolveSectionState(availability, forcedState)\n\n return (\n <Stack gap=\"none\" className=\"min-h-full\">\n <StateSwitcher projectId={project.id} section={section} current={forcedState} />\n {state === 'working' ? (\n children\n ) : (\n <SectionStateView state={state} title={SECTIONS[section].label} />\n )}\n </Stack>\n )\n}\n\n/**\n * Deterministic state switcher (brief §11). Links to the same section with each\n * `?state=` value so agents/humans can reproduce every state by URL or click.\n */\nexport function StateSwitcher({\n projectId,\n section,\n current,\n}: {\n projectId: ProjectId\n section: SectionId\n current?: SectionState\n}) {\n const states: SectionState[] = ['working', 'empty', 'loading', 'error', 'unavailable', 'planned']\n const activeKey = current ?? 'default'\n return (\n <Row\n gap=\"1\"\n wrap\n className=\"shrink-0 border-b border-dashed border-border/70 px-4 py-2 sm:px-6\"\n data-testid=\"state-switcher\"\n >\n <Text size=\"xs\" tone=\"subtle\" mono className=\"mr-1\">\n state\n </Text>\n <Link\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId, _splat: section }}\n search={{}}\n className={cn(\n 'rounded-md px-2 py-0.5 font-mono text-xs transition-colors',\n activeKey === 'default'\n ? 'bg-accent text-accent-foreground'\n : 'text-muted-foreground hover:bg-muted hover:text-foreground',\n )}\n >\n default\n </Link>\n {states.map((s) => {\n const active = s === activeKey\n return (\n <Link\n key={s}\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId, _splat: section }}\n search={{ state: s }}\n className={cn(\n 'rounded-md px-2 py-0.5 font-mono text-xs transition-colors',\n active\n ? 'bg-accent text-accent-foreground'\n : 'text-muted-foreground hover:bg-muted hover:text-foreground',\n )}\n >\n {s}\n </Link>\n )\n })}\n <Code tone=\"muted\" className=\"ml-2 hidden sm:inline-flex\">\n {activeKey === 'default' ? 'auto' : `?state=${activeKey}`}\n </Code>\n </Row>\n )\n}\n"]}
|
|
@@ -1,14 +1,5 @@
|
|
|
1
1
|
import { Blocks, Database, Image, Puzzle, Settings, Signpost } from 'lucide-react';
|
|
2
2
|
import { SECTIONS, SECTION_IDS, availabilityOf, sectionIdSchema } from '@elytracms/core/studio-core';
|
|
3
|
-
/** Which Studio phase delivers each section's live surface (for placeholders). */
|
|
4
|
-
const SECTION_PHASE = {
|
|
5
|
-
components: 'Phase 4',
|
|
6
|
-
cms: 'Phase 6',
|
|
7
|
-
routes: 'Milestone B',
|
|
8
|
-
assets: 'Phase 6',
|
|
9
|
-
extensions: 'Phase 11',
|
|
10
|
-
settings: 'Phase 2',
|
|
11
|
-
};
|
|
12
3
|
const ICONS = {
|
|
13
4
|
components: Blocks,
|
|
14
5
|
cms: Database,
|
|
@@ -34,7 +25,6 @@ export const SECTION_NAV_ITEMS = NAV_IDS.map((id) => ({
|
|
|
34
25
|
label: descriptorOf(id).label,
|
|
35
26
|
description: descriptorOf(id).description,
|
|
36
27
|
icon: ICONS[id],
|
|
37
|
-
phase: SECTION_PHASE[id],
|
|
38
28
|
}));
|
|
39
29
|
export function sectionNavItem(id) {
|
|
40
30
|
return SECTION_NAV_ITEMS.find((item) => item.id === id);
|
|
@@ -1 +1 @@
|
|
|
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;
|
|
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"]}
|
|
@@ -11,9 +11,8 @@
|
|
|
11
11
|
*
|
|
12
12
|
* `projectId` stays a real route param (the `$projectId` segment); the trailing
|
|
13
13
|
* pathname (`_splat`) is matched against the engine route table
|
|
14
|
-
* (`matchStudioRoute`)
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* the disappearing route-file wrappers did.
|
|
14
|
+
* (`matchStudioRoute`) into a module id + segments, and the active module (EC-239)
|
|
15
|
+
* resolves the screen. Every surface already takes its ids as props, so the
|
|
16
|
+
* resolver just hands the module its parsed deep-link.
|
|
18
17
|
*/
|
|
19
18
|
export declare function StudioProjectScreen(): import("react").JSX.Element;
|
|
@@ -1,35 +1,17 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect } from 'react';
|
|
3
|
-
import {
|
|
4
|
-
import { validSampleGraph } from '@elytracms/core/project-graph';
|
|
5
|
-
import { Link, Navigate, STUDIO_PROJECT_ROUTE, studioPath, useParams, useSearch, } from '@elytracms/studio/router';
|
|
3
|
+
import { Link, Navigate, STUDIO_PROJECT_ROUTE, useParams, useSearch } from '@elytracms/studio/router';
|
|
6
4
|
import { matchStudioRoute } from '@elytracms/studio/router/match';
|
|
7
5
|
import { studioSearch } from '@elytracms/studio/router/search';
|
|
8
6
|
import { Button, Heading, Stack, Text } from '@elytracms/studio/ui';
|
|
9
7
|
import { DataProvider } from '@elytracms/studio/lib/data';
|
|
10
|
-
import { getInstantiatedKit } from '@elytracms/studio/lib/starters/kit-session';
|
|
11
8
|
import { useStudio } from './studio-context';
|
|
12
9
|
import { StudioShell } from './studio-shell';
|
|
13
|
-
import { firstAvailableSection } from './sections';
|
|
14
|
-
import { SectionSurface } from './section-surface';
|
|
15
|
-
import { SectionStateView } from './section-state';
|
|
16
|
-
import { SettingsForm } from './settings-form';
|
|
17
|
-
import { AssetLibrary } from './assets/asset-library';
|
|
18
10
|
import { AssetsProvider } from './assets/assets-store';
|
|
19
|
-
import { BuilderProvider } from './builder/builder-store';
|
|
20
11
|
import { CollabProvider } from './collab/collab-context';
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import { ComponentsIndex } from './components/components-index';
|
|
25
|
-
import { ComponentDetail } from './components/component-detail';
|
|
26
|
-
import { CmsSessionGate, ProjectSessionProvider, useProjectSession, } from './project-session';
|
|
27
|
-
import { CmsProvider, useCms } from './cms/cms-store';
|
|
28
|
-
import { CollectionsSidebar } from './cms/collections-sidebar';
|
|
29
|
-
import { CmsIndex } from './cms/cms-index';
|
|
30
|
-
import { CollectionTable } from './cms/collection-table';
|
|
31
|
-
import { DetailEditor } from './cms/detail-editor';
|
|
32
|
-
import { OperationErrorBanner } from './operation-error';
|
|
12
|
+
import { ProjectSessionProvider } from './project-session';
|
|
13
|
+
import { firstAvailableModule, flatPath } from './app-views/module';
|
|
14
|
+
import { useAppViews } from './app-views/default-modules';
|
|
33
15
|
/**
|
|
34
16
|
* EC-223 Stage 6 (catch-all routing): the studio's per-project route component.
|
|
35
17
|
*
|
|
@@ -43,10 +25,9 @@ import { OperationErrorBanner } from './operation-error';
|
|
|
43
25
|
*
|
|
44
26
|
* `projectId` stays a real route param (the `$projectId` segment); the trailing
|
|
45
27
|
* pathname (`_splat`) is matched against the engine route table
|
|
46
|
-
* (`matchStudioRoute`)
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
* the disappearing route-file wrappers did.
|
|
28
|
+
* (`matchStudioRoute`) into a module id + segments, and the active module (EC-239)
|
|
29
|
+
* resolves the screen. Every surface already takes its ids as props, so the
|
|
30
|
+
* resolver just hands the module its parsed deep-link.
|
|
50
31
|
*/
|
|
51
32
|
export function StudioProjectScreen() {
|
|
52
33
|
const params = useParams({ strict: false });
|
|
@@ -71,147 +52,47 @@ export function StudioProjectScreen() {
|
|
|
71
52
|
const search = studioSearch(rawSearch);
|
|
72
53
|
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
54
|
}
|
|
74
|
-
/**
|
|
55
|
+
/**
|
|
56
|
+
* The per-project provider stack, mounted once the session source is known.
|
|
57
|
+
*
|
|
58
|
+
* EC-287: there is no project-level builder graph anymore — layouts are code and
|
|
59
|
+
* the DB holds content only. The block-field canvas mounts its OWN scoped
|
|
60
|
+
* `BuilderProvider` per document (`composition-field-editor`), so no graph is
|
|
61
|
+
* threaded here. `CollabProvider` stays as the canvas's session-scoped
|
|
62
|
+
* presence/perspective store, seeded graph-lessly.
|
|
63
|
+
*/
|
|
75
64
|
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
65
|
return (
|
|
82
|
-
// Key by project id so the session data sources
|
|
83
|
-
//
|
|
84
|
-
|
|
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));
|
|
66
|
+
// Key by project id so the session data sources reset when switching projects.
|
|
67
|
+
// The data store hydrates/persists this project's `cms-data-sources` records.
|
|
68
|
+
_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
69
|
}
|
|
88
70
|
/**
|
|
89
|
-
* The URL→screen switch (EC-223 Stage 6)
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
71
|
+
* The URL→screen switch (EC-223 Stage 6), now driven by the app-view module
|
|
72
|
+
* registry (EC-239). The bare project URL redirects to the first available
|
|
73
|
+
* module; otherwise the matched module id is resolved from the registry (defaults
|
|
74
|
+
* + `config.appViews`) and its `parsePath` interprets the trailing segments into a
|
|
75
|
+
* deep-link the module renders. An unknown module id or an unparseable path is a
|
|
76
|
+
* not-found; module CONFIG problems (duplicate/invalid `appViews`) surface as
|
|
77
|
+
* visible issues in the shell, never silently. Renders inside the shell + provider
|
|
78
|
+
* stack, so every surface sees the same live CMS store and breadcrumb context.
|
|
96
79
|
*/
|
|
97
80
|
function StudioSectionResolver({ project, match, search, }) {
|
|
98
81
|
const projectId = project.id;
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
case 'settings':
|
|
104
|
-
return (_jsx(SectionSurface, { project: project, section: "settings", forcedState: search.state, children: _jsx(SettingsForm, { project: project }) }));
|
|
105
|
-
case 'assets':
|
|
106
|
-
return (_jsx(SectionSurface, { project: project, section: "assets", forcedState: search.state, children: _jsx(AssetLibrary, { projectId: projectId, focusAssetId: search.asset }) }));
|
|
107
|
-
case 'routes':
|
|
108
|
-
// Deterministic forced states (?state=) for the surface (EC-036).
|
|
109
|
-
return search.state && search.state !== 'working' ? (_jsx(SectionStateView, { state: search.state, title: "Routes" })) : (_jsx(RoutesShell, { project: project }));
|
|
110
|
-
case 'extensions':
|
|
111
|
-
return search.state && search.state !== 'working' ? (_jsx(SectionStateView, { state: search.state, title: "Extensions" })) : (_jsx(ExtensionsView, {}));
|
|
112
|
-
case 'builder':
|
|
113
|
-
// Retired surface (EC-187 slice 6): a page is a document in the `page`
|
|
114
|
-
// collection. A `?page=<id>` deep link lands on that document's editor;
|
|
115
|
-
// otherwise the page-collection list. The page-graph-only params have no
|
|
116
|
-
// home here and are dropped.
|
|
117
|
-
return (_jsx(Navigate, { to: STUDIO_PROJECT_ROUTE, params: {
|
|
118
|
-
projectId,
|
|
119
|
-
_splat: search.page
|
|
120
|
-
? studioPath.cmsDocument('page', search.page)
|
|
121
|
-
: studioPath.cmsCollection('page'),
|
|
122
|
-
}, replace: true }));
|
|
123
|
-
case 'components':
|
|
124
|
-
return (_jsx(ComponentsScreen, { projectId: projectId, componentId: match.componentId, state: search.state }));
|
|
125
|
-
case 'cms':
|
|
126
|
-
return (_jsx(CmsScreen, { project: project, collectionId: match.collectionId, documentId: match.documentId, state: search.state, field: search.field }));
|
|
127
|
-
case 'not-found':
|
|
128
|
-
default:
|
|
129
|
-
return _jsx(StudioRouteNotFound, {});
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
/**
|
|
133
|
-
* Components section (EC-050): a registry-browser sidebar beside the active
|
|
134
|
-
* screen — the index (no selection) or a component's detail. Forced states
|
|
135
|
-
* (?state=) replace the whole surface.
|
|
136
|
-
*/
|
|
137
|
-
function ComponentsScreen({ projectId, componentId, state, }) {
|
|
138
|
-
if (state && state !== 'working') {
|
|
139
|
-
return _jsx(SectionStateView, { state: state, title: "Components" });
|
|
140
|
-
}
|
|
141
|
-
return (_jsxs("div", { className: "flex h-full min-h-0", children: [_jsx("aside", { className: "hidden h-full min-h-0 w-[300px] shrink-0 border-r border-border md:block", children: _jsx(RegistryBrowser, { projectId: projectId, activeComponentId: componentId ?? undefined }) }), _jsx("div", { className: "min-h-0 flex-1 overflow-auto", children: componentId === null ? (_jsx(ComponentsIndex, { projectId: projectId })) : (_jsx(ComponentDetail, { projectId: projectId, componentId: componentId })) })] }));
|
|
142
|
-
}
|
|
143
|
-
/**
|
|
144
|
-
* CMS section: hydrates the persisted (or kit-seeded) collections + documents,
|
|
145
|
-
* then renders the collections sidebar beside the active screen (index, a
|
|
146
|
-
* collection table, or a document editor). Forced states (?state=) replace the
|
|
147
|
-
* whole surface before the provider mounts.
|
|
148
|
-
*/
|
|
149
|
-
function CmsScreen({ project, collectionId, documentId, state, field, }) {
|
|
150
|
-
const projectId = project.id;
|
|
151
|
-
if (state && state !== 'working') {
|
|
152
|
-
return _jsx(SectionStateView, { state: state, title: "CMS" });
|
|
153
|
-
}
|
|
154
|
-
// A project created from a starter kit (EC-119) opens the CMS on the kit's schemas + content.
|
|
155
|
-
const kit = getInstantiatedKit(projectId);
|
|
156
|
-
return (
|
|
157
|
-
// EC-177 gap 1: in Convex mode every CMS mount loads the CURRENT persisted
|
|
158
|
-
// schema + documents (fixtures only when nothing is persisted — first run).
|
|
159
|
-
// Playground renders synchronously with `null` → fixture seeds, as before.
|
|
160
|
-
_jsx(CmsSessionGate, { children: (cms) => (_jsx(CmsProvider, { projectId: projectId, initialCollections: cms?.collections ?? kit?.collections, initialDocuments: cms?.documents ?? kit?.documents, initialPublishedKeys: cms?.publishedKeys, initialLocaleConfig: cms
|
|
161
|
-
? // Hydrated sessions follow the project's persisted locale settings.
|
|
162
|
-
{ default: project.settings.defaultLocale, locales: [...project.settings.locales] }
|
|
163
|
-
: kit?.localeConfig, children: _jsxs("div", { className: "flex h-full min-h-0 flex-col", children: [_jsx(CmsPersistErrorBanner, {}), _jsxs("div", { className: "flex min-h-0 flex-1", children: [_jsx("aside", { className: "hidden h-full min-h-0 w-[240px] shrink-0 border-r border-border md:block", children: _jsx(CollectionsSidebar, { projectId: projectId, activeCollectionId: collectionId ?? undefined }) }), _jsx("div", { className: "min-h-0 flex-1 overflow-hidden", children: _jsx(CmsContent, { projectId: projectId, collectionId: collectionId, documentId: documentId, field: field }) })] })] }) }, projectId)) }));
|
|
164
|
-
}
|
|
165
|
-
/** Resolves the active CMS screen inside the provider (needs the live store). */
|
|
166
|
-
function CmsContent({ projectId, collectionId, documentId, field, }) {
|
|
167
|
-
const cms = useCms();
|
|
168
|
-
if (collectionId === null)
|
|
169
|
-
return _jsx(CmsIndex, { projectId: projectId });
|
|
170
|
-
if (documentId === null) {
|
|
171
|
-
const collection = cms.getCollection(collectionId);
|
|
172
|
-
// EC-274: a singleton is a UI lens — at the collection URL it opens its one
|
|
173
|
-
// document directly *while in its expected shape*, but degrades to the list
|
|
174
|
-
// (with a "for review" lane) on drift. SingletonGate decides per the data.
|
|
175
|
-
if (collection && isSingleton(collection)) {
|
|
176
|
-
return _jsx(SingletonGate, { projectId: projectId, collectionId: collectionId });
|
|
177
|
-
}
|
|
178
|
-
return _jsx(CollectionTable, { projectId: projectId, collectionId: collectionId });
|
|
82
|
+
const appViews = useAppViews();
|
|
83
|
+
if (match.section === 'index') {
|
|
84
|
+
// Bare `/$projectId` → the project's first available module.
|
|
85
|
+
return (_jsx(Navigate, { to: STUDIO_PROJECT_ROUTE, params: { projectId, _splat: firstAvailableModule(appViews.modules, project) }, replace: true }));
|
|
179
86
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
const cms = useCms();
|
|
190
|
-
const collection = cms.getCollection(collectionId);
|
|
191
|
-
const docs = cms.documentsOf(collectionId);
|
|
192
|
-
const resolution = collection
|
|
193
|
-
? resolveSingletonDocument(collection, docs)
|
|
194
|
-
: { kind: 'empty' };
|
|
195
|
-
// Materialize the lone document on first visit (an empty draft, persisted like
|
|
196
|
-
// any create). Idempotent in the store; only fires while genuinely empty.
|
|
197
|
-
useEffect(() => {
|
|
198
|
-
if (resolution.kind === 'empty')
|
|
199
|
-
cms.ensureSingletonDocument(collectionId);
|
|
200
|
-
}, [resolution.kind, cms, collectionId]);
|
|
201
|
-
if (resolution.kind === 'ready') {
|
|
202
|
-
return (_jsx(DetailEditor, { projectId: projectId, collectionId: collectionId, documentId: resolution.doc.id }));
|
|
203
|
-
}
|
|
204
|
-
if (resolution.kind === 'drift') {
|
|
205
|
-
return _jsx(CollectionTable, { projectId: projectId, collectionId: collectionId });
|
|
206
|
-
}
|
|
207
|
-
return (_jsx("div", { className: "grid h-full place-items-center p-8", children: _jsxs(Text, { size: "sm", tone: "muted", children: ["Opening ", collection?.form?.label ?? collectionId, "\u2026"] }) }));
|
|
208
|
-
}
|
|
209
|
-
/** Failed/denied persisted CMS mutations are always visible (EC-141). */
|
|
210
|
-
function CmsPersistErrorBanner() {
|
|
211
|
-
const cms = useCms();
|
|
212
|
-
if (!cms.persistError)
|
|
213
|
-
return null;
|
|
214
|
-
return (_jsx("div", { className: "shrink-0 border-b border-border p-2", children: _jsx(OperationErrorBanner, { error: cms.persistError, onDismiss: cms.clearPersistError }) }));
|
|
87
|
+
const module = appViews.get(match.moduleId);
|
|
88
|
+
if (!module)
|
|
89
|
+
return _jsx(StudioRouteNotFound, {});
|
|
90
|
+
// A flat module (no `parsePath`) accepts only the bare path; a nested one
|
|
91
|
+
// (CMS/components) interprets its segments. `null` → an over-deep path → not-found.
|
|
92
|
+
const deepLink = (module.parsePath ?? flatPath)(match.segments);
|
|
93
|
+
if (deepLink === null)
|
|
94
|
+
return _jsx(StudioRouteNotFound, {});
|
|
95
|
+
return _jsx(_Fragment, { children: module.render({ project, projectId, deepLink, search }) });
|
|
215
96
|
}
|
|
216
97
|
function ProjectNotFound() {
|
|
217
98
|
return (_jsx("div", { className: "grid min-h-dvh place-items-center p-8", children: _jsxs(Stack, { gap: "3", align: "center", className: "max-w-sm text-center", children: [_jsx(Heading, { size: "display", children: "Project not found" }), _jsx(Text, { tone: "muted", children: "This project doesn\u2019t exist or was deleted. It may only have existed in this session." }), _jsx(Button, { asChild: true, children: _jsx(Link, { to: "/", children: "Back to projects" }) })] }) }));
|