@elytracms/studio 0.0.29 → 0.0.31

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 (61) hide show
  1. package/convex/redirects.ts +3 -0
  2. package/convex/schema.ts +2 -0
  3. package/dist/components/app-views/default-modules.d.ts +3 -1
  4. package/dist/components/app-views/default-modules.js +22 -18
  5. package/dist/components/app-views/default-modules.js.map +1 -1
  6. package/dist/components/app-views/studio-config.d.ts +10 -0
  7. package/dist/components/app-views/studio-config.js.map +1 -1
  8. package/dist/components/builder/property-inspector.js +3 -2
  9. package/dist/components/builder/property-inspector.js.map +1 -1
  10. package/dist/components/builder/reuse-context.js +2 -1
  11. package/dist/components/builder/reuse-context.js.map +1 -1
  12. package/dist/components/cms/cms-index.js +2 -1
  13. package/dist/components/cms/cms-index.js.map +1 -1
  14. package/dist/components/cms/cms-store.d.ts +11 -2
  15. package/dist/components/cms/cms-store.js +17 -4
  16. package/dist/components/cms/cms-store.js.map +1 -1
  17. package/dist/components/cms/collection-table.js +5 -4
  18. package/dist/components/cms/collection-table.js.map +1 -1
  19. package/dist/components/cms/detail-editor.js +5 -4
  20. package/dist/components/cms/detail-editor.js.map +1 -1
  21. package/dist/components/cms/editable-cell.js +3 -2
  22. package/dist/components/cms/editable-cell.js.map +1 -1
  23. package/dist/components/cms/field-control.js +6 -5
  24. package/dist/components/cms/field-control.js.map +1 -1
  25. package/dist/components/cms/scalar-value-editor.js +2 -1
  26. package/dist/components/cms/scalar-value-editor.js.map +1 -1
  27. package/dist/components/components/field-inspector.js +3 -2
  28. package/dist/components/components/field-inspector.js.map +1 -1
  29. package/dist/components/current-user-menu.js +4 -2
  30. package/dist/components/current-user-menu.js.map +1 -1
  31. package/dist/components/redirects/redirects-collection-view-source.d.ts +4 -3
  32. package/dist/components/redirects/redirects-collection-view-source.js +23 -3
  33. package/dist/components/redirects/redirects-collection-view-source.js.map +1 -1
  34. package/dist/components/redirects/redirects-store.js +1 -0
  35. package/dist/components/redirects/redirects-store.js.map +1 -1
  36. package/dist/customization.d.ts +7 -0
  37. package/dist/customization.js +6 -0
  38. package/dist/customization.js.map +1 -1
  39. package/dist/lib/cms-views/view-model.js +2 -2
  40. package/dist/lib/cms-views/view-model.js.map +1 -1
  41. package/dist/lib/i18n/i18n.d.ts +45 -0
  42. package/dist/lib/i18n/i18n.js +125 -0
  43. package/dist/lib/i18n/i18n.js.map +1 -0
  44. package/dist/lib/i18n/index.d.ts +12 -0
  45. package/dist/lib/i18n/index.js +4 -0
  46. package/dist/lib/i18n/index.js.map +1 -0
  47. package/dist/lib/i18n/messages.en.d.ts +15 -0
  48. package/dist/lib/i18n/messages.en.js +39 -0
  49. package/dist/lib/i18n/messages.en.js.map +1 -0
  50. package/dist/lib/i18n/types.d.ts +45 -0
  51. package/dist/lib/i18n/types.js +19 -0
  52. package/dist/lib/i18n/types.js.map +1 -0
  53. package/dist/lib/persistence/convex-adapter.js +2 -0
  54. package/dist/lib/persistence/convex-adapter.js.map +1 -1
  55. package/dist/lib/references/reference-model.js +4 -3
  56. package/dist/lib/references/reference-model.js.map +1 -1
  57. package/dist/lib/workspace/index.d.ts +8 -0
  58. package/dist/lib/workspace/index.js +10 -0
  59. package/dist/lib/workspace/index.js.map +1 -1
  60. package/dist/ui/badge.d.ts +1 -1
  61. package/package.json +6 -2
@@ -17,6 +17,9 @@ export const save = mutation({
17
17
  to: v.string(),
18
18
  permanent: v.boolean(),
19
19
  enabled: v.boolean(),
20
+ // Schema-driven additive-field bag (EC-311 Tier 2). Absent on a save leaves the
21
+ // stored bag untouched (Convex `patch` only writes provided fields).
22
+ meta: v.optional(v.any()),
20
23
  createdAt: v.string(),
21
24
  updatedAt: v.string(),
22
25
  },
package/convex/schema.ts CHANGED
@@ -162,6 +162,8 @@ export default defineSchema({
162
162
  to: v.string(),
163
163
  permanent: v.boolean(),
164
164
  enabled: v.boolean(),
165
+ // Schema-driven additive-field bag (EC-311 Tier 2) — see the `assets.meta` bag.
166
+ meta: v.optional(v.any()),
165
167
  createdAt: v.string(),
166
168
  updatedAt: v.string(),
167
169
  }).index('by_redirect', ['redirectId']),
@@ -58,7 +58,9 @@ export interface BuiltinViewConfig {
58
58
  * the rail (`form`) and **override built-in fields by equal name** (`fields`):
59
59
  * naming, permissions (`form.readOnly`), rendering (`form.control` → `fieldRenderers`)
60
60
  * and validation. The canonical `from`/`to`/`permanent`/`enabled` names+types are
61
- * contract-locked; a type change or a net-new field is a loud config error.
61
+ * contract-locked (a type change on one is a loud config error), but a *net-new*
62
+ * field is allowed (EC-311 Tier 2): it's appended and persists in the redirect
63
+ * record's `meta` bag — editorial only, so it never reaches delivery/routing.
62
64
  */
63
65
  export interface RedirectsViewConfig extends BuiltinViewConfig {
64
66
  /** Rename the rail (e.g. `{ label: 'URLs' }`). */
@@ -1,9 +1,10 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useEffect } from 'react';
3
3
  import { Blocks, Database, FileCog, Image, LayoutGrid, Puzzle, Settings as SettingsIcon, Signpost, Users, } from 'lucide-react';
4
- import { extendCollectionDef, isSingleton, resolveSingletonDocument } from '@elytracms/core/cms-core';
4
+ import { extendCollectionDef, isSingleton, resolveLabel, resolveSingletonDocument } from '@elytracms/core/cms-core';
5
5
  import { Navigate, STUDIO_PROJECT_ROUTE, studioPath } from '@elytracms/studio/router';
6
6
  import { Text } from '@elytracms/studio/ui';
7
+ import { translate } from '@elytracms/studio/lib/i18n';
7
8
  import { getInstantiatedKit } from '@elytracms/studio/lib/starters/kit-session';
8
9
  import { cmsDeepLink, componentsDeepLink, usersDeepLink, redirectsDeepLink, groupNode, separatorNode, viewNode, } from './module';
9
10
  import { SectionSurface } from '../section-surface';
@@ -50,8 +51,8 @@ export function cmsView(config = {}) {
50
51
  const focus = single ? [single] : config.collections;
51
52
  return {
52
53
  id: cmsViewId(config),
53
- label: config.label ?? 'CMS',
54
- description: config.description ?? 'Model collections, fields, and documents.',
54
+ label: config.label ?? translate('nav.cms.label'),
55
+ description: config.description ?? translate('nav.cms.description'),
55
56
  icon: config.icon ?? Database,
56
57
  availabilitySection: config.availabilitySection ?? 'cms',
57
58
  // A single-collection lens routes `[]`→that collection, `[d]`→its document;
@@ -76,7 +77,8 @@ function cmsCollectionLeaf(collection) {
76
77
  const singleton = isSingleton(collection);
77
78
  return viewNode(cmsView({
78
79
  collection: collection.id,
79
- label: collection.form?.label ?? collection.id,
80
+ // Rail resolution is pure (outside React) → default-language collection label.
81
+ label: resolveLabel(collection.form?.label) ?? collection.id,
80
82
  icon: singleton ? FileCog : collection.kind === 'asset' ? Image : Database,
81
83
  }));
82
84
  }
@@ -84,8 +86,8 @@ function cmsCollectionLeaf(collection) {
84
86
  export function componentsView(config = {}) {
85
87
  return {
86
88
  id: 'components',
87
- label: config.label ?? 'Components',
88
- description: 'Browse the registry of primitives and project components.',
89
+ label: config.label ?? translate('nav.components.label'),
90
+ description: translate('nav.components.description'),
89
91
  icon: config.icon ?? Blocks,
90
92
  availabilitySection: 'components',
91
93
  parsePath: componentsDeepLink,
@@ -102,11 +104,13 @@ export function componentsView(config = {}) {
102
104
  export function redirectsView(config = {}) {
103
105
  // EC-311: merge any field/label overrides over the engine-owned def ONCE, at config
104
106
  // build (config-as-code — a locked violation throws here, at `defineStudio` mount).
105
- const schema = extendCollectionDef(redirectsCollectionDef, config, { additiveFields: 'reject' });
107
+ // Tier 2: `additiveFields: 'allow'` — a net-new field is appended and persists in the
108
+ // redirect record's `meta` bag (the canonical from/to/permanent/enabled stay locked).
109
+ const schema = extendCollectionDef(redirectsCollectionDef, config, { additiveFields: 'allow' });
106
110
  return {
107
111
  id: 'redirects',
108
- label: config.label ?? schema.form?.label ?? 'Redirects',
109
- description: 'Create and manage URL redirects (301/302) for this site.',
112
+ label: config.label ?? resolveLabel(schema.form?.label) ?? translate('nav.redirects.label'),
113
+ description: translate('nav.redirects.description'),
110
114
  icon: config.icon ?? Signpost,
111
115
  availabilitySection: 'cms',
112
116
  parsePath: redirectsDeepLink,
@@ -117,8 +121,8 @@ export function redirectsView(config = {}) {
117
121
  export function assetsView(config = {}) {
118
122
  return {
119
123
  id: 'assets',
120
- label: config.label ?? 'Assets',
121
- description: 'Manage uploaded media and metadata.',
124
+ label: config.label ?? translate('nav.assets.label'),
125
+ description: translate('nav.assets.description'),
122
126
  icon: config.icon ?? Image,
123
127
  availabilitySection: 'assets',
124
128
  render: (ctx) => (_jsx(SectionSurface, { project: ctx.project, section: "assets", forcedState: ctx.search.state, children: _jsx(AssetLibrary, { projectId: ctx.projectId, focusAssetId: ctx.search.asset }) })),
@@ -134,8 +138,8 @@ export function assetsView(config = {}) {
134
138
  export function usersView(config = {}) {
135
139
  return {
136
140
  id: 'users',
137
- label: config.label ?? 'Users',
138
- description: 'Manage who has access to this deployment and their role.',
141
+ label: config.label ?? translate('nav.users.label'),
142
+ description: translate('nav.users.description'),
139
143
  icon: config.icon ?? Users,
140
144
  availabilitySection: 'users',
141
145
  // EC-240: `[]` is the roster; `[userId]` opens that member in the portable edit
@@ -148,8 +152,8 @@ export function usersView(config = {}) {
148
152
  export function extensionsView(config = {}) {
149
153
  return {
150
154
  id: 'extensions',
151
- label: config.label ?? 'Extensions',
152
- description: 'Install components, field types, adapters, and templates from the marketplace.',
155
+ label: config.label ?? translate('nav.extensions.label'),
156
+ description: translate('nav.extensions.description'),
153
157
  icon: config.icon ?? Puzzle,
154
158
  availabilitySection: 'extensions',
155
159
  render: (ctx) => ctx.search.state && ctx.search.state !== 'working' ? (_jsx(SectionStateView, { state: ctx.search.state, title: "Extensions" })) : (_jsx(ExtensionsView, {})),
@@ -159,8 +163,8 @@ export function extensionsView(config = {}) {
159
163
  export function settingsView(config = {}) {
160
164
  return {
161
165
  id: 'settings',
162
- label: config.label ?? 'Settings',
163
- description: 'Configure project metadata and locales.',
166
+ label: config.label ?? translate('nav.settings.label'),
167
+ description: translate('nav.settings.description'),
164
168
  icon: config.icon ?? SettingsIcon,
165
169
  availabilitySection: 'settings',
166
170
  render: (ctx) => (_jsx(SectionSurface, { project: ctx.project, section: "settings", forcedState: ctx.search.state, children: _jsx(SettingsForm, { project: ctx.project }) })),
@@ -345,7 +349,7 @@ function SingletonGate({ projectId, collectionId }) {
345
349
  if (resolution.kind === 'drift') {
346
350
  return _jsx(CollectionTable, { projectId: projectId, collectionId: collectionId });
347
351
  }
348
- 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"] }) }));
352
+ return (_jsx("div", { className: "grid h-full place-items-center p-8", children: _jsxs(Text, { size: "sm", tone: "muted", children: ["Opening ", resolveLabel(collection?.form?.label) ?? collectionId, "\u2026"] }) }));
349
353
  }
350
354
  /** Failed/denied persisted CMS mutations are always visible (EC-141). */
351
355
  function CmsPersistErrorBanner() {
@@ -1 +1 @@
1
- {"version":3,"file":"default-modules.js","sourceRoot":"","sources":["../../../src/components/app-views/default-modules.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useEffect } from 'react'\nimport type { ReactNode } from 'react'\nimport type { LucideIcon } from 'lucide-react'\nimport {\n Blocks,\n Database,\n FileCog,\n Image,\n LayoutGrid,\n Puzzle,\n Settings as SettingsIcon,\n Signpost,\n Users,\n} from 'lucide-react'\nimport { extendCollectionDef, isSingleton, resolveSingletonDocument } from '@elytracms/core/cms-core'\nimport type {\n CollectionDef,\n CollectionFieldOverride,\n CollectionFormMeta,\n} from '@elytracms/core/cms-core'\nimport type { Project, SectionId, SectionState } from '@elytracms/core/studio-core'\nimport { Navigate, STUDIO_PROJECT_ROUTE, studioPath } from '@elytracms/studio/router'\nimport { Text } from '@elytracms/studio/ui'\nimport { getInstantiatedKit } from '@elytracms/studio/lib/starters/kit-session'\nimport {\n cmsDeepLink,\n componentsDeepLink,\n usersDeepLink,\n redirectsDeepLink,\n groupNode,\n separatorNode,\n viewNode,\n type AppViewContext,\n type AppViewDeepLink,\n type AppViewModule,\n type NavPermission,\n type SidebarViewNode,\n} from './module'\nimport { SectionSurface } from '../section-surface'\nimport { SectionStateView } from '../section-state'\nimport { SettingsForm } from '../settings-form'\nimport { AssetLibrary } from '../assets/asset-library'\nimport { UsersModule } from '../users/users-module'\nimport { RedirectsModule } from '../redirects/redirects-module'\nimport { redirectsCollectionDef } from '../redirects/redirects-schema'\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 } from '../project-session'\nimport { CmsProvider, useCms } from '../cms/cms-store'\nimport { focusCollectionList } from '../cms/collection-focus'\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-237/239 — the studio's BUILT-IN views + the consumer-facing `nav` namespace,\n * an instance of the EC-236 customization seam.\n *\n * Each built-in surface (the former `StudioSectionResolver` case body) is promoted\n * to a self-contained {@link AppViewModule} factory. The `nav` namespace is the\n * single, typed builder a consuming repo's `studio.config.tsx` uses to declare its\n * section rail — `nav.cms(...)`, `nav.group(...)`, `nav.view(...)` — passing the\n * view objects (and a focus config) DIRECTLY, never a string id. The CMS surface\n * is a factory ({@link cmsView}) so a focused view (\"Catalog\", \"Categories\") is the\n * SAME surface instantiated with config, not a copy — the EC-239 reuse proof.\n */\n\n// --- The CMS surface, reusable via a config factory (the EC-239 reuse seam) ---\n\n/** Config for a CMS-backed view — `collections`/`collection` focus the lens (EC-239). */\nexport interface CmsViewConfig {\n /** Explicit stable id (the route segment). Omitted → derived from the focus. */\n id?: string\n label?: string\n description?: string\n icon?: LucideIcon\n /**\n * Scope the rail auto-expansion (EC-293) to these collection ids, in order. The\n * parent expands into one focused-lens child per id; the parent index shows the\n * same subset. Omitted → every collection. Ignored for a single-collection lens.\n */\n collections?: readonly string[]\n /** Single-collection lens: a LEAF rail item that opens straight into this collection. */\n collection?: string\n /**\n * EC-293 escape hatch: explicit rail children for this CMS parent, overriding the\n * default per-collection auto-expansion. Ignored for a single-collection lens.\n */\n children?: SidebarViewNode[]\n /** The availability section this view rides on (EC-221); defaults to `cms`. */\n availabilitySection?: SectionId\n /** OPTIONAL, default-open nav-permission predicate (EC-240 / AD-18). See {@link BuiltinViewConfig.visible}. */\n visible?: NavPermission\n}\n\n/** Common label/icon overrides for a built-in view (`nav.redirects({ label: 'URLs' })`). */\nexport interface BuiltinViewConfig {\n label?: string\n icon?: LucideIcon\n /**\n * OPTIONAL, default-open nav-permission predicate (EC-240 / AD-18): hide this\n * rail item for principals it returns `false` for; absent → always visible. The\n * policy (which roles) lives in the consuming project, never the engine. E.g.\n * `nav.users({ visible: ({ role }) => role === 'admin' })`.\n */\n visible?: NavPermission\n}\n\n/**\n * Config for the **redirects** rail (EC-311) — a built-in view whose engine-owned\n * schema is customizable. Beyond the shared label/icon/visible, a mount may rename\n * the rail (`form`) and **override built-in fields by equal name** (`fields`):\n * naming, permissions (`form.readOnly`), rendering (`form.control` → `fieldRenderers`)\n * and validation. The canonical `from`/`to`/`permanent`/`enabled` names+types are\n * contract-locked; a type change or a net-new field is a loud config error.\n */\nexport interface RedirectsViewConfig extends BuiltinViewConfig {\n /** Rename the rail (e.g. `{ label: 'URLs' }`). */\n form?: Partial<CollectionFormMeta>\n /** Override built-in redirect fields, matched by `name`. */\n fields?: readonly CollectionFieldOverride[]\n}\n\n/** Config for a fully custom view (`nav.view({...})`) — the net-new surface escape hatch. */\nexport interface CustomViewConfig {\n /** Explicit stable id (the route segment). Omitted → a slug of the label. */\n id?: string\n label: string\n description?: string\n icon?: LucideIcon\n render: (ctx: AppViewContext) => ReactNode\n parsePath?: (segments: string[]) => AppViewDeepLink | null\n availabilitySection?: SectionId\n /** OPTIONAL, default-open nav-permission predicate (EC-240 / AD-18). See {@link BuiltinViewConfig.visible}. */\n visible?: NavPermission\n}\n\nfunction slugify(label: string): string {\n return label.trim().toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '') || 'view'\n}\n\n/** Derive a stable, deterministic id from the CMS focus (overridable via `config.id`). */\nfunction cmsViewId(config: CmsViewConfig): string {\n if (config.id) return config.id\n if (config.collection) return `cms:${config.collection}`\n if (config.collections && config.collections.length > 0) {\n return `cms:${[...config.collections].join('-')}`\n }\n return 'cms'\n}\n\n/**\n * Build a CMS view — the default `cms` view, or a focused instance. Every instance\n * shares one surface ({@link CmsScreen}); `collections` narrows the sidebar + index\n * to a subset, `collection` is a single-collection lens that opens straight into\n * that collection's list. The id is derived from the focus (deterministic, stable\n * across rail reorders) unless `id` is given — so two focused views never collide.\n */\nexport function cmsView(config: CmsViewConfig = {}): AppViewModule {\n const single = config.collection\n const focus = single ? [single] : config.collections\n return {\n id: cmsViewId(config),\n label: config.label ?? 'CMS',\n description: config.description ?? 'Model collections, fields, and documents.',\n icon: config.icon ?? Database,\n availabilitySection: config.availabilitySection ?? 'cms',\n // A single-collection lens routes `[]`→that collection, `[d]`→its document;\n // an unfocused / multi-collection view uses the full index/collection/document arity.\n parsePath: single\n ? (segments) =>\n segments.length === 0\n ? { collectionId: single, documentId: null }\n : segments.length === 1\n ? { collectionId: single, documentId: segments[0]! }\n : null\n : cmsDeepLink,\n render: (ctx) => (\n <CmsScreen\n project={ctx.project}\n collectionId={ctx.deepLink.collectionId ?? null}\n documentId={ctx.deepLink.documentId ?? null}\n state={ctx.search.state}\n field={ctx.search.field}\n focusCollections={focus}\n />\n ),\n }\n}\n\n/**\n * A focused-lens rail child for one collection (EC-293) — the SAME `cms:<id>` view a\n * single-collection `nav.cms({ collection })` produces, carrying the collection's own\n * label + a singleton-aware icon (the former `CollectionsSidebar` row, lifted into\n * the rail). CMS auto-expansion maps the project's collections through this.\n */\nfunction cmsCollectionLeaf(collection: CollectionDef): SidebarViewNode {\n const singleton = isSingleton(collection)\n return viewNode(\n cmsView({\n collection: collection.id,\n label: collection.form?.label ?? collection.id,\n icon: singleton ? FileCog : collection.kind === 'asset' ? Image : Database,\n }),\n )\n}\n\n/** The components registry browser view. */\nexport function componentsView(config: BuiltinViewConfig = {}): AppViewModule {\n return {\n id: 'components',\n label: config.label ?? 'Components',\n description: 'Browse the registry of primitives and project components.',\n icon: config.icon ?? Blocks,\n availabilitySection: 'components',\n parsePath: componentsDeepLink,\n render: (ctx) => (\n <ComponentsScreen\n projectId={ctx.projectId}\n componentId={ctx.deepLink.componentId ?? null}\n state={ctx.search.state}\n />\n ),\n }\n}\n\n/**\n * The Redirects view (EC-303) — editorial redirects as DATA, an editor-owned CRUD\n * surface (create/edit/delete) over the portable list/edit surfaces. Routes are no\n * longer shown in the admin (they're code; native `next.config`/`vercel` own static\n * forwards, evaluated before these). Rides on the CMS availability (a content/\n * routing concern). `[]` is the list; `[redirectId]` opens that redirect's detail.\n */\nexport function redirectsView(config: RedirectsViewConfig = {}): AppViewModule {\n // EC-311: merge any field/label overrides over the engine-owned def ONCE, at config\n // build (config-as-code — a locked violation throws here, at `defineStudio` mount).\n const schema = extendCollectionDef(redirectsCollectionDef, config, { additiveFields: 'reject' })\n return {\n id: 'redirects',\n label: config.label ?? schema.form?.label ?? 'Redirects',\n description: 'Create and manage URL redirects (301/302) for this site.',\n icon: config.icon ?? Signpost,\n availabilitySection: 'cms',\n parsePath: redirectsDeepLink,\n render: (ctx) =>\n ctx.search.state && ctx.search.state !== 'working' ? (\n <SectionStateView state={ctx.search.state} title=\"Redirects\" />\n ) : (\n <RedirectsModule\n projectId={ctx.projectId}\n redirectId={ctx.deepLink.redirectId ?? null}\n schema={schema}\n />\n ),\n }\n}\n\n/** The asset library view. */\nexport function assetsView(config: BuiltinViewConfig = {}): AppViewModule {\n return {\n id: 'assets',\n label: config.label ?? 'Assets',\n description: 'Manage uploaded media and metadata.',\n icon: config.icon ?? Image,\n availabilitySection: 'assets',\n render: (ctx) => (\n <SectionSurface project={ctx.project} section=\"assets\" forcedState={ctx.search.state}>\n {/* The AssetsProvider is mounted at the project level (EC-195). */}\n <AssetLibrary projectId={ctx.projectId} focusAssetId={ctx.search.asset} />\n </SectionSurface>\n ),\n }\n}\n\n/**\n * The studio Users view (EC-240) — an admin-only roster of who has access to this\n * deployment and their role. A first-class section (its own rail entry), modeled\n * like the Assets library; the surface itself gates to admins and renders a\n * \"restricted\" state otherwise. Rides on the `settings` availability (an\n * admin/config concern, so it appears/hides with a project's settings surface).\n */\nexport function usersView(config: BuiltinViewConfig = {}): AppViewModule {\n return {\n id: 'users',\n label: config.label ?? 'Users',\n description: 'Manage who has access to this deployment and their role.',\n icon: config.icon ?? Users,\n availabilitySection: 'users',\n // EC-240: `[]` is the roster; `[userId]` opens that member in the portable edit\n // shell (AD-19 reuse — the same `DetailEditor` body over the users source).\n parsePath: usersDeepLink,\n render: (ctx) =>\n ctx.search.state && ctx.search.state !== 'working' ? (\n <SectionStateView state={ctx.search.state} title=\"Users\" />\n ) : (\n <UsersModule projectId={ctx.projectId} userId={ctx.deepLink.userId ?? null} />\n ),\n }\n}\n\n/** The extensions marketplace view. */\nexport function extensionsView(config: BuiltinViewConfig = {}): AppViewModule {\n return {\n id: 'extensions',\n label: config.label ?? 'Extensions',\n description: 'Install components, field types, adapters, and templates from the marketplace.',\n icon: config.icon ?? Puzzle,\n availabilitySection: 'extensions',\n render: (ctx) =>\n ctx.search.state && ctx.search.state !== 'working' ? (\n <SectionStateView state={ctx.search.state} title=\"Extensions\" />\n ) : (\n <ExtensionsView />\n ),\n }\n}\n\n/** The project settings view. */\nexport function settingsView(config: BuiltinViewConfig = {}): AppViewModule {\n return {\n id: 'settings',\n label: config.label ?? 'Settings',\n description: 'Configure project metadata and locales.',\n icon: config.icon ?? SettingsIcon,\n availabilitySection: 'settings',\n render: (ctx) => (\n <SectionSurface project={ctx.project} section=\"settings\" forcedState={ctx.search.state}>\n <SettingsForm project={ctx.project} />\n </SectionSurface>\n ),\n }\n}\n\n/** The legacy `builder` redirect — a hidden, routable view (so `/builder?page=` still lands). */\nexport function builderView(): AppViewModule {\n return {\n id: 'builder',\n label: 'Builder',\n description: 'Legacy builder route — redirects to the page document editor.',\n icon: Blocks,\n hidden: true,\n render: (ctx) => (\n // Retired surface (EC-187 slice 6): a page is a document in the `page`\n // collection. `?page=<id>` lands on that document; else the page list.\n <Navigate\n to={STUDIO_PROJECT_ROUTE}\n params={{\n projectId: ctx.projectId,\n _splat: ctx.search.page\n ? studioPath.cmsDocument('page', ctx.search.page)\n : studioPath.cmsCollection('page'),\n }}\n replace\n />\n ),\n }\n}\n\n/** Build a fully custom view (the `nav.view(...)` escape hatch). */\nexport function customView(config: CustomViewConfig): AppViewModule {\n return {\n id: config.id ?? slugify(config.label),\n label: config.label,\n description: config.description ?? config.label,\n icon: config.icon ?? LayoutGrid,\n render: config.render,\n ...(config.parsePath ? { parsePath: config.parsePath } : {}),\n ...(config.availabilitySection ? { availabilitySection: config.availabilitySection } : {}),\n }\n}\n\n/**\n * The studio's built-in views in canonical rail order (components · CMS · routes ·\n * assets · extensions · settings), plus the hidden `builder` redirect (routable but\n * unlisted). The default rail (no `defineStudio({ sidebar })`) is these as plain\n * entries; the router registry always includes them so deep-links resolve even\n * when a consumer's rail omits one.\n */\nexport const DEFAULT_VIEWS: readonly AppViewModule[] = [\n componentsView(),\n cmsView(),\n redirectsView(),\n assetsView(),\n extensionsView(),\n usersView(),\n settingsView(),\n builderView(),\n]\n\n/**\n * The `nav` namespace — the single typed builder for `studio.config.tsx`'s section\n * rail. Each call returns a {@link SidebarViewNode} (or a group / separator) that\n * carries its view DIRECTLY: `nav.cms({ collection })` is a focused CMS view,\n * `nav.view({ render })` a net-new surface, `nav.group(label, [...])` a header with\n * sub-views, `nav.separator()` a divider. No string ids, no `moduleId` — the rail\n * is the views.\n */\n/**\n * Forward the OPTIONAL, default-open nav-permission predicate (EC-240 / AD-18)\n * from a `nav.*` config onto the built view, so gating is settable on EVERY nav\n * item — not just Users. Absent → the view is unchanged (always visible).\n */\nfunction withNavPermission(view: AppViewModule, config: { visible?: NavPermission }): AppViewModule {\n return config.visible ? { ...view, visible: config.visible } : view\n}\n\nexport const nav = {\n /**\n * The CMS view. `nav.cms()` / `nav.cms({ collections })` is a PARENT that nests its\n * collections in the rail (each a focused `cms:<id>` lens); `nav.cms({ collection })`\n * is a single-collection LEAF; `nav.cms({ children })` supplies explicit children.\n */\n cms: (config: CmsViewConfig = {}): SidebarViewNode => {\n const view = withNavPermission(cmsView(config), config)\n // A single-collection lens is a leaf — it opens that collection directly.\n if (config.collection) return viewNode(view)\n // Otherwise a parent that expands its collections as nested rail children (EC-293).\n const node: SidebarViewNode = { kind: 'view', view }\n if (config.children) node.children = config.children\n else\n node.expand = (collections) =>\n focusCollectionList(collections, config.collections).map(cmsCollectionLeaf)\n return node\n },\n /** The component registry browser. */\n components: (config: BuiltinViewConfig = {}): SidebarViewNode =>\n viewNode(withNavPermission(componentsView(config), config)),\n /** The asset library. */\n assets: (config: BuiltinViewConfig = {}): SidebarViewNode =>\n viewNode(withNavPermission(assetsView(config), config)),\n /** Editorial redirects (EC-303); built-in fields customizable by name (EC-311). */\n redirects: (config: RedirectsViewConfig = {}): SidebarViewNode =>\n viewNode(withNavPermission(redirectsView(config), config)),\n /** Project settings. */\n settings: (config: BuiltinViewConfig = {}): SidebarViewNode =>\n viewNode(withNavPermission(settingsView(config), config)),\n /** The extensions marketplace. */\n extensions: (config: BuiltinViewConfig = {}): SidebarViewNode =>\n viewNode(withNavPermission(extensionsView(config), config)),\n /** The admin users roster (EC-240). */\n users: (config: BuiltinViewConfig = {}): SidebarViewNode =>\n viewNode(withNavPermission(usersView(config), config)),\n /** A fully custom view — pass your own `render` (and optional `parsePath`). */\n view: (config: CustomViewConfig): SidebarViewNode =>\n viewNode(withNavPermission(customView(config), config)),\n /** A labelled group of views (a header with sub-entries). */\n group: (label: string, children: SidebarViewNode[]) => groupNode(label, children),\n /** A divider between rail sections. */\n separator: () => separatorNode(),\n} as const\n\n// --- Surfaces (the view render bodies; CMS gains a focus lens) ---\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, then\n * renders the active screen full-width (index, a collection table, or a document\n * editor). EC-293: the collection list lives in the section rail (nested under the\n * CMS view), so there is no in-surface collections sidebar. Forced states (?state=)\n * replace the whole surface. `focusCollections` (EC-239) narrows the index to a\n * subset, so a focused view (\"Catalog\") reuses this surface with config.\n */\nfunction CmsScreen({\n project,\n collectionId,\n documentId,\n state,\n field,\n focusCollections,\n}: {\n project: Project\n collectionId: string | null\n documentId: string | null\n state?: SectionState\n field?: string\n focusCollections?: readonly 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 {/* EC-293: the collection list now lives in the section rail (nested\n under the CMS view), so the surface is full-width — the old 240px\n CollectionsSidebar aside is gone. */}\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 focusCollections={focusCollections}\n />\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 focusCollections,\n}: {\n projectId: string\n collectionId: string | null\n documentId: string | null\n field?: string\n focusCollections?: readonly string[]\n}) {\n const cms = useCms()\n\n if (collectionId === null) return <CmsIndex projectId={projectId} focusCollections={focusCollections} />\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.\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"]}
1
+ {"version":3,"file":"default-modules.js","sourceRoot":"","sources":["../../../src/components/app-views/default-modules.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useEffect } from 'react'\nimport type { ReactNode } from 'react'\nimport type { LucideIcon } from 'lucide-react'\nimport {\n Blocks,\n Database,\n FileCog,\n Image,\n LayoutGrid,\n Puzzle,\n Settings as SettingsIcon,\n Signpost,\n Users,\n} from 'lucide-react'\nimport { extendCollectionDef, isSingleton, resolveLabel, resolveSingletonDocument } from '@elytracms/core/cms-core'\nimport type {\n CollectionDef,\n CollectionFieldOverride,\n CollectionFormMeta,\n} from '@elytracms/core/cms-core'\nimport type { Project, SectionId, SectionState } from '@elytracms/core/studio-core'\nimport { Navigate, STUDIO_PROJECT_ROUTE, studioPath } from '@elytracms/studio/router'\nimport { Text } from '@elytracms/studio/ui'\nimport { translate } from '@elytracms/studio/lib/i18n'\nimport { getInstantiatedKit } from '@elytracms/studio/lib/starters/kit-session'\nimport {\n cmsDeepLink,\n componentsDeepLink,\n usersDeepLink,\n redirectsDeepLink,\n groupNode,\n separatorNode,\n viewNode,\n type AppViewContext,\n type AppViewDeepLink,\n type AppViewModule,\n type NavPermission,\n type SidebarViewNode,\n} from './module'\nimport { SectionSurface } from '../section-surface'\nimport { SectionStateView } from '../section-state'\nimport { SettingsForm } from '../settings-form'\nimport { AssetLibrary } from '../assets/asset-library'\nimport { UsersModule } from '../users/users-module'\nimport { RedirectsModule } from '../redirects/redirects-module'\nimport { redirectsCollectionDef } from '../redirects/redirects-schema'\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 } from '../project-session'\nimport { CmsProvider, useCms } from '../cms/cms-store'\nimport { focusCollectionList } from '../cms/collection-focus'\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-237/239 — the studio's BUILT-IN views + the consumer-facing `nav` namespace,\n * an instance of the EC-236 customization seam.\n *\n * Each built-in surface (the former `StudioSectionResolver` case body) is promoted\n * to a self-contained {@link AppViewModule} factory. The `nav` namespace is the\n * single, typed builder a consuming repo's `studio.config.tsx` uses to declare its\n * section rail — `nav.cms(...)`, `nav.group(...)`, `nav.view(...)` — passing the\n * view objects (and a focus config) DIRECTLY, never a string id. The CMS surface\n * is a factory ({@link cmsView}) so a focused view (\"Catalog\", \"Categories\") is the\n * SAME surface instantiated with config, not a copy — the EC-239 reuse proof.\n */\n\n// --- The CMS surface, reusable via a config factory (the EC-239 reuse seam) ---\n\n/** Config for a CMS-backed view — `collections`/`collection` focus the lens (EC-239). */\nexport interface CmsViewConfig {\n /** Explicit stable id (the route segment). Omitted → derived from the focus. */\n id?: string\n label?: string\n description?: string\n icon?: LucideIcon\n /**\n * Scope the rail auto-expansion (EC-293) to these collection ids, in order. The\n * parent expands into one focused-lens child per id; the parent index shows the\n * same subset. Omitted → every collection. Ignored for a single-collection lens.\n */\n collections?: readonly string[]\n /** Single-collection lens: a LEAF rail item that opens straight into this collection. */\n collection?: string\n /**\n * EC-293 escape hatch: explicit rail children for this CMS parent, overriding the\n * default per-collection auto-expansion. Ignored for a single-collection lens.\n */\n children?: SidebarViewNode[]\n /** The availability section this view rides on (EC-221); defaults to `cms`. */\n availabilitySection?: SectionId\n /** OPTIONAL, default-open nav-permission predicate (EC-240 / AD-18). See {@link BuiltinViewConfig.visible}. */\n visible?: NavPermission\n}\n\n/** Common label/icon overrides for a built-in view (`nav.redirects({ label: 'URLs' })`). */\nexport interface BuiltinViewConfig {\n label?: string\n icon?: LucideIcon\n /**\n * OPTIONAL, default-open nav-permission predicate (EC-240 / AD-18): hide this\n * rail item for principals it returns `false` for; absent → always visible. The\n * policy (which roles) lives in the consuming project, never the engine. E.g.\n * `nav.users({ visible: ({ role }) => role === 'admin' })`.\n */\n visible?: NavPermission\n}\n\n/**\n * Config for the **redirects** rail (EC-311) — a built-in view whose engine-owned\n * schema is customizable. Beyond the shared label/icon/visible, a mount may rename\n * the rail (`form`) and **override built-in fields by equal name** (`fields`):\n * naming, permissions (`form.readOnly`), rendering (`form.control` → `fieldRenderers`)\n * and validation. The canonical `from`/`to`/`permanent`/`enabled` names+types are\n * contract-locked (a type change on one is a loud config error), but a *net-new*\n * field is allowed (EC-311 Tier 2): it's appended and persists in the redirect\n * record's `meta` bag — editorial only, so it never reaches delivery/routing.\n */\nexport interface RedirectsViewConfig extends BuiltinViewConfig {\n /** Rename the rail (e.g. `{ label: 'URLs' }`). */\n form?: Partial<CollectionFormMeta>\n /** Override built-in redirect fields, matched by `name`. */\n fields?: readonly CollectionFieldOverride[]\n}\n\n/** Config for a fully custom view (`nav.view({...})`) — the net-new surface escape hatch. */\nexport interface CustomViewConfig {\n /** Explicit stable id (the route segment). Omitted → a slug of the label. */\n id?: string\n label: string\n description?: string\n icon?: LucideIcon\n render: (ctx: AppViewContext) => ReactNode\n parsePath?: (segments: string[]) => AppViewDeepLink | null\n availabilitySection?: SectionId\n /** OPTIONAL, default-open nav-permission predicate (EC-240 / AD-18). See {@link BuiltinViewConfig.visible}. */\n visible?: NavPermission\n}\n\nfunction slugify(label: string): string {\n return label.trim().toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '') || 'view'\n}\n\n/** Derive a stable, deterministic id from the CMS focus (overridable via `config.id`). */\nfunction cmsViewId(config: CmsViewConfig): string {\n if (config.id) return config.id\n if (config.collection) return `cms:${config.collection}`\n if (config.collections && config.collections.length > 0) {\n return `cms:${[...config.collections].join('-')}`\n }\n return 'cms'\n}\n\n/**\n * Build a CMS view — the default `cms` view, or a focused instance. Every instance\n * shares one surface ({@link CmsScreen}); `collections` narrows the sidebar + index\n * to a subset, `collection` is a single-collection lens that opens straight into\n * that collection's list. The id is derived from the focus (deterministic, stable\n * across rail reorders) unless `id` is given — so two focused views never collide.\n */\nexport function cmsView(config: CmsViewConfig = {}): AppViewModule {\n const single = config.collection\n const focus = single ? [single] : config.collections\n return {\n id: cmsViewId(config),\n label: config.label ?? translate('nav.cms.label'),\n description: config.description ?? translate('nav.cms.description'),\n icon: config.icon ?? Database,\n availabilitySection: config.availabilitySection ?? 'cms',\n // A single-collection lens routes `[]`→that collection, `[d]`→its document;\n // an unfocused / multi-collection view uses the full index/collection/document arity.\n parsePath: single\n ? (segments) =>\n segments.length === 0\n ? { collectionId: single, documentId: null }\n : segments.length === 1\n ? { collectionId: single, documentId: segments[0]! }\n : null\n : cmsDeepLink,\n render: (ctx) => (\n <CmsScreen\n project={ctx.project}\n collectionId={ctx.deepLink.collectionId ?? null}\n documentId={ctx.deepLink.documentId ?? null}\n state={ctx.search.state}\n field={ctx.search.field}\n focusCollections={focus}\n />\n ),\n }\n}\n\n/**\n * A focused-lens rail child for one collection (EC-293) — the SAME `cms:<id>` view a\n * single-collection `nav.cms({ collection })` produces, carrying the collection's own\n * label + a singleton-aware icon (the former `CollectionsSidebar` row, lifted into\n * the rail). CMS auto-expansion maps the project's collections through this.\n */\nfunction cmsCollectionLeaf(collection: CollectionDef): SidebarViewNode {\n const singleton = isSingleton(collection)\n return viewNode(\n cmsView({\n collection: collection.id,\n // Rail resolution is pure (outside React) → default-language collection label.\n label: resolveLabel(collection.form?.label) ?? collection.id,\n icon: singleton ? FileCog : collection.kind === 'asset' ? Image : Database,\n }),\n )\n}\n\n/** The components registry browser view. */\nexport function componentsView(config: BuiltinViewConfig = {}): AppViewModule {\n return {\n id: 'components',\n label: config.label ?? translate('nav.components.label'),\n description: translate('nav.components.description'),\n icon: config.icon ?? Blocks,\n availabilitySection: 'components',\n parsePath: componentsDeepLink,\n render: (ctx) => (\n <ComponentsScreen\n projectId={ctx.projectId}\n componentId={ctx.deepLink.componentId ?? null}\n state={ctx.search.state}\n />\n ),\n }\n}\n\n/**\n * The Redirects view (EC-303) — editorial redirects as DATA, an editor-owned CRUD\n * surface (create/edit/delete) over the portable list/edit surfaces. Routes are no\n * longer shown in the admin (they're code; native `next.config`/`vercel` own static\n * forwards, evaluated before these). Rides on the CMS availability (a content/\n * routing concern). `[]` is the list; `[redirectId]` opens that redirect's detail.\n */\nexport function redirectsView(config: RedirectsViewConfig = {}): AppViewModule {\n // EC-311: merge any field/label overrides over the engine-owned def ONCE, at config\n // build (config-as-code — a locked violation throws here, at `defineStudio` mount).\n // Tier 2: `additiveFields: 'allow'` — a net-new field is appended and persists in the\n // redirect record's `meta` bag (the canonical from/to/permanent/enabled stay locked).\n const schema = extendCollectionDef(redirectsCollectionDef, config, { additiveFields: 'allow' })\n return {\n id: 'redirects',\n label: config.label ?? resolveLabel(schema.form?.label) ?? translate('nav.redirects.label'),\n description: translate('nav.redirects.description'),\n icon: config.icon ?? Signpost,\n availabilitySection: 'cms',\n parsePath: redirectsDeepLink,\n render: (ctx) =>\n ctx.search.state && ctx.search.state !== 'working' ? (\n <SectionStateView state={ctx.search.state} title=\"Redirects\" />\n ) : (\n <RedirectsModule\n projectId={ctx.projectId}\n redirectId={ctx.deepLink.redirectId ?? null}\n schema={schema}\n />\n ),\n }\n}\n\n/** The asset library view. */\nexport function assetsView(config: BuiltinViewConfig = {}): AppViewModule {\n return {\n id: 'assets',\n label: config.label ?? translate('nav.assets.label'),\n description: translate('nav.assets.description'),\n icon: config.icon ?? Image,\n availabilitySection: 'assets',\n render: (ctx) => (\n <SectionSurface project={ctx.project} section=\"assets\" forcedState={ctx.search.state}>\n {/* The AssetsProvider is mounted at the project level (EC-195). */}\n <AssetLibrary projectId={ctx.projectId} focusAssetId={ctx.search.asset} />\n </SectionSurface>\n ),\n }\n}\n\n/**\n * The studio Users view (EC-240) — an admin-only roster of who has access to this\n * deployment and their role. A first-class section (its own rail entry), modeled\n * like the Assets library; the surface itself gates to admins and renders a\n * \"restricted\" state otherwise. Rides on the `settings` availability (an\n * admin/config concern, so it appears/hides with a project's settings surface).\n */\nexport function usersView(config: BuiltinViewConfig = {}): AppViewModule {\n return {\n id: 'users',\n label: config.label ?? translate('nav.users.label'),\n description: translate('nav.users.description'),\n icon: config.icon ?? Users,\n availabilitySection: 'users',\n // EC-240: `[]` is the roster; `[userId]` opens that member in the portable edit\n // shell (AD-19 reuse — the same `DetailEditor` body over the users source).\n parsePath: usersDeepLink,\n render: (ctx) =>\n ctx.search.state && ctx.search.state !== 'working' ? (\n <SectionStateView state={ctx.search.state} title=\"Users\" />\n ) : (\n <UsersModule projectId={ctx.projectId} userId={ctx.deepLink.userId ?? null} />\n ),\n }\n}\n\n/** The extensions marketplace view. */\nexport function extensionsView(config: BuiltinViewConfig = {}): AppViewModule {\n return {\n id: 'extensions',\n label: config.label ?? translate('nav.extensions.label'),\n description: translate('nav.extensions.description'),\n icon: config.icon ?? Puzzle,\n availabilitySection: 'extensions',\n render: (ctx) =>\n ctx.search.state && ctx.search.state !== 'working' ? (\n <SectionStateView state={ctx.search.state} title=\"Extensions\" />\n ) : (\n <ExtensionsView />\n ),\n }\n}\n\n/** The project settings view. */\nexport function settingsView(config: BuiltinViewConfig = {}): AppViewModule {\n return {\n id: 'settings',\n label: config.label ?? translate('nav.settings.label'),\n description: translate('nav.settings.description'),\n icon: config.icon ?? SettingsIcon,\n availabilitySection: 'settings',\n render: (ctx) => (\n <SectionSurface project={ctx.project} section=\"settings\" forcedState={ctx.search.state}>\n <SettingsForm project={ctx.project} />\n </SectionSurface>\n ),\n }\n}\n\n/** The legacy `builder` redirect — a hidden, routable view (so `/builder?page=` still lands). */\nexport function builderView(): AppViewModule {\n return {\n id: 'builder',\n label: 'Builder',\n description: 'Legacy builder route — redirects to the page document editor.',\n icon: Blocks,\n hidden: true,\n render: (ctx) => (\n // Retired surface (EC-187 slice 6): a page is a document in the `page`\n // collection. `?page=<id>` lands on that document; else the page list.\n <Navigate\n to={STUDIO_PROJECT_ROUTE}\n params={{\n projectId: ctx.projectId,\n _splat: ctx.search.page\n ? studioPath.cmsDocument('page', ctx.search.page)\n : studioPath.cmsCollection('page'),\n }}\n replace\n />\n ),\n }\n}\n\n/** Build a fully custom view (the `nav.view(...)` escape hatch). */\nexport function customView(config: CustomViewConfig): AppViewModule {\n return {\n id: config.id ?? slugify(config.label),\n label: config.label,\n description: config.description ?? config.label,\n icon: config.icon ?? LayoutGrid,\n render: config.render,\n ...(config.parsePath ? { parsePath: config.parsePath } : {}),\n ...(config.availabilitySection ? { availabilitySection: config.availabilitySection } : {}),\n }\n}\n\n/**\n * The studio's built-in views in canonical rail order (components · CMS · routes ·\n * assets · extensions · settings), plus the hidden `builder` redirect (routable but\n * unlisted). The default rail (no `defineStudio({ sidebar })`) is these as plain\n * entries; the router registry always includes them so deep-links resolve even\n * when a consumer's rail omits one.\n */\nexport const DEFAULT_VIEWS: readonly AppViewModule[] = [\n componentsView(),\n cmsView(),\n redirectsView(),\n assetsView(),\n extensionsView(),\n usersView(),\n settingsView(),\n builderView(),\n]\n\n/**\n * The `nav` namespace — the single typed builder for `studio.config.tsx`'s section\n * rail. Each call returns a {@link SidebarViewNode} (or a group / separator) that\n * carries its view DIRECTLY: `nav.cms({ collection })` is a focused CMS view,\n * `nav.view({ render })` a net-new surface, `nav.group(label, [...])` a header with\n * sub-views, `nav.separator()` a divider. No string ids, no `moduleId` — the rail\n * is the views.\n */\n/**\n * Forward the OPTIONAL, default-open nav-permission predicate (EC-240 / AD-18)\n * from a `nav.*` config onto the built view, so gating is settable on EVERY nav\n * item — not just Users. Absent → the view is unchanged (always visible).\n */\nfunction withNavPermission(view: AppViewModule, config: { visible?: NavPermission }): AppViewModule {\n return config.visible ? { ...view, visible: config.visible } : view\n}\n\nexport const nav = {\n /**\n * The CMS view. `nav.cms()` / `nav.cms({ collections })` is a PARENT that nests its\n * collections in the rail (each a focused `cms:<id>` lens); `nav.cms({ collection })`\n * is a single-collection LEAF; `nav.cms({ children })` supplies explicit children.\n */\n cms: (config: CmsViewConfig = {}): SidebarViewNode => {\n const view = withNavPermission(cmsView(config), config)\n // A single-collection lens is a leaf — it opens that collection directly.\n if (config.collection) return viewNode(view)\n // Otherwise a parent that expands its collections as nested rail children (EC-293).\n const node: SidebarViewNode = { kind: 'view', view }\n if (config.children) node.children = config.children\n else\n node.expand = (collections) =>\n focusCollectionList(collections, config.collections).map(cmsCollectionLeaf)\n return node\n },\n /** The component registry browser. */\n components: (config: BuiltinViewConfig = {}): SidebarViewNode =>\n viewNode(withNavPermission(componentsView(config), config)),\n /** The asset library. */\n assets: (config: BuiltinViewConfig = {}): SidebarViewNode =>\n viewNode(withNavPermission(assetsView(config), config)),\n /** Editorial redirects (EC-303); built-in fields customizable by name (EC-311). */\n redirects: (config: RedirectsViewConfig = {}): SidebarViewNode =>\n viewNode(withNavPermission(redirectsView(config), config)),\n /** Project settings. */\n settings: (config: BuiltinViewConfig = {}): SidebarViewNode =>\n viewNode(withNavPermission(settingsView(config), config)),\n /** The extensions marketplace. */\n extensions: (config: BuiltinViewConfig = {}): SidebarViewNode =>\n viewNode(withNavPermission(extensionsView(config), config)),\n /** The admin users roster (EC-240). */\n users: (config: BuiltinViewConfig = {}): SidebarViewNode =>\n viewNode(withNavPermission(usersView(config), config)),\n /** A fully custom view — pass your own `render` (and optional `parsePath`). */\n view: (config: CustomViewConfig): SidebarViewNode =>\n viewNode(withNavPermission(customView(config), config)),\n /** A labelled group of views (a header with sub-entries). */\n group: (label: string, children: SidebarViewNode[]) => groupNode(label, children),\n /** A divider between rail sections. */\n separator: () => separatorNode(),\n} as const\n\n// --- Surfaces (the view render bodies; CMS gains a focus lens) ---\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, then\n * renders the active screen full-width (index, a collection table, or a document\n * editor). EC-293: the collection list lives in the section rail (nested under the\n * CMS view), so there is no in-surface collections sidebar. Forced states (?state=)\n * replace the whole surface. `focusCollections` (EC-239) narrows the index to a\n * subset, so a focused view (\"Catalog\") reuses this surface with config.\n */\nfunction CmsScreen({\n project,\n collectionId,\n documentId,\n state,\n field,\n focusCollections,\n}: {\n project: Project\n collectionId: string | null\n documentId: string | null\n state?: SectionState\n field?: string\n focusCollections?: readonly 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 {/* EC-293: the collection list now lives in the section rail (nested\n under the CMS view), so the surface is full-width — the old 240px\n CollectionsSidebar aside is gone. */}\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 focusCollections={focusCollections}\n />\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 focusCollections,\n}: {\n projectId: string\n collectionId: string | null\n documentId: string | null\n field?: string\n focusCollections?: readonly string[]\n}) {\n const cms = useCms()\n\n if (collectionId === null) return <CmsIndex projectId={projectId} focusCollections={focusCollections} />\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.\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 {resolveLabel(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"]}
@@ -1,5 +1,6 @@
1
1
  import type { ReactNode } from 'react';
2
2
  import type { FieldDef } from '@elytracms/core/cms-core';
3
+ import type { StudioI18nConfig } from '@elytracms/studio/lib/i18n';
3
4
  import type { SidebarNode } from './module';
4
5
  /**
5
6
  * EC-236 — the studio customization config: the single, studio-only home for
@@ -37,6 +38,15 @@ export interface StudioConfig {
37
38
  * schema, this map binds that name to a studio-only component.
38
39
  */
39
40
  fieldRenderers?: Record<string, CustomFieldRenderer>;
41
+ /**
42
+ * Studio-UI localization (EC-241): the default locale + imported translation
43
+ * catalogs. English is the built-in source; a consumer adds e.g.
44
+ * `i18n: { locales: { de: deMessages } }` and the studio chrome renders in the
45
+ * browser's preferred locale (no persisted selector in v1). Studio-only — never
46
+ * reaches the delivery bundle. Schema labels localize separately, via a
47
+ * `LocalizedString` on the field/collection def.
48
+ */
49
+ i18n?: StudioI18nConfig;
40
50
  }
41
51
  /** The props a custom field renderer receives — identical to the built-in control. */
42
52
  export interface CustomFieldRendererProps {
@@ -1 +1 @@
1
- {"version":3,"file":"studio-config.js","sourceRoot":"","sources":["../../../src/components/app-views/studio-config.ts"],"names":[],"mappings":"AAoDA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,UAAU,YAAY,CAAC,MAAoB;IAC/C,OAAO,MAAM,CAAA;AACf,CAAC","sourcesContent":["import type { ReactNode } from 'react'\nimport type { FieldDef } from '@elytracms/core/cms-core'\nimport type { SidebarNode } from './module'\n\n/**\n * EC-236 — the studio customization config: the single, studio-only home for\n * everything a consuming repo customizes about the builder UI.\n *\n * This is the \"separate studio-only config solution\": it lives in the consumer's\n * `studio.config.tsx`, is `defineStudio({...})`-typed, and is passed to\n * `createStudio(config, studioConfig)` at the mount — NEVER mixed into the shared\n * `elytra.config.ts`, so the live React it carries (views, icons, field renderers,\n * future branding) never reaches the `frontend/` delivery bundle.\n *\n * The customization PRINCIPLE it embodies: customization is **code passed\n * directly**, not data referenced by a string key. Every field here holds live\n * values/components by reference — `sidebar` is built from `nav.*` view objects,\n * `fieldRenderers` maps a control name to a component. The only string indirection\n * in the whole surface is `fieldRenderers`' key, and ONLY because its other end\n * (`form.control` on a field) lives in the delivery-shared serializable schema and\n * cannot import studio code. This config grows by adding direct-value keys\n * (branding/logo/panels/theme), never by adding registries.\n */\nexport interface StudioConfig {\n /**\n * The section rail (EC-237) as an ordered list of `nav.*` nodes — views, groups,\n * and separators. Omitted → the studio derives the default rail from its built-in\n * views. A rail node carries its view object directly; there are no string ids to\n * keep in sync.\n */\n sidebar?: SidebarNode[]\n /**\n * Custom field renderers (EC-238) keyed by CONTROL name (`form.control`), each a\n * React component replacing the built-in document-editor control. A field opts in\n * per-field via `form: { control: '<name>' }` — so a renderer targets exactly the\n * fields that ask for it, not every field of a datatype. This is the one\n * boundary-crossing seam: the field names its control by string in the shared\n * schema, this map binds that name to a studio-only component.\n */\n fieldRenderers?: Record<string, CustomFieldRenderer>\n}\n\n/** The props a custom field renderer receives — identical to the built-in control. */\nexport interface CustomFieldRendererProps {\n field: FieldDef\n value: unknown\n onChange: (value: unknown) => void\n readOnly: boolean\n id: string\n}\nexport type CustomFieldRenderer = (props: CustomFieldRendererProps) => ReactNode\n\n/**\n * Typed identity helper for `studio.config.tsx` — keeps the config declarative\n * (like {@link import('@elytracms/core/studio-core').defineElytraConfig}). No\n * validation here; the studio resolves + validates the sidebar and field renderers\n * through the EC-236 seam at mount, surfacing every issue visibly (never throws).\n *\n * @example\n * ```tsx\n * // studio.config.tsx\n * import { defineStudio, nav } from '@elytracms/studio/customization'\n * import { ColorField } from './fields/color'\n *\n * export default defineStudio({\n * sidebar: [\n * nav.cms({ label: 'Content' }),\n * nav.group('Catalog', [\n * nav.cms({ label: 'Catalog', collections: ['product', 'productCategory'] }),\n * nav.cms({ label: 'Categories', collection: 'productCategory' }),\n * ]),\n * nav.separator(),\n * nav.assets(),\n * nav.settings(),\n * ],\n * fieldRenderers: { color: ColorField },\n * })\n * ```\n */\nexport function defineStudio(config: StudioConfig): StudioConfig {\n return config\n}\n"]}
1
+ {"version":3,"file":"studio-config.js","sourceRoot":"","sources":["../../../src/components/app-views/studio-config.ts"],"names":[],"mappings":"AA8DA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,UAAU,YAAY,CAAC,MAAoB;IAC/C,OAAO,MAAM,CAAA;AACf,CAAC","sourcesContent":["import type { ReactNode } from 'react'\nimport type { FieldDef } from '@elytracms/core/cms-core'\nimport type { StudioI18nConfig } from '@elytracms/studio/lib/i18n'\nimport type { SidebarNode } from './module'\n\n/**\n * EC-236 — the studio customization config: the single, studio-only home for\n * everything a consuming repo customizes about the builder UI.\n *\n * This is the \"separate studio-only config solution\": it lives in the consumer's\n * `studio.config.tsx`, is `defineStudio({...})`-typed, and is passed to\n * `createStudio(config, studioConfig)` at the mount — NEVER mixed into the shared\n * `elytra.config.ts`, so the live React it carries (views, icons, field renderers,\n * future branding) never reaches the `frontend/` delivery bundle.\n *\n * The customization PRINCIPLE it embodies: customization is **code passed\n * directly**, not data referenced by a string key. Every field here holds live\n * values/components by reference — `sidebar` is built from `nav.*` view objects,\n * `fieldRenderers` maps a control name to a component. The only string indirection\n * in the whole surface is `fieldRenderers`' key, and ONLY because its other end\n * (`form.control` on a field) lives in the delivery-shared serializable schema and\n * cannot import studio code. This config grows by adding direct-value keys\n * (branding/logo/panels/theme), never by adding registries.\n */\nexport interface StudioConfig {\n /**\n * The section rail (EC-237) as an ordered list of `nav.*` nodes — views, groups,\n * and separators. Omitted → the studio derives the default rail from its built-in\n * views. A rail node carries its view object directly; there are no string ids to\n * keep in sync.\n */\n sidebar?: SidebarNode[]\n /**\n * Custom field renderers (EC-238) keyed by CONTROL name (`form.control`), each a\n * React component replacing the built-in document-editor control. A field opts in\n * per-field via `form: { control: '<name>' }` — so a renderer targets exactly the\n * fields that ask for it, not every field of a datatype. This is the one\n * boundary-crossing seam: the field names its control by string in the shared\n * schema, this map binds that name to a studio-only component.\n */\n fieldRenderers?: Record<string, CustomFieldRenderer>\n /**\n * Studio-UI localization (EC-241): the default locale + imported translation\n * catalogs. English is the built-in source; a consumer adds e.g.\n * `i18n: { locales: { de: deMessages } }` and the studio chrome renders in the\n * browser's preferred locale (no persisted selector in v1). Studio-only — never\n * reaches the delivery bundle. Schema labels localize separately, via a\n * `LocalizedString` on the field/collection def.\n */\n i18n?: StudioI18nConfig\n}\n\n/** The props a custom field renderer receives — identical to the built-in control. */\nexport interface CustomFieldRendererProps {\n field: FieldDef\n value: unknown\n onChange: (value: unknown) => void\n readOnly: boolean\n id: string\n}\nexport type CustomFieldRenderer = (props: CustomFieldRendererProps) => ReactNode\n\n/**\n * Typed identity helper for `studio.config.tsx` — keeps the config declarative\n * (like {@link import('@elytracms/core/studio-core').defineElytraConfig}). No\n * validation here; the studio resolves + validates the sidebar and field renderers\n * through the EC-236 seam at mount, surfacing every issue visibly (never throws).\n *\n * @example\n * ```tsx\n * // studio.config.tsx\n * import { defineStudio, nav } from '@elytracms/studio/customization'\n * import { ColorField } from './fields/color'\n *\n * export default defineStudio({\n * sidebar: [\n * nav.cms({ label: 'Content' }),\n * nav.group('Catalog', [\n * nav.cms({ label: 'Catalog', collections: ['product', 'productCategory'] }),\n * nav.cms({ label: 'Categories', collection: 'productCategory' }),\n * ]),\n * nav.separator(),\n * nav.assets(),\n * nav.settings(),\n * ],\n * fieldRenderers: { color: ColorField },\n * })\n * ```\n */\nexport function defineStudio(config: StudioConfig): StudioConfig {\n return config\n}\n"]}
@@ -6,6 +6,7 @@ import { fieldValueValidator } from '@elytracms/core/cms-core';
6
6
  import { getStudioRegistry } from '@elytracms/studio/lib/registry';
7
7
  import { Badge, Code, Field, Input, InspectorSection, PanelHeader, Row, Select, Spacer, Stack, Text, } from '@elytracms/studio/ui';
8
8
  import { humanizeName } from '@elytracms/studio/lib/utils';
9
+ import { localizedLabel } from '@elytracms/studio/lib/i18n';
9
10
  import { componentDisplayName } from '@elytracms/studio/lib/persona/labels';
10
11
  import { FieldControl } from '../cms/field-control';
11
12
  import { FieldDensityProvider } from '../cms/field-density';
@@ -44,7 +45,7 @@ function SettingsTab({ node, manifest, }) {
44
45
  }
45
46
  function PropField({ node, name, spec }) {
46
47
  const value = node.props?.[name];
47
- return (_jsx(Field, { label: spec.form?.label ?? name, htmlFor: `prop-${node.id}-${name}`, children: _jsx(StaticControl, { node: node, name: name, spec: spec, value: value }) }));
48
+ return (_jsx(Field, { label: localizedLabel(spec.form?.label, name), htmlFor: `prop-${node.id}-${name}`, children: _jsx(StaticControl, { node: node, name: name, spec: spec, value: value }) }));
48
49
  }
49
50
  function controlKind(spec) {
50
51
  switch (spec.type) {
@@ -107,7 +108,7 @@ function StaticControl({ node, name, spec, value, }) {
107
108
  // EC-190: select option values are strings (field-def `options`).
108
109
  const opt = options.find((o) => o.value === e.target.value);
109
110
  set(opt ? opt.value : e.target.value);
110
- }, children: options.map((opt) => (_jsx("option", { value: opt.value, children: opt.label ?? opt.value }, opt.value))) }));
111
+ }, children: options.map((opt) => (_jsx("option", { value: opt.value, children: localizedLabel(opt.label, opt.value) }, opt.value))) }));
111
112
  }
112
113
  else if (kind === 'number') {
113
114
  control = _jsx(NumberControl, { id: id, current: current, onSet: set, onClear: clear });
@@ -1 +1 @@
1
- {"version":3,"file":"property-inspector.js","sourceRoot":"","sources":["../../../src/components/builder/property-inspector.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useState } from 'react'\nimport { staticProp } from '@elytracms/core/project-graph'\nimport type { ComponentNode, JsonValue, PropValue } from '@elytracms/core/project-graph'\nimport type { ComponentManifest, PropField } from '@elytracms/core/component-registry'\nimport { manifestSlots } from '@elytracms/core/component-registry'\nimport { fieldValueValidator } from '@elytracms/core/cms-core'\nimport type { FieldDef, SelectOption } from '@elytracms/core/cms-core'\nimport { getStudioRegistry } from '@elytracms/studio/lib/registry'\nimport {\n Badge,\n Code,\n Field,\n Input,\n InspectorSection,\n PanelHeader,\n Row,\n Select,\n Spacer,\n Stack,\n Text,\n} from '@elytracms/studio/ui'\nimport { humanizeName } from '@elytracms/studio/lib/utils'\nimport { componentDisplayName } from '@elytracms/studio/lib/persona/labels'\nimport { FieldControl } from '../cms/field-control'\nimport { FieldDensityProvider } from '../cms/field-density'\nimport { useBuilder } from './builder-store'\nimport { LayoutSection, layoutManagedPropNames } from './layout-inspector'\n\nexport function PropertyInspector() {\n const builder = useBuilder()\n const studio = getStudioRegistry()\n const node = builder.selectedNode\n\n if (!node) {\n return (\n <div className=\"grid h-full place-items-center p-6 text-center\">\n <Text size=\"sm\" tone=\"muted\">\n Select a node in the tree or canvas to edit its properties.\n </Text>\n </div>\n )\n }\n\n const manifest = studio.registry.getManifest(node.componentId)\n\n return (\n <div className=\"flex h-full min-h-0 flex-col\">\n <PanelHeader>\n <Text size=\"sm\" weight=\"semibold\">\n Inspector\n </Text>\n <Spacer />\n <Code tone=\"muted\" title={node.componentId}>\n {componentDisplayName(node.componentId, manifest)}\n </Code>\n </PanelHeader>\n\n <div className=\"min-h-0 flex-1 overflow-auto\">\n <SettingsTab node={node} manifest={manifest} />\n </div>\n </div>\n )\n}\n\n/** The full property surface: node identity, static props, condition, slots. */\nfunction SettingsTab({\n node,\n manifest,\n}: {\n node: ComponentNode\n manifest: ComponentManifest | undefined\n}) {\n // Container layout props render in the dedicated Layout section (EC-162),\n // not as a second set of generic controls.\n const layoutManaged = new Set(layoutManagedPropNames(node))\n // EC-190: `blocks` props are slots (named child regions), edited via the page\n // tree / composition surface, not as a scalar control — so they show in the\n // Slots section, not Props.\n const props = manifest\n ? Object.entries(manifest.props).filter(\n ([name, spec]) => !layoutManaged.has(name) && spec.type !== 'blocks',\n )\n : []\n const slots = manifest ? manifestSlots(manifest) : []\n\n return (\n <>\n {/* EC-192: the editor-facing inspector no longer renders the raw node id or\n namespaced component id (the persona promise). The component's human name\n is in the panel header (with the manifest id in its `title`); the canvas\n and tree provide selection. */}\n {/* Contextual layout inspector for Stack/Grid containers (EC-162). */}\n <LayoutSection node={node} />\n\n {!manifest ? (\n <div className=\"p-3\">\n <Badge variant=\"error\">unknown component</Badge>\n <Text size=\"xs\" tone=\"muted\" className=\"mt-2\">\n No manifest is registered for this component, so its props can't be edited.\n </Text>\n </div>\n ) : (\n <InspectorSection title=\"Props\">\n {props.length === 0 ? (\n <Text size=\"xs\" tone=\"subtle\">\n No editable props.\n </Text>\n ) : (\n <Stack gap=\"4\">\n {props.map(([name, spec]) => (\n // Key by node id so per-node local control state never leaks\n // across a selection change to a same-named prop.\n <PropField key={`${node.id}:${name}`} node={node} name={name} spec={spec} />\n ))}\n </Stack>\n )}\n </InspectorSection>\n )}\n\n {slots.length > 0 ? (\n <InspectorSection title=\"Slots\">\n <Stack gap=\"1\">\n {slots.map((slot) => {\n const count = node.slots?.[slot.name]?.length ?? 0\n const missing = slot.required && count === 0\n return (\n <Row key={slot.name} gap=\"2\">\n <Text size=\"xs\" title={slot.name}>\n {humanizeName(slot.name)}\n </Text>\n {slot.required ? (\n <Badge variant={missing ? 'error' : 'outline'}>required</Badge>\n ) : null}\n <Spacer />\n <Text size=\"xs\" tone=\"subtle\" mono>\n {count}\n </Text>\n </Row>\n )\n })}\n </Stack>\n <Text size=\"xs\" tone=\"subtle\" className=\"mt-2\">\n Add or reorder slot children from the page tree.\n </Text>\n </InspectorSection>\n ) : null}\n </>\n )\n}\n\nfunction PropField({ node, name, spec }: { node: ComponentNode; name: string; spec: PropField }) {\n const value = node.props?.[name]\n\n return (\n <Field label={spec.form?.label ?? name} htmlFor={`prop-${node.id}-${name}`}>\n <StaticControl node={node} name={name} spec={spec} value={value} />\n </Field>\n )\n}\n\n/**\n * EC-190: the inspector control to render for a prop field-def. The old\n * `control.kind` vocabulary derives from the field `type` plus the optional\n * `form.control` presentation hint on `text` fields. Slice 3 unifies this with\n * the CMS `FieldControl`; for now it keeps the existing per-kind control set.\n */\ntype ControlKind =\n | 'text'\n | 'textarea'\n | 'number'\n | 'boolean'\n | 'select'\n | 'json'\n | 'color'\n | 'richtext'\n | 'asset'\n | 'object'\n | 'relation'\nfunction controlKind(spec: PropField): ControlKind {\n switch (spec.type) {\n case 'boolean':\n return 'boolean'\n case 'number':\n return 'number'\n case 'select':\n return 'select'\n case 'richText':\n return 'richtext'\n // EC-195: an `asset`-typed prop (e.g. the Image `src`) picks from the asset\n // library — never a raw asset id typed into a text box (the EC-192 persona).\n case 'asset':\n return 'asset'\n // EC-253: an `object`/repeater prop reuses the CMS repeater control below.\n case 'object':\n return 'object'\n // EC-262: a `relation`-typed prop (e.g. a ProductGrid's `category`) reuses the\n // CMS relation picker — never a text box that stringifies the ref to \"[object Object]\".\n case 'relation':\n return 'relation'\n case 'text': {\n const c = spec.form?.control\n if (c === 'textarea') return 'textarea'\n if (c === 'color') return 'color'\n if (c === 'json') return 'json'\n return 'text'\n }\n default:\n return 'text'\n }\n}\n\nfunction StaticControl({\n node,\n name,\n spec,\n value,\n}: {\n node: ComponentNode\n name: string\n spec: PropField\n value: PropValue | undefined\n}) {\n const builder = useBuilder()\n const current = value?.kind === 'static' ? value.value : (spec.default as JsonValue | undefined)\n const id = `prop-${node.id}-${name}`\n const coalesceKey = `${node.id}:${name}`\n\n function set(v: JsonValue) {\n builder.dispatch({ type: 'setProp', id: node.id, name, value: staticProp(v), coalesceKey })\n }\n function clear() {\n builder.dispatch({ type: 'removeProp', id: node.id, name })\n }\n\n // EC-190: validate with the prop's field-def value validator (was `spec.schema.safeParse`).\n const valid = current === undefined || fieldValueValidator({ ...spec, name } as FieldDef)(current)\n // EC-190: derive the editor control from the field `type` + optional `form.control`.\n const kind = controlKind(spec)\n // Select options live on the `select` field-def (was `spec.control.options`).\n const options: readonly SelectOption[] = spec.type === 'select' ? spec.options : []\n\n let control\n if (kind === 'boolean') {\n control = (\n <label className=\"flex h-8 items-center gap-2 text-sm\">\n <input\n id={id}\n type=\"checkbox\"\n checked={current === true}\n onChange={(e) => set(e.target.checked)}\n className=\"size-4 accent-primary\"\n />\n <Text size=\"sm\" tone=\"muted\">\n {current === true ? 'true' : 'false'}\n </Text>\n </label>\n )\n } else if (kind === 'select') {\n control = (\n <Select\n id={id}\n value={String(current ?? '')}\n onChange={(e) => {\n // EC-190: select option values are strings (field-def `options`).\n const opt = options.find((o) => o.value === e.target.value)\n set(opt ? opt.value : e.target.value)\n }}\n >\n {options.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {opt.label ?? opt.value}\n </option>\n ))}\n </Select>\n )\n } else if (kind === 'number') {\n control = <NumberControl id={id} current={current} onSet={set} onClear={clear} />\n } else if (kind === 'json') {\n control = <JsonControl id={id} current={current} onSet={set} />\n } else if (kind === 'color') {\n control = (\n <Row gap=\"2\">\n <input\n id={id}\n type=\"color\"\n value={\n typeof current === 'string' && /^#[0-9a-f]{6}$/i.test(current) ? current : '#000000'\n }\n onChange={(e) => set(e.target.value)}\n className=\"h-8 w-10 shrink-0 rounded-md border border-input bg-background\"\n />\n <Input\n value={typeof current === 'string' ? current : ''}\n onChange={(e) => set(e.target.value)}\n />\n </Row>\n )\n } else if (kind === 'textarea' || kind === 'richtext') {\n control = (\n <textarea\n id={id}\n rows={2}\n value={typeof current === 'string' ? current : ''}\n onChange={(e) => set(e.target.value)}\n className=\"flex w-full rounded-md border border-input bg-background px-2.5 py-1.5 text-sm text-foreground shadow-sm focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/40 outline-none\"\n />\n )\n } else if (kind === 'asset' || kind === 'object' || kind === 'relation') {\n // EC-195/EC-253/EC-262: reuse the CMS field control. `asset` = library picker +\n // preview; `object` = the nested/repeater form; `relation` = the document picker\n // (resolves the ref to its title, instead of stringifying it to \"[object Object]\").\n // All edit a structured static prop value through the same shared control documents use.\n control = (\n <FieldDensityProvider density=\"compact\">\n <FieldControl\n id={id}\n field={{ ...spec, name } as FieldDef}\n value={current}\n onChange={(v) => (v === undefined || v === null ? clear() : set(v as JsonValue))}\n readOnly={false}\n />\n </FieldDensityProvider>\n )\n } else {\n control = (\n <Input\n id={id}\n value={typeof current === 'string' ? current : String(current ?? '')}\n onChange={(e) => set(e.target.value)}\n />\n )\n }\n\n return (\n <Stack gap=\"1\">\n {control}\n {!valid ? (\n <Text size=\"xs\" className=\"text-destructive\">\n Value doesn't match the prop schema — the canvas falls back to the default.\n </Text>\n ) : null}\n </Stack>\n )\n}\n\n/** Number input with a local buffer: commits only finite values, never NaN; empty clears the prop. */\nfunction NumberControl({\n id,\n current,\n onSet,\n onClear,\n}: {\n id: string\n current: JsonValue | undefined\n onSet: (v: number) => void\n onClear: () => void\n}) {\n const [raw, setRaw] = useState(typeof current === 'number' ? String(current) : '')\n useEffect(() => {\n setRaw(typeof current === 'number' ? String(current) : '')\n }, [current])\n return (\n <Input\n id={id}\n type=\"number\"\n value={raw}\n onChange={(e) => {\n const v = e.target.value\n setRaw(v)\n if (v.trim() === '') {\n onClear()\n return\n }\n const n = Number(v)\n if (Number.isFinite(n)) onSet(n)\n }}\n />\n )\n}\n\n/** JSON textarea with a local buffer: commits only when the text parses; flags parse errors. */\nfunction JsonControl({\n id,\n current,\n onSet,\n}: {\n id: string\n current: JsonValue | undefined\n onSet: (v: JsonValue) => void\n}) {\n const [raw, setRaw] = useState(() => JSON.stringify(current ?? null))\n const [error, setError] = useState<string | null>(null)\n useEffect(() => {\n setRaw(JSON.stringify(current ?? null))\n setError(null)\n }, [current])\n return (\n <Stack gap=\"1\">\n <textarea\n id={id}\n rows={3}\n value={raw}\n onChange={(e) => {\n const v = e.target.value\n setRaw(v)\n try {\n onSet(JSON.parse(v) as JsonValue)\n setError(null)\n } catch {\n setError('Invalid JSON')\n }\n }}\n className=\"flex w-full rounded-md border border-input bg-background px-2.5 py-1.5 font-mono text-xs text-foreground shadow-sm focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/40 outline-none\"\n />\n {error ? (\n <Text size=\"xs\" className=\"text-destructive\">\n {error}\n </Text>\n ) : null}\n </Stack>\n )\n}\n\n"]}
1
+ {"version":3,"file":"property-inspector.js","sourceRoot":"","sources":["../../../src/components/builder/property-inspector.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAA;AAG1D,OAAO,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAA;AAClE,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAE9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AAClE,OAAO,EACL,KAAK,EACL,IAAI,EACJ,KAAK,EACL,KAAK,EACL,gBAAgB,EAChB,WAAW,EACX,GAAG,EACH,MAAM,EACN,MAAM,EACN,KAAK,EACL,IAAI,GACL,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAA;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAA;AAC3D,OAAO,EAAE,oBAAoB,EAAE,MAAM,sCAAsC,CAAA;AAC3E,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AACnD,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAC5C,OAAO,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAA;AAE1E,MAAM,UAAU,iBAAiB;IAC/B,MAAM,OAAO,GAAG,UAAU,EAAE,CAAA;IAC5B,MAAM,MAAM,GAAG,iBAAiB,EAAE,CAAA;IAClC,MAAM,IAAI,GAAG,OAAO,CAAC,YAAY,CAAA;IAEjC,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,CACL,cAAK,SAAS,EAAC,gDAAgD,YAC7D,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,OAAO,4EAErB,GACH,CACP,CAAA;IACH,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;IAE9D,OAAO,CACL,eAAK,SAAS,EAAC,8BAA8B,aAC3C,MAAC,WAAW,eACV,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,MAAM,EAAC,UAAU,0BAE1B,EACP,KAAC,MAAM,KAAG,EACV,KAAC,IAAI,IAAC,IAAI,EAAC,OAAO,EAAC,KAAK,EAAE,IAAI,CAAC,WAAW,YACvC,oBAAoB,CAAC,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,GAC5C,IACK,EAEd,cAAK,SAAS,EAAC,8BAA8B,YAC3C,KAAC,WAAW,IAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,GAAI,GAC3C,IACF,CACP,CAAA;AACH,CAAC;AAED,gFAAgF;AAChF,SAAS,WAAW,CAAC,EACnB,IAAI,EACJ,QAAQ,GAIT;IACC,0EAA0E;IAC1E,2CAA2C;IAC3C,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,sBAAsB,CAAC,IAAI,CAAC,CAAC,CAAA;IAC3D,8EAA8E;IAC9E,4EAA4E;IAC5E,4BAA4B;IAC5B,MAAM,KAAK,GAAG,QAAQ;QACpB,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,MAAM,CACnC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,CACrE;QACH,CAAC,CAAC,EAAE,CAAA;IACN,MAAM,KAAK,GAAG,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;IAErD,OAAO,CACL,8BAME,KAAC,aAAa,IAAC,IAAI,EAAE,IAAI,GAAI,EAE5B,CAAC,QAAQ,CAAC,CAAC,CAAC,CACX,eAAK,SAAS,EAAC,KAAK,aAClB,KAAC,KAAK,IAAC,OAAO,EAAC,OAAO,kCAA0B,EAChD,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,OAAO,EAAC,SAAS,EAAC,MAAM,4FAEtC,IACH,CACP,CAAC,CAAC,CAAC,CACF,KAAC,gBAAgB,IAAC,KAAK,EAAC,OAAO,YAC5B,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CACpB,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,QAAQ,mCAEtB,CACR,CAAC,CAAC,CAAC,CACF,KAAC,KAAK,IAAC,GAAG,EAAC,GAAG,YACX,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC;oBAC3B,6DAA6D;oBAC7D,kDAAkD;oBAClD,KAAC,SAAS,IAA4B,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,IAAxD,GAAG,IAAI,CAAC,EAAE,IAAI,IAAI,EAAE,CAAwC,CAC7E,CAAC,GACI,CACT,GACgB,CACpB,EAEA,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAClB,MAAC,gBAAgB,IAAC,KAAK,EAAC,OAAO,aAC7B,KAAC,KAAK,IAAC,GAAG,EAAC,GAAG,YACX,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;4BAClB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,IAAI,CAAC,CAAA;4BAClD,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,IAAI,KAAK,KAAK,CAAC,CAAA;4BAC5C,OAAO,CACL,MAAC,GAAG,IAAiB,GAAG,EAAC,GAAG,aAC1B,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,KAAK,EAAE,IAAI,CAAC,IAAI,YAC7B,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,GACnB,EACN,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CACf,KAAC,KAAK,IAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,yBAAkB,CAChE,CAAC,CAAC,CAAC,IAAI,EACR,KAAC,MAAM,KAAG,EACV,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,QAAQ,EAAC,IAAI,kBAC/B,KAAK,GACD,KAVC,IAAI,CAAC,IAAI,CAWb,CACP,CAAA;wBACH,CAAC,CAAC,GACI,EACR,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,QAAQ,EAAC,SAAS,EAAC,MAAM,iEAEvC,IACU,CACpB,CAAC,CAAC,CAAC,IAAI,IACP,CACJ,CAAA;AACH,CAAC;AAED,SAAS,SAAS,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAA0D;IAC7F,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,CAAA;IAEhC,OAAO,CACL,KAAC,KAAK,IAAC,KAAK,EAAE,cAAc,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,QAAQ,IAAI,CAAC,EAAE,IAAI,IAAI,EAAE,YACtF,KAAC,aAAa,IAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,GAAI,GAC7D,CACT,CAAA;AACH,CAAC;AAoBD,SAAS,WAAW,CAAC,IAAe;IAClC,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,KAAK,SAAS;YACZ,OAAO,SAAS,CAAA;QAClB,KAAK,QAAQ;YACX,OAAO,QAAQ,CAAA;QACjB,KAAK,QAAQ;YACX,OAAO,QAAQ,CAAA;QACjB,KAAK,UAAU;YACb,OAAO,UAAU,CAAA;QACnB,4EAA4E;QAC5E,6EAA6E;QAC7E,KAAK,OAAO;YACV,OAAO,OAAO,CAAA;QAChB,2EAA2E;QAC3E,KAAK,QAAQ;YACX,OAAO,QAAQ,CAAA;QACjB,+EAA+E;QAC/E,wFAAwF;QACxF,KAAK,UAAU;YACb,OAAO,UAAU,CAAA;QACnB,KAAK,MAAM,CAAC,CAAC,CAAC;YACZ,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,OAAO,CAAA;YAC5B,IAAI,CAAC,KAAK,UAAU;gBAAE,OAAO,UAAU,CAAA;YACvC,IAAI,CAAC,KAAK,OAAO;gBAAE,OAAO,OAAO,CAAA;YACjC,IAAI,CAAC,KAAK,MAAM;gBAAE,OAAO,MAAM,CAAA;YAC/B,OAAO,MAAM,CAAA;QACf,CAAC;QACD;YACE,OAAO,MAAM,CAAA;IACjB,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,EACrB,IAAI,EACJ,IAAI,EACJ,IAAI,EACJ,KAAK,GAMN;IACC,MAAM,OAAO,GAAG,UAAU,EAAE,CAAA;IAC5B,MAAM,OAAO,GAAG,KAAK,EAAE,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAE,IAAI,CAAC,OAAiC,CAAA;IAChG,MAAM,EAAE,GAAG,QAAQ,IAAI,CAAC,EAAE,IAAI,IAAI,EAAE,CAAA;IACpC,MAAM,WAAW,GAAG,GAAG,IAAI,CAAC,EAAE,IAAI,IAAI,EAAE,CAAA;IAExC,SAAS,GAAG,CAAC,CAAY;QACvB,OAAO,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,CAAA;IAC7F,CAAC;IACD,SAAS,KAAK;QACZ,OAAO,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAA;IAC7D,CAAC;IAED,4FAA4F;IAC5F,MAAM,KAAK,GAAG,OAAO,KAAK,SAAS,IAAI,mBAAmB,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,EAAc,CAAC,CAAC,OAAO,CAAC,CAAA;IAClG,qFAAqF;IACrF,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,CAAA;IAC9B,8EAA8E;IAC9E,MAAM,OAAO,GAA4B,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAA;IAEnF,IAAI,OAAO,CAAA;IACX,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,OAAO,GAAG,CACR,iBAAO,SAAS,EAAC,qCAAqC,aACpD,gBACE,EAAE,EAAE,EAAE,EACN,IAAI,EAAC,UAAU,EACf,OAAO,EAAE,OAAO,KAAK,IAAI,EACzB,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,EACtC,SAAS,EAAC,uBAAuB,GACjC,EACF,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,OAAO,YACzB,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,GAC/B,IACD,CACT,CAAA;IACH,CAAC;SAAM,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC7B,OAAO,GAAG,CACR,KAAC,MAAM,IACL,EAAE,EAAE,EAAE,EACN,KAAK,EAAE,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,EAC5B,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE;gBACd,kEAAkE;gBAClE,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;gBAC3D,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;YACvC,CAAC,YAEA,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CACpB,iBAAwB,KAAK,EAAE,GAAG,CAAC,KAAK,YACrC,cAAc,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,KAAK,CAAC,IAD1B,GAAG,CAAC,KAAK,CAEb,CACV,CAAC,GACK,CACV,CAAA;IACH,CAAC;SAAM,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC7B,OAAO,GAAG,KAAC,aAAa,IAAC,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,GAAI,CAAA;IACnF,CAAC;SAAM,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;QAC3B,OAAO,GAAG,KAAC,WAAW,IAAC,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,GAAI,CAAA;IACjE,CAAC;SAAM,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;QAC5B,OAAO,GAAG,CACR,MAAC,GAAG,IAAC,GAAG,EAAC,GAAG,aACV,gBACE,EAAE,EAAE,EAAE,EACN,IAAI,EAAC,OAAO,EACZ,KAAK,EACH,OAAO,OAAO,KAAK,QAAQ,IAAI,iBAAiB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,EAEtF,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EACpC,SAAS,EAAC,gEAAgE,GAC1E,EACF,KAAC,KAAK,IACJ,KAAK,EAAE,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EACjD,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,GACpC,IACE,CACP,CAAA;IACH,CAAC;SAAM,IAAI,IAAI,KAAK,UAAU,IAAI,IAAI,KAAK,UAAU,EAAE,CAAC;QACtD,OAAO,GAAG,CACR,mBACE,EAAE,EAAE,EAAE,EACN,IAAI,EAAE,CAAC,EACP,KAAK,EAAE,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EACjD,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EACpC,SAAS,EAAC,iMAAiM,GAC3M,CACH,CAAA;IACH,CAAC;SAAM,IAAI,IAAI,KAAK,OAAO,IAAI,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,UAAU,EAAE,CAAC;QACxE,gFAAgF;QAChF,iFAAiF;QACjF,oFAAoF;QACpF,yFAAyF;QACzF,OAAO,GAAG,CACR,KAAC,oBAAoB,IAAC,OAAO,EAAC,SAAS,YACrC,KAAC,YAAY,IACX,EAAE,EAAE,EAAE,EACN,KAAK,EAAE,EAAE,GAAG,IAAI,EAAE,IAAI,EAAc,EACpC,KAAK,EAAE,OAAO,EACd,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAc,CAAC,CAAC,EAChF,QAAQ,EAAE,KAAK,GACf,GACmB,CACxB,CAAA;IACH,CAAC;SAAM,CAAC;QACN,OAAO,GAAG,CACR,KAAC,KAAK,IACJ,EAAE,EAAE,EAAE,EACN,KAAK,EAAE,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,EACpE,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,GACpC,CACH,CAAA;IACH,CAAC;IAED,OAAO,CACL,MAAC,KAAK,IAAC,GAAG,EAAC,GAAG,aACX,OAAO,EACP,CAAC,KAAK,CAAC,CAAC,CAAC,CACR,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,SAAS,EAAC,kBAAkB,iGAErC,CACR,CAAC,CAAC,CAAC,IAAI,IACF,CACT,CAAA;AACH,CAAC;AAED,sGAAsG;AACtG,SAAS,aAAa,CAAC,EACrB,EAAE,EACF,OAAO,EACP,KAAK,EACL,OAAO,GAMR;IACC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,QAAQ,CAAC,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;IAClF,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,CAAC,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;IAC5D,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAA;IACb,OAAO,CACL,KAAC,KAAK,IACJ,EAAE,EAAE,EAAE,EACN,IAAI,EAAC,QAAQ,EACb,KAAK,EAAE,GAAG,EACV,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE;YACd,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAA;YACxB,MAAM,CAAC,CAAC,CAAC,CAAA;YACT,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;gBACpB,OAAO,EAAE,CAAA;gBACT,OAAM;YACR,CAAC;YACD,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAA;YACnB,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;gBAAE,KAAK,CAAC,CAAC,CAAC,CAAA;QAClC,CAAC,GACD,CACH,CAAA;AACH,CAAC;AAED,gGAAgG;AAChG,SAAS,WAAW,CAAC,EACnB,EAAE,EACF,OAAO,EACP,KAAK,GAKN;IACC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,IAAI,IAAI,CAAC,CAAC,CAAA;IACrE,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAA;IACvD,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,IAAI,IAAI,CAAC,CAAC,CAAA;QACvC,QAAQ,CAAC,IAAI,CAAC,CAAA;IAChB,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAA;IACb,OAAO,CACL,MAAC,KAAK,IAAC,GAAG,EAAC,GAAG,aACZ,mBACE,EAAE,EAAE,EAAE,EACN,IAAI,EAAE,CAAC,EACP,KAAK,EAAE,GAAG,EACV,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE;oBACd,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAA;oBACxB,MAAM,CAAC,CAAC,CAAC,CAAA;oBACT,IAAI,CAAC;wBACH,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAc,CAAC,CAAA;wBACjC,QAAQ,CAAC,IAAI,CAAC,CAAA;oBAChB,CAAC;oBAAC,MAAM,CAAC;wBACP,QAAQ,CAAC,cAAc,CAAC,CAAA;oBAC1B,CAAC;gBACH,CAAC,EACD,SAAS,EAAC,2MAA2M,GACrN,EACD,KAAK,CAAC,CAAC,CAAC,CACP,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,SAAS,EAAC,kBAAkB,YACzC,KAAK,GACD,CACR,CAAC,CAAC,CAAC,IAAI,IACF,CACT,CAAA;AACH,CAAC","sourcesContent":["import { useEffect, useState } from 'react'\nimport { staticProp } from '@elytracms/core/project-graph'\nimport type { ComponentNode, JsonValue, PropValue } from '@elytracms/core/project-graph'\nimport type { ComponentManifest, PropField } from '@elytracms/core/component-registry'\nimport { manifestSlots } from '@elytracms/core/component-registry'\nimport { fieldValueValidator } from '@elytracms/core/cms-core'\nimport type { FieldDef, SelectOption } from '@elytracms/core/cms-core'\nimport { getStudioRegistry } from '@elytracms/studio/lib/registry'\nimport {\n Badge,\n Code,\n Field,\n Input,\n InspectorSection,\n PanelHeader,\n Row,\n Select,\n Spacer,\n Stack,\n Text,\n} from '@elytracms/studio/ui'\nimport { humanizeName } from '@elytracms/studio/lib/utils'\nimport { localizedLabel } from '@elytracms/studio/lib/i18n'\nimport { componentDisplayName } from '@elytracms/studio/lib/persona/labels'\nimport { FieldControl } from '../cms/field-control'\nimport { FieldDensityProvider } from '../cms/field-density'\nimport { useBuilder } from './builder-store'\nimport { LayoutSection, layoutManagedPropNames } from './layout-inspector'\n\nexport function PropertyInspector() {\n const builder = useBuilder()\n const studio = getStudioRegistry()\n const node = builder.selectedNode\n\n if (!node) {\n return (\n <div className=\"grid h-full place-items-center p-6 text-center\">\n <Text size=\"sm\" tone=\"muted\">\n Select a node in the tree or canvas to edit its properties.\n </Text>\n </div>\n )\n }\n\n const manifest = studio.registry.getManifest(node.componentId)\n\n return (\n <div className=\"flex h-full min-h-0 flex-col\">\n <PanelHeader>\n <Text size=\"sm\" weight=\"semibold\">\n Inspector\n </Text>\n <Spacer />\n <Code tone=\"muted\" title={node.componentId}>\n {componentDisplayName(node.componentId, manifest)}\n </Code>\n </PanelHeader>\n\n <div className=\"min-h-0 flex-1 overflow-auto\">\n <SettingsTab node={node} manifest={manifest} />\n </div>\n </div>\n )\n}\n\n/** The full property surface: node identity, static props, condition, slots. */\nfunction SettingsTab({\n node,\n manifest,\n}: {\n node: ComponentNode\n manifest: ComponentManifest | undefined\n}) {\n // Container layout props render in the dedicated Layout section (EC-162),\n // not as a second set of generic controls.\n const layoutManaged = new Set(layoutManagedPropNames(node))\n // EC-190: `blocks` props are slots (named child regions), edited via the page\n // tree / composition surface, not as a scalar control — so they show in the\n // Slots section, not Props.\n const props = manifest\n ? Object.entries(manifest.props).filter(\n ([name, spec]) => !layoutManaged.has(name) && spec.type !== 'blocks',\n )\n : []\n const slots = manifest ? manifestSlots(manifest) : []\n\n return (\n <>\n {/* EC-192: the editor-facing inspector no longer renders the raw node id or\n namespaced component id (the persona promise). The component's human name\n is in the panel header (with the manifest id in its `title`); the canvas\n and tree provide selection. */}\n {/* Contextual layout inspector for Stack/Grid containers (EC-162). */}\n <LayoutSection node={node} />\n\n {!manifest ? (\n <div className=\"p-3\">\n <Badge variant=\"error\">unknown component</Badge>\n <Text size=\"xs\" tone=\"muted\" className=\"mt-2\">\n No manifest is registered for this component, so its props can't be edited.\n </Text>\n </div>\n ) : (\n <InspectorSection title=\"Props\">\n {props.length === 0 ? (\n <Text size=\"xs\" tone=\"subtle\">\n No editable props.\n </Text>\n ) : (\n <Stack gap=\"4\">\n {props.map(([name, spec]) => (\n // Key by node id so per-node local control state never leaks\n // across a selection change to a same-named prop.\n <PropField key={`${node.id}:${name}`} node={node} name={name} spec={spec} />\n ))}\n </Stack>\n )}\n </InspectorSection>\n )}\n\n {slots.length > 0 ? (\n <InspectorSection title=\"Slots\">\n <Stack gap=\"1\">\n {slots.map((slot) => {\n const count = node.slots?.[slot.name]?.length ?? 0\n const missing = slot.required && count === 0\n return (\n <Row key={slot.name} gap=\"2\">\n <Text size=\"xs\" title={slot.name}>\n {humanizeName(slot.name)}\n </Text>\n {slot.required ? (\n <Badge variant={missing ? 'error' : 'outline'}>required</Badge>\n ) : null}\n <Spacer />\n <Text size=\"xs\" tone=\"subtle\" mono>\n {count}\n </Text>\n </Row>\n )\n })}\n </Stack>\n <Text size=\"xs\" tone=\"subtle\" className=\"mt-2\">\n Add or reorder slot children from the page tree.\n </Text>\n </InspectorSection>\n ) : null}\n </>\n )\n}\n\nfunction PropField({ node, name, spec }: { node: ComponentNode; name: string; spec: PropField }) {\n const value = node.props?.[name]\n\n return (\n <Field label={localizedLabel(spec.form?.label, name)} htmlFor={`prop-${node.id}-${name}`}>\n <StaticControl node={node} name={name} spec={spec} value={value} />\n </Field>\n )\n}\n\n/**\n * EC-190: the inspector control to render for a prop field-def. The old\n * `control.kind` vocabulary derives from the field `type` plus the optional\n * `form.control` presentation hint on `text` fields. Slice 3 unifies this with\n * the CMS `FieldControl`; for now it keeps the existing per-kind control set.\n */\ntype ControlKind =\n | 'text'\n | 'textarea'\n | 'number'\n | 'boolean'\n | 'select'\n | 'json'\n | 'color'\n | 'richtext'\n | 'asset'\n | 'object'\n | 'relation'\nfunction controlKind(spec: PropField): ControlKind {\n switch (spec.type) {\n case 'boolean':\n return 'boolean'\n case 'number':\n return 'number'\n case 'select':\n return 'select'\n case 'richText':\n return 'richtext'\n // EC-195: an `asset`-typed prop (e.g. the Image `src`) picks from the asset\n // library — never a raw asset id typed into a text box (the EC-192 persona).\n case 'asset':\n return 'asset'\n // EC-253: an `object`/repeater prop reuses the CMS repeater control below.\n case 'object':\n return 'object'\n // EC-262: a `relation`-typed prop (e.g. a ProductGrid's `category`) reuses the\n // CMS relation picker — never a text box that stringifies the ref to \"[object Object]\".\n case 'relation':\n return 'relation'\n case 'text': {\n const c = spec.form?.control\n if (c === 'textarea') return 'textarea'\n if (c === 'color') return 'color'\n if (c === 'json') return 'json'\n return 'text'\n }\n default:\n return 'text'\n }\n}\n\nfunction StaticControl({\n node,\n name,\n spec,\n value,\n}: {\n node: ComponentNode\n name: string\n spec: PropField\n value: PropValue | undefined\n}) {\n const builder = useBuilder()\n const current = value?.kind === 'static' ? value.value : (spec.default as JsonValue | undefined)\n const id = `prop-${node.id}-${name}`\n const coalesceKey = `${node.id}:${name}`\n\n function set(v: JsonValue) {\n builder.dispatch({ type: 'setProp', id: node.id, name, value: staticProp(v), coalesceKey })\n }\n function clear() {\n builder.dispatch({ type: 'removeProp', id: node.id, name })\n }\n\n // EC-190: validate with the prop's field-def value validator (was `spec.schema.safeParse`).\n const valid = current === undefined || fieldValueValidator({ ...spec, name } as FieldDef)(current)\n // EC-190: derive the editor control from the field `type` + optional `form.control`.\n const kind = controlKind(spec)\n // Select options live on the `select` field-def (was `spec.control.options`).\n const options: readonly SelectOption[] = spec.type === 'select' ? spec.options : []\n\n let control\n if (kind === 'boolean') {\n control = (\n <label className=\"flex h-8 items-center gap-2 text-sm\">\n <input\n id={id}\n type=\"checkbox\"\n checked={current === true}\n onChange={(e) => set(e.target.checked)}\n className=\"size-4 accent-primary\"\n />\n <Text size=\"sm\" tone=\"muted\">\n {current === true ? 'true' : 'false'}\n </Text>\n </label>\n )\n } else if (kind === 'select') {\n control = (\n <Select\n id={id}\n value={String(current ?? '')}\n onChange={(e) => {\n // EC-190: select option values are strings (field-def `options`).\n const opt = options.find((o) => o.value === e.target.value)\n set(opt ? opt.value : e.target.value)\n }}\n >\n {options.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {localizedLabel(opt.label, opt.value)}\n </option>\n ))}\n </Select>\n )\n } else if (kind === 'number') {\n control = <NumberControl id={id} current={current} onSet={set} onClear={clear} />\n } else if (kind === 'json') {\n control = <JsonControl id={id} current={current} onSet={set} />\n } else if (kind === 'color') {\n control = (\n <Row gap=\"2\">\n <input\n id={id}\n type=\"color\"\n value={\n typeof current === 'string' && /^#[0-9a-f]{6}$/i.test(current) ? current : '#000000'\n }\n onChange={(e) => set(e.target.value)}\n className=\"h-8 w-10 shrink-0 rounded-md border border-input bg-background\"\n />\n <Input\n value={typeof current === 'string' ? current : ''}\n onChange={(e) => set(e.target.value)}\n />\n </Row>\n )\n } else if (kind === 'textarea' || kind === 'richtext') {\n control = (\n <textarea\n id={id}\n rows={2}\n value={typeof current === 'string' ? current : ''}\n onChange={(e) => set(e.target.value)}\n className=\"flex w-full rounded-md border border-input bg-background px-2.5 py-1.5 text-sm text-foreground shadow-sm focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/40 outline-none\"\n />\n )\n } else if (kind === 'asset' || kind === 'object' || kind === 'relation') {\n // EC-195/EC-253/EC-262: reuse the CMS field control. `asset` = library picker +\n // preview; `object` = the nested/repeater form; `relation` = the document picker\n // (resolves the ref to its title, instead of stringifying it to \"[object Object]\").\n // All edit a structured static prop value through the same shared control documents use.\n control = (\n <FieldDensityProvider density=\"compact\">\n <FieldControl\n id={id}\n field={{ ...spec, name } as FieldDef}\n value={current}\n onChange={(v) => (v === undefined || v === null ? clear() : set(v as JsonValue))}\n readOnly={false}\n />\n </FieldDensityProvider>\n )\n } else {\n control = (\n <Input\n id={id}\n value={typeof current === 'string' ? current : String(current ?? '')}\n onChange={(e) => set(e.target.value)}\n />\n )\n }\n\n return (\n <Stack gap=\"1\">\n {control}\n {!valid ? (\n <Text size=\"xs\" className=\"text-destructive\">\n Value doesn't match the prop schema — the canvas falls back to the default.\n </Text>\n ) : null}\n </Stack>\n )\n}\n\n/** Number input with a local buffer: commits only finite values, never NaN; empty clears the prop. */\nfunction NumberControl({\n id,\n current,\n onSet,\n onClear,\n}: {\n id: string\n current: JsonValue | undefined\n onSet: (v: number) => void\n onClear: () => void\n}) {\n const [raw, setRaw] = useState(typeof current === 'number' ? String(current) : '')\n useEffect(() => {\n setRaw(typeof current === 'number' ? String(current) : '')\n }, [current])\n return (\n <Input\n id={id}\n type=\"number\"\n value={raw}\n onChange={(e) => {\n const v = e.target.value\n setRaw(v)\n if (v.trim() === '') {\n onClear()\n return\n }\n const n = Number(v)\n if (Number.isFinite(n)) onSet(n)\n }}\n />\n )\n}\n\n/** JSON textarea with a local buffer: commits only when the text parses; flags parse errors. */\nfunction JsonControl({\n id,\n current,\n onSet,\n}: {\n id: string\n current: JsonValue | undefined\n onSet: (v: JsonValue) => void\n}) {\n const [raw, setRaw] = useState(() => JSON.stringify(current ?? null))\n const [error, setError] = useState<string | null>(null)\n useEffect(() => {\n setRaw(JSON.stringify(current ?? null))\n setError(null)\n }, [current])\n return (\n <Stack gap=\"1\">\n <textarea\n id={id}\n rows={3}\n value={raw}\n onChange={(e) => {\n const v = e.target.value\n setRaw(v)\n try {\n onSet(JSON.parse(v) as JsonValue)\n setError(null)\n } catch {\n setError('Invalid JSON')\n }\n }}\n className=\"flex w-full rounded-md border border-input bg-background px-2.5 py-1.5 font-mono text-xs text-foreground shadow-sm focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/40 outline-none\"\n />\n {error ? (\n <Text size=\"xs\" className=\"text-destructive\">\n {error}\n </Text>\n ) : null}\n </Stack>\n )\n}\n\n"]}
@@ -4,6 +4,7 @@ import { blockRootsOf, getNode } from '@elytracms/core/project-graph';
4
4
  import { readDocumentRef } from '@elytracms/core/cms-core';
5
5
  import { Button, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, Field, Input, Stack, toast, } from '@elytracms/studio/ui';
6
6
  import { getStudioRegistry } from '@elytracms/studio/lib/registry';
7
+ import { localizedLabel } from '@elytracms/studio/lib/i18n';
7
8
  import { useCms } from '../cms/cms-store';
8
9
  import { useBuilder } from './builder-store';
9
10
  import { buildReusableValues, makeEmbedNode, resolveReuseCapability } from './reuse';
@@ -39,7 +40,7 @@ export function ReuseProvider({ children }) {
39
40
  return;
40
41
  const manifestTitle = manifests.find((m) => m.id === node.componentId)?.title;
41
42
  const collection = capability ? cms.getCollection(capability.collectionId) : undefined;
42
- const defaultName = manifestTitle ?? collection?.form?.labelSingular ?? collection?.form?.label ?? 'Reusable element';
43
+ const defaultName = manifestTitle ?? (localizedLabel(collection?.form?.labelSingular) || localizedLabel(collection?.form?.label) || 'Reusable element');
43
44
  setPending({ nodeId, defaultName });
44
45
  }, [canMakeReusable, builder, manifests, capability, cms]);
45
46
  const confirmMakeReusable = useCallback((name, publish) => {