@elytracms/studio 0.0.61 → 0.0.62

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.
@@ -46,7 +46,12 @@ export function AccountSettings({ projectId }) {
46
46
  }
47
47
  function LoadedAccount({ projectId, self, refresh, }) {
48
48
  const source = useSelfAccountViewSource({ projectId, self, refresh });
49
- return (_jsxs("div", { className: "flex h-full flex-col overflow-y-auto", children: [_jsx("div", { className: "flex min-h-[28rem] flex-col", children: _jsx(PortableDetailEditor, { source: source, projectId: projectId, documentId: self.id }) }), _jsx("div", { className: "mx-auto w-full max-w-2xl px-4 pb-8 sm:px-6", children: _jsx(CredentialPanel, {}) })] }));
49
+ // EC-396: ONE editor surface. The identity form renders through the portable edit
50
+ // shell (name/avatar editable), and the credential seam mounts through the shell's
51
+ // form-column appendix — so the CredentialPanel sits IN the form column (aligned,
52
+ // same width as the fieldset cards) and the shell owns the scroll + full height,
53
+ // making the Issues sidebar span the whole surface. No parallel card wrapper.
54
+ return (_jsx(PortableDetailEditor, { source: source, projectId: projectId, documentId: self.id, formAppendix: _jsx(CredentialPanel, {}) }));
50
55
  }
51
56
  function CenteredNote({ children, testId }) {
52
57
  return (_jsx(Stack, { className: "h-full place-items-center p-8", align: "center", justify: "center", children: _jsx(Text, { size: "sm", tone: "muted", ...(testId ? { 'data-testid': testId } : {}), children: children }) }));
@@ -1 +1 @@
1
- {"version":3,"file":"account-settings.js","sourceRoot":"","sources":["../../../src/components/account/account-settings.tsx"],"names":[],"mappings":"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","sourcesContent":["import { UserCog } from 'lucide-react'\nimport type { UserRecord } from '@elytracms/core/persistence'\nimport { Heading, Stack, Text } from '@elytracms/studio/ui'\nimport { listStoreState, useOperationQuery } from '@elytracms/studio/lib/query'\nimport { translate } from '@elytracms/studio/lib/i18n'\nimport { useAuth } from '../auth-context'\nimport { PortableDetailEditor } from '../cms/detail-editor'\nimport { CredentialPanel } from './credential-panel'\nimport { useSelfAccountViewSource } from './self-account-view-source'\n\n/**\n * The **account-settings** surface (EC-240 H-tail items 1–4). A member — admin or\n * not — opens their OWN record here (via the user menu → `users/<selfId>`). It is\n * self-permitted: the self record is read through `users.getSelf` (never the\n * admin-only `users.list`/`get`), so a non-admin reaches ONLY their own account.\n *\n * Reuses the portable edit shell (AD-19) for the identity form (name/avatar\n * editable → `updateProfile`; email/role read-only) and appends the credential\n * seam (change password). No bespoke form — the shell + the self source carry it.\n */\nexport function AccountSettings({ projectId }: { projectId: string }) {\n const auth = useAuth()\n const userId = auth.session?.user?.id ?? null\n\n const query = useOperationQuery<UserRecord | null>({\n queryKey: ['account', projectId, userId ?? '∅'],\n loader: async (operations) => {\n const result = await operations.users.getSelf({ projectId })\n if (!result.ok) throw new Error(result.error.message)\n return result.value ?? null\n },\n enabled: userId !== null,\n })\n const { status, errorMessage } = listStoreState(query)\n const self = query.data ?? null\n\n if (status === 'loading') {\n return <CenteredNote>{translate('account.loading')}</CenteredNote>\n }\n if (status === 'error') {\n return (\n <CenteredNote testId=\"account-error\">\n {errorMessage ?? translate('account.loadFailed')}\n </CenteredNote>\n )\n }\n if (!self) {\n // Authenticated but not a member (no role yet) — honest, not a raw auth error.\n return (\n <Stack gap=\"2\" align=\"center\" justify=\"center\" className=\"h-full p-8 text-center\">\n <UserCog className=\"size-6 text-muted-foreground\" />\n <Heading level={2} size=\"h3\">\n {translate('account.notMemberTitle')}\n </Heading>\n <Text size=\"sm\" tone=\"muted\" className=\"max-w-sm\">\n {translate('account.notMemberBody')}\n </Text>\n </Stack>\n )\n }\n\n return <LoadedAccount projectId={projectId} self={self} refresh={async () => void (await query.refetch())} />\n}\n\nfunction LoadedAccount({\n projectId,\n self,\n refresh,\n}: {\n projectId: string\n self: UserRecord\n refresh: () => Promise<void>\n}) {\n const source = useSelfAccountViewSource({ projectId, self, refresh })\n return (\n <div className=\"flex h-full flex-col overflow-y-auto\">\n {/* The identity form via the portable edit shell (name/avatar editable). */}\n <div className=\"flex min-h-[28rem] flex-col\">\n <PortableDetailEditor source={source} projectId={projectId} documentId={self.id} />\n </div>\n {/* The credential seam — part of the property surface, below the identity form. */}\n <div className=\"mx-auto w-full max-w-2xl px-4 pb-8 sm:px-6\">\n <CredentialPanel />\n </div>\n </div>\n )\n}\n\nfunction CenteredNote({ children, testId }: { children: React.ReactNode; testId?: string }) {\n return (\n <Stack className=\"h-full place-items-center p-8\" align=\"center\" justify=\"center\">\n <Text size=\"sm\" tone=\"muted\" {...(testId ? { 'data-testid': testId } : {})}>\n {children}\n </Text>\n </Stack>\n )\n}\n"]}
1
+ {"version":3,"file":"account-settings.js","sourceRoot":"","sources":["../../../src/components/account/account-settings.tsx"],"names":[],"mappings":"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","sourcesContent":["import { UserCog } from 'lucide-react'\nimport type { UserRecord } from '@elytracms/core/persistence'\nimport { Heading, Stack, Text } from '@elytracms/studio/ui'\nimport { listStoreState, useOperationQuery } from '@elytracms/studio/lib/query'\nimport { translate } from '@elytracms/studio/lib/i18n'\nimport { useAuth } from '../auth-context'\nimport { PortableDetailEditor } from '../cms/detail-editor'\nimport { CredentialPanel } from './credential-panel'\nimport { useSelfAccountViewSource } from './self-account-view-source'\n\n/**\n * The **account-settings** surface (EC-240 H-tail items 1–4). A member — admin or\n * not — opens their OWN record here (via the user menu → `users/<selfId>`). It is\n * self-permitted: the self record is read through `users.getSelf` (never the\n * admin-only `users.list`/`get`), so a non-admin reaches ONLY their own account.\n *\n * Reuses the portable edit shell (AD-19) for the identity form (name/avatar\n * editable → `updateProfile`; email/role read-only) and appends the credential\n * seam (change password). No bespoke form — the shell + the self source carry it.\n */\nexport function AccountSettings({ projectId }: { projectId: string }) {\n const auth = useAuth()\n const userId = auth.session?.user?.id ?? null\n\n const query = useOperationQuery<UserRecord | null>({\n queryKey: ['account', projectId, userId ?? '∅'],\n loader: async (operations) => {\n const result = await operations.users.getSelf({ projectId })\n if (!result.ok) throw new Error(result.error.message)\n return result.value ?? null\n },\n enabled: userId !== null,\n })\n const { status, errorMessage } = listStoreState(query)\n const self = query.data ?? null\n\n if (status === 'loading') {\n return <CenteredNote>{translate('account.loading')}</CenteredNote>\n }\n if (status === 'error') {\n return (\n <CenteredNote testId=\"account-error\">\n {errorMessage ?? translate('account.loadFailed')}\n </CenteredNote>\n )\n }\n if (!self) {\n // Authenticated but not a member (no role yet) — honest, not a raw auth error.\n return (\n <Stack gap=\"2\" align=\"center\" justify=\"center\" className=\"h-full p-8 text-center\">\n <UserCog className=\"size-6 text-muted-foreground\" />\n <Heading level={2} size=\"h3\">\n {translate('account.notMemberTitle')}\n </Heading>\n <Text size=\"sm\" tone=\"muted\" className=\"max-w-sm\">\n {translate('account.notMemberBody')}\n </Text>\n </Stack>\n )\n }\n\n return <LoadedAccount projectId={projectId} self={self} refresh={async () => void (await query.refetch())} />\n}\n\nfunction LoadedAccount({\n projectId,\n self,\n refresh,\n}: {\n projectId: string\n self: UserRecord\n refresh: () => Promise<void>\n}) {\n const source = useSelfAccountViewSource({ projectId, self, refresh })\n // EC-396: ONE editor surface. The identity form renders through the portable edit\n // shell (name/avatar editable), and the credential seam mounts through the shell's\n // form-column appendix — so the CredentialPanel sits IN the form column (aligned,\n // same width as the fieldset cards) and the shell owns the scroll + full height,\n // making the Issues sidebar span the whole surface. No parallel card wrapper.\n return (\n <PortableDetailEditor\n source={source}\n projectId={projectId}\n documentId={self.id}\n formAppendix={<CredentialPanel />}\n />\n )\n}\n\nfunction CenteredNote({ children, testId }: { children: React.ReactNode; testId?: string }) {\n return (\n <Stack className=\"h-full place-items-center p-8\" align=\"center\" justify=\"center\">\n <Text size=\"sm\" tone=\"muted\" {...(testId ? { 'data-testid': testId } : {})}>\n {children}\n </Text>\n </Stack>\n )\n}\n"]}
@@ -1,6 +1,7 @@
1
1
  import { collectionDefSchema } from '@elytracms/core/cms-core';
2
2
  import { roleOptions } from '@elytracms/core/persistence';
3
3
  import { msgLabel } from '@elytracms/studio/lib/i18n';
4
+ import { userTitleFallback } from '../users/users-schema.js';
4
5
  /**
5
6
  * The **account-settings** field lens (EC-240 H-tail) — the schema the portable
6
7
  * edit shell renders when a member opens their OWN record (via the user menu). It
@@ -21,7 +22,9 @@ import { msgLabel } from '@elytracms/studio/lib/i18n';
21
22
  export const selfAccountCollectionDef = collectionDefSchema.parse({
22
23
  id: 'account',
23
24
  kind: 'document',
24
- titleField: 'name',
25
+ // EC-396: name → email → id (shared with the users rail) so the account header,
26
+ // breadcrumb, and user menu never show the raw internal id when an email exists.
27
+ titleField: userTitleFallback,
25
28
  form: {
26
29
  label: msgLabel('account.rail.label'),
27
30
  labelSingular: msgLabel('account.rail.labelSingular'),
@@ -1 +1 @@
1
- {"version":3,"file":"self-account-schema.js","sourceRoot":"","sources":["../../../src/components/account/self-account-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAE9D,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAA;AACzD,OAAO,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAA;AAErD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAkB,mBAAmB,CAAC,KAAK,CAAC;IAC/E,EAAE,EAAE,SAAS;IACb,IAAI,EAAE,UAAU;IAChB,UAAU,EAAE,MAAM;IAClB,IAAI,EAAE;QACJ,KAAK,EAAE,QAAQ,CAAC,oBAAoB,CAAC;QACrC,aAAa,EAAE,QAAQ,CAAC,4BAA4B,CAAC;KACtD;IACD,MAAM,EAAE;QACN,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,0BAA0B,CAAC,EAAE,EAAE;QACrF;YACE,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE;gBACJ,KAAK,EAAE,QAAQ,CAAC,4BAA4B,CAAC;gBAC7C,WAAW,EAAE,QAAQ,CAAC,2BAA2B,CAAC;aACnD;SACF;QACD;YACE,IAAI,EAAE,OAAO;YACb,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,2BAA2B,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE;SACvE;QACD;YACE,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,CAAC,GAAG,WAAW,EAAE,CAAC;YAC3B,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,0BAA0B,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE;SACtE;KACF;CACF,CAAC,CAAA","sourcesContent":["import { collectionDefSchema } from '@elytracms/core/cms-core'\nimport type { CollectionDef } from '@elytracms/core/cms-core'\nimport { roleOptions } from '@elytracms/core/persistence'\nimport { msgLabel } from '@elytracms/studio/lib/i18n'\n\n/**\n * The **account-settings** field lens (EC-240 H-tail) — the schema the portable\n * edit shell renders when a member opens their OWN record (via the user menu). It\n * diverges from the admin `usersCollectionDef` in exactly the way the ratified\n * scope dictates:\n *\n * - **name / avatar are EDITABLE** (not `form.readOnly`) — the person edits their\n * own identity; the source's `save` routes these to `AuthAdapter.updateProfile`\n * (auth-owned), never the members rail.\n * - **email is read-only** (email change is a separate, deferred seam).\n * - **role is read-only** — you never change your own role (the server integrity\n * floor also guards it).\n *\n * Avatar is a plain URL text field for now; the media-picker convention is a\n * deferred polish item — this keeps the mechanism (name + avatar → updateProfile)\n * honest and complete without a half-built picker.\n */\nexport const selfAccountCollectionDef: CollectionDef = collectionDefSchema.parse({\n id: 'account',\n kind: 'document',\n titleField: 'name',\n form: {\n label: msgLabel('account.rail.label'),\n labelSingular: msgLabel('account.rail.labelSingular'),\n },\n fields: [\n { name: 'name', type: 'text', form: { label: msgLabel('account.field.name.label') } },\n {\n name: 'avatar',\n type: 'text',\n form: {\n label: msgLabel('account.field.avatar.label'),\n description: msgLabel('account.field.avatar.hint'),\n },\n },\n {\n name: 'email',\n type: 'text',\n form: { label: msgLabel('account.field.email.label'), readOnly: true },\n },\n {\n name: 'role',\n type: 'select',\n options: [...roleOptions()],\n form: { label: msgLabel('account.field.role.label'), readOnly: true },\n },\n ],\n})\n"]}
1
+ {"version":3,"file":"self-account-schema.js","sourceRoot":"","sources":["../../../src/components/account/self-account-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAE9D,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAA;AACzD,OAAO,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAA;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AAEzD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAkB,mBAAmB,CAAC,KAAK,CAAC;IAC/E,EAAE,EAAE,SAAS;IACb,IAAI,EAAE,UAAU;IAChB,gFAAgF;IAChF,iFAAiF;IACjF,UAAU,EAAE,iBAAiB;IAC7B,IAAI,EAAE;QACJ,KAAK,EAAE,QAAQ,CAAC,oBAAoB,CAAC;QACrC,aAAa,EAAE,QAAQ,CAAC,4BAA4B,CAAC;KACtD;IACD,MAAM,EAAE;QACN,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,0BAA0B,CAAC,EAAE,EAAE;QACrF;YACE,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE;gBACJ,KAAK,EAAE,QAAQ,CAAC,4BAA4B,CAAC;gBAC7C,WAAW,EAAE,QAAQ,CAAC,2BAA2B,CAAC;aACnD;SACF;QACD;YACE,IAAI,EAAE,OAAO;YACb,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,2BAA2B,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE;SACvE;QACD;YACE,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,CAAC,GAAG,WAAW,EAAE,CAAC;YAC3B,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,0BAA0B,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE;SACtE;KACF;CACF,CAAC,CAAA","sourcesContent":["import { collectionDefSchema } from '@elytracms/core/cms-core'\nimport type { CollectionDef } from '@elytracms/core/cms-core'\nimport { roleOptions } from '@elytracms/core/persistence'\nimport { msgLabel } from '@elytracms/studio/lib/i18n'\nimport { userTitleFallback } from '../users/users-schema'\n\n/**\n * The **account-settings** field lens (EC-240 H-tail) — the schema the portable\n * edit shell renders when a member opens their OWN record (via the user menu). It\n * diverges from the admin `usersCollectionDef` in exactly the way the ratified\n * scope dictates:\n *\n * - **name / avatar are EDITABLE** (not `form.readOnly`) — the person edits their\n * own identity; the source's `save` routes these to `AuthAdapter.updateProfile`\n * (auth-owned), never the members rail.\n * - **email is read-only** (email change is a separate, deferred seam).\n * - **role is read-only** — you never change your own role (the server integrity\n * floor also guards it).\n *\n * Avatar is a plain URL text field for now; the media-picker convention is a\n * deferred polish item — this keeps the mechanism (name + avatar → updateProfile)\n * honest and complete without a half-built picker.\n */\nexport const selfAccountCollectionDef: CollectionDef = collectionDefSchema.parse({\n id: 'account',\n kind: 'document',\n // EC-396: name → email → id (shared with the users rail) so the account header,\n // breadcrumb, and user menu never show the raw internal id when an email exists.\n titleField: userTitleFallback,\n form: {\n label: msgLabel('account.rail.label'),\n labelSingular: msgLabel('account.rail.labelSingular'),\n },\n fields: [\n { name: 'name', type: 'text', form: { label: msgLabel('account.field.name.label') } },\n {\n name: 'avatar',\n type: 'text',\n form: {\n label: msgLabel('account.field.avatar.label'),\n description: msgLabel('account.field.avatar.hint'),\n },\n },\n {\n name: 'email',\n type: 'text',\n form: { label: msgLabel('account.field.email.label'), readOnly: true },\n },\n {\n name: 'role',\n type: 'select',\n options: [...roleOptions()],\n form: { label: msgLabel('account.field.role.label'), readOnly: true },\n },\n ],\n})\n"]}
@@ -1 +1,22 @@
1
+ import type { ComponentNode } from '@elytracms/core/project-graph';
2
+ import type { ComponentManifest, PropField } from '@elytracms/core/component-registry';
1
3
  export declare function PropertyInspector(): import("react").JSX.Element;
4
+ /**
5
+ * The prop rows the inspector renders for a node (EC-393). Manifest props MINUS:
6
+ * layout-managed props (they render in the Layout section, EC-162); `blocks` props
7
+ * (they are child regions shown in Slots, EC-190); and any prop hidden RIGHT NOW by
8
+ * its `form.hidden` — static `true`, or a predicate over the SIBLING prop values.
9
+ *
10
+ * The predicate's context is the component instance's EFFECTIVE prop values
11
+ * (`effectivePropValues` — static values with manifest defaults filled), the prop
12
+ * analogue of the document form's active-locale draft values (`isFieldHidden` there).
13
+ * So a conditional row shows/hides live as a sibling prop changes and unmounts
14
+ * exactly like a document-form field. Pure (no store) so the behaviour is testable.
15
+ */
16
+ export declare function visibleInspectorProps(manifest: ComponentManifest, node: ComponentNode, layoutManaged: ReadonlySet<string>): [string, PropField][];
17
+ declare function PropField({ node, name, spec }: {
18
+ node: ComponentNode;
19
+ name: string;
20
+ spec: PropField;
21
+ }): import("react").JSX.Element;
22
+ export {};
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { staticProp } from '@elytracms/core/project-graph';
3
- import { manifestSlots } from '@elytracms/core/component-registry';
3
+ import { effectivePropValues, manifestSlots } from '@elytracms/core/component-registry';
4
+ import { isFieldHidden } from '@elytracms/core/cms-core';
4
5
  import { getStudioRegistry } from '@elytracms/studio/lib/registry';
5
6
  import { Badge, Code, Field, InspectorSection, PanelHeader, Row, Spacer, Stack, Text, } from '@elytracms/studio/ui';
6
7
  import { humanizeName } from '@elytracms/studio/lib/utils';
@@ -19,17 +20,32 @@ export function PropertyInspector() {
19
20
  const manifest = studio.registry.getManifest(node.componentId);
20
21
  return (_jsxs("div", { className: "flex h-full min-h-0 flex-col", children: [_jsxs(PanelHeader, { children: [_jsx(Text, { size: "sm", weight: "semibold", children: translate('builder.inspector.title') }), _jsx(Spacer, {}), _jsx(Code, { tone: "muted", title: node.componentId, children: componentDisplayName(node.componentId, manifest) })] }), _jsx("div", { className: "min-h-0 flex-1 overflow-auto", children: _jsx(SettingsTab, { node: node, manifest: manifest }) })] }));
21
22
  }
23
+ /**
24
+ * The prop rows the inspector renders for a node (EC-393). Manifest props MINUS:
25
+ * layout-managed props (they render in the Layout section, EC-162); `blocks` props
26
+ * (they are child regions shown in Slots, EC-190); and any prop hidden RIGHT NOW by
27
+ * its `form.hidden` — static `true`, or a predicate over the SIBLING prop values.
28
+ *
29
+ * The predicate's context is the component instance's EFFECTIVE prop values
30
+ * (`effectivePropValues` — static values with manifest defaults filled), the prop
31
+ * analogue of the document form's active-locale draft values (`isFieldHidden` there).
32
+ * So a conditional row shows/hides live as a sibling prop changes and unmounts
33
+ * exactly like a document-form field. Pure (no store) so the behaviour is testable.
34
+ */
35
+ export function visibleInspectorProps(manifest, node, layoutManaged) {
36
+ const ctx = { values: effectivePropValues(manifest, node) };
37
+ return Object.entries(manifest.props).filter(([name, spec]) => !layoutManaged.has(name) &&
38
+ spec.type !== 'blocks' &&
39
+ !isFieldHidden({ ...spec, name }, ctx));
40
+ }
22
41
  /** The full property surface: node identity, static props, condition, slots. */
23
42
  function SettingsTab({ node, manifest, }) {
24
43
  // Container layout props render in the dedicated Layout section (EC-162),
25
44
  // not as a second set of generic controls.
26
45
  const layoutManaged = new Set(layoutManagedPropNames(node));
27
- // EC-190: `blocks` props are slots (named child regions), edited via the page
28
- // tree / composition surface, not as a scalar control — so they show in the
29
- // Slots section, not Props.
30
- const props = manifest
31
- ? Object.entries(manifest.props).filter(([name, spec]) => !layoutManaged.has(name) && spec.type !== 'blocks')
32
- : [];
46
+ // EC-190: `blocks` props are slots; EC-393: `form.hidden` props are omitted —
47
+ // evaluated against the node's effective sibling prop values, live as they change.
48
+ const props = manifest ? visibleInspectorProps(manifest, node, layoutManaged) : [];
33
49
  const slots = manifest ? manifestSlots(manifest) : [];
34
50
  return (_jsxs(_Fragment, { children: [_jsx(LayoutSection, { node: node }), !manifest ? (_jsxs("div", { className: "p-3", children: [_jsx(Badge, { variant: "error", children: translate('builder.inspector.unknownComponent') }), _jsx(Text, { size: "xs", tone: "muted", className: "mt-2", children: translate('builder.inspector.noManifest') })] })) : (_jsx(InspectorSection, { title: translate('builder.inspector.props'), children: props.length === 0 ? (_jsx(Text, { size: "xs", tone: "subtle", children: translate('builder.inspector.noEditableProps') })) : (_jsx(Stack, { gap: "4", children: props.map(([name, spec]) => (_jsx(PropField, { node: node, name: name, spec: spec }, `${node.id}:${name}`))) })) })), slots.length > 0 ? (_jsxs(InspectorSection, { title: translate('builder.inspector.slots'), children: [_jsx(Stack, { gap: "1", children: slots.map((slot) => {
35
51
  const count = node.slots?.[slot.name]?.length ?? 0;
@@ -43,6 +59,6 @@ function PropField({ node, name, spec }) {
43
59
  const id = `prop-${node.id}-${name}`;
44
60
  // Coalesce rapid edits to the same prop into one undo step (EC-190).
45
61
  const coalesceKey = `${node.id}:${name}`;
46
- return (_jsx(Field, { label: localizedLabel(spec.form?.label, name), htmlFor: id, children: _jsx(PropValueControl, { id: id, name: name, spec: spec, value: value, onSet: (v) => builder.dispatch({ type: 'setProp', id: node.id, name, value: staticProp(v), coalesceKey }), onClear: () => builder.dispatch({ type: 'removeProp', id: node.id, name }) }) }));
62
+ return (_jsx(Field, { label: localizedLabel(spec.form?.label, name), htmlFor: id, hint: localizedLabel(spec.form?.description) || undefined, children: _jsx(PropValueControl, { id: id, name: name, spec: spec, value: value, onSet: (v) => builder.dispatch({ type: 'setProp', id: node.id, name, value: staticProp(v), coalesceKey }), onClear: () => builder.dispatch({ type: 'removeProp', id: node.id, name }) }) }));
47
63
  }
48
64
  //# sourceMappingURL=property-inspector.js.map
@@ -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 { staticProp } from '@elytracms/core/project-graph'\nimport type { ComponentNode } from '@elytracms/core/project-graph'\nimport type { ComponentManifest, PropField } from '@elytracms/core/component-registry'\nimport { manifestSlots } from '@elytracms/core/component-registry'\nimport { getStudioRegistry } from '@elytracms/studio/lib/registry'\nimport {\n Badge,\n Code,\n Field,\n InspectorSection,\n PanelHeader,\n Row,\n Spacer,\n Stack,\n Text,\n} from '@elytracms/studio/ui'\nimport { humanizeName } from '@elytracms/studio/lib/utils'\nimport { localizedLabel, translate } from '@elytracms/studio/lib/i18n'\nimport { componentDisplayName } from '@elytracms/studio/lib/persona/labels'\nimport { PropValueControl } from '../cms/prop-fields'\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 {translate('builder.inspector.emptySelection')}\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 {translate('builder.inspector.title')}\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\">{translate('builder.inspector.unknownComponent')}</Badge>\n <Text size=\"xs\" tone=\"muted\" className=\"mt-2\">\n {translate('builder.inspector.noManifest')}\n </Text>\n </div>\n ) : (\n <InspectorSection title={translate('builder.inspector.props')}>\n {props.length === 0 ? (\n <Text size=\"xs\" tone=\"subtle\">\n {translate('builder.inspector.noEditableProps')}\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={translate('builder.inspector.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'}>{translate('common.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 {translate('builder.inspector.slotsHint')}\n </Text>\n </InspectorSection>\n ) : null}\n </>\n )\n}\n\nfunction PropField({ node, name, spec }: { node: ComponentNode; name: string; spec: PropField }) {\n const builder = useBuilder()\n const value = node.props?.[name]\n const id = `prop-${node.id}-${name}`\n // Coalesce rapid edits to the same prop into one undo step (EC-190).\n const coalesceKey = `${node.id}:${name}`\n\n return (\n <Field label={localizedLabel(spec.form?.label, name)} htmlFor={id}>\n <PropValueControl\n id={id}\n name={name}\n spec={spec}\n value={value}\n onSet={(v) => builder.dispatch({ type: 'setProp', id: node.id, name, value: staticProp(v), coalesceKey })}\n onClear={() => builder.dispatch({ type: 'removeProp', id: node.id, name })}\n />\n </Field>\n )\n}\n\n"]}
1
+ {"version":3,"file":"property-inspector.js","sourceRoot":"","sources":["../../../src/components/builder/property-inspector.tsx"],"names":[],"mappings":"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","sourcesContent":["import { staticProp } from '@elytracms/core/project-graph'\nimport type { ComponentNode } from '@elytracms/core/project-graph'\nimport type { ComponentManifest, PropField } from '@elytracms/core/component-registry'\nimport { effectivePropValues, manifestSlots } from '@elytracms/core/component-registry'\nimport { isFieldHidden } from '@elytracms/core/cms-core'\nimport type { FieldConditionContext, FieldDef } from '@elytracms/core/cms-core'\nimport { getStudioRegistry } from '@elytracms/studio/lib/registry'\nimport {\n Badge,\n Code,\n Field,\n InspectorSection,\n PanelHeader,\n Row,\n Spacer,\n Stack,\n Text,\n} from '@elytracms/studio/ui'\nimport { humanizeName } from '@elytracms/studio/lib/utils'\nimport { localizedLabel, translate } from '@elytracms/studio/lib/i18n'\nimport { componentDisplayName } from '@elytracms/studio/lib/persona/labels'\nimport { PropValueControl } from '../cms/prop-fields'\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 {translate('builder.inspector.emptySelection')}\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 {translate('builder.inspector.title')}\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/**\n * The prop rows the inspector renders for a node (EC-393). Manifest props MINUS:\n * layout-managed props (they render in the Layout section, EC-162); `blocks` props\n * (they are child regions shown in Slots, EC-190); and any prop hidden RIGHT NOW by\n * its `form.hidden` — static `true`, or a predicate over the SIBLING prop values.\n *\n * The predicate's context is the component instance's EFFECTIVE prop values\n * (`effectivePropValues` — static values with manifest defaults filled), the prop\n * analogue of the document form's active-locale draft values (`isFieldHidden` there).\n * So a conditional row shows/hides live as a sibling prop changes and unmounts\n * exactly like a document-form field. Pure (no store) so the behaviour is testable.\n */\nexport function visibleInspectorProps(\n manifest: ComponentManifest,\n node: ComponentNode,\n layoutManaged: ReadonlySet<string>,\n): [string, PropField][] {\n const ctx: FieldConditionContext = { values: effectivePropValues(manifest, node) }\n return Object.entries(manifest.props).filter(\n ([name, spec]) =>\n !layoutManaged.has(name) &&\n spec.type !== 'blocks' &&\n !isFieldHidden({ ...spec, name } as FieldDef, ctx),\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; EC-393: `form.hidden` props are omitted —\n // evaluated against the node's effective sibling prop values, live as they change.\n const props = manifest ? visibleInspectorProps(manifest, node, layoutManaged) : []\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\">{translate('builder.inspector.unknownComponent')}</Badge>\n <Text size=\"xs\" tone=\"muted\" className=\"mt-2\">\n {translate('builder.inspector.noManifest')}\n </Text>\n </div>\n ) : (\n <InspectorSection title={translate('builder.inspector.props')}>\n {props.length === 0 ? (\n <Text size=\"xs\" tone=\"subtle\">\n {translate('builder.inspector.noEditableProps')}\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={translate('builder.inspector.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'}>{translate('common.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 {translate('builder.inspector.slotsHint')}\n </Text>\n </InspectorSection>\n ) : null}\n </>\n )\n}\n\nfunction PropField({ node, name, spec }: { node: ComponentNode; name: string; spec: PropField }) {\n const builder = useBuilder()\n const value = node.props?.[name]\n const id = `prop-${node.id}-${name}`\n // Coalesce rapid edits to the same prop into one undo step (EC-190).\n const coalesceKey = `${node.id}:${name}`\n\n return (\n // EC-393: honor `form.description` as the field hint, at parity with the document\n // form (`detail-editor.tsx` passes the same to its `Field`).\n <Field\n label={localizedLabel(spec.form?.label, name)}\n htmlFor={id}\n hint={localizedLabel(spec.form?.description) || undefined}\n >\n <PropValueControl\n id={id}\n name={name}\n spec={spec}\n value={value}\n onSet={(v) => builder.dispatch({ type: 'setProp', id: node.id, name, value: staticProp(v), coalesceKey })}\n onClear={() => builder.dispatch({ type: 'removeProp', id: node.id, name })}\n />\n </Field>\n )\n}\n\n"]}
@@ -1,3 +1,4 @@
1
+ import type { ReactNode } from 'react';
1
2
  import type { CollectionViewSource } from '@elytracms/studio/lib/collection-view/collection-view-source';
2
3
  export declare function DetailEditor({ projectId, collectionId, documentId, focusPath, }: {
3
4
  projectId: string;
@@ -13,9 +14,19 @@ export declare function DetailEditor({ projectId, collectionId, documentId, focu
13
14
  * routing (the "back to list" target, the not-found link) is rail-owned via
14
15
  * `source.listPath`, so the shell never hard-codes the CMS route.
15
16
  */
16
- export declare function PortableDetailEditor({ source, projectId, documentId, focusPath, }: {
17
+ export declare function PortableDetailEditor({ source, projectId, documentId, focusPath, formAppendix, }: {
17
18
  source: CollectionViewSource;
18
19
  projectId: string;
19
20
  documentId: string;
20
21
  focusPath?: string;
22
+ /**
23
+ * EC-396 — a form-column appendix seam (a direct-value node, per the customization
24
+ * principle — no registry): rendered INSIDE the editor's form column, below the
25
+ * fieldset cards, at the same `max-w-2xl` width and gap-6 rhythm. The account
26
+ * surface mounts its `CredentialPanel` through it so the account page is ONE editor
27
+ * surface (the editor owns the scroll + fills the height, so the Issues sidebar
28
+ * spans the full surface), never a second parallel card wrapper. Absent for the CMS
29
+ * and users entry points, which pass nothing.
30
+ */
31
+ formAppendix?: ReactNode;
21
32
  }): import("react").JSX.Element;
@@ -70,7 +70,7 @@ export function DetailEditor({ projectId, collectionId, documentId, focusPath, }
70
70
  * routing (the "back to list" target, the not-found link) is rail-owned via
71
71
  * `source.listPath`, so the shell never hard-codes the CMS route.
72
72
  */
73
- export function PortableDetailEditor({ source, projectId, documentId, focusPath, }) {
73
+ export function PortableDetailEditor({ source, projectId, documentId, focusPath, formAppendix, }) {
74
74
  const doc = source.get({ collection: source.collectionId, id: documentId });
75
75
  // EC-274: singleton opening is owned by `SingletonGate` (it resolves the lone
76
76
  // document and materializes it). The detail editor just renders the document it
@@ -78,13 +78,13 @@ export function PortableDetailEditor({ source, projectId, documentId, focusPath,
78
78
  if (!doc) {
79
79
  return (_jsx(DocumentNotFound, { projectId: projectId, listPath: source.listPath, label: localizedLabel(source.schema.form?.label, source.collectionId) }));
80
80
  }
81
- return (_jsx(CollectionViewProvider, { source: source, children: _jsx(EditorBody, { projectId: projectId, collection: source.schema, doc: doc, focusPath: focusPath }, `${source.collectionId}/${documentId}`) }));
81
+ return (_jsx(CollectionViewProvider, { source: source, children: _jsx(EditorBody, { projectId: projectId, collection: source.schema, doc: doc, focusPath: focusPath, formAppendix: formAppendix }, `${source.collectionId}/${documentId}`) }));
82
82
  }
83
83
  /** The record-not-found state — a heading + a rail-owned "back to list" link. */
84
84
  function DocumentNotFound({ projectId, listPath, label, }) {
85
85
  return (_jsx("div", { className: "grid h-full place-items-center p-8", children: _jsxs(Stack, { gap: "3", align: "center", className: "text-center", children: [_jsx(Heading, { level: 2, size: "h2", children: translate('cms.editor.notFoundTitle') }), listPath !== undefined ? (_jsx(Link, { to: STUDIO_PROJECT_ROUTE, params: { projectId, _splat: listPath }, className: "text-sm text-primary hover:underline", children: translate('cms.editor.backToList', { label }) })) : null] }) }));
86
86
  }
87
- function EditorBody({ projectId, collection, doc, focusPath, }) {
87
+ function EditorBody({ projectId, collection, doc, focusPath, formAppendix, }) {
88
88
  const source = useCollectionView();
89
89
  const navigate = useNavigate();
90
90
  // AD-19: capability flags gate whole shell regions AND their method groups, so
@@ -508,26 +508,26 @@ function EditorBody({ projectId, collection, doc, focusPath, }) {
508
508
  ? 'bg-accent text-accent-foreground'
509
509
  : 'text-muted-foreground hover:bg-muted'), children: [_jsxs("button", { type: "button", onClick: () => setActiveTab(COMPARE_TAB), "aria-pressed": activeTab === COMPARE_TAB, className: "inline-flex items-center gap-1", children: [_jsx(GitCompareArrows, { className: "size-3", "aria-hidden": true }), " ", translate('cms.version.compare')] }), _jsx("button", { type: "button", onClick: exitCompare, "aria-label": translate('cms.version.closeCompare'), className: "ml-0.5 rounded p-0.5 hover:bg-background/40", children: _jsx(X, { className: "size-3" }) })] })) : null] }), _jsx(Spacer, {}), canLocales ? _jsx(LocaleSwitcher, {}) : null, canPublish ? (_jsxs(Row, { gap: "px", className: "rounded-md bg-muted/60 p-0.5", children: [_jsx(PerspectiveButton, { label: translate('cms.editor.draftState'), active: perspective === 'draft', onClick: () => setPerspective('draft') }), _jsx(PerspectiveButton, { label: translate('cms.editor.publishedState'), active: perspective === 'published', accent: "info", disabled: !published, title: published ? undefined : translate('cms.editor.publishToViewPublished'), onClick: () => setPerspective('published') })] })) : null] })) : null] }), viewReadOnly ? (_jsxs(Row, { gap: "2", className: "shrink-0 border-b border-info/30 bg-info/10 px-4 py-1.5 text-info sm:px-6", "data-testid": "published-view-banner", children: [_jsx(Lock, { className: "size-3.5 shrink-0", "aria-hidden": true }), _jsx(Text, { size: "xs", className: "text-info", children: translate('cms.editor.publishedReadOnlyBanner') }), _jsx(Spacer, {}), _jsx("button", { type: "button", onClick: () => setPerspective('draft'), className: "text-xs font-medium underline-offset-2 hover:underline", children: translate('cms.editor.switchToDraft') })] })) : null, publishedPending ? (_jsx("div", { className: "grid min-h-0 flex-1 place-items-center p-8", children: _jsx(Text, { size: "sm", tone: "muted", children: publishedSnapshot.status === 'loading'
510
510
  ? translate('cms.editor.loadingPublishedVersion')
511
- : translate('cms.editor.noPublishedVersion') }) })) : activeTab === COMPARE_TAB && compare ? (_jsxs("div", { className: "flex min-h-0 flex-1 overflow-hidden", children: [_jsx("div", { className: "min-h-0 flex-1 overflow-auto", children: _jsx(VersionCompare, {}) }), _jsx(InspectorSidebar, { panes: trailingPanes, activeId: inspectorPaneId, onActiveChange: setInspectorPaneId, className: "w-[320px] shrink-0 border-l border-border" })] })) : canvasField ? (_jsx("div", { className: "min-h-0 flex-1 overflow-hidden", children: _jsx(CompositionFieldEditor, { field: canvasField, value: getValue(canvasField), onChange: (v) => setValue(canvasField, v), readOnly: viewReadOnly || source.isFieldReadOnly(canvasField), trailingPanes: trailingPanes }, `${canvasField.name}:${restoreNonce}:${perspective}`) })) : activeTab === REFERENCES_TAB ? (_jsx("div", { className: "flex min-h-0 flex-1 overflow-hidden", children: _jsx("div", { className: "min-h-0 flex-1 overflow-auto p-4 sm:p-6", children: _jsx("div", { className: "mx-auto w-full max-w-2xl", children: _jsx(ReferencesPanel, { projectId: projectId, source: { type: 'document', id: documentKey(ref) }, target: { type: 'document', id: documentKey(ref) } }) }) }) })) : (_jsxs("div", { className: "flex min-h-0 flex-1 overflow-hidden", children: [_jsx("div", { className: "min-h-0 flex-1 overflow-auto p-4 sm:p-6", children: _jsx("div", { className: "mx-auto w-full max-w-2xl", children: _jsx(Stack, { gap: "6", children: groups.map((group) => {
512
- // EC-306: a conditionally/statically hidden group section is skipped
513
- // entirely (its fields are also exempt from validation).
514
- if (hiddenGroups.has(group.name))
515
- return null;
516
- // EC-273/EC-240: drop a fieldset whose every field is hidden — whether
517
- // content-conditionally or for this principal — so the form never
518
- // shows an empty section header.
519
- const visible = group.fields.filter(isFieldShown);
520
- if (visible.length === 0)
521
- return null;
522
- const section = group.section;
523
- // EC-241: a section `title` is a LocalizedString ({ en, de } or a plain
524
- // string) — resolve it against the active UI locale before rendering.
525
- const sectionTitle = section?.title !== undefined ? localizedLabel(section.title, group.name) : undefined;
526
- return (_jsx(CollapsibleSection, { collapsible: section?.collapsible, defaultOpen: section?.defaultOpen, title: _jsx(Text, { size: "sm", weight: "semibold", className: sectionTitle || group.name === DEFAULT_FIELD_GROUP ? undefined : 'capitalize', children: sectionTitle ??
527
- (group.name === DEFAULT_FIELD_GROUP
528
- ? translate('cms.editor.defaultGroup')
529
- : group.name) }), children: _jsx("div", { className: "grid grid-cols-1 gap-4 p-4 md:grid-cols-12", children: visible.map((field) => (_jsx("div", { className: cn('col-span-1', fieldWidthColSpan(field.form?.width)), children: renderFieldRow(field) }, field.name))) }) }, group.name));
530
- }) }) }) }), activeEmbed ? (_jsx(EmbedInspector, { payload: activeEmbed, className: "w-[320px] shrink-0 border-l border-border" })) : (_jsx(InspectorSidebar, { panes: trailingPanes, activeId: inspectorPaneId, onActiveChange: setInspectorPaneId, className: "w-[320px] shrink-0 border-l border-border" }))] })), explicitSave ? (_jsx(UnsavedChangesGuard, { when: dirty, onSave: () => {
511
+ : translate('cms.editor.noPublishedVersion') }) })) : activeTab === COMPARE_TAB && compare ? (_jsxs("div", { className: "flex min-h-0 flex-1 overflow-hidden", children: [_jsx("div", { className: "min-h-0 flex-1 overflow-auto", children: _jsx(VersionCompare, {}) }), _jsx(InspectorSidebar, { panes: trailingPanes, activeId: inspectorPaneId, onActiveChange: setInspectorPaneId, className: "w-[320px] shrink-0 border-l border-border" })] })) : canvasField ? (_jsx("div", { className: "min-h-0 flex-1 overflow-hidden", children: _jsx(CompositionFieldEditor, { field: canvasField, value: getValue(canvasField), onChange: (v) => setValue(canvasField, v), readOnly: viewReadOnly || source.isFieldReadOnly(canvasField), trailingPanes: trailingPanes }, `${canvasField.name}:${restoreNonce}:${perspective}`) })) : activeTab === REFERENCES_TAB ? (_jsx("div", { className: "flex min-h-0 flex-1 overflow-hidden", children: _jsx("div", { className: "min-h-0 flex-1 overflow-auto p-4 sm:p-6", children: _jsx("div", { className: "mx-auto w-full max-w-2xl", children: _jsx(ReferencesPanel, { projectId: projectId, source: { type: 'document', id: documentKey(ref) }, target: { type: 'document', id: documentKey(ref) } }) }) }) })) : (_jsxs("div", { className: "flex min-h-0 flex-1 overflow-hidden", children: [_jsx("div", { className: "min-h-0 flex-1 overflow-auto p-4 sm:p-6", children: _jsx("div", { className: "mx-auto w-full max-w-2xl", children: _jsxs(Stack, { gap: "6", children: [groups.map((group) => {
512
+ // EC-306: a conditionally/statically hidden group section is skipped
513
+ // entirely (its fields are also exempt from validation).
514
+ if (hiddenGroups.has(group.name))
515
+ return null;
516
+ // EC-273/EC-240: drop a fieldset whose every field is hidden — whether
517
+ // content-conditionally or for this principal — so the form never
518
+ // shows an empty section header.
519
+ const visible = group.fields.filter(isFieldShown);
520
+ if (visible.length === 0)
521
+ return null;
522
+ const section = group.section;
523
+ // EC-241: a section `title` is a LocalizedString ({ en, de } or a plain
524
+ // string) — resolve it against the active UI locale before rendering.
525
+ const sectionTitle = section?.title !== undefined ? localizedLabel(section.title, group.name) : undefined;
526
+ return (_jsx(CollapsibleSection, { collapsible: section?.collapsible, defaultOpen: section?.defaultOpen, title: _jsx(Text, { size: "sm", weight: "semibold", className: sectionTitle || group.name === DEFAULT_FIELD_GROUP ? undefined : 'capitalize', children: sectionTitle ??
527
+ (group.name === DEFAULT_FIELD_GROUP
528
+ ? translate('cms.editor.defaultGroup')
529
+ : group.name) }), children: _jsx("div", { className: "grid grid-cols-1 gap-4 p-4 md:grid-cols-12", children: visible.map((field) => (_jsx("div", { className: cn('col-span-1', fieldWidthColSpan(field.form?.width)), children: renderFieldRow(field) }, field.name))) }) }, group.name));
530
+ }), formAppendix] }) }) }), activeEmbed ? (_jsx(EmbedInspector, { payload: activeEmbed, className: "w-[320px] shrink-0 border-l border-border" })) : (_jsx(InspectorSidebar, { panes: trailingPanes, activeId: inspectorPaneId, onActiveChange: setInspectorPaneId, className: "w-[320px] shrink-0 border-l border-border" }))] })), explicitSave ? (_jsx(UnsavedChangesGuard, { when: dirty, onSave: () => {
531
531
  save();
532
532
  return true;
533
533
  }, description: translate('cms.editor.unsavedChangesGuard') })) : null, confirmAction !== null ? (_jsx(ReferenceConfirmDialog, { projectId: projectId, action: confirmAction, targets: confirmTargets, open: true, onOpenChange: (open) => {
@@ -1 +1 @@
1
- {"version":3,"file":"detail-editor.js","sourceRoot":"","sources":["../../../src/components/cms/detail-editor.tsx"],"names":[],"mappings":"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","sourcesContent":["import { Link, STUDIO_PROJECT_ROUTE, studioPath, useNavigate } from '@elytracms/studio/router'\nimport {\n Check,\n ChevronDown,\n CircleAlert,\n CircleCheck,\n Eye,\n EyeOff,\n GitCompareArrows,\n History,\n Loader2,\n Lock,\n MoreHorizontal,\n RotateCcw,\n Save,\n Tag,\n Trash2,\n X,\n} from 'lucide-react'\nimport { useCallback, useMemo, useState } from 'react'\nimport {\n FIELD_WIDTH_SPANS,\n SIDEBAR_FIELD_GROUP,\n documentKey,\n groupOrder,\n groupSectionOf,\n hasAliases,\n hiddenGroupNames,\n isBlocksField,\n isFieldHidden,\n isFieldLocalized,\n isSingleton,\n resolveDocumentForLocale,\n resolveDocumentTitle,\n resolveField,\n sidebarFields,\n tabDescriptorOf,\n tabOrder,\n titleFieldOf,\n staticFieldHidden,\n} from '@elytracms/core/cms-core'\nimport type {\n CmsDocument,\n CmsValidationIssue,\n CollectionDef,\n DocumentRef,\n FieldDef,\n FieldGroupSection,\n FieldWidth,\n Locale,\n LocaleConfig,\n} from '@elytracms/core/cms-core'\nimport { blockRootsOf } from '@elytracms/core/project-graph'\nimport {\n Badge,\n Button,\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n CollapsibleSection,\n Field,\n Heading,\n Row,\n Spacer,\n Stack,\n Text,\n toast,\n} from '@elytracms/studio/ui'\nimport { getStudioRegistry } from '@elytracms/studio/lib/registry'\nimport { collectEmbedRefs, resolveReuseCapability } from '../builder/reuse'\nimport { cn } from '@elytracms/studio/lib/utils'\nimport { localizedLabel, translate } from '@elytracms/studio/lib/i18n'\nimport {\n LocaleSwitcher,\n activeLocaleValues,\n displayedFieldFallback,\n fieldConnection,\n fillFieldFromBase,\n resetFieldToBase,\n setFieldValue,\n} from '@elytracms/studio/lib/locale'\nimport type { ReferenceAction } from '@elytracms/studio/lib/references'\nimport { FallbackChip } from './cell'\nimport { FieldConnectionControl } from './field-connection'\nimport { FieldControl } from './field-control'\n// EC-322: the lazy-wrapped page builder — the heavy canvas subtree code-splits out\n// of the detail-view critical path and downloads only when a `blocks` field opens.\nimport { CompositionFieldEditor } from '../builder/composition-field-editor-lazy'\nimport { fieldLabel } from './cms-labels'\nimport { useCmsCollectionViewSource } from '../collection-view/cms-collection-view-source'\nimport { CollectionViewProvider, useCollectionView } from '../collection-view/collection-view-context'\nimport { resolveFieldAccess } from '@elytracms/studio/lib/collection-view/collection-view-source'\nimport type { CollectionViewSource } from '@elytracms/studio/lib/collection-view/collection-view-source'\nimport { DocStateBadge } from './doc-state'\nimport { UnsavedChangesGuard } from '../guards'\nimport { ReferenceConfirmDialog } from './reference-confirm'\nimport { ReferencesPanel } from '../references/references-panel'\nimport { InspectorSidebar } from '../inspector/inspector-sidebar'\nimport type { InspectorPane } from '../inspector/inspector-sidebar'\nimport { DocumentEditorProvider } from './document-editor-context'\nimport type { CompareState } from './document-editor-context'\nimport { TranslateDocumentAction } from '../ai/translate-document-action'\nimport { VersionsPane } from './versions-pane'\n// EC-322: the lazy-wrapped compare view — its diff UI code-splits off the detail-view\n// critical path and loads only when the Compare tab opens.\nimport { VersionCompare } from './version-compare-lazy'\nimport { IssuesPane } from './issues-pane'\nimport { DetailsPane } from './details-pane'\nimport { DocumentUrlPath } from './document-url-path'\n// 0.0.41: selection-driven embed inspector. Both modules are deliberately TipTap-free\n// so the editor runtime stays behind rich-text/lazy (EC-322) — see their doc comments.\nimport { EmbedInspectorProvider } from './rich-text/embed-inspector-context'\nimport { EmbedInspector } from './rich-text/embed-inspector'\nimport { useSetBreadcrumb } from '../breadcrumb-context'\nimport type { BreadcrumbItem } from '../breadcrumb-context'\nimport { useAiCurrentDocumentTap } from '@elytracms/studio/lib/ai-current-document'\nimport { resolveDeploymentRoutes } from '@elytracms/studio/lib/workspace'\n// EC-325 — the controller hooks EditorBody orchestrates (each independently\n// unit-tested at its pure core); the shell threads shared draft/ref/replaceDraft\n// state across their boundaries.\nimport { usePublishedPerspective } from './use-published-perspective'\nimport { useSaveMode } from './use-save-mode'\nimport { useDiscardController } from './use-discard-controller'\nimport { usePublishController, publishGate } from './use-publish-controller'\nimport { useFieldFocus } from './use-field-focus'\nimport { useCompareSelection } from './use-compare-selection'\nimport { useLiveUrl } from './use-live-url'\nimport { useEmbedInspectorHost } from './use-embed-inspector-host'\n\nexport function DetailEditor({\n projectId,\n collectionId,\n documentId,\n focusPath,\n}: {\n projectId: string\n collectionId: string\n documentId: string\n /** Reference-index field path (`?field=`, EC-172): highlight + scroll to the exact field. */\n focusPath?: string\n}) {\n // AD-19: the edit shell is a portable surface over a CollectionViewSource, just\n // like the list. `DetailEditor` is the CMS entry point — it builds the CMS source\n // and hands it to the portable {@link PortableDetailEditor} so `EditorBody` (and\n // the shared field controls / pickers) render over the source, never `useCms()`.\n const source = useCmsCollectionViewSource(collectionId)\n if (!source) {\n return (\n <DocumentNotFound\n projectId={projectId}\n listPath={studioPath.cmsCollection(collectionId)}\n label={collectionId}\n />\n )\n }\n return (\n <PortableDetailEditor source={source} projectId={projectId} documentId={documentId} focusPath={focusPath} />\n )\n}\n\n/**\n * The portable edit shell (EC-240 / AD-19): resolve the record by id from ANY\n * {@link CollectionViewSource}, then render the shared {@link EditorBody} over it.\n * The CMS entry point ({@link DetailEditor}) and the users rail both mount this —\n * routing (the \"back to list\" target, the not-found link) is rail-owned via\n * `source.listPath`, so the shell never hard-codes the CMS route.\n */\nexport function PortableDetailEditor({\n source,\n projectId,\n documentId,\n focusPath,\n}: {\n source: CollectionViewSource\n projectId: string\n documentId: string\n focusPath?: string\n}) {\n const doc = source.get({ collection: source.collectionId, id: documentId })\n // EC-274: singleton opening is owned by `SingletonGate` (it resolves the lone\n // document and materializes it). The detail editor just renders the document it\n // is given by id — singletons need no special case here.\n if (!doc) {\n return (\n <DocumentNotFound\n projectId={projectId}\n listPath={source.listPath}\n label={localizedLabel(source.schema.form?.label, source.collectionId)}\n />\n )\n }\n return (\n <CollectionViewProvider source={source}>\n <EditorBody\n key={`${source.collectionId}/${documentId}`}\n projectId={projectId}\n collection={source.schema}\n doc={doc}\n focusPath={focusPath}\n />\n </CollectionViewProvider>\n )\n}\n\n/** The record-not-found state — a heading + a rail-owned \"back to list\" link. */\nfunction DocumentNotFound({\n projectId,\n listPath,\n label,\n}: {\n projectId: string\n listPath?: string\n label: string\n}) {\n return (\n <div className=\"grid h-full place-items-center p-8\">\n <Stack gap=\"3\" align=\"center\" className=\"text-center\">\n <Heading level={2} size=\"h2\">\n {translate('cms.editor.notFoundTitle')}\n </Heading>\n {listPath !== undefined ? (\n <Link\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId, _splat: listPath }}\n className=\"text-sm text-primary hover:underline\"\n >\n {translate('cms.editor.backToList', { label })}\n </Link>\n ) : null}\n </Stack>\n </div>\n )\n}\n\nfunction EditorBody({\n projectId,\n collection,\n doc,\n focusPath,\n}: {\n projectId: string\n collection: CollectionDef\n doc: CmsDocument\n focusPath?: string\n}) {\n const source = useCollectionView()\n const navigate = useNavigate()\n // AD-19: capability flags gate whole shell regions AND their method groups, so\n // the SAME edit shell serves a non-publishing / non-versioned / single-locale\n // rail (the users rail, EC-240) without a bespoke editor. A false flag hides its\n // region entirely — no dead controls, no reads fired on that region's behalf.\n const canPublish = source.capabilities.publish\n const canVersions = source.capabilities.versions\n const canLocales = source.capabilities.locales\n // References is the CMS content-graph surface (the reference index tracks the\n // publishable document rail). A non-publishing rail (users, future redirects) is\n // not part of that graph, so its References tab would be structurally empty —\n // gate it off. A dedicated `references` capability can split this later if a\n // non-publishing-but-referenceable rail appears (YAGNI today).\n const showReferences = canPublish\n // Principal-based field access (EC-240, AD-18): `edit|read|hidden`, resolved\n // per field for the current principal. Default-open — a source with no policy\n // derives it from `isFieldReadOnly`. Access depends on the record's identity, not\n // its edited values (content-conditional hiding is the separate `isHidden` axis),\n // so it keys off the stable `doc`, never the live draft.\n const accessOf = useCallback(\n (field: FieldDef) => resolveFieldAccess(source, field, doc),\n [source, doc],\n )\n // EC-217: a singleton collection has no list — there is nowhere to go \"back\"\n // to, and its one document is fixed (never deleted). The header drops the\n // back-crumb/id line and the Delete action accordingly.\n const singleton = isSingleton(collection)\n const [draft, setDraft] = useState<CmsDocument>(() => structuredClone(doc))\n // The studio's ambient active locale (EC-176): the form displays and edits\n // exactly this locale — switched via the shared LocaleSwitcher, never per field.\n const locale = source.activeLocale\n // Bumped when a version restore replaces the draft out-of-band, to remount the\n // (uncontrolled) canvas editor from the restored value (EC-224).\n const [restoreNonce, setRestoreNonce] = useState(0)\n // EC-173: unpublish/delete go through the reference-aware confirmation.\n const [confirmAction, setConfirmAction] = useState<ReferenceAction | null>(null)\n // Stable so the document-editor context value (EC-224) doesn't churn each render.\n const ref = useMemo(() => ({ collection: collection.id, id: doc.id }), [collection.id, doc.id])\n const confirmTargets = useMemo(\n () => [{ collection: collection.id, id: doc.id }],\n [collection.id, doc.id],\n )\n\n // EC-224 — the working draft's two out-of-band mutators, shared across the\n // controller hooks below and the document-editor context. `save` persists the\n // current buffer; `replaceDraft` adopts a restored snapshot and bumps the nonce\n // to REMOUNT the uncontrolled canvas from it (else the next edit clobbers the\n // restore — the canvas builds its graph once at mount and lifts edits OUT).\n function save() {\n source.save(draft)\n }\n function replaceDraft(next: CmsDocument) {\n setDraft(next)\n setRestoreNonce((n) => n + 1)\n }\n\n // EC-378 — the current-document seam: publish WHICH document is open (and the\n // draft-adoption channel) so the AI assistant resolves \"this document\" without\n // asking, and an applied update replaces this buffer instead of being clobbered\n // by the next autosave. A narrow read-only tap + the existing `replaceDraft`;\n // nothing else of the editor is exposed.\n useAiCurrentDocumentTap({\n collection: collection.id,\n id: doc.id,\n title: primaryTitle(draft, collection, locale, source.localeConfig) || doc.id,\n adoptDraft: replaceDraft,\n })\n\n // EC-226 / EC-316 / EC-325 — the Draft/Published VIEW perspective AND the pinned\n // published snapshot, both derived from the ONE shared version-history query\n // (AD-17: the single operations-backed cache — no raw `source.versioning.load`\n // in this component). `canDiscard`/`versionKey`/`publishedVersionNumber`/\n // `publishedSnapshotDoc` feed the Discard controller below off that same query.\n const {\n perspective,\n setPerspective,\n published,\n canDiscard,\n versionKey,\n publishedVersionNumber,\n publishedSnapshotDoc,\n publishedSnapshot,\n viewReadOnly,\n viewDoc,\n publishedPending,\n } = usePublishedPerspective({ ref, source, canPublish, canVersions, draft })\n\n const dirty = useMemo(() => JSON.stringify(draft) !== JSON.stringify(doc), [draft, doc])\n const schemaIssues = useMemo(() => source.validate(draft), [source, draft])\n // EC-300/AD-16: composition embeds (hard references) whose target isn't published\n // would render empty on the live site. Surface them as errors in the Issues panel,\n // but they DON'T gate the Publish button (schema errors do) — publishing is\n // confirm-through, so the editor can publish the page and the reusable together\n // knowingly. The publish guard below catches them.\n const referenceIssues = useMemo(\n () => unpublishedEmbedIssues(collection, draft, source),\n [collection, draft, source],\n )\n const issues = useMemo(\n () => [...schemaIssues, ...referenceIssues],\n [schemaIssues, referenceIssues],\n )\n const errorCount = schemaIssues.filter((i) => i.severity === 'error').length\n const hasErrors = errorCount > 0\n const hasUnpublishedRefs = referenceIssues.length > 0\n\n // EC-225 autosave · EC-240 explicit save — the persistence discipline for this\n // rail (a publishing rail autosaves + flushes on unmount; a non-publishing rail\n // saves explicitly and relies on the `UnsavedChangesGuard` below for navigation).\n const { explicitSave } = useSaveMode({ canPublish, dirty, draft, source })\n\n // EC-316 / EC-325 — the \"Discard changes\" controller: the pure enablement\n // decision plus the shared restore sequence (flush → restore the pinned published\n // version whole → replaceDraft → invalidate). Destructured to the names the header\n // menu + confirm dialog already use.\n const {\n status: discardStatus,\n disabledReason: discardDisabledReason,\n confirmOpen: discardConfirm,\n setConfirmOpen: setDiscardConfirm,\n busy: discardBusy,\n discard: handleDiscardDraft,\n } = useDiscardController({\n canDiscard,\n published,\n viewReadOnly,\n publishedSnapshotDoc,\n publishedVersionNumber,\n versionKey,\n collection,\n draft,\n source,\n docRef: ref,\n replaceDraft,\n dirty,\n save,\n })\n const errorsByField = useMemo(() => {\n const map = new Map<string, string>()\n for (const issue of issues) {\n // validateDocument populates meta.field for field-level issues (works for\n // both `values` and `localized/<locale>` paths, unlike positional indexing).\n const field = issue.meta?.field\n if (typeof field === 'string' && !map.has(field)) map.set(field, issue.message)\n }\n return map\n }, [issues])\n\n // EC-241: `form.tabs` may declare tabs as bare ids OR `{ id, label }` objects with a\n // localizable title — resolve to the ordered id list for all tab logic; labels are\n // resolved for display in `renderContentTab`.\n const declaredTabs = tabOrder(collection.form)\n const tabs = declaredTabs.length > 0 ? declaredTabs : ['main']\n // EC-269: References gets its own dedicated, visibility-gated tab — it mounts\n // (and loads) only when selected, and unmounts on leave, so normal editing\n // never triggers reference fetching. Appended after the collection's content tabs.\n const REFERENCES_TAB = 'references'\n // EC-298: the version-compare view is a sticky framework tab (kept open while\n // you tab to the real fields; closed only by its ✕).\n const COMPARE_TAB = 'compare'\n const [activeTab, setActiveTab] = useState(tabs[0]!)\n // EC-285: the reserved `sidebar` group is document-level metadata (slug, author…)\n // — it renders in the Details inspector pane, not as a content tab, so it is\n // excluded from the tab fields here and collected once below.\n const sidebarFieldDefs = useMemo(() => sidebarFields(collection), [collection])\n // EC-273: only STATICALLY hidden fields (`hidden: true`) are structurally\n // excluded here; a conditionally (predicate) hidden field stays in the tab and\n // is evaluated per-render against the current values (it may show or hide as the\n // editor types).\n const tabFields = collection.fields.filter(\n (f) =>\n (f.form?.tab ?? tabs[0]) === activeTab &&\n !staticFieldHidden(f) &&\n f.form?.group !== SIDEBAR_FIELD_GROUP,\n )\n const groups = groupFields(tabFields, collection)\n // EC-188 (AD-12): a composition (`blocks`) field is a special content feature,\n // not a field-row — it takes over its tab with the full page-builder canvas.\n // Convention: such a field lives on its own dedicated fieldset tab.\n const canvasField = tabFields.find(isBlocksField)\n\n // EC-172 / EC-283 — the field-focus/highlight controller (jump-to-field from the\n // reference deep-link `focusPath` and the Issues pane). Owns the highlight + the\n // controlled inspector-pane state and the scroll/clear timers.\n const { highlightedField, inspectorPaneId, setInspectorPaneId, focusField } = useFieldFocus({\n collection,\n source,\n focusPath,\n setActiveTab,\n })\n // 0.0.41 — the selection-driven embed-inspector host: a rich-text `componentEmbed`\n // selection swaps the inspector sidebar for the embed's prop form (the `api` is fed\n // to the rich-text field through the EmbedInspector context).\n const { activeEmbed, embedInspectorApi } = useEmbedInspectorHost()\n // EC-298 — the sticky version-compare selection. Opening it focuses the Compare tab\n // + the Versions pane; tabbing to the real fields keeps it open (only its ✕ closes\n // it). `activeTab === COMPARE_TAB` is when the diff is visible.\n const { compare, startCompare, setCompareBase, setCompareTarget, exitCompare } = useCompareSelection({\n tabs,\n compareTab: COMPARE_TAB,\n setActiveTab,\n setInspectorPaneId,\n })\n // A content tab (schema tab or References). Selecting one leaves the Compare\n // view VISIBLE-wise but keeps the comparison open (sticky) — the diff is one\n // click away on its still-present tab chip.\n const renderContentTab = (tab: string) => {\n const active = tab === activeTab\n // EC-241 labels: the References framework tab is engine chrome (catalog); a schema\n // tab with a declared `{ id, label }` shows its localized label as authored; a bare\n // id keeps the title-cased-id behaviour (`capitalize`).\n const declaredLabel = tab === REFERENCES_TAB ? undefined : tabDescriptorOf(collection.form, tab)?.label\n const display =\n tab === REFERENCES_TAB\n ? translate('references.title')\n : declaredLabel !== undefined\n ? localizedLabel(declaredLabel, tab)\n : tab\n const titleCase = tab !== REFERENCES_TAB && declaredLabel === undefined\n return (\n <button\n key={tab}\n type=\"button\"\n aria-pressed={active}\n onClick={() => setActiveTab(tab)}\n className={cn(\n 'rounded-md px-2.5 py-1 text-xs font-medium transition-colors',\n titleCase && 'capitalize',\n active ? 'bg-accent text-accent-foreground' : 'text-muted-foreground hover:bg-muted',\n )}\n >\n {display}\n </button>\n )\n }\n function getValue(field: FieldDef): unknown {\n // EC-226: read from the active VIEW (the draft, or the pinned published\n // snapshot when viewing Published) — never directly from `draft`.\n if (isFieldLocalized(collection, field.name)) {\n // Display the explicit variant when it exists, else the EC-018 fallback\n // value — labeled as such by the field's fallback marker (EC-176), so a\n // German view shows the English text visibly, never an unexplained blank.\n const explicit = viewDoc.localized?.[locale]?.[field.name]\n if (explicit !== undefined) return explicit\n return resolveField(collection, viewDoc, field.name, locale, source.localeConfig).value\n }\n return viewDoc.values[field.name]\n }\n\n // EC-176/EC-318: the present/absent-key write is single-sourced in `setFieldValue`\n // — a localized field writes `localized[locale]`, clearing (value === undefined)\n // DELETES the key (reconnecting to base). `fill from base` / `reset to base` below\n // reuse the same foundation, so typing, filling, and resetting can never diverge.\n function setValue(field: FieldDef, value: unknown) {\n setDraft((d) => setFieldValue(collection, d, field.name, locale, value))\n }\n\n // EC-273: conditional-visibility context — the draft's values for the active\n // locale, against which a field's `hidden(values)` predicate is evaluated. A\n // hidden field's row is omitted (and it is not validated). Recomputed as the\n // draft changes, so a field shows/hides live as the editor types.\n const conditionValues = activeLocaleValues(draft, locale)\n const isHidden = (field: FieldDef) => isFieldHidden(field, { values: conditionValues })\n // Visible = not content-hidden AND not principal-hidden (EC-240 access === 'hidden').\n const isFieldShown = (field: FieldDef) => !isHidden(field) && accessOf(field) !== 'hidden'\n const visibleSidebarFields = sidebarFieldDefs.filter(isFieldShown)\n // EC-306: presentational group sections hidden right now (static or predicate) —\n // skipped entirely, like a hidden field. Recomputed live as the draft changes.\n const hiddenGroups = hiddenGroupNames(collection, { values: conditionValues })\n\n // One document field row (label + fallback/translation/read-only badges + the\n // control), shared by the content groups and the EC-285 Details pane so a\n // sidebar field renders identically — same control, validation, localization,\n // and EC-283 highlight/jump target — wherever it surfaces. Returns null when the\n // field is conditionally hidden (EC-273).\n function renderFieldRow(field: FieldDef) {\n if (isHidden(field)) return null\n // EC-240: principal-based access. `hidden` drops the row entirely (this\n // principal can't see the field); `read` renders it read-only. Distinct from\n // the content-conditional `isHidden` above and the view-level read-only\n // (Published perspective) — the control honors all three.\n const access = accessOf(field)\n if (access === 'hidden') return null\n const fieldLevelReadOnly = access === 'read'\n const fieldReadOnly = viewReadOnly || fieldLevelReadOnly\n const localized = isFieldLocalized(collection, field.name)\n // EC-318 — connected fields: the per-locale connection state of a localized\n // field (connected/diverged + fill-from-base), DERIVED from the draft, never\n // stored. Null for a non-localized field or the base-locale view. Suppressed\n // while the row is read-only (Published view or a field-level `read` grant) —\n // there is nothing to connect or diverge there.\n const connection = localized\n ? fieldConnection(collection, draft, field.name, locale, source.localeConfig)\n : null\n return (\n <Field\n key={field.name}\n className={cn(\n 'rounded-md transition-shadow',\n highlightedField === field.name && 'bg-primary/5 p-2 ring-2 ring-primary/70',\n )}\n label={\n <Row gap=\"2\">\n <span>{fieldLabel(field)}</span>\n {field.validation?.required ? <span className=\"text-destructive\">*</span> : null}\n {localized ? <Badge variant=\"outline\">{locale}</Badge> : null}\n {fieldLevelReadOnly ? (\n <Badge variant=\"outline\">\n <Lock className=\"size-3\" /> {translate('cms.editor.readOnlyBadge')}\n </Badge>\n ) : null}\n <Spacer />\n {/* EC-318: the connected-field affordance replaces EC-176's passive\n \"showing EN\" text — a quiet chip (Follows EN / Translated) plus the\n matching action (Fill from EN / Reset to EN, the latter confirmed).\n Right-aligned so it stays out of the way on every localized row. */}\n {connection && !fieldReadOnly ? (\n <FieldConnectionControl\n connection={connection}\n fieldName={field.name}\n locale={locale}\n onFillFromBase={() =>\n setDraft((d) => fillFieldFromBase(collection, d, field.name, locale, source.localeConfig))\n }\n onResetToBase={() =>\n setDraft((d) => resetFieldToBase(collection, d, field.name, locale))\n }\n />\n ) : null}\n </Row>\n }\n htmlFor={`f-${field.name}`}\n hint={localizedLabel(field.form?.description) || undefined}\n error={fieldReadOnly ? undefined : errorsByField.get(field.name)}\n >\n <FieldControl\n id={`f-${field.name}`}\n field={field}\n value={getValue(field)}\n onChange={(v) => setValue(field, v)}\n readOnly={fieldReadOnly}\n />\n {/* EC-315: the slug field's derived composed-path preview now lives inside the\n slug control itself (field-control's `SlugField`), which subsumes the old\n EC-246d PathPreview — so nothing extra is rendered here. */}\n </Field>\n )\n }\n\n // The frame-owned `Versions` pane (EC-224) — appended after any view-supplied\n // pane (the canvas's `Properties`). One instance feeds both the form view's\n // sidebar and the canvas view's sidebar.\n const versionsPane = useMemo<InspectorPane>(\n () => ({\n id: 'versions',\n label: translate('cms.version.paneLabel'),\n icon: <History className=\"size-3.5\" />,\n render: () => <VersionsPane />,\n }),\n [],\n )\n // The frame-owned `Issues` pane (EC-283) — the document-scoped validation\n // surface that replaces the always-on footer band. The tab carries a count\n // badge (error- vs warning-toned) so a non-zero count is discoverable from\n // any tab; document-level issues (e.g. EC-260 nesting-depth) live only here.\n const issuesPane = useMemo<InspectorPane>(\n () => ({\n id: 'issues',\n label: translate('cms.editor.issuesPane'),\n icon: <CircleAlert className=\"size-3.5\" />,\n badge:\n issues.length === 0 ? undefined : (\n <Badge\n variant={issues.some((i) => i.severity === 'error') ? 'error' : 'warning'}\n className=\"px-1.5 py-0 leading-none\"\n >\n {issues.length}\n </Badge>\n ),\n render: () => (\n <IssuesPane issues={issues} collection={collection} onJumpToField={focusField} />\n ),\n }),\n [issues, errorCount, collection, focusField],\n )\n // EC-284 + EC-285: the document-metadata pane — slot assignment (EC-284) and/or\n // the document-level `sidebar`-group fields (EC-285). Present only when the\n // collection has either, so it never adds an empty tab elsewhere. First in the\n // order, so a slotted / sidebar-bearing document opens straight to it. Not\n // memoized: its render closes over per-render edit state (draft, highlight,\n // field errors) via `renderFieldRow`, and the inspector re-renders with the rest\n // of the editor anyway, so a stable pane identity would buy nothing here.\n // 0.0.42 (round 3): whether a deployment route addresses this collection — gates the\n // header's composed-URL row (which carries the live-page affordance, EC-315 hybrid\n // split). A cheap synchronous accessor, resolved on every render (NOT frozen in a\n // `useMemo` with a static dep — the same first-paint-empty-routes trap the SlugField\n // fix removed: routes arrive after the workspace/config loads).\n const routableCollection = resolveDeploymentRoutes().some(\n (route) => route.document?.collection === collection.id,\n )\n // The composed URL moved OUT of this pane and up to the header (round 3): a lone URL\n // row on an otherwise-empty pane looked odd. So the Details pane exists only for slots\n // and/or sidebar fields — never merely because the collection is routable (that would\n // resurrect the empty-pane problem). A routable page keeps its pane via its slug\n // sidebar field; a routable collection with neither has no pane (URL is in the header).\n const detailsPane: InspectorPane | null =\n hasAliases(collection) || visibleSidebarFields.length > 0\n ? {\n id: 'details',\n label: translate('cms.editor.detailsPane'),\n icon: <Tag className=\"size-3.5\" />,\n render: () => (\n <DetailsPane\n collection={collection}\n doc={draft}\n siblings={source.documents()}\n readOnly={viewReadOnly}\n fields={visibleSidebarFields}\n renderField={renderFieldRow}\n onAliasChange={(alias) =>\n setDraft((d) => {\n const next = { ...d }\n if (alias === undefined) delete next.alias\n else next.alias = alias\n return next\n })\n }\n />\n ),\n }\n : null\n // Versions is capability-gated (AD-19): a non-versioned rail (users) never mounts\n // the pane, so no version read fires on its records.\n const trailingPanes = [\n ...(detailsPane ? [detailsPane] : []),\n ...(canVersions ? [versionsPane] : []),\n issuesPane,\n ]\n\n // EC-268 / EC-300 / 0.0.39 — the publish controller: the publish indicator state,\n // the EC-300 confirm-through guard (unpublished hard refs would render empty on\n // the live site), and the \"Publish & close\" navigation. Handlers are named to\n // match the header split button + the confirm-through dialog below.\n const {\n publishState,\n publishConfirm,\n handlePublish,\n handlePublishAndClose,\n confirmPublish,\n cancelPublishConfirm,\n } = usePublishController({\n source,\n docRef: ref,\n collection,\n projectId,\n draft,\n setDraft,\n dirty,\n hasUnpublishedRefs,\n })\n\n function handleConfirmedAction() {\n if (confirmAction === 'unpublish') {\n setConfirmAction(null)\n void source.publishing?.unpublish(ref)\n return\n }\n if (confirmAction === 'delete') {\n setConfirmAction(null)\n source.remove(ref)\n // Return to the rail's list (rail-owned `listPath`), or the project index if\n // the rail has no list surface (AD-19 — never a hard-coded CMS route).\n void navigate({\n to: STUDIO_PROJECT_ROUTE,\n params: { projectId, _splat: source.listPath ?? studioPath.index() },\n })\n }\n }\n\n // While the published snapshot is pending, don't derive the title from the\n // draft (it must not read as the published title) — fall back to the id.\n const title = publishedPending ? '' : primaryTitle(viewDoc, collection, locale, source.localeConfig)\n // EC-242: the untranslated-title chip applies only to a field-backed title; a\n // function title pattern has no single source field to mark as a fallback.\n const titleFallback =\n publishedPending || typeof collection.titleField === 'function'\n ? null\n : displayedFieldFallback(\n collection,\n viewDoc,\n titleFieldOf(collection)?.name ?? 'title',\n locale,\n source.localeConfig,\n )\n\n // EC-214: the document is the active record — contribute the collection (a\n // link back to its list) and the document title to the global header\n // breadcrumb, replacing the editor's own back-link + collection/id line. A\n // singleton has no list, so it contributes a single, non-linked crumb. The\n // title tracks the live draft so a rename updates the crumb immediately.\n const breadcrumbItems = useMemo<BreadcrumbItem[]>(() => {\n const collectionLabel = localizedLabel(collection.form?.label, collection.id)\n // A singleton (no list) or a rail with no list surface contributes a single,\n // non-linked crumb; otherwise the collection crumb links back to its list via\n // the rail-owned `listPath` (never a hard-coded CMS route — AD-19).\n if (singleton || source.listPath === undefined) return [{ label: collectionLabel }]\n const docTitle = primaryTitle(draft, collection, locale, source.localeConfig) || doc.id\n return [\n {\n label: collectionLabel,\n to: STUDIO_PROJECT_ROUTE,\n params: { projectId, _splat: source.listPath },\n },\n { label: docTitle },\n ]\n }, [singleton, collection, draft, locale, source.localeConfig, source.listPath, doc.id, projectId])\n useSetBreadcrumb(breadcrumbItems)\n\n // EC-205/EC-216 — the header composed-URL row's live-link descriptor. Derives\n // strictly from PUBLISHED data (the pinned snapshot + published ancestors), never\n // the draft, so an unpublished slug/parent edit can't leak into the live URL; the\n // draft-derived path itself (rendered by `DocumentUrlPath`) still updates as you\n // type — only the OPEN-live action is published-gated. `undefined` when the\n // collection is non-routable.\n const { headerLive } = useLiveUrl({ source, collection, doc, locale, published, routableCollection })\n\n // EC-240: hide the tab bar when there is nothing to navigate — a single content\n // tab with no References/Compare (the users rail). When framework tabs ARE\n // present we KEEP the content tab(s) so there is always a way back to the form;\n // only a genuinely single-tab surface drops them. And drop the whole lens row\n // when it would be empty (no tabs to show, no locale/perspective controls), so a\n // bare rail (users) shows no orphaned row.\n const hasFrameworkTabs = showReferences || compare !== null\n const showContentTabs = tabs.length > 1 || hasFrameworkTabs\n const showLensRow = showContentTabs || canLocales || canPublish\n\n // 0.0.39 split Publish button — the shared gate (disabled + title), computed once\n // (pure `publishGate`) so the primary \"Publish\" and the \"Publish & close\" item can\n // never diverge.\n const { disabled: publishDisabled, title: publishTitle } = publishGate({\n hasErrors,\n viewReadOnly,\n publishState,\n errorCount,\n })\n const publishButtonLabel =\n publishState === 'pending' ? (\n <>\n <Loader2 className=\"animate-spin\" /> {translate('cms.editor.publishing')}\n </>\n ) : publishState === 'success' ? (\n <>\n <CircleCheck /> {translate('cms.editor.publishedState')}\n </>\n ) : (\n <>\n <Eye /> {translate('cms.editor.publish')}\n </>\n )\n // Offer \"Publish & close\" only when there IS a list to return to — the same gate\n // the removed EC-316 \"Save & close\" used (never a singleton or a list-less rail).\n const canPublishClose = !singleton && source.listPath !== undefined\n\n return (\n <DocumentEditorProvider\n value={{\n projectId,\n collection,\n docRef: ref,\n draft,\n dirty,\n hasErrors,\n viewReadOnly,\n save,\n replaceDraft,\n compare,\n compareActive: activeTab === COMPARE_TAB,\n startCompare,\n setCompareBase,\n setCompareTarget,\n exitCompare,\n }}\n >\n <EmbedInspectorProvider value={embedInspectorApi}>\n <div className={cn('flex h-full min-h-0 flex-col', viewReadOnly && 'bg-info/[0.04]')}>\n {/* Header — the back-link + collection/id line moved into the global\n breadcrumb (EC-214); this keeps only the record title + actions. */}\n <div className=\"shrink-0 border-b border-border px-4 py-3 sm:px-6\">\n <Row gap=\"3\" align=\"start\">\n {/* Title + the composed-URL row beneath it (round 3): the URL is a\n first-class document property, so it sits under the (dominant) title in\n secondary/muted type. Routable collections only — a non-routable record\n (asset, singleton, users) shows the title alone, with no gap. The left\n block is `min-w-0` so a long title/URL truncates instead of shoving the\n header actions around. */}\n <Stack gap=\"1\" className=\"min-w-0\">\n <Row gap=\"2\" align=\"center\" className=\"min-w-0\">\n <Heading level={1} size=\"h2\" className=\"truncate\">\n {singleton ? localizedLabel(collection.form?.label, collection.id) : title || doc.id}\n </Heading>\n {singleton ? null : <FallbackChip fallback={titleFallback} testId=\"doc-title-fallback\" />}\n </Row>\n {routableCollection ? (\n <DocumentUrlPath\n projectId={projectId}\n collection={collection}\n doc={draft}\n siblings={source.documents()}\n routes={resolveDeploymentRoutes()}\n localeConfig={source.localeConfig}\n locale={locale}\n live={headerLive}\n />\n ) : null}\n </Stack>\n <Spacer />\n {/* Save status — an autosave rail reports Saving…/Saved; an explicit rail\n (users) reports Unsaved changes until the Save button commits. */}\n <Text size=\"xs\" tone=\"muted\" className=\"whitespace-nowrap\" data-testid=\"autosave-status\">\n {source.lastError\n ? translate('cms.editor.saveFailed')\n : explicitSave\n ? dirty\n ? translate('cms.editor.unsavedChanges')\n : translate('cms.editor.saved')\n : dirty\n ? translate('cms.editor.saving')\n : translate('cms.editor.saved')}\n </Text>\n {canPublish ? <DocStateBadge published={published} /> : null}\n {/* EC-216 / EC-205: the standalone \"View live\" button is retired (round 3).\n Opening the live page now hangs off the composed-URL row's LEAF segment\n under the title — same published-gating, one fewer header button. */}\n {/* 0.0.39: the Publish button is a SPLIT button — primary Publish (stay) +\n a \"Publish & close\" dropdown item (publish, then return to the list). The\n retired EC-316 \"Save & close\" is gone: autosave already persists edits, so\n a bare \"Save\" was meaningless. Both actions share identical gating (schema\n errors / view-read-only / in-flight); the close item is offered only when\n there IS a list to return to (never a singleton or list-less rail). */}\n {canPublish ? (\n <Row gap=\"px\" align=\"center\">\n <Button\n size=\"sm\"\n onClick={handlePublish}\n disabled={publishDisabled}\n title={publishTitle}\n className={canPublishClose ? 'rounded-r-none' : undefined}\n data-testid=\"doc-publish\"\n >\n {publishButtonLabel}\n </Button>\n {canPublishClose ? (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n size=\"sm\"\n disabled={publishDisabled}\n aria-label={translate('cms.editor.publishActions')}\n data-testid=\"doc-publish-actions\"\n className=\"rounded-l-none border-l border-primary-foreground/25 px-1.5\"\n >\n <ChevronDown className=\"size-3.5\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuItem onClick={handlePublishAndClose} data-testid=\"doc-publish-and-close\">\n <Save /> {translate('cms.editor.publishAndClose')}\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n ) : null}\n </Row>\n ) : null}\n {/* EC-240: an explicit-save rail commits the draft on demand (no publish\n step). Disabled until there are unsaved edits; the guard covers leaving. */}\n {explicitSave ? (\n <Button size=\"sm\" onClick={save} disabled={!dirty} data-testid=\"doc-save\">\n <Check /> {translate('common.save')}\n </Button>\n ) : null}\n {/* EC-332: AI \"Translate document\" — fills empty locale values on the draft.\n The gate renders nothing unless the studio is ai-configured AND the\n collection is localized with a target locale (so it is absent for\n single-locale collections, assets, and unconfigured studios). */}\n {!viewReadOnly ? (\n <TranslateDocumentAction\n projectId={projectId}\n collection={collection}\n docRef={ref}\n document={draft}\n localeConfig={source.localeConfig}\n localesEnabled={canLocales}\n onApplied={(next) => {\n source.save(next)\n replaceDraft(next)\n }}\n />\n ) : null}\n {/* EC-217: a singleton is never deleted; with nothing else to show\n (an unpublished singleton) the actions menu is omitted entirely. */}\n {published || !singleton ? (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button variant=\"outline\" size=\"icon-sm\" aria-label={translate('cms.editor.moreActions')}>\n <MoreHorizontal />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n {/* EC-316: \"Discard changes\" — reverts the draft to exactly the\n published content (the same restore machinery the Compare\n view's \"Restore version\" uses, EC-298). Shown only when a\n published version exists to revert to; enabled once the\n draft actually differs from it — decided by the same\n semantic comparator the Versions pane uses, so the two\n never contradict each other. */}\n {discardStatus !== 'hidden' ? (\n <DropdownMenuItem\n data-testid=\"doc-discard-draft\"\n disabled={discardStatus !== 'ready'}\n title={discardDisabledReason}\n onClick={() => setDiscardConfirm(true)}\n >\n <RotateCcw /> {translate('cms.editor.discardDraft')}\n </DropdownMenuItem>\n ) : null}\n {published ? (\n <DropdownMenuItem onClick={() => setConfirmAction('unpublish')}>\n <EyeOff /> {translate('cms.editor.unpublish')}\n </DropdownMenuItem>\n ) : null}\n {singleton ? null : (\n <DropdownMenuItem\n data-testid=\"doc-delete\"\n className=\"text-destructive\"\n onClick={() => setConfirmAction('delete')}\n >\n <Trash2 /> {translate('common.delete')}\n </DropdownMenuItem>\n )}\n </DropdownMenuContent>\n </DropdownMenu>\n ) : null}\n </Row>\n\n {/* EC-246a consistency: this lens row mirrors the list header — the tabs\n (EC-073) lead on the left (like the list's view tabs), and the lens\n CONTROLS sit on the right: the ambient locale switcher (EC-176) then the\n Draft/Published view perspective (EC-226) — exactly where the list keeps\n locale + Display, so the locale no longer jumps sides between views. */}\n {showLensRow ? (\n <Row gap=\"3\" className=\"mt-3\" wrap>\n <Row gap=\"1\" align=\"center\">\n {/* Schema content tabs (EC-073), then a divider, then the framework\n tabs (EC-269 References; EC-298 the transient Compare chip). While\n comparing, no content tab is highlighted — the Compare chip is the\n active view, and clicking a content tab closes the comparison. A\n single-tab surface with no framework tabs hides these entirely. */}\n {showContentTabs ? tabs.map((tab) => renderContentTab(tab)) : null}\n {showReferences ? (\n <>\n <div className=\"mx-1 h-4 w-px bg-border\" aria-hidden />\n {renderContentTab(REFERENCES_TAB)}\n </>\n ) : null}\n {compare ? (\n <span\n className={cn(\n 'inline-flex items-center gap-1 rounded-md px-2.5 py-1 text-xs font-medium transition-colors',\n activeTab === COMPARE_TAB\n ? 'bg-accent text-accent-foreground'\n : 'text-muted-foreground hover:bg-muted',\n )}\n >\n <button\n type=\"button\"\n onClick={() => setActiveTab(COMPARE_TAB)}\n aria-pressed={activeTab === COMPARE_TAB}\n className=\"inline-flex items-center gap-1\"\n >\n <GitCompareArrows className=\"size-3\" aria-hidden /> {translate('cms.version.compare')}\n </button>\n <button\n type=\"button\"\n onClick={exitCompare}\n aria-label={translate('cms.version.closeCompare')}\n className=\"ml-0.5 rounded p-0.5 hover:bg-background/40\"\n >\n <X className=\"size-3\" />\n </button>\n </span>\n ) : null}\n </Row>\n <Spacer />\n {canLocales ? <LocaleSwitcher /> : null}\n {canPublish ? (\n <Row gap=\"px\" className=\"rounded-md bg-muted/60 p-0.5\">\n <PerspectiveButton\n label={translate('cms.editor.draftState')}\n active={perspective === 'draft'}\n onClick={() => setPerspective('draft')}\n />\n <PerspectiveButton\n label={translate('cms.editor.publishedState')}\n active={perspective === 'published'}\n accent=\"info\"\n disabled={!published}\n title={published ? undefined : translate('cms.editor.publishToViewPublished')}\n onClick={() => setPerspective('published')}\n />\n </Row>\n ) : null}\n </Row>\n ) : null}\n </div>\n\n {/* EC-214: an unmistakable banner whenever the read-only Published snapshot\n is in view — disabled controls + a footer notice alone read as too\n subtle. Paired with the tinted surface and the accented toggle. */}\n {viewReadOnly ? (\n <Row\n gap=\"2\"\n className=\"shrink-0 border-b border-info/30 bg-info/10 px-4 py-1.5 text-info sm:px-6\"\n data-testid=\"published-view-banner\"\n >\n <Lock className=\"size-3.5 shrink-0\" aria-hidden />\n <Text size=\"xs\" className=\"text-info\">\n {translate('cms.editor.publishedReadOnlyBanner')}\n </Text>\n <Spacer />\n <button\n type=\"button\"\n onClick={() => setPerspective('draft')}\n className=\"text-xs font-medium underline-offset-2 hover:underline\"\n >\n {translate('cms.editor.switchToDraft')}\n </button>\n </Row>\n ) : null}\n\n {/* Body — a blocks field takes over the body with the page-builder canvas\n (which carries its own inspector sidebar, EC-188/EC-224); otherwise the\n field form sits beside the shared inspector sidebar (Versions). In\n Published view the snapshot must load before rendering — the draft is\n never shown as published (EC-226). */}\n {publishedPending ? (\n <div className=\"grid min-h-0 flex-1 place-items-center p-8\">\n <Text size=\"sm\" tone=\"muted\">\n {publishedSnapshot.status === 'loading'\n ? translate('cms.editor.loadingPublishedVersion')\n : translate('cms.editor.noPublishedVersion')}\n </Text>\n </div>\n ) : activeTab === COMPARE_TAB && compare ? (\n // EC-298: the version compare view takes over the main area, with the\n // shared inspector (Versions pane) staying on as the navigator — an\n // in-app surface, not a modal, so arbitrary versions can be browsed.\n <div className=\"flex min-h-0 flex-1 overflow-hidden\">\n <div className=\"min-h-0 flex-1 overflow-auto\">\n <VersionCompare />\n </div>\n <InspectorSidebar\n panes={trailingPanes}\n activeId={inspectorPaneId}\n onActiveChange={setInspectorPaneId}\n className=\"w-[320px] shrink-0 border-l border-border\"\n />\n </div>\n ) : canvasField ? (\n <div className=\"min-h-0 flex-1 overflow-hidden\">\n <CompositionFieldEditor\n key={`${canvasField.name}:${restoreNonce}:${perspective}`}\n field={canvasField}\n value={getValue(canvasField)}\n onChange={(v) => setValue(canvasField, v)}\n readOnly={viewReadOnly || source.isFieldReadOnly(canvasField)}\n trailingPanes={trailingPanes}\n />\n </div>\n ) : activeTab === REFERENCES_TAB ? (\n // EC-269: mounted only while this tab is active → reference data loads on\n // demand and is torn down on leave (no fetching during normal editing).\n <div className=\"flex min-h-0 flex-1 overflow-hidden\">\n <div className=\"min-h-0 flex-1 overflow-auto p-4 sm:p-6\">\n <div className=\"mx-auto w-full max-w-2xl\">\n <ReferencesPanel\n projectId={projectId}\n source={{ type: 'document', id: documentKey(ref) }}\n target={{ type: 'document', id: documentKey(ref) }}\n />\n </div>\n </div>\n </div>\n ) : (\n <div className=\"flex min-h-0 flex-1 overflow-hidden\">\n <div className=\"min-h-0 flex-1 overflow-auto p-4 sm:p-6\">\n <div className=\"mx-auto w-full max-w-2xl\">\n <Stack gap=\"6\">\n {groups.map((group) => {\n // EC-306: a conditionally/statically hidden group section is skipped\n // entirely (its fields are also exempt from validation).\n if (hiddenGroups.has(group.name)) return null\n // EC-273/EC-240: drop a fieldset whose every field is hidden — whether\n // content-conditionally or for this principal — so the form never\n // shows an empty section header.\n const visible = group.fields.filter(isFieldShown)\n if (visible.length === 0) return null\n const section = group.section\n // EC-241: a section `title` is a LocalizedString ({ en, de } or a plain\n // string) — resolve it against the active UI locale before rendering.\n const sectionTitle =\n section?.title !== undefined ? localizedLabel(section.title, group.name) : undefined\n return (\n <CollapsibleSection\n key={group.name}\n collapsible={section?.collapsible}\n defaultOpen={section?.defaultOpen}\n title={\n <Text\n size=\"sm\"\n weight=\"semibold\"\n className={sectionTitle || group.name === DEFAULT_FIELD_GROUP ? undefined : 'capitalize'}\n >\n {sectionTitle ??\n (group.name === DEFAULT_FIELD_GROUP\n ? translate('cms.editor.defaultGroup')\n : group.name)}\n </Text>\n }\n >\n {/* EC-306: fractional field widths — a 12-col grid honoring each\n field's `form.width`, collapsing to one column on narrow widths. */}\n <div className=\"grid grid-cols-1 gap-4 p-4 md:grid-cols-12\">\n {visible.map((field) => (\n <div\n key={field.name}\n className={cn('col-span-1', fieldWidthColSpan(field.form?.width))}\n >\n {renderFieldRow(field)}\n </div>\n ))}\n </div>\n </CollapsibleSection>\n )\n })}\n </Stack>\n </div>\n </div>\n {/* 0.0.41: selecting a rich-text component embed swaps this sidebar for the\n embed's prop form (the product-owner's \"no third form surface\" ask); the\n normal inspector returns on deselect. Only the form view swaps — the\n canvas view carries its own inspector and Compare has nothing to edit. */}\n {activeEmbed ? (\n <EmbedInspector\n payload={activeEmbed}\n className=\"w-[320px] shrink-0 border-l border-border\"\n />\n ) : (\n <InspectorSidebar\n panes={trailingPanes}\n activeId={inspectorPaneId}\n onActiveChange={setInspectorPaneId}\n className=\"w-[320px] shrink-0 border-l border-border\"\n />\n )}\n </div>\n )}\n\n {/* EC-283: the always-on validation footer is retired. Validation now lives\n in the document-scoped `Issues` inspector pane (full list + jump-to-field,\n count badge on its tab), field issues stay inline, and the Publish button\n gates on errors. Read-only is announced by the top banner (EC-214). */}\n\n {/* EC-240: on an explicit-save rail (no autosave), guard navigation away from\n unsaved edits — offer Save / Discard / Cancel. Autosave rails flush on\n unmount instead, so the guard is inert there. */}\n {explicitSave ? (\n <UnsavedChangesGuard\n when={dirty}\n onSave={() => {\n save()\n return true\n }}\n description={translate('cms.editor.unsavedChangesGuard')}\n />\n ) : null}\n\n {/* Reference-aware unpublish/delete confirmation (EC-173). */}\n {confirmAction !== null ? (\n <ReferenceConfirmDialog\n projectId={projectId}\n action={confirmAction}\n targets={confirmTargets}\n open\n onOpenChange={(open) => {\n if (!open) setConfirmAction(null)\n }}\n onConfirm={handleConfirmedAction}\n />\n ) : null}\n\n {/* EC-316: \"Discard changes\" confirmation — reverting the draft to the\n published content is destructive to the working edits (though itself\n undoable: the restore op snapshots the pre-discard draft first, EC-298),\n so it confirms before dispatching. */}\n <Dialog open={discardConfirm} onOpenChange={(open) => { if (!open) setDiscardConfirm(false) }}>\n <DialogContent>\n <DialogHeader>\n <DialogTitle>{translate('cms.editor.discardDraftTitle')}</DialogTitle>\n <DialogDescription>\n {translate('cms.editor.discardDraftDescription')}\n </DialogDescription>\n </DialogHeader>\n <DialogFooter className=\"mt-4\">\n <Button variant=\"ghost\" size=\"sm\" onClick={() => setDiscardConfirm(false)} disabled={discardBusy}>\n {translate('common.cancel')}\n </Button>\n <Button\n variant=\"destructive\"\n size=\"sm\"\n data-testid=\"discard-draft-confirm\"\n disabled={discardBusy}\n onClick={() => void handleDiscardDraft()}\n >\n {discardBusy ? (\n <>\n <Loader2 className=\"animate-spin\" /> {translate('cms.editor.discardingDraft')}\n </>\n ) : (\n <>\n <RotateCcw /> {translate('cms.editor.discardDraftConfirm')}\n </>\n )}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n\n {/* EC-300/AD-16: confirm-through guard for unpublished hard references. The\n schema is valid (those errors block the button); these would just render\n empty on the live site, so the editor confirms rather than being blocked. */}\n <Dialog open={publishConfirm} onOpenChange={(open) => { if (!open) cancelPublishConfirm() }}>\n <DialogContent>\n <DialogHeader>\n <DialogTitle>{translate('cms.editor.publishUnpublishedRefsTitle')}</DialogTitle>\n <DialogDescription>\n {translate('cms.editor.publishUnpublishedRefsDescription')}\n </DialogDescription>\n </DialogHeader>\n <Stack gap=\"1\">\n {referenceIssues.map((issue, i) => (\n <Text key={`${issue.meta?.id ?? i}`} size=\"xs\" tone=\"muted\">\n • {issue.message}\n </Text>\n ))}\n {/* Awareness only — other (non-blocking) issues don't gate publish, but\n surfacing the count keeps the confirm honest (AD-16). Schema errors\n can't be here: they disable the button before it opens. */}\n {schemaIssues.length > 0 ? (\n <Text size=\"xs\" tone=\"muted\" className=\"mt-1\">\n {translate(\n schemaIssues.length === 1\n ? 'cms.editor.publishAlsoHasIssues.one'\n : 'cms.editor.publishAlsoHasIssues.other',\n { count: schemaIssues.length },\n )}\n </Text>\n ) : null}\n </Stack>\n <DialogFooter className=\"mt-4\">\n <Button variant=\"ghost\" size=\"sm\" onClick={cancelPublishConfirm}>\n {translate('common.cancel')}\n </Button>\n <Button size=\"sm\" data-testid=\"publish-anyway\" onClick={confirmPublish}>\n {translate('cms.editor.publishAnyway')}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n </div>\n </EmbedInspectorProvider>\n </DocumentEditorProvider>\n )\n}\n\n/**\n * EC-300/AD-16 — errors for composition embeds (hard references) whose target isn't\n * published: they render empty on the live site. Walks every `blocks` field of the\n * document for reuse embeds; a missing target is an `unknown-relation-target` error,\n * an unpublished one an `unpublished-relation-target` error. Publishing is\n * confirm-through (not blocked), so these feed the Issues panel + the publish\n * confirmation, never the button's disabled gate. No reuse capability → none.\n */\nfunction unpublishedEmbedIssues(\n collection: CollectionDef,\n draft: CmsDocument,\n source: CollectionViewSource,\n): CmsValidationIssue[] {\n const capability = resolveReuseCapability(getStudioRegistry().registry.list(), source.collections)\n if (!capability) return []\n const issues: CmsValidationIssue[] = []\n const seen = new Set<string>()\n for (const field of collection.fields) {\n if (!isBlocksField(field)) continue\n for (const ref of collectEmbedRefs(blockRootsOf(draft.values?.[field.name]), capability)) {\n const key = `${ref.collection}:${ref.id}`\n if (seen.has(key)) continue\n seen.add(key)\n const exists = source.get(ref) !== undefined\n if (exists && (source.publishing?.isPublished(ref) ?? false)) continue\n issues.push(unpublishedEmbedIssue(collection, draft, field.name, ref, exists, source))\n }\n }\n return issues\n}\n\n/** One unpublished/missing reuse-embed issue (EC-300). */\nfunction unpublishedEmbedIssue(\n collection: CollectionDef,\n draft: CmsDocument,\n field: string,\n ref: DocumentRef,\n exists: boolean,\n source: CollectionViewSource,\n): CmsValidationIssue {\n return {\n code: exists ? 'unpublished-relation-target' : 'unknown-relation-target',\n severity: 'error',\n message: exists\n ? translate('cms.editor.embedUnpublishedIssue', { title: source.titleOf(ref) })\n : translate('cms.editor.embedMissingIssue', { id: ref.id }),\n path: [field],\n collectionId: collection.id,\n documentId: draft.id,\n meta: { field, target: ref.collection, id: ref.id },\n }\n}\n\n/**\n * A segmented-control button for the Draft/Published view toggle (EC-226). The\n * `accent` variant (EC-214) gives the active Published segment a distinct info\n * treatment, so the read-only mode is obvious at the control itself.\n */\nfunction PerspectiveButton({\n label,\n active,\n accent,\n disabled,\n title,\n onClick,\n}: {\n label: string\n active: boolean\n accent?: 'info'\n disabled?: boolean\n title?: string\n onClick: () => void\n}) {\n return (\n <button\n type=\"button\"\n aria-pressed={active}\n disabled={disabled}\n title={title}\n onClick={onClick}\n className={cn(\n 'rounded px-2.5 py-1 text-xs font-medium transition-colors disabled:opacity-40',\n active\n ? accent === 'info'\n ? 'bg-info text-info-foreground shadow-sm'\n : 'bg-background text-foreground shadow-sm'\n : 'text-muted-foreground hover:text-foreground',\n )}\n >\n {label}\n </button>\n )\n}\n\ninterface FieldGroup {\n name: string\n fields: FieldDef[]\n /** EC-306: the presentational section descriptor, if one was declared for this group. */\n section?: FieldGroupSection\n}\n\n/** The implicit fieldset that holds fields with no declared `form.group` (localized via `cms.editor.defaultGroup`). */\nconst DEFAULT_FIELD_GROUP = 'fields'\n\nfunction groupFields(fields: FieldDef[], collection: CollectionDef): FieldGroup[] {\n const order = groupOrder(collection.form)\n const byGroup = new Map<string, FieldDef[]>()\n for (const field of fields) {\n const group = field.form?.group ?? DEFAULT_FIELD_GROUP\n if (!byGroup.has(group)) byGroup.set(group, [])\n byGroup.get(group)!.push(field)\n }\n const names = [...byGroup.keys()].sort((a, b) => {\n const ia = order.indexOf(a)\n const ib = order.indexOf(b)\n return (ia < 0 ? 99 : ia) - (ib < 0 ? 99 : ib)\n })\n return names.map((name) => ({\n name,\n section: groupSectionOf(collection.form, name),\n fields: byGroup.get(name)!.sort((a, b) => (a.form?.order ?? 99) - (b.form?.order ?? 99)),\n }))\n}\n\n// EC-306: translate a field's grid span (FIELD_WIDTH_SPANS, owned + tested in\n// core) into a literal Tailwind col-span class. Literal strings so Tailwind's\n// scanner generates them; the span math itself is single-sourced in core.\nconst COL_SPAN_CLASS: Record<number, string> = {\n 3: 'md:col-span-3',\n 4: 'md:col-span-4',\n 6: 'md:col-span-6',\n 8: 'md:col-span-8',\n 9: 'md:col-span-9',\n 12: 'md:col-span-12',\n}\n\nfunction fieldWidthColSpan(width: FieldWidth | undefined): string {\n return COL_SPAN_CLASS[FIELD_WIDTH_SPANS[width ?? '100%']] ?? 'md:col-span-12'\n}\n\nfunction primaryTitle(\n doc: CmsDocument,\n collection: CollectionDef,\n locale: Locale,\n config: LocaleConfig,\n): string {\n // EC-242: the header title is the document identity (resolveDocumentTitle) — a\n // string `titleField` or a function pattern — over the fallback-resolved values,\n // the same resolver that drives relation labels and the list title column.\n return resolveDocumentTitle(collection, resolveDocumentForLocale(collection, doc, locale, config), '')\n}\n"]}
1
+ {"version":3,"file":"detail-editor.js","sourceRoot":"","sources":["../../../src/components/cms/detail-editor.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,IAAI,EAAE,oBAAoB,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AAC9F,OAAO,EACL,KAAK,EACL,WAAW,EACX,WAAW,EACX,WAAW,EACX,GAAG,EACH,MAAM,EACN,gBAAgB,EAChB,OAAO,EACP,OAAO,EACP,IAAI,EACJ,cAAc,EACd,SAAS,EACT,IAAI,EACJ,GAAG,EACH,MAAM,EACN,CAAC,GACF,MAAM,cAAc,CAAA;AACrB,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAEtD,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,WAAW,EACX,UAAU,EACV,cAAc,EACd,UAAU,EACV,gBAAgB,EAChB,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,WAAW,EACX,wBAAwB,EACxB,oBAAoB,EACpB,YAAY,EACZ,aAAa,EACb,eAAe,EACf,QAAQ,EACR,YAAY,EACZ,iBAAiB,GAClB,MAAM,0BAA0B,CAAA;AAYjC,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAA;AAC5D,OAAO,EACL,KAAK,EACL,MAAM,EACN,MAAM,EACN,aAAa,EACb,iBAAiB,EACjB,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,mBAAmB,EACnB,gBAAgB,EAChB,mBAAmB,EACnB,kBAAkB,EAClB,KAAK,EACL,OAAO,EACP,GAAG,EACH,MAAM,EACN,KAAK,EACL,IAAI,EACJ,KAAK,GACN,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AAClE,OAAO,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAA;AAC3E,OAAO,EAAE,EAAE,EAAE,MAAM,6BAA6B,CAAA;AAChD,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAA;AACtE,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,sBAAsB,EACtB,eAAe,EACf,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,GACd,MAAM,8BAA8B,CAAA;AAErC,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAA;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAC9C,mFAAmF;AACnF,mFAAmF;AACnF,OAAO,EAAE,sBAAsB,EAAE,MAAM,0CAA0C,CAAA;AACjF,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACzC,OAAO,EAAE,0BAA0B,EAAE,MAAM,+CAA+C,CAAA;AAC1F,OAAO,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,4CAA4C,CAAA;AACtG,OAAO,EAAE,kBAAkB,EAAE,MAAM,8DAA8D,CAAA;AAEjG,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAA;AAC/C,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAA;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAA;AAChE,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAA;AAEjE,OAAO,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAA;AAElE,OAAO,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAA;AACzE,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAC9C,sFAAsF;AACtF,2DAA2D;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AACrD,sFAAsF;AACtF,uFAAuF;AACvF,OAAO,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAA;AAC5E,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAExD,OAAO,EAAE,uBAAuB,EAAE,MAAM,2CAA2C,CAAA;AACnF,OAAO,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAA;AACzE,4EAA4E;AAC5E,iFAAiF;AACjF,iCAAiC;AACjC,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAA;AACrE,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAC7C,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAA;AAC/D,OAAO,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AAC5E,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAA;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAC3C,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAA;AAElE,MAAM,UAAU,YAAY,CAAC,EAC3B,SAAS,EACT,YAAY,EACZ,UAAU,EACV,SAAS,GAOV;IACC,gFAAgF;IAChF,kFAAkF;IAClF,iFAAiF;IACjF,iFAAiF;IACjF,MAAM,MAAM,GAAG,0BAA0B,CAAC,YAAY,CAAC,CAAA;IACvD,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,CACL,KAAC,gBAAgB,IACf,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE,UAAU,CAAC,aAAa,CAAC,YAAY,CAAC,EAChD,KAAK,EAAE,YAAY,GACnB,CACH,CAAA;IACH,CAAC;IACD,OAAO,CACL,KAAC,oBAAoB,IAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,GAAI,CAC7G,CAAA;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB,CAAC,EACnC,MAAM,EACN,SAAS,EACT,UAAU,EACV,SAAS,EACT,YAAY,GAgBb;IACC,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,YAAY,EAAE,EAAE,EAAE,UAAU,EAAE,CAAC,CAAA;IAC3E,8EAA8E;IAC9E,gFAAgF;IAChF,yDAAyD;IACzD,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,OAAO,CACL,KAAC,gBAAgB,IACf,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE,MAAM,CAAC,QAAQ,EACzB,KAAK,EAAE,cAAc,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,YAAY,CAAC,GACrE,CACH,CAAA;IACH,CAAC;IACD,OAAO,CACL,KAAC,sBAAsB,IAAC,MAAM,EAAE,MAAM,YACpC,KAAC,UAAU,IAET,SAAS,EAAE,SAAS,EACpB,UAAU,EAAE,MAAM,CAAC,MAAM,EACzB,GAAG,EAAE,GAAG,EACR,SAAS,EAAE,SAAS,EACpB,YAAY,EAAE,YAAY,IALrB,GAAG,MAAM,CAAC,YAAY,IAAI,UAAU,EAAE,CAM3C,GACqB,CAC1B,CAAA;AACH,CAAC;AAED,iFAAiF;AACjF,SAAS,gBAAgB,CAAC,EACxB,SAAS,EACT,QAAQ,EACR,KAAK,GAKN;IACC,OAAO,CACL,cAAK,SAAS,EAAC,oCAAoC,YACjD,MAAC,KAAK,IAAC,GAAG,EAAC,GAAG,EAAC,KAAK,EAAC,QAAQ,EAAC,SAAS,EAAC,aAAa,aACnD,KAAC,OAAO,IAAC,KAAK,EAAE,CAAC,EAAE,IAAI,EAAC,IAAI,YACzB,SAAS,CAAC,0BAA0B,CAAC,GAC9B,EACT,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,CACxB,KAAC,IAAI,IACH,EAAE,EAAE,oBAAoB,EACxB,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,EACvC,SAAS,EAAC,sCAAsC,YAE/C,SAAS,CAAC,uBAAuB,EAAE,EAAE,KAAK,EAAE,CAAC,GACzC,CACR,CAAC,CAAC,CAAC,IAAI,IACF,GACJ,CACP,CAAA;AACH,CAAC;AAED,SAAS,UAAU,CAAC,EAClB,SAAS,EACT,UAAU,EACV,GAAG,EACH,SAAS,EACT,YAAY,GAQb;IACC,MAAM,MAAM,GAAG,iBAAiB,EAAE,CAAA;IAClC,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAA;IAC9B,+EAA+E;IAC/E,8EAA8E;IAC9E,iFAAiF;IACjF,8EAA8E;IAC9E,MAAM,UAAU,GAAG,MAAM,CAAC,YAAY,CAAC,OAAO,CAAA;IAC9C,MAAM,WAAW,GAAG,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAA;IAChD,MAAM,UAAU,GAAG,MAAM,CAAC,YAAY,CAAC,OAAO,CAAA;IAC9C,8EAA8E;IAC9E,iFAAiF;IACjF,8EAA8E;IAC9E,6EAA6E;IAC7E,+DAA+D;IAC/D,MAAM,cAAc,GAAG,UAAU,CAAA;IACjC,6EAA6E;IAC7E,8EAA8E;IAC9E,kFAAkF;IAClF,kFAAkF;IAClF,yDAAyD;IACzD,MAAM,QAAQ,GAAG,WAAW,CAC1B,CAAC,KAAe,EAAE,EAAE,CAAC,kBAAkB,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,CAAC,EAC3D,CAAC,MAAM,EAAE,GAAG,CAAC,CACd,CAAA;IACD,6EAA6E;IAC7E,0EAA0E;IAC1E,wDAAwD;IACxD,MAAM,SAAS,GAAG,WAAW,CAAC,UAAU,CAAC,CAAA;IACzC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAc,GAAG,EAAE,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,CAAA;IAC3E,2EAA2E;IAC3E,iFAAiF;IACjF,MAAM,MAAM,GAAG,MAAM,CAAC,YAAY,CAAA;IAClC,+EAA+E;IAC/E,iEAAiE;IACjE,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAA;IACnD,wEAAwE;IACxE,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC,GAAG,QAAQ,CAAyB,IAAI,CAAC,CAAA;IAChF,kFAAkF;IAClF,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,UAAU,EAAE,UAAU,CAAC,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,UAAU,CAAC,EAAE,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAA;IAC/F,MAAM,cAAc,GAAG,OAAO,CAC5B,GAAG,EAAE,CAAC,CAAC,EAAE,UAAU,EAAE,UAAU,CAAC,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,EACjD,CAAC,UAAU,CAAC,EAAE,EAAE,GAAG,CAAC,EAAE,CAAC,CACxB,CAAA;IAED,2EAA2E;IAC3E,8EAA8E;IAC9E,gFAAgF;IAChF,8EAA8E;IAC9E,4EAA4E;IAC5E,SAAS,IAAI;QACX,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IACpB,CAAC;IACD,SAAS,YAAY,CAAC,IAAiB;QACrC,QAAQ,CAAC,IAAI,CAAC,CAAA;QACd,eAAe,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;IAC/B,CAAC;IAED,8EAA8E;IAC9E,+EAA+E;IAC/E,gFAAgF;IAChF,8EAA8E;IAC9E,yCAAyC;IACzC,uBAAuB,CAAC;QACtB,UAAU,EAAE,UAAU,CAAC,EAAE;QACzB,EAAE,EAAE,GAAG,CAAC,EAAE;QACV,KAAK,EAAE,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,YAAY,CAAC,IAAI,GAAG,CAAC,EAAE;QAC7E,UAAU,EAAE,YAAY;KACzB,CAAC,CAAA;IAEF,iFAAiF;IACjF,6EAA6E;IAC7E,+EAA+E;IAC/E,0EAA0E;IAC1E,gFAAgF;IAChF,MAAM,EACJ,WAAW,EACX,cAAc,EACd,SAAS,EACT,UAAU,EACV,UAAU,EACV,sBAAsB,EACtB,oBAAoB,EACpB,iBAAiB,EACjB,YAAY,EACZ,OAAO,EACP,gBAAgB,GACjB,GAAG,uBAAuB,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,CAAA;IAE5E,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAA;IACxF,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAA;IAC3E,kFAAkF;IAClF,mFAAmF;IACnF,4EAA4E;IAC5E,gFAAgF;IAChF,mDAAmD;IACnD,MAAM,eAAe,GAAG,OAAO,CAC7B,GAAG,EAAE,CAAC,sBAAsB,CAAC,UAAU,EAAE,KAAK,EAAE,MAAM,CAAC,EACvD,CAAC,UAAU,EAAE,KAAK,EAAE,MAAM,CAAC,CAC5B,CAAA;IACD,MAAM,MAAM,GAAG,OAAO,CACpB,GAAG,EAAE,CAAC,CAAC,GAAG,YAAY,EAAE,GAAG,eAAe,CAAC,EAC3C,CAAC,YAAY,EAAE,eAAe,CAAC,CAChC,CAAA;IACD,MAAM,UAAU,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,MAAM,CAAA;IAC5E,MAAM,SAAS,GAAG,UAAU,GAAG,CAAC,CAAA;IAChC,MAAM,kBAAkB,GAAG,eAAe,CAAC,MAAM,GAAG,CAAC,CAAA;IAErD,+EAA+E;IAC/E,gFAAgF;IAChF,kFAAkF;IAClF,MAAM,EAAE,YAAY,EAAE,GAAG,WAAW,CAAC,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAA;IAE1E,0EAA0E;IAC1E,kFAAkF;IAClF,mFAAmF;IACnF,qCAAqC;IACrC,MAAM,EACJ,MAAM,EAAE,aAAa,EACrB,cAAc,EAAE,qBAAqB,EACrC,WAAW,EAAE,cAAc,EAC3B,cAAc,EAAE,iBAAiB,EACjC,IAAI,EAAE,WAAW,EACjB,OAAO,EAAE,kBAAkB,GAC5B,GAAG,oBAAoB,CAAC;QACvB,UAAU;QACV,SAAS;QACT,YAAY;QACZ,oBAAoB;QACpB,sBAAsB;QACtB,UAAU;QACV,UAAU;QACV,KAAK;QACL,MAAM;QACN,MAAM,EAAE,GAAG;QACX,YAAY;QACZ,KAAK;QACL,IAAI;KACL,CAAC,CAAA;IACF,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,EAAE;QACjC,MAAM,GAAG,GAAG,IAAI,GAAG,EAAkB,CAAA;QACrC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,0EAA0E;YAC1E,6EAA6E;YAC7E,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,EAAE,KAAK,CAAA;YAC/B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC;gBAAE,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,CAAA;QACjF,CAAC;QACD,OAAO,GAAG,CAAA;IACZ,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAA;IAEZ,qFAAqF;IACrF,mFAAmF;IACnF,8CAA8C;IAC9C,MAAM,YAAY,GAAG,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,CAAA;IAC9C,MAAM,IAAI,GAAG,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAA;IAC9D,8EAA8E;IAC9E,2EAA2E;IAC3E,mFAAmF;IACnF,MAAM,cAAc,GAAG,YAAY,CAAA;IACnC,8EAA8E;IAC9E,qDAAqD;IACrD,MAAM,WAAW,GAAG,SAAS,CAAA;IAC7B,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,CAAA;IACpD,kFAAkF;IAClF,6EAA6E;IAC7E,8DAA8D;IAC9D,MAAM,gBAAgB,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,aAAa,CAAC,UAAU,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAA;IAC/E,0EAA0E;IAC1E,+EAA+E;IAC/E,iFAAiF;IACjF,iBAAiB;IACjB,MAAM,SAAS,GAAG,UAAU,CAAC,MAAM,CAAC,MAAM,CACxC,CAAC,CAAC,EAAE,EAAE,CACJ,CAAC,CAAC,CAAC,IAAI,EAAE,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,SAAS;QACtC,CAAC,iBAAiB,CAAC,CAAC,CAAC;QACrB,CAAC,CAAC,IAAI,EAAE,KAAK,KAAK,mBAAmB,CACxC,CAAA;IACD,MAAM,MAAM,GAAG,WAAW,CAAC,SAAS,EAAE,UAAU,CAAC,CAAA;IACjD,+EAA+E;IAC/E,6EAA6E;IAC7E,oEAAoE;IACpE,MAAM,WAAW,GAAG,SAAS,CAAC,IAAI,CAAC,aAAa,CAAC,CAAA;IAEjD,iFAAiF;IACjF,iFAAiF;IACjF,+DAA+D;IAC/D,MAAM,EAAE,gBAAgB,EAAE,eAAe,EAAE,kBAAkB,EAAE,UAAU,EAAE,GAAG,aAAa,CAAC;QAC1F,UAAU;QACV,MAAM;QACN,SAAS;QACT,YAAY;KACb,CAAC,CAAA;IACF,mFAAmF;IACnF,oFAAoF;IACpF,8DAA8D;IAC9D,MAAM,EAAE,WAAW,EAAE,iBAAiB,EAAE,GAAG,qBAAqB,EAAE,CAAA;IAClE,oFAAoF;IACpF,mFAAmF;IACnF,gEAAgE;IAChE,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,WAAW,EAAE,GAAG,mBAAmB,CAAC;QACnG,IAAI;QACJ,UAAU,EAAE,WAAW;QACvB,YAAY;QACZ,kBAAkB;KACnB,CAAC,CAAA;IACF,6EAA6E;IAC7E,6EAA6E;IAC7E,4CAA4C;IAC5C,MAAM,gBAAgB,GAAG,CAAC,GAAW,EAAE,EAAE;QACvC,MAAM,MAAM,GAAG,GAAG,KAAK,SAAS,CAAA;QAChC,mFAAmF;QACnF,oFAAoF;QACpF,wDAAwD;QACxD,MAAM,aAAa,GAAG,GAAG,KAAK,cAAc,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,eAAe,CAAC,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,KAAK,CAAA;QACvG,MAAM,OAAO,GACX,GAAG,KAAK,cAAc;YACpB,CAAC,CAAC,SAAS,CAAC,kBAAkB,CAAC;YAC/B,CAAC,CAAC,aAAa,KAAK,SAAS;gBAC3B,CAAC,CAAC,cAAc,CAAC,aAAa,EAAE,GAAG,CAAC;gBACpC,CAAC,CAAC,GAAG,CAAA;QACX,MAAM,SAAS,GAAG,GAAG,KAAK,cAAc,IAAI,aAAa,KAAK,SAAS,CAAA;QACvE,OAAO,CACL,iBAEE,IAAI,EAAC,QAAQ,kBACC,MAAM,EACpB,OAAO,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,EAChC,SAAS,EAAE,EAAE,CACX,8DAA8D,EAC9D,SAAS,IAAI,YAAY,EACzB,MAAM,CAAC,CAAC,CAAC,kCAAkC,CAAC,CAAC,CAAC,sCAAsC,CACrF,YAEA,OAAO,IAVH,GAAG,CAWD,CACV,CAAA;IACH,CAAC,CAAA;IACD,SAAS,QAAQ,CAAC,KAAe;QAC/B,wEAAwE;QACxE,kEAAkE;QAClE,IAAI,gBAAgB,CAAC,UAAU,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YAC7C,wEAAwE;YACxE,wEAAwE;YACxE,0EAA0E;YAC1E,MAAM,QAAQ,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;YAC1D,IAAI,QAAQ,KAAK,SAAS;gBAAE,OAAO,QAAQ,CAAA;YAC3C,OAAO,YAAY,CAAC,UAAU,EAAE,OAAO,EAAE,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC,KAAK,CAAA;QACzF,CAAC;QACD,OAAO,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IACnC,CAAC;IAED,mFAAmF;IACnF,iFAAiF;IACjF,mFAAmF;IACnF,kFAAkF;IAClF,SAAS,QAAQ,CAAC,KAAe,EAAE,KAAc;QAC/C,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,CAAC,EAAE,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,CAAA;IAC1E,CAAC;IAED,6EAA6E;IAC7E,6EAA6E;IAC7E,6EAA6E;IAC7E,kEAAkE;IAClE,MAAM,eAAe,GAAG,kBAAkB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;IACzD,MAAM,QAAQ,GAAG,CAAC,KAAe,EAAE,EAAE,CAAC,aAAa,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC,CAAA;IACvF,sFAAsF;IACtF,MAAM,YAAY,GAAG,CAAC,KAAe,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,QAAQ,CAAC,KAAK,CAAC,KAAK,QAAQ,CAAA;IAC1F,MAAM,oBAAoB,GAAG,gBAAgB,CAAC,MAAM,CAAC,YAAY,CAAC,CAAA;IAClE,iFAAiF;IACjF,+EAA+E;IAC/E,MAAM,YAAY,GAAG,gBAAgB,CAAC,UAAU,EAAE,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC,CAAA;IAE9E,8EAA8E;IAC9E,0EAA0E;IAC1E,8EAA8E;IAC9E,iFAAiF;IACjF,0CAA0C;IAC1C,SAAS,cAAc,CAAC,KAAe;QACrC,IAAI,QAAQ,CAAC,KAAK,CAAC;YAAE,OAAO,IAAI,CAAA;QAChC,wEAAwE;QACxE,6EAA6E;QAC7E,wEAAwE;QACxE,0DAA0D;QAC1D,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;QAC9B,IAAI,MAAM,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAA;QACpC,MAAM,kBAAkB,GAAG,MAAM,KAAK,MAAM,CAAA;QAC5C,MAAM,aAAa,GAAG,YAAY,IAAI,kBAAkB,CAAA;QACxD,MAAM,SAAS,GAAG,gBAAgB,CAAC,UAAU,EAAE,KAAK,CAAC,IAAI,CAAC,CAAA;QAC1D,4EAA4E;QAC5E,6EAA6E;QAC7E,6EAA6E;QAC7E,8EAA8E;QAC9E,gDAAgD;QAChD,MAAM,UAAU,GAAG,SAAS;YAC1B,CAAC,CAAC,eAAe,CAAC,UAAU,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,YAAY,CAAC;YAC7E,CAAC,CAAC,IAAI,CAAA;QACR,OAAO,CACL,KAAC,KAAK,IAEJ,SAAS,EAAE,EAAE,CACX,8BAA8B,EAC9B,gBAAgB,KAAK,KAAK,CAAC,IAAI,IAAI,yCAAyC,CAC7E,EACD,KAAK,EACH,MAAC,GAAG,IAAC,GAAG,EAAC,GAAG,aACV,yBAAO,UAAU,CAAC,KAAK,CAAC,GAAQ,EAC/B,KAAK,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC,eAAM,SAAS,EAAC,kBAAkB,kBAAS,CAAC,CAAC,CAAC,IAAI,EAC/E,SAAS,CAAC,CAAC,CAAC,KAAC,KAAK,IAAC,OAAO,EAAC,SAAS,YAAE,MAAM,GAAS,CAAC,CAAC,CAAC,IAAI,EAC5D,kBAAkB,CAAC,CAAC,CAAC,CACpB,MAAC,KAAK,IAAC,OAAO,EAAC,SAAS,aACtB,KAAC,IAAI,IAAC,SAAS,EAAC,QAAQ,GAAG,OAAE,SAAS,CAAC,0BAA0B,CAAC,IAC5D,CACT,CAAC,CAAC,CAAC,IAAI,EACR,KAAC,MAAM,KAAG,EAKT,UAAU,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAC9B,KAAC,sBAAsB,IACrB,UAAU,EAAE,UAAU,EACtB,SAAS,EAAE,KAAK,CAAC,IAAI,EACrB,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,GAAG,EAAE,CACnB,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,UAAU,EAAE,CAAC,EAAE,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC,EAE5F,aAAa,EAAE,GAAG,EAAE,CAClB,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,gBAAgB,CAAC,UAAU,EAAE,CAAC,EAAE,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,GAEtE,CACH,CAAC,CAAC,CAAC,IAAI,IACJ,EAER,OAAO,EAAE,KAAK,KAAK,CAAC,IAAI,EAAE,EAC1B,IAAI,EAAE,cAAc,CAAC,KAAK,CAAC,IAAI,EAAE,WAAW,CAAC,IAAI,SAAS,EAC1D,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,YAEhE,KAAC,YAAY,IACX,EAAE,EAAE,KAAK,KAAK,CAAC,IAAI,EAAE,EACrB,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAC,EACtB,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,EACnC,QAAQ,EAAE,aAAa,GACvB,IA7CG,KAAK,CAAC,IAAI,CAiDT,CACT,CAAA;IACH,CAAC;IAED,8EAA8E;IAC9E,4EAA4E;IAC5E,yCAAyC;IACzC,MAAM,YAAY,GAAG,OAAO,CAC1B,GAAG,EAAE,CAAC,CAAC;QACL,EAAE,EAAE,UAAU;QACd,KAAK,EAAE,SAAS,CAAC,uBAAuB,CAAC;QACzC,IAAI,EAAE,KAAC,OAAO,IAAC,SAAS,EAAC,UAAU,GAAG;QACtC,MAAM,EAAE,GAAG,EAAE,CAAC,KAAC,YAAY,KAAG;KAC/B,CAAC,EACF,EAAE,CACH,CAAA;IACD,0EAA0E;IAC1E,2EAA2E;IAC3E,2EAA2E;IAC3E,6EAA6E;IAC7E,MAAM,UAAU,GAAG,OAAO,CACxB,GAAG,EAAE,CAAC,CAAC;QACL,EAAE,EAAE,QAAQ;QACZ,KAAK,EAAE,SAAS,CAAC,uBAAuB,CAAC;QACzC,IAAI,EAAE,KAAC,WAAW,IAAC,SAAS,EAAC,UAAU,GAAG;QAC1C,KAAK,EACH,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAChC,KAAC,KAAK,IACJ,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,EACzE,SAAS,EAAC,0BAA0B,YAEnC,MAAM,CAAC,MAAM,GACR,CACT;QACH,MAAM,EAAE,GAAG,EAAE,CAAC,CACZ,KAAC,UAAU,IAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,UAAU,GAAI,CAClF;KACF,CAAC,EACF,CAAC,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,CAAC,CAC7C,CAAA;IACD,gFAAgF;IAChF,4EAA4E;IAC5E,+EAA+E;IAC/E,2EAA2E;IAC3E,4EAA4E;IAC5E,iFAAiF;IACjF,0EAA0E;IAC1E,qFAAqF;IACrF,mFAAmF;IACnF,kFAAkF;IAClF,qFAAqF;IACrF,gEAAgE;IAChE,MAAM,kBAAkB,GAAG,uBAAuB,EAAE,CAAC,IAAI,CACvD,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,EAAE,UAAU,KAAK,UAAU,CAAC,EAAE,CACxD,CAAA;IACD,qFAAqF;IACrF,uFAAuF;IACvF,sFAAsF;IACtF,iFAAiF;IACjF,wFAAwF;IACxF,MAAM,WAAW,GACf,UAAU,CAAC,UAAU,CAAC,IAAI,oBAAoB,CAAC,MAAM,GAAG,CAAC;QACvD,CAAC,CAAC;YACE,EAAE,EAAE,SAAS;YACb,KAAK,EAAE,SAAS,CAAC,wBAAwB,CAAC;YAC1C,IAAI,EAAE,KAAC,GAAG,IAAC,SAAS,EAAC,UAAU,GAAG;YAClC,MAAM,EAAE,GAAG,EAAE,CAAC,CACZ,KAAC,WAAW,IACV,UAAU,EAAE,UAAU,EACtB,GAAG,EAAE,KAAK,EACV,QAAQ,EAAE,MAAM,CAAC,SAAS,EAAE,EAC5B,QAAQ,EAAE,YAAY,EACtB,MAAM,EAAE,oBAAoB,EAC5B,WAAW,EAAE,cAAc,EAC3B,aAAa,EAAE,CAAC,KAAK,EAAE,EAAE,CACvB,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE;oBACb,MAAM,IAAI,GAAG,EAAE,GAAG,CAAC,EAAE,CAAA;oBACrB,IAAI,KAAK,KAAK,SAAS;wBAAE,OAAO,IAAI,CAAC,KAAK,CAAA;;wBACrC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;oBACvB,OAAO,IAAI,CAAA;gBACb,CAAC,CAAC,GAEJ,CACH;SACF;QACH,CAAC,CAAC,IAAI,CAAA;IACV,kFAAkF;IAClF,qDAAqD;IACrD,MAAM,aAAa,GAAG;QACpB,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACrC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACtC,UAAU;KACX,CAAA;IAED,kFAAkF;IAClF,gFAAgF;IAChF,8EAA8E;IAC9E,oEAAoE;IACpE,MAAM,EACJ,YAAY,EACZ,cAAc,EACd,aAAa,EACb,qBAAqB,EACrB,cAAc,EACd,oBAAoB,GACrB,GAAG,oBAAoB,CAAC;QACvB,MAAM;QACN,MAAM,EAAE,GAAG;QACX,UAAU;QACV,SAAS;QACT,KAAK;QACL,QAAQ;QACR,KAAK;QACL,kBAAkB;KACnB,CAAC,CAAA;IAEF,SAAS,qBAAqB;QAC5B,IAAI,aAAa,KAAK,WAAW,EAAE,CAAC;YAClC,gBAAgB,CAAC,IAAI,CAAC,CAAA;YACtB,KAAK,MAAM,CAAC,UAAU,EAAE,SAAS,CAAC,GAAG,CAAC,CAAA;YACtC,OAAM;QACR,CAAC;QACD,IAAI,aAAa,KAAK,QAAQ,EAAE,CAAC;YAC/B,gBAAgB,CAAC,IAAI,CAAC,CAAA;YACtB,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;YAClB,6EAA6E;YAC7E,uEAAuE;YACvE,KAAK,QAAQ,CAAC;gBACZ,EAAE,EAAE,oBAAoB;gBACxB,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,QAAQ,IAAI,UAAU,CAAC,KAAK,EAAE,EAAE;aACrE,CAAC,CAAA;QACJ,CAAC;IACH,CAAC;IAED,2EAA2E;IAC3E,yEAAyE;IACzE,MAAM,KAAK,GAAG,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,YAAY,CAAC,CAAA;IACpG,8EAA8E;IAC9E,2EAA2E;IAC3E,MAAM,aAAa,GACjB,gBAAgB,IAAI,OAAO,UAAU,CAAC,UAAU,KAAK,UAAU;QAC7D,CAAC,CAAC,IAAI;QACN,CAAC,CAAC,sBAAsB,CACpB,UAAU,EACV,OAAO,EACP,YAAY,CAAC,UAAU,CAAC,EAAE,IAAI,IAAI,OAAO,EACzC,MAAM,EACN,MAAM,CAAC,YAAY,CACpB,CAAA;IAEP,2EAA2E;IAC3E,qEAAqE;IACrE,2EAA2E;IAC3E,2EAA2E;IAC3E,yEAAyE;IACzE,MAAM,eAAe,GAAG,OAAO,CAAmB,GAAG,EAAE;QACrD,MAAM,eAAe,GAAG,cAAc,CAAC,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE,UAAU,CAAC,EAAE,CAAC,CAAA;QAC7E,6EAA6E;QAC7E,8EAA8E;QAC9E,oEAAoE;QACpE,IAAI,SAAS,IAAI,MAAM,CAAC,QAAQ,KAAK,SAAS;YAAE,OAAO,CAAC,EAAE,KAAK,EAAE,eAAe,EAAE,CAAC,CAAA;QACnF,MAAM,QAAQ,GAAG,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,YAAY,CAAC,IAAI,GAAG,CAAC,EAAE,CAAA;QACvF,OAAO;YACL;gBACE,KAAK,EAAE,eAAe;gBACtB,EAAE,EAAE,oBAAoB;gBACxB,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,QAAQ,EAAE;aAC/C;YACD,EAAE,KAAK,EAAE,QAAQ,EAAE;SACpB,CAAA;IACH,CAAC,EAAE,CAAC,SAAS,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,QAAQ,EAAE,GAAG,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC,CAAA;IACnG,gBAAgB,CAAC,eAAe,CAAC,CAAA;IAEjC,8EAA8E;IAC9E,kFAAkF;IAClF,kFAAkF;IAClF,iFAAiF;IACjF,4EAA4E;IAC5E,8BAA8B;IAC9B,MAAM,EAAE,UAAU,EAAE,GAAG,UAAU,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,kBAAkB,EAAE,CAAC,CAAA;IAErG,gFAAgF;IAChF,2EAA2E;IAC3E,gFAAgF;IAChF,8EAA8E;IAC9E,iFAAiF;IACjF,2CAA2C;IAC3C,MAAM,gBAAgB,GAAG,cAAc,IAAI,OAAO,KAAK,IAAI,CAAA;IAC3D,MAAM,eAAe,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,gBAAgB,CAAA;IAC3D,MAAM,WAAW,GAAG,eAAe,IAAI,UAAU,IAAI,UAAU,CAAA;IAE/D,kFAAkF;IAClF,mFAAmF;IACnF,iBAAiB;IACjB,MAAM,EAAE,QAAQ,EAAE,eAAe,EAAE,KAAK,EAAE,YAAY,EAAE,GAAG,WAAW,CAAC;QACrE,SAAS;QACT,YAAY;QACZ,YAAY;QACZ,UAAU;KACX,CAAC,CAAA;IACF,MAAM,kBAAkB,GACtB,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,CAC3B,8BACE,KAAC,OAAO,IAAC,SAAS,EAAC,cAAc,GAAG,OAAE,SAAS,CAAC,uBAAuB,CAAC,IACvE,CACJ,CAAC,CAAC,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,CAC/B,8BACE,KAAC,WAAW,KAAG,OAAE,SAAS,CAAC,2BAA2B,CAAC,IACtD,CACJ,CAAC,CAAC,CAAC,CACF,8BACE,KAAC,GAAG,KAAG,OAAE,SAAS,CAAC,oBAAoB,CAAC,IACvC,CACJ,CAAA;IACH,iFAAiF;IACjF,kFAAkF;IAClF,MAAM,eAAe,GAAG,CAAC,SAAS,IAAI,MAAM,CAAC,QAAQ,KAAK,SAAS,CAAA;IAEnE,OAAO,CACL,KAAC,sBAAsB,IACrB,KAAK,EAAE;YACL,SAAS;YACT,UAAU;YACV,MAAM,EAAE,GAAG;YACX,KAAK;YACL,KAAK;YACL,SAAS;YACT,YAAY;YACZ,IAAI;YACJ,YAAY;YACZ,OAAO;YACP,aAAa,EAAE,SAAS,KAAK,WAAW;YACxC,YAAY;YACZ,cAAc;YACd,gBAAgB;YAChB,WAAW;SACZ,YAED,KAAC,sBAAsB,IAAC,KAAK,EAAE,iBAAiB,YAChD,eAAK,SAAS,EAAE,EAAE,CAAC,8BAA8B,EAAE,YAAY,IAAI,gBAAgB,CAAC,aAGpF,eAAK,SAAS,EAAC,mDAAmD,aAChE,MAAC,GAAG,IAAC,GAAG,EAAC,GAAG,EAAC,KAAK,EAAC,OAAO,aAOxB,MAAC,KAAK,IAAC,GAAG,EAAC,GAAG,EAAC,SAAS,EAAC,SAAS,aAChC,MAAC,GAAG,IAAC,GAAG,EAAC,GAAG,EAAC,KAAK,EAAC,QAAQ,EAAC,SAAS,EAAC,SAAS,aAC7C,KAAC,OAAO,IAAC,KAAK,EAAE,CAAC,EAAE,IAAI,EAAC,IAAI,EAAC,SAAS,EAAC,UAAU,YAC9C,SAAS,CAAC,CAAC,CAAC,cAAc,CAAC,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,CAAC,EAAE,GAC5E,EACT,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAC,YAAY,IAAC,QAAQ,EAAE,aAAa,EAAE,MAAM,EAAC,oBAAoB,GAAG,IACrF,EACL,kBAAkB,CAAC,CAAC,CAAC,CACpB,KAAC,eAAe,IACd,SAAS,EAAE,SAAS,EACpB,UAAU,EAAE,UAAU,EACtB,GAAG,EAAE,KAAK,EACV,QAAQ,EAAE,MAAM,CAAC,SAAS,EAAE,EAC5B,MAAM,EAAE,uBAAuB,EAAE,EACjC,YAAY,EAAE,MAAM,CAAC,YAAY,EACjC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,UAAU,GAChB,CACH,CAAC,CAAC,CAAC,IAAI,IACF,EACR,KAAC,MAAM,KAAG,EAGV,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,OAAO,EAAC,SAAS,EAAC,mBAAmB,iBAAa,iBAAiB,YACrF,MAAM,CAAC,SAAS;4CACf,CAAC,CAAC,SAAS,CAAC,uBAAuB,CAAC;4CACpC,CAAC,CAAC,YAAY;gDACZ,CAAC,CAAC,KAAK;oDACL,CAAC,CAAC,SAAS,CAAC,2BAA2B,CAAC;oDACxC,CAAC,CAAC,SAAS,CAAC,kBAAkB,CAAC;gDACjC,CAAC,CAAC,KAAK;oDACL,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC;oDAChC,CAAC,CAAC,SAAS,CAAC,kBAAkB,CAAC,GAChC,EACN,UAAU,CAAC,CAAC,CAAC,KAAC,aAAa,IAAC,SAAS,EAAE,SAAS,GAAI,CAAC,CAAC,CAAC,IAAI,EAU3D,UAAU,CAAC,CAAC,CAAC,CACZ,MAAC,GAAG,IAAC,GAAG,EAAC,IAAI,EAAC,KAAK,EAAC,QAAQ,aAC1B,KAAC,MAAM,IACL,IAAI,EAAC,IAAI,EACT,OAAO,EAAE,aAAa,EACtB,QAAQ,EAAE,eAAe,EACzB,KAAK,EAAE,YAAY,EACnB,SAAS,EAAE,eAAe,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,SAAS,iBAC7C,aAAa,YAExB,kBAAkB,GACZ,EACR,eAAe,CAAC,CAAC,CAAC,CACjB,MAAC,YAAY,eACX,KAAC,mBAAmB,IAAC,OAAO,kBAC1B,KAAC,MAAM,IACL,IAAI,EAAC,IAAI,EACT,QAAQ,EAAE,eAAe,gBACb,SAAS,CAAC,2BAA2B,CAAC,iBACtC,qBAAqB,EACjC,SAAS,EAAC,6DAA6D,YAEvE,KAAC,WAAW,IAAC,SAAS,EAAC,UAAU,GAAG,GAC7B,GACW,EACtB,KAAC,mBAAmB,IAAC,KAAK,EAAC,KAAK,YAC9B,MAAC,gBAAgB,IAAC,OAAO,EAAE,qBAAqB,iBAAc,uBAAuB,aACnF,KAAC,IAAI,KAAG,OAAE,SAAS,CAAC,4BAA4B,CAAC,IAChC,GACC,IACT,CAChB,CAAC,CAAC,CAAC,IAAI,IACJ,CACP,CAAC,CAAC,CAAC,IAAI,EAGP,YAAY,CAAC,CAAC,CAAC,CACd,MAAC,MAAM,IAAC,IAAI,EAAC,IAAI,EAAC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,KAAK,iBAAc,UAAU,aACvE,KAAC,KAAK,KAAG,OAAE,SAAS,CAAC,aAAa,CAAC,IAC5B,CACV,CAAC,CAAC,CAAC,IAAI,EAKP,CAAC,YAAY,CAAC,CAAC,CAAC,CACf,KAAC,uBAAuB,IACtB,SAAS,EAAE,SAAS,EACpB,UAAU,EAAE,UAAU,EACtB,MAAM,EAAE,GAAG,EACX,QAAQ,EAAE,KAAK,EACf,YAAY,EAAE,MAAM,CAAC,YAAY,EACjC,cAAc,EAAE,UAAU,EAC1B,SAAS,EAAE,CAAC,IAAI,EAAE,EAAE;4CAClB,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;4CACjB,YAAY,CAAC,IAAI,CAAC,CAAA;wCACpB,CAAC,GACD,CACH,CAAC,CAAC,CAAC,IAAI,EAGP,SAAS,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CACzB,MAAC,YAAY,eACX,KAAC,mBAAmB,IAAC,OAAO,kBAC1B,KAAC,MAAM,IAAC,OAAO,EAAC,SAAS,EAAC,IAAI,EAAC,SAAS,gBAAa,SAAS,CAAC,wBAAwB,CAAC,YACtF,KAAC,cAAc,KAAG,GACX,GACW,EACtB,MAAC,mBAAmB,IAAC,KAAK,EAAC,KAAK,aAQ7B,aAAa,KAAK,QAAQ,CAAC,CAAC,CAAC,CAC5B,MAAC,gBAAgB,mBACH,mBAAmB,EAC/B,QAAQ,EAAE,aAAa,KAAK,OAAO,EACnC,KAAK,EAAE,qBAAqB,EAC5B,OAAO,EAAE,GAAG,EAAE,CAAC,iBAAiB,CAAC,IAAI,CAAC,aAEtC,KAAC,SAAS,KAAG,OAAE,SAAS,CAAC,yBAAyB,CAAC,IAClC,CACpB,CAAC,CAAC,CAAC,IAAI,EACP,SAAS,CAAC,CAAC,CAAC,CACX,MAAC,gBAAgB,IAAC,OAAO,EAAE,GAAG,EAAE,CAAC,gBAAgB,CAAC,WAAW,CAAC,aAC5D,KAAC,MAAM,KAAG,OAAE,SAAS,CAAC,sBAAsB,CAAC,IAC5B,CACpB,CAAC,CAAC,CAAC,IAAI,EACP,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAClB,MAAC,gBAAgB,mBACH,YAAY,EACxB,SAAS,EAAC,kBAAkB,EAC5B,OAAO,EAAE,GAAG,EAAE,CAAC,gBAAgB,CAAC,QAAQ,CAAC,aAEzC,KAAC,MAAM,KAAG,OAAE,SAAS,CAAC,eAAe,CAAC,IACrB,CACpB,IACmB,IACT,CAChB,CAAC,CAAC,CAAC,IAAI,IACJ,EAOL,WAAW,CAAC,CAAC,CAAC,CACf,MAAC,GAAG,IAAC,GAAG,EAAC,GAAG,EAAC,SAAS,EAAC,MAAM,EAAC,IAAI,mBAChC,MAAC,GAAG,IAAC,GAAG,EAAC,GAAG,EAAC,KAAK,EAAC,QAAQ,aAMxB,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EACjE,cAAc,CAAC,CAAC,CAAC,CAChB,8BACE,cAAK,SAAS,EAAC,yBAAyB,wBAAe,EACtD,gBAAgB,CAAC,cAAc,CAAC,IAChC,CACJ,CAAC,CAAC,CAAC,IAAI,EACP,OAAO,CAAC,CAAC,CAAC,CACT,gBACE,SAAS,EAAE,EAAE,CACX,6FAA6F,EAC7F,SAAS,KAAK,WAAW;oDACvB,CAAC,CAAC,kCAAkC;oDACpC,CAAC,CAAC,sCAAsC,CAC3C,aAED,kBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,WAAW,CAAC,kBAC1B,SAAS,KAAK,WAAW,EACvC,SAAS,EAAC,gCAAgC,aAE1C,KAAC,gBAAgB,IAAC,SAAS,EAAC,QAAQ,wBAAe,OAAE,SAAS,CAAC,qBAAqB,CAAC,IAC9E,EACT,iBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,WAAW,gBACR,SAAS,CAAC,0BAA0B,CAAC,EACjD,SAAS,EAAC,6CAA6C,YAEvD,KAAC,CAAC,IAAC,SAAS,EAAC,QAAQ,GAAG,GACjB,IACJ,CACR,CAAC,CAAC,CAAC,IAAI,IACJ,EACN,KAAC,MAAM,KAAG,EACT,UAAU,CAAC,CAAC,CAAC,KAAC,cAAc,KAAG,CAAC,CAAC,CAAC,IAAI,EACtC,UAAU,CAAC,CAAC,CAAC,CACZ,MAAC,GAAG,IAAC,GAAG,EAAC,IAAI,EAAC,SAAS,EAAC,8BAA8B,aACpD,KAAC,iBAAiB,IAChB,KAAK,EAAE,SAAS,CAAC,uBAAuB,CAAC,EACzC,MAAM,EAAE,WAAW,KAAK,OAAO,EAC/B,OAAO,EAAE,GAAG,EAAE,CAAC,cAAc,CAAC,OAAO,CAAC,GACtC,EACF,KAAC,iBAAiB,IAChB,KAAK,EAAE,SAAS,CAAC,2BAA2B,CAAC,EAC7C,MAAM,EAAE,WAAW,KAAK,WAAW,EACnC,MAAM,EAAC,MAAM,EACb,QAAQ,EAAE,CAAC,SAAS,EACpB,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,mCAAmC,CAAC,EAC7E,OAAO,EAAE,GAAG,EAAE,CAAC,cAAc,CAAC,WAAW,CAAC,GAC1C,IACE,CACP,CAAC,CAAC,CAAC,IAAI,IACJ,CACL,CAAC,CAAC,CAAC,IAAI,IACJ,EAKL,YAAY,CAAC,CAAC,CAAC,CACd,MAAC,GAAG,IACF,GAAG,EAAC,GAAG,EACP,SAAS,EAAC,2EAA2E,iBACzE,uBAAuB,aAEnC,KAAC,IAAI,IAAC,SAAS,EAAC,mBAAmB,wBAAe,EAClD,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,SAAS,EAAC,WAAW,YAClC,SAAS,CAAC,oCAAoC,CAAC,GAC3C,EACP,KAAC,MAAM,KAAG,EACV,iBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,GAAG,EAAE,CAAC,cAAc,CAAC,OAAO,CAAC,EACtC,SAAS,EAAC,wDAAwD,YAEjE,SAAS,CAAC,0BAA0B,CAAC,GAC/B,IACL,CACP,CAAC,CAAC,CAAC,IAAI,EAOP,gBAAgB,CAAC,CAAC,CAAC,CAClB,cAAK,SAAS,EAAC,4CAA4C,YACzD,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,OAAO,YACzB,iBAAiB,CAAC,MAAM,KAAK,SAAS;gCACrC,CAAC,CAAC,SAAS,CAAC,oCAAoC,CAAC;gCACjD,CAAC,CAAC,SAAS,CAAC,+BAA+B,CAAC,GACzC,GACH,CACP,CAAC,CAAC,CAAC,SAAS,KAAK,WAAW,IAAI,OAAO,CAAC,CAAC,CAAC,CAIzC,eAAK,SAAS,EAAC,qCAAqC,aAClD,cAAK,SAAS,EAAC,8BAA8B,YAC3C,KAAC,cAAc,KAAG,GACd,EACN,KAAC,gBAAgB,IACf,KAAK,EAAE,aAAa,EACpB,QAAQ,EAAE,eAAe,EACzB,cAAc,EAAE,kBAAkB,EAClC,SAAS,EAAC,2CAA2C,GACrD,IACE,CACP,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAChB,cAAK,SAAS,EAAC,gCAAgC,YAC7C,KAAC,sBAAsB,IAErB,KAAK,EAAE,WAAW,EAClB,KAAK,EAAE,QAAQ,CAAC,WAAW,CAAC,EAC5B,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC,EACzC,QAAQ,EAAE,YAAY,IAAI,MAAM,CAAC,eAAe,CAAC,WAAW,CAAC,EAC7D,aAAa,EAAE,aAAa,IALvB,GAAG,WAAW,CAAC,IAAI,IAAI,YAAY,IAAI,WAAW,EAAE,CAMzD,GACE,CACP,CAAC,CAAC,CAAC,SAAS,KAAK,cAAc,CAAC,CAAC,CAAC,CAGjC,cAAK,SAAS,EAAC,qCAAqC,YAClD,cAAK,SAAS,EAAC,yCAAyC,YACtD,cAAK,SAAS,EAAC,0BAA0B,YACvC,KAAC,eAAe,IACd,SAAS,EAAE,SAAS,EACpB,MAAM,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,WAAW,CAAC,GAAG,CAAC,EAAE,EAClD,MAAM,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,WAAW,CAAC,GAAG,CAAC,EAAE,GAClD,GACE,GACF,GACF,CACP,CAAC,CAAC,CAAC,CACF,eAAK,SAAS,EAAC,qCAAqC,aAClD,cAAK,SAAS,EAAC,yCAAyC,YAC1D,cAAK,SAAS,EAAC,0BAA0B,YACvC,MAAC,KAAK,IAAC,GAAG,EAAC,GAAG,aACX,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;gDACpB,qEAAqE;gDACrE,yDAAyD;gDACzD,IAAI,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC;oDAAE,OAAO,IAAI,CAAA;gDAC7C,uEAAuE;gDACvE,kEAAkE;gDAClE,iCAAiC;gDACjC,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAA;gDACjD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;oDAAE,OAAO,IAAI,CAAA;gDACrC,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAA;gDAC7B,wEAAwE;gDACxE,sEAAsE;gDACtE,MAAM,YAAY,GAChB,OAAO,EAAE,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,cAAc,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;gDACtF,OAAO,CACL,KAAC,kBAAkB,IAEjB,WAAW,EAAE,OAAO,EAAE,WAAW,EACjC,WAAW,EAAE,OAAO,EAAE,WAAW,EACjC,KAAK,EACH,KAAC,IAAI,IACH,IAAI,EAAC,IAAI,EACT,MAAM,EAAC,UAAU,EACjB,SAAS,EAAE,YAAY,IAAI,KAAK,CAAC,IAAI,KAAK,mBAAmB,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,YAAY,YAEvF,YAAY;4DACX,CAAC,KAAK,CAAC,IAAI,KAAK,mBAAmB;gEACjC,CAAC,CAAC,SAAS,CAAC,yBAAyB,CAAC;gEACtC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,GACZ,YAKT,cAAK,SAAS,EAAC,4CAA4C,YACxD,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CACtB,cAEE,SAAS,EAAE,EAAE,CAAC,YAAY,EAAE,iBAAiB,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,YAEhE,cAAc,CAAC,KAAK,CAAC,IAHjB,KAAK,CAAC,IAAI,CAIX,CACP,CAAC,GACE,IA3BD,KAAK,CAAC,IAAI,CA4BI,CACtB,CAAA;4CACH,CAAC,CAAC,EAKD,YAAY,IACP,GACA,GACF,EAKL,WAAW,CAAC,CAAC,CAAC,CACb,KAAC,cAAc,IACb,OAAO,EAAE,WAAW,EACpB,SAAS,EAAC,2CAA2C,GACrD,CACH,CAAC,CAAC,CAAC,CACF,KAAC,gBAAgB,IACf,KAAK,EAAE,aAAa,EACpB,QAAQ,EAAE,eAAe,EACzB,cAAc,EAAE,kBAAkB,EAClC,SAAS,EAAC,2CAA2C,GACrD,CACH,IACG,CACP,EAUA,YAAY,CAAC,CAAC,CAAC,CACd,KAAC,mBAAmB,IAClB,IAAI,EAAE,KAAK,EACX,MAAM,EAAE,GAAG,EAAE;4BACX,IAAI,EAAE,CAAA;4BACN,OAAO,IAAI,CAAA;wBACb,CAAC,EACD,WAAW,EAAE,SAAS,CAAC,gCAAgC,CAAC,GACxD,CACH,CAAC,CAAC,CAAC,IAAI,EAGP,aAAa,KAAK,IAAI,CAAC,CAAC,CAAC,CACxB,KAAC,sBAAsB,IACrB,SAAS,EAAE,SAAS,EACpB,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,cAAc,EACvB,IAAI,QACJ,YAAY,EAAE,CAAC,IAAI,EAAE,EAAE;4BACrB,IAAI,CAAC,IAAI;gCAAE,gBAAgB,CAAC,IAAI,CAAC,CAAA;wBACnC,CAAC,EACD,SAAS,EAAE,qBAAqB,GAChC,CACH,CAAC,CAAC,CAAC,IAAI,EAMR,KAAC,MAAM,IAAC,IAAI,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC,IAAI,EAAE,EAAE,GAAG,IAAI,CAAC,IAAI;4BAAE,iBAAiB,CAAC,KAAK,CAAC,CAAA,CAAC,CAAC,YAC3F,MAAC,aAAa,eACZ,MAAC,YAAY,eACX,KAAC,WAAW,cAAE,SAAS,CAAC,8BAA8B,CAAC,GAAe,EACtE,KAAC,iBAAiB,cACf,SAAS,CAAC,oCAAoC,CAAC,GAC9B,IACP,EACf,MAAC,YAAY,IAAC,SAAS,EAAC,MAAM,aAC5B,KAAC,MAAM,IAAC,OAAO,EAAC,OAAO,EAAC,IAAI,EAAC,IAAI,EAAC,OAAO,EAAE,GAAG,EAAE,CAAC,iBAAiB,CAAC,KAAK,CAAC,EAAE,QAAQ,EAAE,WAAW,YAC7F,SAAS,CAAC,eAAe,CAAC,GACpB,EACT,KAAC,MAAM,IACL,OAAO,EAAC,aAAa,EACrB,IAAI,EAAC,IAAI,iBACG,uBAAuB,EACnC,QAAQ,EAAE,WAAW,EACrB,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,kBAAkB,EAAE,YAEvC,WAAW,CAAC,CAAC,CAAC,CACb,8BACE,KAAC,OAAO,IAAC,SAAS,EAAC,cAAc,GAAG,OAAE,SAAS,CAAC,4BAA4B,CAAC,IAC5E,CACJ,CAAC,CAAC,CAAC,CACF,8BACE,KAAC,SAAS,KAAG,OAAE,SAAS,CAAC,gCAAgC,CAAC,IACzD,CACJ,GACM,IACI,IACD,GACT,EAKT,KAAC,MAAM,IAAC,IAAI,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC,IAAI,EAAE,EAAE,GAAG,IAAI,CAAC,IAAI;4BAAE,oBAAoB,EAAE,CAAA,CAAC,CAAC,YACzF,MAAC,aAAa,eACZ,MAAC,YAAY,eACX,KAAC,WAAW,cAAE,SAAS,CAAC,wCAAwC,CAAC,GAAe,EAChF,KAAC,iBAAiB,cACf,SAAS,CAAC,8CAA8C,CAAC,GACxC,IACP,EACf,MAAC,KAAK,IAAC,GAAG,EAAC,GAAG,aACX,eAAe,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,CACjC,MAAC,IAAI,IAAgC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,OAAO,wBACtD,KAAK,CAAC,OAAO,KADP,GAAG,KAAK,CAAC,IAAI,EAAE,EAAE,IAAI,CAAC,EAAE,CAE5B,CACR,CAAC,EAID,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CACzB,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,OAAO,EAAC,SAAS,EAAC,MAAM,YAC1C,SAAS,CACR,YAAY,CAAC,MAAM,KAAK,CAAC;gDACvB,CAAC,CAAC,qCAAqC;gDACvC,CAAC,CAAC,uCAAuC,EAC3C,EAAE,KAAK,EAAE,YAAY,CAAC,MAAM,EAAE,CAC/B,GACI,CACR,CAAC,CAAC,CAAC,IAAI,IACF,EACR,MAAC,YAAY,IAAC,SAAS,EAAC,MAAM,aAC5B,KAAC,MAAM,IAAC,OAAO,EAAC,OAAO,EAAC,IAAI,EAAC,IAAI,EAAC,OAAO,EAAE,oBAAoB,YAC5D,SAAS,CAAC,eAAe,CAAC,GACpB,EACT,KAAC,MAAM,IAAC,IAAI,EAAC,IAAI,iBAAa,gBAAgB,EAAC,OAAO,EAAE,cAAc,YACnE,SAAS,CAAC,0BAA0B,CAAC,GAC/B,IACI,IACD,GACT,IACH,GACmB,GACF,CAC1B,CAAA;AACH,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,sBAAsB,CAC7B,UAAyB,EACzB,KAAkB,EAClB,MAA4B;IAE5B,MAAM,UAAU,GAAG,sBAAsB,CAAC,iBAAiB,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,MAAM,CAAC,WAAW,CAAC,CAAA;IAClG,IAAI,CAAC,UAAU;QAAE,OAAO,EAAE,CAAA;IAC1B,MAAM,MAAM,GAAyB,EAAE,CAAA;IACvC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAA;IAC9B,KAAK,MAAM,KAAK,IAAI,UAAU,CAAC,MAAM,EAAE,CAAC;QACtC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC;YAAE,SAAQ;QACnC,KAAK,MAAM,GAAG,IAAI,gBAAgB,CAAC,YAAY,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,UAAU,CAAC,EAAE,CAAC;YACzF,MAAM,GAAG,GAAG,GAAG,GAAG,CAAC,UAAU,IAAI,GAAG,CAAC,EAAE,EAAE,CAAA;YACzC,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,SAAQ;YAC3B,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;YACb,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,SAAS,CAAA;YAC5C,IAAI,MAAM,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,WAAW,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC;gBAAE,SAAQ;YACtE,MAAM,CAAC,IAAI,CAAC,qBAAqB,CAAC,UAAU,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;QACxF,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC;AAED,0DAA0D;AAC1D,SAAS,qBAAqB,CAC5B,UAAyB,EACzB,KAAkB,EAClB,KAAa,EACb,GAAgB,EAChB,MAAe,EACf,MAA4B;IAE5B,OAAO;QACL,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,6BAA6B,CAAC,CAAC,CAAC,yBAAyB;QACxE,QAAQ,EAAE,OAAO;QACjB,OAAO,EAAE,MAAM;YACb,CAAC,CAAC,SAAS,CAAC,kCAAkC,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YAC/E,CAAC,CAAC,SAAS,CAAC,8BAA8B,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC;QAC7D,IAAI,EAAE,CAAC,KAAK,CAAC;QACb,YAAY,EAAE,UAAU,CAAC,EAAE;QAC3B,UAAU,EAAE,KAAK,CAAC,EAAE;QACpB,IAAI,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,UAAU,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE;KACpD,CAAA;AACH,CAAC;AAED;;;;GAIG;AACH,SAAS,iBAAiB,CAAC,EACzB,KAAK,EACL,MAAM,EACN,MAAM,EACN,QAAQ,EACR,KAAK,EACL,OAAO,GAQR;IACC,OAAO,CACL,iBACE,IAAI,EAAC,QAAQ,kBACC,MAAM,EACpB,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,EAAE,CACX,+EAA+E,EAC/E,MAAM;YACJ,CAAC,CAAC,MAAM,KAAK,MAAM;gBACjB,CAAC,CAAC,wCAAwC;gBAC1C,CAAC,CAAC,yCAAyC;YAC7C,CAAC,CAAC,6CAA6C,CAClD,YAEA,KAAK,GACC,CACV,CAAA;AACH,CAAC;AASD,uHAAuH;AACvH,MAAM,mBAAmB,GAAG,QAAQ,CAAA;AAEpC,SAAS,WAAW,CAAC,MAAkB,EAAE,UAAyB;IAChE,MAAM,KAAK,GAAG,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,CAAA;IACzC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAsB,CAAA;IAC7C,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,EAAE,KAAK,IAAI,mBAAmB,CAAA;QACtD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC;YAAE,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;QAC/C,OAAO,CAAC,GAAG,CAAC,KAAK,CAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IACjC,CAAC;IACD,MAAM,KAAK,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QAC9C,MAAM,EAAE,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;QAC3B,MAAM,EAAE,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;QAC3B,OAAO,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;IAChD,CAAC,CAAC,CAAA;IACF,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAC1B,IAAI;QACJ,OAAO,EAAE,cAAc,CAAC,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC;QAC9C,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC;KACzF,CAAC,CAAC,CAAA;AACL,CAAC;AAED,8EAA8E;AAC9E,8EAA8E;AAC9E,0EAA0E;AAC1E,MAAM,cAAc,GAA2B;IAC7C,CAAC,EAAE,eAAe;IAClB,CAAC,EAAE,eAAe;IAClB,CAAC,EAAE,eAAe;IAClB,CAAC,EAAE,eAAe;IAClB,CAAC,EAAE,eAAe;IAClB,EAAE,EAAE,gBAAgB;CACrB,CAAA;AAED,SAAS,iBAAiB,CAAC,KAA6B;IACtD,OAAO,cAAc,CAAC,iBAAiB,CAAC,KAAK,IAAI,MAAM,CAAC,CAAC,IAAI,gBAAgB,CAAA;AAC/E,CAAC;AAED,SAAS,YAAY,CACnB,GAAgB,EAChB,UAAyB,EACzB,MAAc,EACd,MAAoB;IAEpB,+EAA+E;IAC/E,iFAAiF;IACjF,2EAA2E;IAC3E,OAAO,oBAAoB,CAAC,UAAU,EAAE,wBAAwB,CAAC,UAAU,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC,CAAA;AACxG,CAAC","sourcesContent":["import { Link, STUDIO_PROJECT_ROUTE, studioPath, useNavigate } from '@elytracms/studio/router'\nimport {\n Check,\n ChevronDown,\n CircleAlert,\n CircleCheck,\n Eye,\n EyeOff,\n GitCompareArrows,\n History,\n Loader2,\n Lock,\n MoreHorizontal,\n RotateCcw,\n Save,\n Tag,\n Trash2,\n X,\n} from 'lucide-react'\nimport { useCallback, useMemo, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport {\n FIELD_WIDTH_SPANS,\n SIDEBAR_FIELD_GROUP,\n documentKey,\n groupOrder,\n groupSectionOf,\n hasAliases,\n hiddenGroupNames,\n isBlocksField,\n isFieldHidden,\n isFieldLocalized,\n isSingleton,\n resolveDocumentForLocale,\n resolveDocumentTitle,\n resolveField,\n sidebarFields,\n tabDescriptorOf,\n tabOrder,\n titleFieldOf,\n staticFieldHidden,\n} from '@elytracms/core/cms-core'\nimport type {\n CmsDocument,\n CmsValidationIssue,\n CollectionDef,\n DocumentRef,\n FieldDef,\n FieldGroupSection,\n FieldWidth,\n Locale,\n LocaleConfig,\n} from '@elytracms/core/cms-core'\nimport { blockRootsOf } from '@elytracms/core/project-graph'\nimport {\n Badge,\n Button,\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n CollapsibleSection,\n Field,\n Heading,\n Row,\n Spacer,\n Stack,\n Text,\n toast,\n} from '@elytracms/studio/ui'\nimport { getStudioRegistry } from '@elytracms/studio/lib/registry'\nimport { collectEmbedRefs, resolveReuseCapability } from '../builder/reuse'\nimport { cn } from '@elytracms/studio/lib/utils'\nimport { localizedLabel, translate } from '@elytracms/studio/lib/i18n'\nimport {\n LocaleSwitcher,\n activeLocaleValues,\n displayedFieldFallback,\n fieldConnection,\n fillFieldFromBase,\n resetFieldToBase,\n setFieldValue,\n} from '@elytracms/studio/lib/locale'\nimport type { ReferenceAction } from '@elytracms/studio/lib/references'\nimport { FallbackChip } from './cell'\nimport { FieldConnectionControl } from './field-connection'\nimport { FieldControl } from './field-control'\n// EC-322: the lazy-wrapped page builder — the heavy canvas subtree code-splits out\n// of the detail-view critical path and downloads only when a `blocks` field opens.\nimport { CompositionFieldEditor } from '../builder/composition-field-editor-lazy'\nimport { fieldLabel } from './cms-labels'\nimport { useCmsCollectionViewSource } from '../collection-view/cms-collection-view-source'\nimport { CollectionViewProvider, useCollectionView } from '../collection-view/collection-view-context'\nimport { resolveFieldAccess } from '@elytracms/studio/lib/collection-view/collection-view-source'\nimport type { CollectionViewSource } from '@elytracms/studio/lib/collection-view/collection-view-source'\nimport { DocStateBadge } from './doc-state'\nimport { UnsavedChangesGuard } from '../guards'\nimport { ReferenceConfirmDialog } from './reference-confirm'\nimport { ReferencesPanel } from '../references/references-panel'\nimport { InspectorSidebar } from '../inspector/inspector-sidebar'\nimport type { InspectorPane } from '../inspector/inspector-sidebar'\nimport { DocumentEditorProvider } from './document-editor-context'\nimport type { CompareState } from './document-editor-context'\nimport { TranslateDocumentAction } from '../ai/translate-document-action'\nimport { VersionsPane } from './versions-pane'\n// EC-322: the lazy-wrapped compare view — its diff UI code-splits off the detail-view\n// critical path and loads only when the Compare tab opens.\nimport { VersionCompare } from './version-compare-lazy'\nimport { IssuesPane } from './issues-pane'\nimport { DetailsPane } from './details-pane'\nimport { DocumentUrlPath } from './document-url-path'\n// 0.0.41: selection-driven embed inspector. Both modules are deliberately TipTap-free\n// so the editor runtime stays behind rich-text/lazy (EC-322) — see their doc comments.\nimport { EmbedInspectorProvider } from './rich-text/embed-inspector-context'\nimport { EmbedInspector } from './rich-text/embed-inspector'\nimport { useSetBreadcrumb } from '../breadcrumb-context'\nimport type { BreadcrumbItem } from '../breadcrumb-context'\nimport { useAiCurrentDocumentTap } from '@elytracms/studio/lib/ai-current-document'\nimport { resolveDeploymentRoutes } from '@elytracms/studio/lib/workspace'\n// EC-325 — the controller hooks EditorBody orchestrates (each independently\n// unit-tested at its pure core); the shell threads shared draft/ref/replaceDraft\n// state across their boundaries.\nimport { usePublishedPerspective } from './use-published-perspective'\nimport { useSaveMode } from './use-save-mode'\nimport { useDiscardController } from './use-discard-controller'\nimport { usePublishController, publishGate } from './use-publish-controller'\nimport { useFieldFocus } from './use-field-focus'\nimport { useCompareSelection } from './use-compare-selection'\nimport { useLiveUrl } from './use-live-url'\nimport { useEmbedInspectorHost } from './use-embed-inspector-host'\n\nexport function DetailEditor({\n projectId,\n collectionId,\n documentId,\n focusPath,\n}: {\n projectId: string\n collectionId: string\n documentId: string\n /** Reference-index field path (`?field=`, EC-172): highlight + scroll to the exact field. */\n focusPath?: string\n}) {\n // AD-19: the edit shell is a portable surface over a CollectionViewSource, just\n // like the list. `DetailEditor` is the CMS entry point — it builds the CMS source\n // and hands it to the portable {@link PortableDetailEditor} so `EditorBody` (and\n // the shared field controls / pickers) render over the source, never `useCms()`.\n const source = useCmsCollectionViewSource(collectionId)\n if (!source) {\n return (\n <DocumentNotFound\n projectId={projectId}\n listPath={studioPath.cmsCollection(collectionId)}\n label={collectionId}\n />\n )\n }\n return (\n <PortableDetailEditor source={source} projectId={projectId} documentId={documentId} focusPath={focusPath} />\n )\n}\n\n/**\n * The portable edit shell (EC-240 / AD-19): resolve the record by id from ANY\n * {@link CollectionViewSource}, then render the shared {@link EditorBody} over it.\n * The CMS entry point ({@link DetailEditor}) and the users rail both mount this —\n * routing (the \"back to list\" target, the not-found link) is rail-owned via\n * `source.listPath`, so the shell never hard-codes the CMS route.\n */\nexport function PortableDetailEditor({\n source,\n projectId,\n documentId,\n focusPath,\n formAppendix,\n}: {\n source: CollectionViewSource\n projectId: string\n documentId: string\n focusPath?: string\n /**\n * EC-396 — a form-column appendix seam (a direct-value node, per the customization\n * principle — no registry): rendered INSIDE the editor's form column, below the\n * fieldset cards, at the same `max-w-2xl` width and gap-6 rhythm. The account\n * surface mounts its `CredentialPanel` through it so the account page is ONE editor\n * surface (the editor owns the scroll + fills the height, so the Issues sidebar\n * spans the full surface), never a second parallel card wrapper. Absent for the CMS\n * and users entry points, which pass nothing.\n */\n formAppendix?: ReactNode\n}) {\n const doc = source.get({ collection: source.collectionId, id: documentId })\n // EC-274: singleton opening is owned by `SingletonGate` (it resolves the lone\n // document and materializes it). The detail editor just renders the document it\n // is given by id — singletons need no special case here.\n if (!doc) {\n return (\n <DocumentNotFound\n projectId={projectId}\n listPath={source.listPath}\n label={localizedLabel(source.schema.form?.label, source.collectionId)}\n />\n )\n }\n return (\n <CollectionViewProvider source={source}>\n <EditorBody\n key={`${source.collectionId}/${documentId}`}\n projectId={projectId}\n collection={source.schema}\n doc={doc}\n focusPath={focusPath}\n formAppendix={formAppendix}\n />\n </CollectionViewProvider>\n )\n}\n\n/** The record-not-found state — a heading + a rail-owned \"back to list\" link. */\nfunction DocumentNotFound({\n projectId,\n listPath,\n label,\n}: {\n projectId: string\n listPath?: string\n label: string\n}) {\n return (\n <div className=\"grid h-full place-items-center p-8\">\n <Stack gap=\"3\" align=\"center\" className=\"text-center\">\n <Heading level={2} size=\"h2\">\n {translate('cms.editor.notFoundTitle')}\n </Heading>\n {listPath !== undefined ? (\n <Link\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId, _splat: listPath }}\n className=\"text-sm text-primary hover:underline\"\n >\n {translate('cms.editor.backToList', { label })}\n </Link>\n ) : null}\n </Stack>\n </div>\n )\n}\n\nfunction EditorBody({\n projectId,\n collection,\n doc,\n focusPath,\n formAppendix,\n}: {\n projectId: string\n collection: CollectionDef\n doc: CmsDocument\n focusPath?: string\n /** EC-396: rendered below the fieldset cards, inside the form column (see {@link PortableDetailEditor}). */\n formAppendix?: ReactNode\n}) {\n const source = useCollectionView()\n const navigate = useNavigate()\n // AD-19: capability flags gate whole shell regions AND their method groups, so\n // the SAME edit shell serves a non-publishing / non-versioned / single-locale\n // rail (the users rail, EC-240) without a bespoke editor. A false flag hides its\n // region entirely — no dead controls, no reads fired on that region's behalf.\n const canPublish = source.capabilities.publish\n const canVersions = source.capabilities.versions\n const canLocales = source.capabilities.locales\n // References is the CMS content-graph surface (the reference index tracks the\n // publishable document rail). A non-publishing rail (users, future redirects) is\n // not part of that graph, so its References tab would be structurally empty —\n // gate it off. A dedicated `references` capability can split this later if a\n // non-publishing-but-referenceable rail appears (YAGNI today).\n const showReferences = canPublish\n // Principal-based field access (EC-240, AD-18): `edit|read|hidden`, resolved\n // per field for the current principal. Default-open — a source with no policy\n // derives it from `isFieldReadOnly`. Access depends on the record's identity, not\n // its edited values (content-conditional hiding is the separate `isHidden` axis),\n // so it keys off the stable `doc`, never the live draft.\n const accessOf = useCallback(\n (field: FieldDef) => resolveFieldAccess(source, field, doc),\n [source, doc],\n )\n // EC-217: a singleton collection has no list — there is nowhere to go \"back\"\n // to, and its one document is fixed (never deleted). The header drops the\n // back-crumb/id line and the Delete action accordingly.\n const singleton = isSingleton(collection)\n const [draft, setDraft] = useState<CmsDocument>(() => structuredClone(doc))\n // The studio's ambient active locale (EC-176): the form displays and edits\n // exactly this locale — switched via the shared LocaleSwitcher, never per field.\n const locale = source.activeLocale\n // Bumped when a version restore replaces the draft out-of-band, to remount the\n // (uncontrolled) canvas editor from the restored value (EC-224).\n const [restoreNonce, setRestoreNonce] = useState(0)\n // EC-173: unpublish/delete go through the reference-aware confirmation.\n const [confirmAction, setConfirmAction] = useState<ReferenceAction | null>(null)\n // Stable so the document-editor context value (EC-224) doesn't churn each render.\n const ref = useMemo(() => ({ collection: collection.id, id: doc.id }), [collection.id, doc.id])\n const confirmTargets = useMemo(\n () => [{ collection: collection.id, id: doc.id }],\n [collection.id, doc.id],\n )\n\n // EC-224 — the working draft's two out-of-band mutators, shared across the\n // controller hooks below and the document-editor context. `save` persists the\n // current buffer; `replaceDraft` adopts a restored snapshot and bumps the nonce\n // to REMOUNT the uncontrolled canvas from it (else the next edit clobbers the\n // restore — the canvas builds its graph once at mount and lifts edits OUT).\n function save() {\n source.save(draft)\n }\n function replaceDraft(next: CmsDocument) {\n setDraft(next)\n setRestoreNonce((n) => n + 1)\n }\n\n // EC-378 — the current-document seam: publish WHICH document is open (and the\n // draft-adoption channel) so the AI assistant resolves \"this document\" without\n // asking, and an applied update replaces this buffer instead of being clobbered\n // by the next autosave. A narrow read-only tap + the existing `replaceDraft`;\n // nothing else of the editor is exposed.\n useAiCurrentDocumentTap({\n collection: collection.id,\n id: doc.id,\n title: primaryTitle(draft, collection, locale, source.localeConfig) || doc.id,\n adoptDraft: replaceDraft,\n })\n\n // EC-226 / EC-316 / EC-325 — the Draft/Published VIEW perspective AND the pinned\n // published snapshot, both derived from the ONE shared version-history query\n // (AD-17: the single operations-backed cache — no raw `source.versioning.load`\n // in this component). `canDiscard`/`versionKey`/`publishedVersionNumber`/\n // `publishedSnapshotDoc` feed the Discard controller below off that same query.\n const {\n perspective,\n setPerspective,\n published,\n canDiscard,\n versionKey,\n publishedVersionNumber,\n publishedSnapshotDoc,\n publishedSnapshot,\n viewReadOnly,\n viewDoc,\n publishedPending,\n } = usePublishedPerspective({ ref, source, canPublish, canVersions, draft })\n\n const dirty = useMemo(() => JSON.stringify(draft) !== JSON.stringify(doc), [draft, doc])\n const schemaIssues = useMemo(() => source.validate(draft), [source, draft])\n // EC-300/AD-16: composition embeds (hard references) whose target isn't published\n // would render empty on the live site. Surface them as errors in the Issues panel,\n // but they DON'T gate the Publish button (schema errors do) — publishing is\n // confirm-through, so the editor can publish the page and the reusable together\n // knowingly. The publish guard below catches them.\n const referenceIssues = useMemo(\n () => unpublishedEmbedIssues(collection, draft, source),\n [collection, draft, source],\n )\n const issues = useMemo(\n () => [...schemaIssues, ...referenceIssues],\n [schemaIssues, referenceIssues],\n )\n const errorCount = schemaIssues.filter((i) => i.severity === 'error').length\n const hasErrors = errorCount > 0\n const hasUnpublishedRefs = referenceIssues.length > 0\n\n // EC-225 autosave · EC-240 explicit save — the persistence discipline for this\n // rail (a publishing rail autosaves + flushes on unmount; a non-publishing rail\n // saves explicitly and relies on the `UnsavedChangesGuard` below for navigation).\n const { explicitSave } = useSaveMode({ canPublish, dirty, draft, source })\n\n // EC-316 / EC-325 — the \"Discard changes\" controller: the pure enablement\n // decision plus the shared restore sequence (flush → restore the pinned published\n // version whole → replaceDraft → invalidate). Destructured to the names the header\n // menu + confirm dialog already use.\n const {\n status: discardStatus,\n disabledReason: discardDisabledReason,\n confirmOpen: discardConfirm,\n setConfirmOpen: setDiscardConfirm,\n busy: discardBusy,\n discard: handleDiscardDraft,\n } = useDiscardController({\n canDiscard,\n published,\n viewReadOnly,\n publishedSnapshotDoc,\n publishedVersionNumber,\n versionKey,\n collection,\n draft,\n source,\n docRef: ref,\n replaceDraft,\n dirty,\n save,\n })\n const errorsByField = useMemo(() => {\n const map = new Map<string, string>()\n for (const issue of issues) {\n // validateDocument populates meta.field for field-level issues (works for\n // both `values` and `localized/<locale>` paths, unlike positional indexing).\n const field = issue.meta?.field\n if (typeof field === 'string' && !map.has(field)) map.set(field, issue.message)\n }\n return map\n }, [issues])\n\n // EC-241: `form.tabs` may declare tabs as bare ids OR `{ id, label }` objects with a\n // localizable title — resolve to the ordered id list for all tab logic; labels are\n // resolved for display in `renderContentTab`.\n const declaredTabs = tabOrder(collection.form)\n const tabs = declaredTabs.length > 0 ? declaredTabs : ['main']\n // EC-269: References gets its own dedicated, visibility-gated tab — it mounts\n // (and loads) only when selected, and unmounts on leave, so normal editing\n // never triggers reference fetching. Appended after the collection's content tabs.\n const REFERENCES_TAB = 'references'\n // EC-298: the version-compare view is a sticky framework tab (kept open while\n // you tab to the real fields; closed only by its ✕).\n const COMPARE_TAB = 'compare'\n const [activeTab, setActiveTab] = useState(tabs[0]!)\n // EC-285: the reserved `sidebar` group is document-level metadata (slug, author…)\n // — it renders in the Details inspector pane, not as a content tab, so it is\n // excluded from the tab fields here and collected once below.\n const sidebarFieldDefs = useMemo(() => sidebarFields(collection), [collection])\n // EC-273: only STATICALLY hidden fields (`hidden: true`) are structurally\n // excluded here; a conditionally (predicate) hidden field stays in the tab and\n // is evaluated per-render against the current values (it may show or hide as the\n // editor types).\n const tabFields = collection.fields.filter(\n (f) =>\n (f.form?.tab ?? tabs[0]) === activeTab &&\n !staticFieldHidden(f) &&\n f.form?.group !== SIDEBAR_FIELD_GROUP,\n )\n const groups = groupFields(tabFields, collection)\n // EC-188 (AD-12): a composition (`blocks`) field is a special content feature,\n // not a field-row — it takes over its tab with the full page-builder canvas.\n // Convention: such a field lives on its own dedicated fieldset tab.\n const canvasField = tabFields.find(isBlocksField)\n\n // EC-172 / EC-283 — the field-focus/highlight controller (jump-to-field from the\n // reference deep-link `focusPath` and the Issues pane). Owns the highlight + the\n // controlled inspector-pane state and the scroll/clear timers.\n const { highlightedField, inspectorPaneId, setInspectorPaneId, focusField } = useFieldFocus({\n collection,\n source,\n focusPath,\n setActiveTab,\n })\n // 0.0.41 — the selection-driven embed-inspector host: a rich-text `componentEmbed`\n // selection swaps the inspector sidebar for the embed's prop form (the `api` is fed\n // to the rich-text field through the EmbedInspector context).\n const { activeEmbed, embedInspectorApi } = useEmbedInspectorHost()\n // EC-298 — the sticky version-compare selection. Opening it focuses the Compare tab\n // + the Versions pane; tabbing to the real fields keeps it open (only its ✕ closes\n // it). `activeTab === COMPARE_TAB` is when the diff is visible.\n const { compare, startCompare, setCompareBase, setCompareTarget, exitCompare } = useCompareSelection({\n tabs,\n compareTab: COMPARE_TAB,\n setActiveTab,\n setInspectorPaneId,\n })\n // A content tab (schema tab or References). Selecting one leaves the Compare\n // view VISIBLE-wise but keeps the comparison open (sticky) — the diff is one\n // click away on its still-present tab chip.\n const renderContentTab = (tab: string) => {\n const active = tab === activeTab\n // EC-241 labels: the References framework tab is engine chrome (catalog); a schema\n // tab with a declared `{ id, label }` shows its localized label as authored; a bare\n // id keeps the title-cased-id behaviour (`capitalize`).\n const declaredLabel = tab === REFERENCES_TAB ? undefined : tabDescriptorOf(collection.form, tab)?.label\n const display =\n tab === REFERENCES_TAB\n ? translate('references.title')\n : declaredLabel !== undefined\n ? localizedLabel(declaredLabel, tab)\n : tab\n const titleCase = tab !== REFERENCES_TAB && declaredLabel === undefined\n return (\n <button\n key={tab}\n type=\"button\"\n aria-pressed={active}\n onClick={() => setActiveTab(tab)}\n className={cn(\n 'rounded-md px-2.5 py-1 text-xs font-medium transition-colors',\n titleCase && 'capitalize',\n active ? 'bg-accent text-accent-foreground' : 'text-muted-foreground hover:bg-muted',\n )}\n >\n {display}\n </button>\n )\n }\n function getValue(field: FieldDef): unknown {\n // EC-226: read from the active VIEW (the draft, or the pinned published\n // snapshot when viewing Published) — never directly from `draft`.\n if (isFieldLocalized(collection, field.name)) {\n // Display the explicit variant when it exists, else the EC-018 fallback\n // value — labeled as such by the field's fallback marker (EC-176), so a\n // German view shows the English text visibly, never an unexplained blank.\n const explicit = viewDoc.localized?.[locale]?.[field.name]\n if (explicit !== undefined) return explicit\n return resolveField(collection, viewDoc, field.name, locale, source.localeConfig).value\n }\n return viewDoc.values[field.name]\n }\n\n // EC-176/EC-318: the present/absent-key write is single-sourced in `setFieldValue`\n // — a localized field writes `localized[locale]`, clearing (value === undefined)\n // DELETES the key (reconnecting to base). `fill from base` / `reset to base` below\n // reuse the same foundation, so typing, filling, and resetting can never diverge.\n function setValue(field: FieldDef, value: unknown) {\n setDraft((d) => setFieldValue(collection, d, field.name, locale, value))\n }\n\n // EC-273: conditional-visibility context — the draft's values for the active\n // locale, against which a field's `hidden(values)` predicate is evaluated. A\n // hidden field's row is omitted (and it is not validated). Recomputed as the\n // draft changes, so a field shows/hides live as the editor types.\n const conditionValues = activeLocaleValues(draft, locale)\n const isHidden = (field: FieldDef) => isFieldHidden(field, { values: conditionValues })\n // Visible = not content-hidden AND not principal-hidden (EC-240 access === 'hidden').\n const isFieldShown = (field: FieldDef) => !isHidden(field) && accessOf(field) !== 'hidden'\n const visibleSidebarFields = sidebarFieldDefs.filter(isFieldShown)\n // EC-306: presentational group sections hidden right now (static or predicate) —\n // skipped entirely, like a hidden field. Recomputed live as the draft changes.\n const hiddenGroups = hiddenGroupNames(collection, { values: conditionValues })\n\n // One document field row (label + fallback/translation/read-only badges + the\n // control), shared by the content groups and the EC-285 Details pane so a\n // sidebar field renders identically — same control, validation, localization,\n // and EC-283 highlight/jump target — wherever it surfaces. Returns null when the\n // field is conditionally hidden (EC-273).\n function renderFieldRow(field: FieldDef) {\n if (isHidden(field)) return null\n // EC-240: principal-based access. `hidden` drops the row entirely (this\n // principal can't see the field); `read` renders it read-only. Distinct from\n // the content-conditional `isHidden` above and the view-level read-only\n // (Published perspective) — the control honors all three.\n const access = accessOf(field)\n if (access === 'hidden') return null\n const fieldLevelReadOnly = access === 'read'\n const fieldReadOnly = viewReadOnly || fieldLevelReadOnly\n const localized = isFieldLocalized(collection, field.name)\n // EC-318 — connected fields: the per-locale connection state of a localized\n // field (connected/diverged + fill-from-base), DERIVED from the draft, never\n // stored. Null for a non-localized field or the base-locale view. Suppressed\n // while the row is read-only (Published view or a field-level `read` grant) —\n // there is nothing to connect or diverge there.\n const connection = localized\n ? fieldConnection(collection, draft, field.name, locale, source.localeConfig)\n : null\n return (\n <Field\n key={field.name}\n className={cn(\n 'rounded-md transition-shadow',\n highlightedField === field.name && 'bg-primary/5 p-2 ring-2 ring-primary/70',\n )}\n label={\n <Row gap=\"2\">\n <span>{fieldLabel(field)}</span>\n {field.validation?.required ? <span className=\"text-destructive\">*</span> : null}\n {localized ? <Badge variant=\"outline\">{locale}</Badge> : null}\n {fieldLevelReadOnly ? (\n <Badge variant=\"outline\">\n <Lock className=\"size-3\" /> {translate('cms.editor.readOnlyBadge')}\n </Badge>\n ) : null}\n <Spacer />\n {/* EC-318: the connected-field affordance replaces EC-176's passive\n \"showing EN\" text — a quiet chip (Follows EN / Translated) plus the\n matching action (Fill from EN / Reset to EN, the latter confirmed).\n Right-aligned so it stays out of the way on every localized row. */}\n {connection && !fieldReadOnly ? (\n <FieldConnectionControl\n connection={connection}\n fieldName={field.name}\n locale={locale}\n onFillFromBase={() =>\n setDraft((d) => fillFieldFromBase(collection, d, field.name, locale, source.localeConfig))\n }\n onResetToBase={() =>\n setDraft((d) => resetFieldToBase(collection, d, field.name, locale))\n }\n />\n ) : null}\n </Row>\n }\n htmlFor={`f-${field.name}`}\n hint={localizedLabel(field.form?.description) || undefined}\n error={fieldReadOnly ? undefined : errorsByField.get(field.name)}\n >\n <FieldControl\n id={`f-${field.name}`}\n field={field}\n value={getValue(field)}\n onChange={(v) => setValue(field, v)}\n readOnly={fieldReadOnly}\n />\n {/* EC-315: the slug field's derived composed-path preview now lives inside the\n slug control itself (field-control's `SlugField`), which subsumes the old\n EC-246d PathPreview — so nothing extra is rendered here. */}\n </Field>\n )\n }\n\n // The frame-owned `Versions` pane (EC-224) — appended after any view-supplied\n // pane (the canvas's `Properties`). One instance feeds both the form view's\n // sidebar and the canvas view's sidebar.\n const versionsPane = useMemo<InspectorPane>(\n () => ({\n id: 'versions',\n label: translate('cms.version.paneLabel'),\n icon: <History className=\"size-3.5\" />,\n render: () => <VersionsPane />,\n }),\n [],\n )\n // The frame-owned `Issues` pane (EC-283) — the document-scoped validation\n // surface that replaces the always-on footer band. The tab carries a count\n // badge (error- vs warning-toned) so a non-zero count is discoverable from\n // any tab; document-level issues (e.g. EC-260 nesting-depth) live only here.\n const issuesPane = useMemo<InspectorPane>(\n () => ({\n id: 'issues',\n label: translate('cms.editor.issuesPane'),\n icon: <CircleAlert className=\"size-3.5\" />,\n badge:\n issues.length === 0 ? undefined : (\n <Badge\n variant={issues.some((i) => i.severity === 'error') ? 'error' : 'warning'}\n className=\"px-1.5 py-0 leading-none\"\n >\n {issues.length}\n </Badge>\n ),\n render: () => (\n <IssuesPane issues={issues} collection={collection} onJumpToField={focusField} />\n ),\n }),\n [issues, errorCount, collection, focusField],\n )\n // EC-284 + EC-285: the document-metadata pane — slot assignment (EC-284) and/or\n // the document-level `sidebar`-group fields (EC-285). Present only when the\n // collection has either, so it never adds an empty tab elsewhere. First in the\n // order, so a slotted / sidebar-bearing document opens straight to it. Not\n // memoized: its render closes over per-render edit state (draft, highlight,\n // field errors) via `renderFieldRow`, and the inspector re-renders with the rest\n // of the editor anyway, so a stable pane identity would buy nothing here.\n // 0.0.42 (round 3): whether a deployment route addresses this collection — gates the\n // header's composed-URL row (which carries the live-page affordance, EC-315 hybrid\n // split). A cheap synchronous accessor, resolved on every render (NOT frozen in a\n // `useMemo` with a static dep — the same first-paint-empty-routes trap the SlugField\n // fix removed: routes arrive after the workspace/config loads).\n const routableCollection = resolveDeploymentRoutes().some(\n (route) => route.document?.collection === collection.id,\n )\n // The composed URL moved OUT of this pane and up to the header (round 3): a lone URL\n // row on an otherwise-empty pane looked odd. So the Details pane exists only for slots\n // and/or sidebar fields — never merely because the collection is routable (that would\n // resurrect the empty-pane problem). A routable page keeps its pane via its slug\n // sidebar field; a routable collection with neither has no pane (URL is in the header).\n const detailsPane: InspectorPane | null =\n hasAliases(collection) || visibleSidebarFields.length > 0\n ? {\n id: 'details',\n label: translate('cms.editor.detailsPane'),\n icon: <Tag className=\"size-3.5\" />,\n render: () => (\n <DetailsPane\n collection={collection}\n doc={draft}\n siblings={source.documents()}\n readOnly={viewReadOnly}\n fields={visibleSidebarFields}\n renderField={renderFieldRow}\n onAliasChange={(alias) =>\n setDraft((d) => {\n const next = { ...d }\n if (alias === undefined) delete next.alias\n else next.alias = alias\n return next\n })\n }\n />\n ),\n }\n : null\n // Versions is capability-gated (AD-19): a non-versioned rail (users) never mounts\n // the pane, so no version read fires on its records.\n const trailingPanes = [\n ...(detailsPane ? [detailsPane] : []),\n ...(canVersions ? [versionsPane] : []),\n issuesPane,\n ]\n\n // EC-268 / EC-300 / 0.0.39 — the publish controller: the publish indicator state,\n // the EC-300 confirm-through guard (unpublished hard refs would render empty on\n // the live site), and the \"Publish & close\" navigation. Handlers are named to\n // match the header split button + the confirm-through dialog below.\n const {\n publishState,\n publishConfirm,\n handlePublish,\n handlePublishAndClose,\n confirmPublish,\n cancelPublishConfirm,\n } = usePublishController({\n source,\n docRef: ref,\n collection,\n projectId,\n draft,\n setDraft,\n dirty,\n hasUnpublishedRefs,\n })\n\n function handleConfirmedAction() {\n if (confirmAction === 'unpublish') {\n setConfirmAction(null)\n void source.publishing?.unpublish(ref)\n return\n }\n if (confirmAction === 'delete') {\n setConfirmAction(null)\n source.remove(ref)\n // Return to the rail's list (rail-owned `listPath`), or the project index if\n // the rail has no list surface (AD-19 — never a hard-coded CMS route).\n void navigate({\n to: STUDIO_PROJECT_ROUTE,\n params: { projectId, _splat: source.listPath ?? studioPath.index() },\n })\n }\n }\n\n // While the published snapshot is pending, don't derive the title from the\n // draft (it must not read as the published title) — fall back to the id.\n const title = publishedPending ? '' : primaryTitle(viewDoc, collection, locale, source.localeConfig)\n // EC-242: the untranslated-title chip applies only to a field-backed title; a\n // function title pattern has no single source field to mark as a fallback.\n const titleFallback =\n publishedPending || typeof collection.titleField === 'function'\n ? null\n : displayedFieldFallback(\n collection,\n viewDoc,\n titleFieldOf(collection)?.name ?? 'title',\n locale,\n source.localeConfig,\n )\n\n // EC-214: the document is the active record — contribute the collection (a\n // link back to its list) and the document title to the global header\n // breadcrumb, replacing the editor's own back-link + collection/id line. A\n // singleton has no list, so it contributes a single, non-linked crumb. The\n // title tracks the live draft so a rename updates the crumb immediately.\n const breadcrumbItems = useMemo<BreadcrumbItem[]>(() => {\n const collectionLabel = localizedLabel(collection.form?.label, collection.id)\n // A singleton (no list) or a rail with no list surface contributes a single,\n // non-linked crumb; otherwise the collection crumb links back to its list via\n // the rail-owned `listPath` (never a hard-coded CMS route — AD-19).\n if (singleton || source.listPath === undefined) return [{ label: collectionLabel }]\n const docTitle = primaryTitle(draft, collection, locale, source.localeConfig) || doc.id\n return [\n {\n label: collectionLabel,\n to: STUDIO_PROJECT_ROUTE,\n params: { projectId, _splat: source.listPath },\n },\n { label: docTitle },\n ]\n }, [singleton, collection, draft, locale, source.localeConfig, source.listPath, doc.id, projectId])\n useSetBreadcrumb(breadcrumbItems)\n\n // EC-205/EC-216 — the header composed-URL row's live-link descriptor. Derives\n // strictly from PUBLISHED data (the pinned snapshot + published ancestors), never\n // the draft, so an unpublished slug/parent edit can't leak into the live URL; the\n // draft-derived path itself (rendered by `DocumentUrlPath`) still updates as you\n // type — only the OPEN-live action is published-gated. `undefined` when the\n // collection is non-routable.\n const { headerLive } = useLiveUrl({ source, collection, doc, locale, published, routableCollection })\n\n // EC-240: hide the tab bar when there is nothing to navigate — a single content\n // tab with no References/Compare (the users rail). When framework tabs ARE\n // present we KEEP the content tab(s) so there is always a way back to the form;\n // only a genuinely single-tab surface drops them. And drop the whole lens row\n // when it would be empty (no tabs to show, no locale/perspective controls), so a\n // bare rail (users) shows no orphaned row.\n const hasFrameworkTabs = showReferences || compare !== null\n const showContentTabs = tabs.length > 1 || hasFrameworkTabs\n const showLensRow = showContentTabs || canLocales || canPublish\n\n // 0.0.39 split Publish button — the shared gate (disabled + title), computed once\n // (pure `publishGate`) so the primary \"Publish\" and the \"Publish & close\" item can\n // never diverge.\n const { disabled: publishDisabled, title: publishTitle } = publishGate({\n hasErrors,\n viewReadOnly,\n publishState,\n errorCount,\n })\n const publishButtonLabel =\n publishState === 'pending' ? (\n <>\n <Loader2 className=\"animate-spin\" /> {translate('cms.editor.publishing')}\n </>\n ) : publishState === 'success' ? (\n <>\n <CircleCheck /> {translate('cms.editor.publishedState')}\n </>\n ) : (\n <>\n <Eye /> {translate('cms.editor.publish')}\n </>\n )\n // Offer \"Publish & close\" only when there IS a list to return to — the same gate\n // the removed EC-316 \"Save & close\" used (never a singleton or a list-less rail).\n const canPublishClose = !singleton && source.listPath !== undefined\n\n return (\n <DocumentEditorProvider\n value={{\n projectId,\n collection,\n docRef: ref,\n draft,\n dirty,\n hasErrors,\n viewReadOnly,\n save,\n replaceDraft,\n compare,\n compareActive: activeTab === COMPARE_TAB,\n startCompare,\n setCompareBase,\n setCompareTarget,\n exitCompare,\n }}\n >\n <EmbedInspectorProvider value={embedInspectorApi}>\n <div className={cn('flex h-full min-h-0 flex-col', viewReadOnly && 'bg-info/[0.04]')}>\n {/* Header — the back-link + collection/id line moved into the global\n breadcrumb (EC-214); this keeps only the record title + actions. */}\n <div className=\"shrink-0 border-b border-border px-4 py-3 sm:px-6\">\n <Row gap=\"3\" align=\"start\">\n {/* Title + the composed-URL row beneath it (round 3): the URL is a\n first-class document property, so it sits under the (dominant) title in\n secondary/muted type. Routable collections only — a non-routable record\n (asset, singleton, users) shows the title alone, with no gap. The left\n block is `min-w-0` so a long title/URL truncates instead of shoving the\n header actions around. */}\n <Stack gap=\"1\" className=\"min-w-0\">\n <Row gap=\"2\" align=\"center\" className=\"min-w-0\">\n <Heading level={1} size=\"h2\" className=\"truncate\">\n {singleton ? localizedLabel(collection.form?.label, collection.id) : title || doc.id}\n </Heading>\n {singleton ? null : <FallbackChip fallback={titleFallback} testId=\"doc-title-fallback\" />}\n </Row>\n {routableCollection ? (\n <DocumentUrlPath\n projectId={projectId}\n collection={collection}\n doc={draft}\n siblings={source.documents()}\n routes={resolveDeploymentRoutes()}\n localeConfig={source.localeConfig}\n locale={locale}\n live={headerLive}\n />\n ) : null}\n </Stack>\n <Spacer />\n {/* Save status — an autosave rail reports Saving…/Saved; an explicit rail\n (users) reports Unsaved changes until the Save button commits. */}\n <Text size=\"xs\" tone=\"muted\" className=\"whitespace-nowrap\" data-testid=\"autosave-status\">\n {source.lastError\n ? translate('cms.editor.saveFailed')\n : explicitSave\n ? dirty\n ? translate('cms.editor.unsavedChanges')\n : translate('cms.editor.saved')\n : dirty\n ? translate('cms.editor.saving')\n : translate('cms.editor.saved')}\n </Text>\n {canPublish ? <DocStateBadge published={published} /> : null}\n {/* EC-216 / EC-205: the standalone \"View live\" button is retired (round 3).\n Opening the live page now hangs off the composed-URL row's LEAF segment\n under the title — same published-gating, one fewer header button. */}\n {/* 0.0.39: the Publish button is a SPLIT button — primary Publish (stay) +\n a \"Publish & close\" dropdown item (publish, then return to the list). The\n retired EC-316 \"Save & close\" is gone: autosave already persists edits, so\n a bare \"Save\" was meaningless. Both actions share identical gating (schema\n errors / view-read-only / in-flight); the close item is offered only when\n there IS a list to return to (never a singleton or list-less rail). */}\n {canPublish ? (\n <Row gap=\"px\" align=\"center\">\n <Button\n size=\"sm\"\n onClick={handlePublish}\n disabled={publishDisabled}\n title={publishTitle}\n className={canPublishClose ? 'rounded-r-none' : undefined}\n data-testid=\"doc-publish\"\n >\n {publishButtonLabel}\n </Button>\n {canPublishClose ? (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n size=\"sm\"\n disabled={publishDisabled}\n aria-label={translate('cms.editor.publishActions')}\n data-testid=\"doc-publish-actions\"\n className=\"rounded-l-none border-l border-primary-foreground/25 px-1.5\"\n >\n <ChevronDown className=\"size-3.5\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuItem onClick={handlePublishAndClose} data-testid=\"doc-publish-and-close\">\n <Save /> {translate('cms.editor.publishAndClose')}\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n ) : null}\n </Row>\n ) : null}\n {/* EC-240: an explicit-save rail commits the draft on demand (no publish\n step). Disabled until there are unsaved edits; the guard covers leaving. */}\n {explicitSave ? (\n <Button size=\"sm\" onClick={save} disabled={!dirty} data-testid=\"doc-save\">\n <Check /> {translate('common.save')}\n </Button>\n ) : null}\n {/* EC-332: AI \"Translate document\" — fills empty locale values on the draft.\n The gate renders nothing unless the studio is ai-configured AND the\n collection is localized with a target locale (so it is absent for\n single-locale collections, assets, and unconfigured studios). */}\n {!viewReadOnly ? (\n <TranslateDocumentAction\n projectId={projectId}\n collection={collection}\n docRef={ref}\n document={draft}\n localeConfig={source.localeConfig}\n localesEnabled={canLocales}\n onApplied={(next) => {\n source.save(next)\n replaceDraft(next)\n }}\n />\n ) : null}\n {/* EC-217: a singleton is never deleted; with nothing else to show\n (an unpublished singleton) the actions menu is omitted entirely. */}\n {published || !singleton ? (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button variant=\"outline\" size=\"icon-sm\" aria-label={translate('cms.editor.moreActions')}>\n <MoreHorizontal />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n {/* EC-316: \"Discard changes\" — reverts the draft to exactly the\n published content (the same restore machinery the Compare\n view's \"Restore version\" uses, EC-298). Shown only when a\n published version exists to revert to; enabled once the\n draft actually differs from it — decided by the same\n semantic comparator the Versions pane uses, so the two\n never contradict each other. */}\n {discardStatus !== 'hidden' ? (\n <DropdownMenuItem\n data-testid=\"doc-discard-draft\"\n disabled={discardStatus !== 'ready'}\n title={discardDisabledReason}\n onClick={() => setDiscardConfirm(true)}\n >\n <RotateCcw /> {translate('cms.editor.discardDraft')}\n </DropdownMenuItem>\n ) : null}\n {published ? (\n <DropdownMenuItem onClick={() => setConfirmAction('unpublish')}>\n <EyeOff /> {translate('cms.editor.unpublish')}\n </DropdownMenuItem>\n ) : null}\n {singleton ? null : (\n <DropdownMenuItem\n data-testid=\"doc-delete\"\n className=\"text-destructive\"\n onClick={() => setConfirmAction('delete')}\n >\n <Trash2 /> {translate('common.delete')}\n </DropdownMenuItem>\n )}\n </DropdownMenuContent>\n </DropdownMenu>\n ) : null}\n </Row>\n\n {/* EC-246a consistency: this lens row mirrors the list header — the tabs\n (EC-073) lead on the left (like the list's view tabs), and the lens\n CONTROLS sit on the right: the ambient locale switcher (EC-176) then the\n Draft/Published view perspective (EC-226) — exactly where the list keeps\n locale + Display, so the locale no longer jumps sides between views. */}\n {showLensRow ? (\n <Row gap=\"3\" className=\"mt-3\" wrap>\n <Row gap=\"1\" align=\"center\">\n {/* Schema content tabs (EC-073), then a divider, then the framework\n tabs (EC-269 References; EC-298 the transient Compare chip). While\n comparing, no content tab is highlighted — the Compare chip is the\n active view, and clicking a content tab closes the comparison. A\n single-tab surface with no framework tabs hides these entirely. */}\n {showContentTabs ? tabs.map((tab) => renderContentTab(tab)) : null}\n {showReferences ? (\n <>\n <div className=\"mx-1 h-4 w-px bg-border\" aria-hidden />\n {renderContentTab(REFERENCES_TAB)}\n </>\n ) : null}\n {compare ? (\n <span\n className={cn(\n 'inline-flex items-center gap-1 rounded-md px-2.5 py-1 text-xs font-medium transition-colors',\n activeTab === COMPARE_TAB\n ? 'bg-accent text-accent-foreground'\n : 'text-muted-foreground hover:bg-muted',\n )}\n >\n <button\n type=\"button\"\n onClick={() => setActiveTab(COMPARE_TAB)}\n aria-pressed={activeTab === COMPARE_TAB}\n className=\"inline-flex items-center gap-1\"\n >\n <GitCompareArrows className=\"size-3\" aria-hidden /> {translate('cms.version.compare')}\n </button>\n <button\n type=\"button\"\n onClick={exitCompare}\n aria-label={translate('cms.version.closeCompare')}\n className=\"ml-0.5 rounded p-0.5 hover:bg-background/40\"\n >\n <X className=\"size-3\" />\n </button>\n </span>\n ) : null}\n </Row>\n <Spacer />\n {canLocales ? <LocaleSwitcher /> : null}\n {canPublish ? (\n <Row gap=\"px\" className=\"rounded-md bg-muted/60 p-0.5\">\n <PerspectiveButton\n label={translate('cms.editor.draftState')}\n active={perspective === 'draft'}\n onClick={() => setPerspective('draft')}\n />\n <PerspectiveButton\n label={translate('cms.editor.publishedState')}\n active={perspective === 'published'}\n accent=\"info\"\n disabled={!published}\n title={published ? undefined : translate('cms.editor.publishToViewPublished')}\n onClick={() => setPerspective('published')}\n />\n </Row>\n ) : null}\n </Row>\n ) : null}\n </div>\n\n {/* EC-214: an unmistakable banner whenever the read-only Published snapshot\n is in view — disabled controls + a footer notice alone read as too\n subtle. Paired with the tinted surface and the accented toggle. */}\n {viewReadOnly ? (\n <Row\n gap=\"2\"\n className=\"shrink-0 border-b border-info/30 bg-info/10 px-4 py-1.5 text-info sm:px-6\"\n data-testid=\"published-view-banner\"\n >\n <Lock className=\"size-3.5 shrink-0\" aria-hidden />\n <Text size=\"xs\" className=\"text-info\">\n {translate('cms.editor.publishedReadOnlyBanner')}\n </Text>\n <Spacer />\n <button\n type=\"button\"\n onClick={() => setPerspective('draft')}\n className=\"text-xs font-medium underline-offset-2 hover:underline\"\n >\n {translate('cms.editor.switchToDraft')}\n </button>\n </Row>\n ) : null}\n\n {/* Body — a blocks field takes over the body with the page-builder canvas\n (which carries its own inspector sidebar, EC-188/EC-224); otherwise the\n field form sits beside the shared inspector sidebar (Versions). In\n Published view the snapshot must load before rendering — the draft is\n never shown as published (EC-226). */}\n {publishedPending ? (\n <div className=\"grid min-h-0 flex-1 place-items-center p-8\">\n <Text size=\"sm\" tone=\"muted\">\n {publishedSnapshot.status === 'loading'\n ? translate('cms.editor.loadingPublishedVersion')\n : translate('cms.editor.noPublishedVersion')}\n </Text>\n </div>\n ) : activeTab === COMPARE_TAB && compare ? (\n // EC-298: the version compare view takes over the main area, with the\n // shared inspector (Versions pane) staying on as the navigator — an\n // in-app surface, not a modal, so arbitrary versions can be browsed.\n <div className=\"flex min-h-0 flex-1 overflow-hidden\">\n <div className=\"min-h-0 flex-1 overflow-auto\">\n <VersionCompare />\n </div>\n <InspectorSidebar\n panes={trailingPanes}\n activeId={inspectorPaneId}\n onActiveChange={setInspectorPaneId}\n className=\"w-[320px] shrink-0 border-l border-border\"\n />\n </div>\n ) : canvasField ? (\n <div className=\"min-h-0 flex-1 overflow-hidden\">\n <CompositionFieldEditor\n key={`${canvasField.name}:${restoreNonce}:${perspective}`}\n field={canvasField}\n value={getValue(canvasField)}\n onChange={(v) => setValue(canvasField, v)}\n readOnly={viewReadOnly || source.isFieldReadOnly(canvasField)}\n trailingPanes={trailingPanes}\n />\n </div>\n ) : activeTab === REFERENCES_TAB ? (\n // EC-269: mounted only while this tab is active → reference data loads on\n // demand and is torn down on leave (no fetching during normal editing).\n <div className=\"flex min-h-0 flex-1 overflow-hidden\">\n <div className=\"min-h-0 flex-1 overflow-auto p-4 sm:p-6\">\n <div className=\"mx-auto w-full max-w-2xl\">\n <ReferencesPanel\n projectId={projectId}\n source={{ type: 'document', id: documentKey(ref) }}\n target={{ type: 'document', id: documentKey(ref) }}\n />\n </div>\n </div>\n </div>\n ) : (\n <div className=\"flex min-h-0 flex-1 overflow-hidden\">\n <div className=\"min-h-0 flex-1 overflow-auto p-4 sm:p-6\">\n <div className=\"mx-auto w-full max-w-2xl\">\n <Stack gap=\"6\">\n {groups.map((group) => {\n // EC-306: a conditionally/statically hidden group section is skipped\n // entirely (its fields are also exempt from validation).\n if (hiddenGroups.has(group.name)) return null\n // EC-273/EC-240: drop a fieldset whose every field is hidden — whether\n // content-conditionally or for this principal — so the form never\n // shows an empty section header.\n const visible = group.fields.filter(isFieldShown)\n if (visible.length === 0) return null\n const section = group.section\n // EC-241: a section `title` is a LocalizedString ({ en, de } or a plain\n // string) — resolve it against the active UI locale before rendering.\n const sectionTitle =\n section?.title !== undefined ? localizedLabel(section.title, group.name) : undefined\n return (\n <CollapsibleSection\n key={group.name}\n collapsible={section?.collapsible}\n defaultOpen={section?.defaultOpen}\n title={\n <Text\n size=\"sm\"\n weight=\"semibold\"\n className={sectionTitle || group.name === DEFAULT_FIELD_GROUP ? undefined : 'capitalize'}\n >\n {sectionTitle ??\n (group.name === DEFAULT_FIELD_GROUP\n ? translate('cms.editor.defaultGroup')\n : group.name)}\n </Text>\n }\n >\n {/* EC-306: fractional field widths — a 12-col grid honoring each\n field's `form.width`, collapsing to one column on narrow widths. */}\n <div className=\"grid grid-cols-1 gap-4 p-4 md:grid-cols-12\">\n {visible.map((field) => (\n <div\n key={field.name}\n className={cn('col-span-1', fieldWidthColSpan(field.form?.width))}\n >\n {renderFieldRow(field)}\n </div>\n ))}\n </div>\n </CollapsibleSection>\n )\n })}\n {/* EC-396: the form-column appendix seam — a direct-value node mounted\n below the fieldset cards, inside this same `max-w-2xl` column so it\n shares the cards' width and gap-6 rhythm (the account surface's\n CredentialPanel rides here). Renders only in the form view. */}\n {formAppendix}\n </Stack>\n </div>\n </div>\n {/* 0.0.41: selecting a rich-text component embed swaps this sidebar for the\n embed's prop form (the product-owner's \"no third form surface\" ask); the\n normal inspector returns on deselect. Only the form view swaps — the\n canvas view carries its own inspector and Compare has nothing to edit. */}\n {activeEmbed ? (\n <EmbedInspector\n payload={activeEmbed}\n className=\"w-[320px] shrink-0 border-l border-border\"\n />\n ) : (\n <InspectorSidebar\n panes={trailingPanes}\n activeId={inspectorPaneId}\n onActiveChange={setInspectorPaneId}\n className=\"w-[320px] shrink-0 border-l border-border\"\n />\n )}\n </div>\n )}\n\n {/* EC-283: the always-on validation footer is retired. Validation now lives\n in the document-scoped `Issues` inspector pane (full list + jump-to-field,\n count badge on its tab), field issues stay inline, and the Publish button\n gates on errors. Read-only is announced by the top banner (EC-214). */}\n\n {/* EC-240: on an explicit-save rail (no autosave), guard navigation away from\n unsaved edits — offer Save / Discard / Cancel. Autosave rails flush on\n unmount instead, so the guard is inert there. */}\n {explicitSave ? (\n <UnsavedChangesGuard\n when={dirty}\n onSave={() => {\n save()\n return true\n }}\n description={translate('cms.editor.unsavedChangesGuard')}\n />\n ) : null}\n\n {/* Reference-aware unpublish/delete confirmation (EC-173). */}\n {confirmAction !== null ? (\n <ReferenceConfirmDialog\n projectId={projectId}\n action={confirmAction}\n targets={confirmTargets}\n open\n onOpenChange={(open) => {\n if (!open) setConfirmAction(null)\n }}\n onConfirm={handleConfirmedAction}\n />\n ) : null}\n\n {/* EC-316: \"Discard changes\" confirmation — reverting the draft to the\n published content is destructive to the working edits (though itself\n undoable: the restore op snapshots the pre-discard draft first, EC-298),\n so it confirms before dispatching. */}\n <Dialog open={discardConfirm} onOpenChange={(open) => { if (!open) setDiscardConfirm(false) }}>\n <DialogContent>\n <DialogHeader>\n <DialogTitle>{translate('cms.editor.discardDraftTitle')}</DialogTitle>\n <DialogDescription>\n {translate('cms.editor.discardDraftDescription')}\n </DialogDescription>\n </DialogHeader>\n <DialogFooter className=\"mt-4\">\n <Button variant=\"ghost\" size=\"sm\" onClick={() => setDiscardConfirm(false)} disabled={discardBusy}>\n {translate('common.cancel')}\n </Button>\n <Button\n variant=\"destructive\"\n size=\"sm\"\n data-testid=\"discard-draft-confirm\"\n disabled={discardBusy}\n onClick={() => void handleDiscardDraft()}\n >\n {discardBusy ? (\n <>\n <Loader2 className=\"animate-spin\" /> {translate('cms.editor.discardingDraft')}\n </>\n ) : (\n <>\n <RotateCcw /> {translate('cms.editor.discardDraftConfirm')}\n </>\n )}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n\n {/* EC-300/AD-16: confirm-through guard for unpublished hard references. The\n schema is valid (those errors block the button); these would just render\n empty on the live site, so the editor confirms rather than being blocked. */}\n <Dialog open={publishConfirm} onOpenChange={(open) => { if (!open) cancelPublishConfirm() }}>\n <DialogContent>\n <DialogHeader>\n <DialogTitle>{translate('cms.editor.publishUnpublishedRefsTitle')}</DialogTitle>\n <DialogDescription>\n {translate('cms.editor.publishUnpublishedRefsDescription')}\n </DialogDescription>\n </DialogHeader>\n <Stack gap=\"1\">\n {referenceIssues.map((issue, i) => (\n <Text key={`${issue.meta?.id ?? i}`} size=\"xs\" tone=\"muted\">\n • {issue.message}\n </Text>\n ))}\n {/* Awareness only — other (non-blocking) issues don't gate publish, but\n surfacing the count keeps the confirm honest (AD-16). Schema errors\n can't be here: they disable the button before it opens. */}\n {schemaIssues.length > 0 ? (\n <Text size=\"xs\" tone=\"muted\" className=\"mt-1\">\n {translate(\n schemaIssues.length === 1\n ? 'cms.editor.publishAlsoHasIssues.one'\n : 'cms.editor.publishAlsoHasIssues.other',\n { count: schemaIssues.length },\n )}\n </Text>\n ) : null}\n </Stack>\n <DialogFooter className=\"mt-4\">\n <Button variant=\"ghost\" size=\"sm\" onClick={cancelPublishConfirm}>\n {translate('common.cancel')}\n </Button>\n <Button size=\"sm\" data-testid=\"publish-anyway\" onClick={confirmPublish}>\n {translate('cms.editor.publishAnyway')}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n </div>\n </EmbedInspectorProvider>\n </DocumentEditorProvider>\n )\n}\n\n/**\n * EC-300/AD-16 — errors for composition embeds (hard references) whose target isn't\n * published: they render empty on the live site. Walks every `blocks` field of the\n * document for reuse embeds; a missing target is an `unknown-relation-target` error,\n * an unpublished one an `unpublished-relation-target` error. Publishing is\n * confirm-through (not blocked), so these feed the Issues panel + the publish\n * confirmation, never the button's disabled gate. No reuse capability → none.\n */\nfunction unpublishedEmbedIssues(\n collection: CollectionDef,\n draft: CmsDocument,\n source: CollectionViewSource,\n): CmsValidationIssue[] {\n const capability = resolveReuseCapability(getStudioRegistry().registry.list(), source.collections)\n if (!capability) return []\n const issues: CmsValidationIssue[] = []\n const seen = new Set<string>()\n for (const field of collection.fields) {\n if (!isBlocksField(field)) continue\n for (const ref of collectEmbedRefs(blockRootsOf(draft.values?.[field.name]), capability)) {\n const key = `${ref.collection}:${ref.id}`\n if (seen.has(key)) continue\n seen.add(key)\n const exists = source.get(ref) !== undefined\n if (exists && (source.publishing?.isPublished(ref) ?? false)) continue\n issues.push(unpublishedEmbedIssue(collection, draft, field.name, ref, exists, source))\n }\n }\n return issues\n}\n\n/** One unpublished/missing reuse-embed issue (EC-300). */\nfunction unpublishedEmbedIssue(\n collection: CollectionDef,\n draft: CmsDocument,\n field: string,\n ref: DocumentRef,\n exists: boolean,\n source: CollectionViewSource,\n): CmsValidationIssue {\n return {\n code: exists ? 'unpublished-relation-target' : 'unknown-relation-target',\n severity: 'error',\n message: exists\n ? translate('cms.editor.embedUnpublishedIssue', { title: source.titleOf(ref) })\n : translate('cms.editor.embedMissingIssue', { id: ref.id }),\n path: [field],\n collectionId: collection.id,\n documentId: draft.id,\n meta: { field, target: ref.collection, id: ref.id },\n }\n}\n\n/**\n * A segmented-control button for the Draft/Published view toggle (EC-226). The\n * `accent` variant (EC-214) gives the active Published segment a distinct info\n * treatment, so the read-only mode is obvious at the control itself.\n */\nfunction PerspectiveButton({\n label,\n active,\n accent,\n disabled,\n title,\n onClick,\n}: {\n label: string\n active: boolean\n accent?: 'info'\n disabled?: boolean\n title?: string\n onClick: () => void\n}) {\n return (\n <button\n type=\"button\"\n aria-pressed={active}\n disabled={disabled}\n title={title}\n onClick={onClick}\n className={cn(\n 'rounded px-2.5 py-1 text-xs font-medium transition-colors disabled:opacity-40',\n active\n ? accent === 'info'\n ? 'bg-info text-info-foreground shadow-sm'\n : 'bg-background text-foreground shadow-sm'\n : 'text-muted-foreground hover:text-foreground',\n )}\n >\n {label}\n </button>\n )\n}\n\ninterface FieldGroup {\n name: string\n fields: FieldDef[]\n /** EC-306: the presentational section descriptor, if one was declared for this group. */\n section?: FieldGroupSection\n}\n\n/** The implicit fieldset that holds fields with no declared `form.group` (localized via `cms.editor.defaultGroup`). */\nconst DEFAULT_FIELD_GROUP = 'fields'\n\nfunction groupFields(fields: FieldDef[], collection: CollectionDef): FieldGroup[] {\n const order = groupOrder(collection.form)\n const byGroup = new Map<string, FieldDef[]>()\n for (const field of fields) {\n const group = field.form?.group ?? DEFAULT_FIELD_GROUP\n if (!byGroup.has(group)) byGroup.set(group, [])\n byGroup.get(group)!.push(field)\n }\n const names = [...byGroup.keys()].sort((a, b) => {\n const ia = order.indexOf(a)\n const ib = order.indexOf(b)\n return (ia < 0 ? 99 : ia) - (ib < 0 ? 99 : ib)\n })\n return names.map((name) => ({\n name,\n section: groupSectionOf(collection.form, name),\n fields: byGroup.get(name)!.sort((a, b) => (a.form?.order ?? 99) - (b.form?.order ?? 99)),\n }))\n}\n\n// EC-306: translate a field's grid span (FIELD_WIDTH_SPANS, owned + tested in\n// core) into a literal Tailwind col-span class. Literal strings so Tailwind's\n// scanner generates them; the span math itself is single-sourced in core.\nconst COL_SPAN_CLASS: Record<number, string> = {\n 3: 'md:col-span-3',\n 4: 'md:col-span-4',\n 6: 'md:col-span-6',\n 8: 'md:col-span-8',\n 9: 'md:col-span-9',\n 12: 'md:col-span-12',\n}\n\nfunction fieldWidthColSpan(width: FieldWidth | undefined): string {\n return COL_SPAN_CLASS[FIELD_WIDTH_SPANS[width ?? '100%']] ?? 'md:col-span-12'\n}\n\nfunction primaryTitle(\n doc: CmsDocument,\n collection: CollectionDef,\n locale: Locale,\n config: LocaleConfig,\n): string {\n // EC-242: the header title is the document identity (resolveDocumentTitle) — a\n // string `titleField` or a function pattern — over the fallback-resolved values,\n // the same resolver that drives relation labels and the list title column.\n return resolveDocumentTitle(collection, resolveDocumentForLocale(collection, doc, locale, config), '')\n}\n"]}
@@ -19,7 +19,7 @@ import type { PropField } from '@elytracms/core/component-registry';
19
19
  * vocabulary derives from the field `type` plus the optional `form.control`
20
20
  * presentation hint on `text` fields.
21
21
  */
22
- export type ControlKind = 'text' | 'textarea' | 'number' | 'boolean' | 'select' | 'json' | 'color' | 'richtext' | 'asset' | 'object' | 'relation';
22
+ export type ControlKind = 'text' | 'textarea' | 'number' | 'boolean' | 'select' | 'options' | 'json' | 'color' | 'richtext' | 'asset' | 'object' | 'relation';
23
23
  export declare function controlKind(spec: PropField): ControlKind;
24
24
  /**
25
25
  * EC-387: route a rich-text editor emission to the prop value callbacks.
@@ -5,6 +5,7 @@ import { fieldValueValidator } from '@elytracms/core/cms-core';
5
5
  import { Input, Row, Select, Stack, Text } from '@elytracms/studio/ui';
6
6
  import { localizedLabel, translate } from '@elytracms/studio/lib/i18n';
7
7
  import { FieldControl } from './field-control.js';
8
+ import { SegmentedOptions } from './field-control-lib.js';
8
9
  import { FieldDensityProvider } from './field-density.js';
9
10
  import { RichTextEditor } from './rich-text/index.js';
10
11
  import { useOptionalCollectionView } from '../collection-view/collection-view-context.js';
@@ -15,6 +16,12 @@ export function controlKind(spec) {
15
16
  case 'number':
16
17
  return 'number';
17
18
  case 'select':
19
+ // EC-393: a single-select prop opting into `form.control: 'options'` renders the
20
+ // SAME segmented button group the document form uses (EC-295), not the native
21
+ // dropdown — parity with `field-control.tsx`. Segmented is single-select only,
22
+ // so a `multiple` select stays on the native control (mirroring field-control).
23
+ if (spec.form?.control === 'options' && !spec.multiple)
24
+ return 'options';
18
25
  return 'select';
19
26
  case 'richText':
20
27
  return 'richtext';
@@ -91,6 +98,13 @@ export function PropValueControl({ id, name, spec, value, onSet, onClear }) {
91
98
  onSet(opt ? opt.value : e.target.value);
92
99
  }, children: options.map((opt) => (_jsx("option", { value: opt.value, children: localizedLabel(opt.label, opt.value) }, opt.value))) }));
93
100
  }
101
+ else if (kind === 'options') {
102
+ // EC-393: reuse the EC-295 segmented control the document form renders (via
103
+ // `field-control.tsx`) — delegation over a parallel dropdown. It reads the
104
+ // field-def's own `options` (label + per-option `description` tooltip); single-
105
+ // select only (picks one, no unset — an unset select keeps the native dropdown).
106
+ control = (_jsx(SegmentedOptions, { id: id, field: { ...spec, name }, value: current, onChange: (v) => onSet(v), readOnly: false }));
107
+ }
94
108
  else if (kind === 'number') {
95
109
  control = _jsx(NumberControl, { id: id, current: current, onSet: onSet, onClear: onClear });
96
110
  }
@@ -1 +1 @@
1
- {"version":3,"file":"prop-fields.js","sourceRoot":"","sources":["../../../src/components/cms/prop-fields.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useState } from 'react'\nimport { isStaticProp } from '@elytracms/core/project-graph'\nimport type { JsonValue, PropValue } from '@elytracms/core/project-graph'\nimport type { PropField } from '@elytracms/core/component-registry'\nimport { fieldValueValidator } from '@elytracms/core/cms-core'\nimport type { FieldDef, SelectOption } from '@elytracms/core/cms-core'\nimport { Input, Row, Select, Stack, Text } from '@elytracms/studio/ui'\nimport { localizedLabel, translate } from '@elytracms/studio/lib/i18n'\nimport { FieldControl } from './field-control'\nimport { FieldDensityProvider } from './field-density'\nimport { RichTextEditor } from './rich-text'\nimport { useOptionalCollectionView } from '../collection-view/collection-view-context'\n\n/**\n * The EC-190 \"props-as-fields\" value control — the single renderer for a\n * manifest-declared prop field, shared by the canvas property inspector\n * (`builder/property-inspector.tsx`) and the rich-text component-embed prop editor\n * (`rich-text/embed-props.tsx`). Extracted here so both surfaces render prop fields\n * through *one* control set (asset → library picker, relation → document picker,\n * object → nested form, …) rather than two parallel form systems.\n *\n * It is deliberately store-agnostic: it reads a prop's current value and reports\n * edits through `onSet(rawValue)` / `onClear()`. The caller owns *where* the value\n * lands — the inspector dispatches a builder command; the embed editor rewrites a\n * ProseMirror node attr — and is responsible for wrapping the raw value in a\n * `staticProp` envelope.\n */\n\n/**\n * The inspector control to render for a prop field-def. The `control.kind`\n * vocabulary derives from the field `type` plus the optional `form.control`\n * presentation hint on `text` fields.\n */\nexport type ControlKind =\n | 'text'\n | 'textarea'\n | 'number'\n | 'boolean'\n | 'select'\n | 'json'\n | 'color'\n | 'richtext'\n | 'asset'\n | 'object'\n | 'relation'\n\nexport function 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\n/**\n * EC-387: route a rich-text editor emission to the prop value callbacks.\n *\n * The shared {@link RichTextEditor} emits either an emptied-editor `undefined`, the\n * exact stored envelope (an untouched/reverted edit), or a fresh `{ version, doc }`\n * value — never a plain string. An `undefined` clears the prop (reverting to the\n * manifest default, matching the asset/relation/object controls); anything else is a\n * structured `JsonValue` envelope set verbatim. Extracted pure so the wiring — the\n * exact bug the retired textarea stub had (it wrote a bare string over the envelope) —\n * is regression-pinned without driving TipTap.\n */\nexport function applyRichTextPropChange(\n next: unknown,\n handlers: { onSet: (value: JsonValue) => void; onClear: () => void },\n): void {\n if (next === undefined) handlers.onClear()\n else handlers.onSet(next as JsonValue)\n}\n\nexport interface PropValueControlProps {\n /** DOM id for the control's primary input (label `htmlFor` target). */\n id: string\n /** The prop's field name — needed to synthesize the `FieldDef` for reused controls. */\n name: string\n /** The manifest prop field-def driving the control kind + validation. */\n spec: PropField\n /** The prop's current value envelope (or `undefined` when unset). */\n value: PropValue | undefined\n /** Commit a new raw value (the caller wraps it in a `staticProp`). */\n onSet: (value: JsonValue) => void\n /** Remove the prop (revert to the manifest default). */\n onClear: () => void\n}\n\n/**\n * Render + edit a single manifest prop value. Store-agnostic: emits through\n * `onSet`/`onClear`. Shows a validation issue when the current value fails the\n * prop's field-def validator.\n */\nexport function PropValueControl({ id, name, spec, value, onSet, onClear }: PropValueControlProps) {\n const current = isStaticProp(value) ? value.value : (spec.default as JsonValue | undefined)\n // The `asset`/`relation`/`object` controls delegate to the CMS `FieldControl`,\n // which reads the ambient `CollectionViewSource` (asset library, relation\n // candidates, nested form). The component-registry preview surface has no\n // document/collection context, so outside a provider those controls must degrade\n // to a read-only value display instead of throwing \"useCollectionView must be\n // used within a <CollectionViewProvider>\" (the Konditorei Components-view crash).\n const hasWorkspaceContext = useOptionalCollectionView() != null\n\n // EC-190: validate with the prop's field-def value validator.\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.\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) => onSet(e.target.checked)}\n className=\"size-4 accent-primary\"\n />\n <Text size=\"sm\" tone=\"muted\">\n {current === true ? translate('common.true') : translate('common.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 onSet(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={onSet} onClear={onClear} />\n } else if (kind === 'json') {\n control = <JsonControl id={id} current={current} onSet={onSet} />\n } else if (kind === 'color') {\n control = (\n <Row gap=\"2\">\n <input\n id={id}\n type=\"color\"\n value={typeof current === 'string' && /^#[0-9a-f]{6}$/i.test(current) ? current : '#000000'}\n onChange={(e) => onSet(e.target.value)}\n className=\"h-8 w-10 shrink-0 rounded-md border border-input bg-background\"\n />\n <Input value={typeof current === 'string' ? current : ''} onChange={(e) => onSet(e.target.value)} />\n </Row>\n )\n } else if (kind === 'richtext') {\n // EC-387: a richText PROP now uses the SAME real editor as the collection-field\n // path (`field-control.tsx` `case 'richText'`) — one editing surface, one\n // `{ version, doc }` value contract, never a second implementation. The retired\n // textarea stub read `typeof current === 'string'`, so a stored envelope showed\n // EMPTY and any edit clobbered the structured document with a bare string\n // (silent data destruction). The editor reads the envelope (or a legacy string,\n // or empty), always emits an envelope, and routes an emptied editor's `undefined`\n // to a prop clear via {@link applyRichTextPropChange}.\n //\n // Like the asset/relation/object controls below, the editor needs the ambient\n // workspace — its asset-image picker reads the REQUIRED collection-view context.\n // The block property inspector (the EC-387 target) is inside the detail editor's\n // `CollectionViewProvider`, so it gets the full editor; the component-registry\n // preview surface (no provider, nothing to edit against) degrades to the same\n // read-only value display the sibling controls use, instead of crashing with\n // \"useCollectionView must be used within a <CollectionViewProvider>\".\n control = hasWorkspaceContext ? (\n <RichTextEditor\n id={id}\n value={current}\n onChange={(next) => applyRichTextPropChange(next, { onSet, onClear })}\n allow={spec.type === 'richText' ? spec.allow : undefined}\n />\n ) : (\n <WorkspaceValueFallback value={current} />\n )\n } else if (kind === 'textarea') {\n control = (\n <textarea\n id={id}\n rows={2}\n value={typeof current === 'string' ? current : ''}\n onChange={(e) => onSet(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 // Without a workspace context (the Components preview), degrade to a read-only\n // value display so the pane renders instead of throwing.\n control = hasWorkspaceContext ? (\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 ? onClear() : onSet(v as JsonValue))}\n readOnly={false}\n />\n </FieldDensityProvider>\n ) : (\n <WorkspaceValueFallback value={current} />\n )\n } else {\n control = (\n <Input\n id={id}\n value={typeof current === 'string' ? current : String(current ?? '')}\n onChange={(e) => onSet(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 {translate('builder.inspector.invalidValue')}\n </Text>\n ) : null}\n </Stack>\n )\n}\n\n/**\n * Read-only stand-in for an `asset`/`relation`/`object` prop when there is no\n * ambient `CollectionViewSource` (the component-registry preview, which has no\n * document/collection context). Shows the current value and a hint that it is\n * editable inside a document — never the live picker, which needs a workspace.\n */\nfunction WorkspaceValueFallback({ value }: { value: JsonValue | undefined }) {\n const text =\n value === undefined || value === null\n ? '—'\n : typeof value === 'string'\n ? value\n : JSON.stringify(value)\n return (\n <Stack gap=\"1\">\n <div className=\"truncate rounded-md border border-input bg-muted/30 px-2.5 py-1.5 text-sm text-muted-foreground\">\n {text}\n </div>\n <Text size=\"xs\" tone=\"muted\">\n {translate('components.preview.propNeedsDocument')}\n </Text>\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(translate('builder.inspector.invalidJson'))\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"]}
1
+ {"version":3,"file":"prop-fields.js","sourceRoot":"","sources":["../../../src/components/cms/prop-fields.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAA;AAG5D,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAE9D,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAA;AACtE,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAA;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AACtD,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAC5C,OAAO,EAAE,yBAAyB,EAAE,MAAM,4CAA4C,CAAA;AAoCtF,MAAM,UAAU,WAAW,CAAC,IAAe;IACzC,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,KAAK,SAAS;YACZ,OAAO,SAAS,CAAA;QAClB,KAAK,QAAQ;YACX,OAAO,QAAQ,CAAA;QACjB,KAAK,QAAQ;YACX,iFAAiF;YACjF,8EAA8E;YAC9E,+EAA+E;YAC/E,gFAAgF;YAChF,IAAI,IAAI,CAAC,IAAI,EAAE,OAAO,KAAK,SAAS,IAAI,CAAC,IAAI,CAAC,QAAQ;gBAAE,OAAO,SAAS,CAAA;YACxE,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;;;;;;;;;;GAUG;AACH,MAAM,UAAU,uBAAuB,CACrC,IAAa,EACb,QAAoE;IAEpE,IAAI,IAAI,KAAK,SAAS;QAAE,QAAQ,CAAC,OAAO,EAAE,CAAA;;QACrC,QAAQ,CAAC,KAAK,CAAC,IAAiB,CAAC,CAAA;AACxC,CAAC;AAiBD;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAyB;IAC/F,MAAM,OAAO,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAE,IAAI,CAAC,OAAiC,CAAA;IAC3F,+EAA+E;IAC/E,0EAA0E;IAC1E,0EAA0E;IAC1E,iFAAiF;IACjF,8EAA8E;IAC9E,kFAAkF;IAClF,MAAM,mBAAmB,GAAG,yBAAyB,EAAE,IAAI,IAAI,CAAA;IAE/D,8DAA8D;IAC9D,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,iDAAiD;IACjD,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,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,EACxC,SAAS,EAAC,uBAAuB,GACjC,EACF,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,OAAO,YACzB,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,GACnE,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,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;YACzC,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,SAAS,EAAE,CAAC;QAC9B,4EAA4E;QAC5E,2EAA2E;QAC3E,gFAAgF;QAChF,iFAAiF;QACjF,OAAO,GAAG,CACR,KAAC,gBAAgB,IACf,EAAE,EAAE,EAAE,EACN,KAAK,EAAE,EAAE,GAAG,IAAI,EAAE,IAAI,EAAoB,EAC1C,KAAK,EAAE,OAAO,EACd,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAc,CAAC,EACtC,QAAQ,EAAE,KAAK,GACf,CACH,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,KAAK,EAAE,OAAO,EAAE,OAAO,GAAI,CAAA;IACvF,CAAC;SAAM,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;QAC3B,OAAO,GAAG,KAAC,WAAW,IAAC,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,GAAI,CAAA;IACnE,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,EAAE,OAAO,OAAO,KAAK,QAAQ,IAAI,iBAAiB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,EAC3F,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EACtC,SAAS,EAAC,gEAAgE,GAC1E,EACF,KAAC,KAAK,IAAC,KAAK,EAAE,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,GAAI,IAChG,CACP,CAAA;IACH,CAAC;SAAM,IAAI,IAAI,KAAK,UAAU,EAAE,CAAC;QAC/B,gFAAgF;QAChF,0EAA0E;QAC1E,gFAAgF;QAChF,gFAAgF;QAChF,0EAA0E;QAC1E,gFAAgF;QAChF,kFAAkF;QAClF,uDAAuD;QACvD,EAAE;QACF,8EAA8E;QAC9E,iFAAiF;QACjF,iFAAiF;QACjF,+EAA+E;QAC/E,8EAA8E;QAC9E,6EAA6E;QAC7E,sEAAsE;QACtE,OAAO,GAAG,mBAAmB,CAAC,CAAC,CAAC,CAC9B,KAAC,cAAc,IACb,EAAE,EAAE,EAAE,EACN,KAAK,EAAE,OAAO,EACd,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,uBAAuB,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,EACrE,KAAK,EAAE,IAAI,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,GACxD,CACH,CAAC,CAAC,CAAC,CACF,KAAC,sBAAsB,IAAC,KAAK,EAAE,OAAO,GAAI,CAC3C,CAAA;IACH,CAAC;SAAM,IAAI,IAAI,KAAK,UAAU,EAAE,CAAC;QAC/B,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,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EACtC,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,+EAA+E;QAC/E,yDAAyD;QACzD,OAAO,GAAG,mBAAmB,CAAC,CAAC,CAAC,CAC9B,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,OAAO,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAc,CAAC,CAAC,EACpF,QAAQ,EAAE,KAAK,GACf,GACmB,CACxB,CAAC,CAAC,CAAC,CACF,KAAC,sBAAsB,IAAC,KAAK,EAAE,OAAO,GAAI,CAC3C,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,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,GACtC,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,YACzC,SAAS,CAAC,gCAAgC,CAAC,GACvC,CACR,CAAC,CAAC,CAAC,IAAI,IACF,CACT,CAAA;AACH,CAAC;AAED;;;;;GAKG;AACH,SAAS,sBAAsB,CAAC,EAAE,KAAK,EAAoC;IACzE,MAAM,IAAI,GACR,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI;QACnC,CAAC,CAAC,GAAG;QACL,CAAC,CAAC,OAAO,KAAK,KAAK,QAAQ;YACzB,CAAC,CAAC,KAAK;YACP,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;IAC7B,OAAO,CACL,MAAC,KAAK,IAAC,GAAG,EAAC,GAAG,aACZ,cAAK,SAAS,EAAC,iGAAiG,YAC7G,IAAI,GACD,EACN,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,OAAO,YACzB,SAAS,CAAC,sCAAsC,CAAC,GAC7C,IACD,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,SAAS,CAAC,+BAA+B,CAAC,CAAC,CAAA;oBACtD,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 { isStaticProp } from '@elytracms/core/project-graph'\nimport type { JsonValue, PropValue } from '@elytracms/core/project-graph'\nimport type { PropField } from '@elytracms/core/component-registry'\nimport { fieldValueValidator } from '@elytracms/core/cms-core'\nimport type { FieldDef, SelectFieldDef, SelectOption } from '@elytracms/core/cms-core'\nimport { Input, Row, Select, Stack, Text } from '@elytracms/studio/ui'\nimport { localizedLabel, translate } from '@elytracms/studio/lib/i18n'\nimport { FieldControl } from './field-control'\nimport { SegmentedOptions } from './field-control-lib'\nimport { FieldDensityProvider } from './field-density'\nimport { RichTextEditor } from './rich-text'\nimport { useOptionalCollectionView } from '../collection-view/collection-view-context'\n\n/**\n * The EC-190 \"props-as-fields\" value control — the single renderer for a\n * manifest-declared prop field, shared by the canvas property inspector\n * (`builder/property-inspector.tsx`) and the rich-text component-embed prop editor\n * (`rich-text/embed-props.tsx`). Extracted here so both surfaces render prop fields\n * through *one* control set (asset → library picker, relation → document picker,\n * object → nested form, …) rather than two parallel form systems.\n *\n * It is deliberately store-agnostic: it reads a prop's current value and reports\n * edits through `onSet(rawValue)` / `onClear()`. The caller owns *where* the value\n * lands — the inspector dispatches a builder command; the embed editor rewrites a\n * ProseMirror node attr — and is responsible for wrapping the raw value in a\n * `staticProp` envelope.\n */\n\n/**\n * The inspector control to render for a prop field-def. The `control.kind`\n * vocabulary derives from the field `type` plus the optional `form.control`\n * presentation hint on `text` fields.\n */\nexport type ControlKind =\n | 'text'\n | 'textarea'\n | 'number'\n | 'boolean'\n | 'select'\n | 'options'\n | 'json'\n | 'color'\n | 'richtext'\n | 'asset'\n | 'object'\n | 'relation'\n\nexport function controlKind(spec: PropField): ControlKind {\n switch (spec.type) {\n case 'boolean':\n return 'boolean'\n case 'number':\n return 'number'\n case 'select':\n // EC-393: a single-select prop opting into `form.control: 'options'` renders the\n // SAME segmented button group the document form uses (EC-295), not the native\n // dropdown — parity with `field-control.tsx`. Segmented is single-select only,\n // so a `multiple` select stays on the native control (mirroring field-control).\n if (spec.form?.control === 'options' && !spec.multiple) return 'options'\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\n/**\n * EC-387: route a rich-text editor emission to the prop value callbacks.\n *\n * The shared {@link RichTextEditor} emits either an emptied-editor `undefined`, the\n * exact stored envelope (an untouched/reverted edit), or a fresh `{ version, doc }`\n * value — never a plain string. An `undefined` clears the prop (reverting to the\n * manifest default, matching the asset/relation/object controls); anything else is a\n * structured `JsonValue` envelope set verbatim. Extracted pure so the wiring — the\n * exact bug the retired textarea stub had (it wrote a bare string over the envelope) —\n * is regression-pinned without driving TipTap.\n */\nexport function applyRichTextPropChange(\n next: unknown,\n handlers: { onSet: (value: JsonValue) => void; onClear: () => void },\n): void {\n if (next === undefined) handlers.onClear()\n else handlers.onSet(next as JsonValue)\n}\n\nexport interface PropValueControlProps {\n /** DOM id for the control's primary input (label `htmlFor` target). */\n id: string\n /** The prop's field name — needed to synthesize the `FieldDef` for reused controls. */\n name: string\n /** The manifest prop field-def driving the control kind + validation. */\n spec: PropField\n /** The prop's current value envelope (or `undefined` when unset). */\n value: PropValue | undefined\n /** Commit a new raw value (the caller wraps it in a `staticProp`). */\n onSet: (value: JsonValue) => void\n /** Remove the prop (revert to the manifest default). */\n onClear: () => void\n}\n\n/**\n * Render + edit a single manifest prop value. Store-agnostic: emits through\n * `onSet`/`onClear`. Shows a validation issue when the current value fails the\n * prop's field-def validator.\n */\nexport function PropValueControl({ id, name, spec, value, onSet, onClear }: PropValueControlProps) {\n const current = isStaticProp(value) ? value.value : (spec.default as JsonValue | undefined)\n // The `asset`/`relation`/`object` controls delegate to the CMS `FieldControl`,\n // which reads the ambient `CollectionViewSource` (asset library, relation\n // candidates, nested form). The component-registry preview surface has no\n // document/collection context, so outside a provider those controls must degrade\n // to a read-only value display instead of throwing \"useCollectionView must be\n // used within a <CollectionViewProvider>\" (the Konditorei Components-view crash).\n const hasWorkspaceContext = useOptionalCollectionView() != null\n\n // EC-190: validate with the prop's field-def value validator.\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.\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) => onSet(e.target.checked)}\n className=\"size-4 accent-primary\"\n />\n <Text size=\"sm\" tone=\"muted\">\n {current === true ? translate('common.true') : translate('common.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 onSet(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 === 'options') {\n // EC-393: reuse the EC-295 segmented control the document form renders (via\n // `field-control.tsx`) — delegation over a parallel dropdown. It reads the\n // field-def's own `options` (label + per-option `description` tooltip); single-\n // select only (picks one, no unset — an unset select keeps the native dropdown).\n control = (\n <SegmentedOptions\n id={id}\n field={{ ...spec, name } as SelectFieldDef}\n value={current}\n onChange={(v) => onSet(v as JsonValue)}\n readOnly={false}\n />\n )\n } else if (kind === 'number') {\n control = <NumberControl id={id} current={current} onSet={onSet} onClear={onClear} />\n } else if (kind === 'json') {\n control = <JsonControl id={id} current={current} onSet={onSet} />\n } else if (kind === 'color') {\n control = (\n <Row gap=\"2\">\n <input\n id={id}\n type=\"color\"\n value={typeof current === 'string' && /^#[0-9a-f]{6}$/i.test(current) ? current : '#000000'}\n onChange={(e) => onSet(e.target.value)}\n className=\"h-8 w-10 shrink-0 rounded-md border border-input bg-background\"\n />\n <Input value={typeof current === 'string' ? current : ''} onChange={(e) => onSet(e.target.value)} />\n </Row>\n )\n } else if (kind === 'richtext') {\n // EC-387: a richText PROP now uses the SAME real editor as the collection-field\n // path (`field-control.tsx` `case 'richText'`) — one editing surface, one\n // `{ version, doc }` value contract, never a second implementation. The retired\n // textarea stub read `typeof current === 'string'`, so a stored envelope showed\n // EMPTY and any edit clobbered the structured document with a bare string\n // (silent data destruction). The editor reads the envelope (or a legacy string,\n // or empty), always emits an envelope, and routes an emptied editor's `undefined`\n // to a prop clear via {@link applyRichTextPropChange}.\n //\n // Like the asset/relation/object controls below, the editor needs the ambient\n // workspace — its asset-image picker reads the REQUIRED collection-view context.\n // The block property inspector (the EC-387 target) is inside the detail editor's\n // `CollectionViewProvider`, so it gets the full editor; the component-registry\n // preview surface (no provider, nothing to edit against) degrades to the same\n // read-only value display the sibling controls use, instead of crashing with\n // \"useCollectionView must be used within a <CollectionViewProvider>\".\n control = hasWorkspaceContext ? (\n <RichTextEditor\n id={id}\n value={current}\n onChange={(next) => applyRichTextPropChange(next, { onSet, onClear })}\n allow={spec.type === 'richText' ? spec.allow : undefined}\n />\n ) : (\n <WorkspaceValueFallback value={current} />\n )\n } else if (kind === 'textarea') {\n control = (\n <textarea\n id={id}\n rows={2}\n value={typeof current === 'string' ? current : ''}\n onChange={(e) => onSet(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 // Without a workspace context (the Components preview), degrade to a read-only\n // value display so the pane renders instead of throwing.\n control = hasWorkspaceContext ? (\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 ? onClear() : onSet(v as JsonValue))}\n readOnly={false}\n />\n </FieldDensityProvider>\n ) : (\n <WorkspaceValueFallback value={current} />\n )\n } else {\n control = (\n <Input\n id={id}\n value={typeof current === 'string' ? current : String(current ?? '')}\n onChange={(e) => onSet(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 {translate('builder.inspector.invalidValue')}\n </Text>\n ) : null}\n </Stack>\n )\n}\n\n/**\n * Read-only stand-in for an `asset`/`relation`/`object` prop when there is no\n * ambient `CollectionViewSource` (the component-registry preview, which has no\n * document/collection context). Shows the current value and a hint that it is\n * editable inside a document — never the live picker, which needs a workspace.\n */\nfunction WorkspaceValueFallback({ value }: { value: JsonValue | undefined }) {\n const text =\n value === undefined || value === null\n ? '—'\n : typeof value === 'string'\n ? value\n : JSON.stringify(value)\n return (\n <Stack gap=\"1\">\n <div className=\"truncate rounded-md border border-input bg-muted/30 px-2.5 py-1.5 text-sm text-muted-foreground\">\n {text}\n </div>\n <Text size=\"xs\" tone=\"muted\">\n {translate('components.preview.propNeedsDocument')}\n </Text>\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(translate('builder.inspector.invalidJson'))\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"]}
@@ -1,4 +1,14 @@
1
- import type { CollectionDef } from '@elytracms/core/cms-core';
1
+ import type { CollectionDef, DocumentTitleFn } from '@elytracms/core/cms-core';
2
+ /**
3
+ * EC-396 — the display-title fallback shared by the users rail and the account lens
4
+ * (`selfAccountCollectionDef`): a member's **name**, else their **email** — never the
5
+ * raw internal id when an email exists. A function-pattern `titleField` (config-as-code,
6
+ * studio-evaluated), so list rows, breadcrumbs, relation labels, and the account header
7
+ * resolve identity the same way. Returns `''` when NEITHER is present, so
8
+ * `resolveDocumentTitle` degrades to the document id (its `fallbackId`) — the only case a
9
+ * raw id ever surfaces.
10
+ */
11
+ export declare const userTitleFallback: DocumentTitleFn;
2
12
  /**
3
13
  * The built-in **users** field lens (EC-240, AD-19). Users are a separate rail —
4
14
  * NOT a `cmsDocuments` collection (`collectionKindSchema` is `document|asset`) —
@@ -1,6 +1,21 @@
1
1
  import { collectionDefSchema } from '@elytracms/core/cms-core';
2
2
  import { roleOptions } from '@elytracms/core/persistence';
3
3
  import { msgLabel } from '@elytracms/studio/lib/i18n';
4
+ /**
5
+ * EC-396 — the display-title fallback shared by the users rail and the account lens
6
+ * (`selfAccountCollectionDef`): a member's **name**, else their **email** — never the
7
+ * raw internal id when an email exists. A function-pattern `titleField` (config-as-code,
8
+ * studio-evaluated), so list rows, breadcrumbs, relation labels, and the account header
9
+ * resolve identity the same way. Returns `''` when NEITHER is present, so
10
+ * `resolveDocumentTitle` degrades to the document id (its `fallbackId`) — the only case a
11
+ * raw id ever surfaces.
12
+ */
13
+ export const userTitleFallback = (values) => {
14
+ const name = typeof values.name === 'string' ? values.name.trim() : '';
15
+ if (name)
16
+ return name;
17
+ return typeof values.email === 'string' ? values.email.trim() : '';
18
+ };
4
19
  /**
5
20
  * The built-in **users** field lens (EC-240, AD-19). Users are a separate rail —
6
21
  * NOT a `cmsDocuments` collection (`collectionKindSchema` is `document|asset`) —
@@ -22,7 +37,8 @@ export const usersCollectionDef = collectionDefSchema.parse({
22
37
  // Users aren't a CMS `kind`; 'document' just satisfies the lens schema. The
23
38
  // source's capabilities (adminOnly, no publish/versions/create) carry the semantics.
24
39
  kind: 'document',
25
- titleField: 'name',
40
+ // EC-396: name → email → id (never the raw id when an email exists).
41
+ titleField: userTitleFallback,
26
42
  // EC-241: built-in rail labels resolve through the message catalog (msgLabel) — a
27
43
  // consumer's translation catalog localizes them without a per-rail override.
28
44
  form: { label: msgLabel('users.rail.label'), labelSingular: msgLabel('users.rail.labelSingular') },
@@ -1 +1 @@
1
- {"version":3,"file":"users-schema.js","sourceRoot":"","sources":["../../../src/components/users/users-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAE9D,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAA;AACzD,OAAO,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAA;AAErD;;;;;;;;;;;;GAYG;AACH,kFAAkF;AAClF,oFAAoF;AACpF,MAAM,CAAC,MAAM,iBAAiB,GAAG,WAAW,EAAE,CAAA;AAE9C,MAAM,CAAC,MAAM,kBAAkB,GAAkB,mBAAmB,CAAC,KAAK,CAAC;IACzE,EAAE,EAAE,MAAM;IACV,4EAA4E;IAC5E,qFAAqF;IACrF,IAAI,EAAE,UAAU;IAChB,UAAU,EAAE,MAAM;IAClB,kFAAkF;IAClF,6EAA6E;IAC7E,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,kBAAkB,CAAC,EAAE,aAAa,EAAE,QAAQ,CAAC,0BAA0B,CAAC,EAAE;IAClG,MAAM,EAAE;QACN,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,wBAAwB,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE;QACrH,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,yBAAyB,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE;QACvH;YACE,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,CAAC,GAAG,iBAAiB,CAAC;YAC/B,UAAU,EAAE,IAAI;YAChB,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,wBAAwB,CAAC,EAAE;SACpD;KACF;CACF,CAAC,CAAA","sourcesContent":["import { collectionDefSchema } from '@elytracms/core/cms-core'\nimport type { CollectionDef } from '@elytracms/core/cms-core'\nimport { roleOptions } from '@elytracms/core/persistence'\nimport { msgLabel } from '@elytracms/studio/lib/i18n'\n\n/**\n * The built-in **users** field lens (EC-240, AD-19). Users are a separate rail —\n * NOT a `cmsDocuments` collection (`collectionKindSchema` is `document|asset`) —\n * so this `CollectionDef` exists only to drive the portable list/edit surfaces\n * over the users {@link CollectionViewSource}. The rail's *user-ness* comes from\n * the source's capabilities (admin-only, no publish/versions/create), never the\n * schema `kind`.\n *\n * Identity fields (`name`, `email`) are **auth-owned** and read-only here — an\n * admin manages a member's *role* (and, later, project-declared custom fields\n * over the `meta` bag, mirroring assets); the person edits their own name/email\n * via account settings. A v1 default; a project may extend the field set later.\n */\n// Derived from the ONE role policy (EC-240c) — no hardcoded duplicate of the role\n// set. Reflects the shipped default today; a project's custom policy flows through.\nexport const USER_ROLE_OPTIONS = roleOptions()\n\nexport const usersCollectionDef: CollectionDef = collectionDefSchema.parse({\n id: 'user',\n // Users aren't a CMS `kind`; 'document' just satisfies the lens schema. The\n // source's capabilities (adminOnly, no publish/versions/create) carry the semantics.\n kind: 'document',\n titleField: 'name',\n // EC-241: built-in rail labels resolve through the message catalog (msgLabel) — a\n // consumer's translation catalog localizes them without a per-rail override.\n form: { label: msgLabel('users.rail.label'), labelSingular: msgLabel('users.rail.labelSingular') },\n fields: [\n { name: 'name', type: 'text', form: { label: msgLabel('users.field.name.label'), readOnly: true }, filterable: true },\n { name: 'email', type: 'text', form: { label: msgLabel('users.field.email.label'), readOnly: true }, filterable: true },\n {\n name: 'role',\n type: 'select',\n options: [...USER_ROLE_OPTIONS],\n filterable: true,\n form: { label: msgLabel('users.field.role.label') },\n },\n ],\n})\n"]}
1
+ {"version":3,"file":"users-schema.js","sourceRoot":"","sources":["../../../src/components/users/users-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAE9D,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAA;AACzD,OAAO,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAA;AAErD;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAoB,CAAC,MAAM,EAAE,EAAE;IAC3D,MAAM,IAAI,GAAG,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;IACtE,IAAI,IAAI;QAAE,OAAO,IAAI,CAAA;IACrB,OAAO,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;AACpE,CAAC,CAAA;AAED;;;;;;;;;;;;GAYG;AACH,kFAAkF;AAClF,oFAAoF;AACpF,MAAM,CAAC,MAAM,iBAAiB,GAAG,WAAW,EAAE,CAAA;AAE9C,MAAM,CAAC,MAAM,kBAAkB,GAAkB,mBAAmB,CAAC,KAAK,CAAC;IACzE,EAAE,EAAE,MAAM;IACV,4EAA4E;IAC5E,qFAAqF;IACrF,IAAI,EAAE,UAAU;IAChB,qEAAqE;IACrE,UAAU,EAAE,iBAAiB;IAC7B,kFAAkF;IAClF,6EAA6E;IAC7E,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,kBAAkB,CAAC,EAAE,aAAa,EAAE,QAAQ,CAAC,0BAA0B,CAAC,EAAE;IAClG,MAAM,EAAE;QACN,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,wBAAwB,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE;QACrH,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,yBAAyB,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE;QACvH;YACE,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,CAAC,GAAG,iBAAiB,CAAC;YAC/B,UAAU,EAAE,IAAI;YAChB,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,wBAAwB,CAAC,EAAE;SACpD;KACF;CACF,CAAC,CAAA","sourcesContent":["import { collectionDefSchema } from '@elytracms/core/cms-core'\nimport type { CollectionDef, DocumentTitleFn } from '@elytracms/core/cms-core'\nimport { roleOptions } from '@elytracms/core/persistence'\nimport { msgLabel } from '@elytracms/studio/lib/i18n'\n\n/**\n * EC-396 — the display-title fallback shared by the users rail and the account lens\n * (`selfAccountCollectionDef`): a member's **name**, else their **email** — never the\n * raw internal id when an email exists. A function-pattern `titleField` (config-as-code,\n * studio-evaluated), so list rows, breadcrumbs, relation labels, and the account header\n * resolve identity the same way. Returns `''` when NEITHER is present, so\n * `resolveDocumentTitle` degrades to the document id (its `fallbackId`) — the only case a\n * raw id ever surfaces.\n */\nexport const userTitleFallback: DocumentTitleFn = (values) => {\n const name = typeof values.name === 'string' ? values.name.trim() : ''\n if (name) return name\n return typeof values.email === 'string' ? values.email.trim() : ''\n}\n\n/**\n * The built-in **users** field lens (EC-240, AD-19). Users are a separate rail —\n * NOT a `cmsDocuments` collection (`collectionKindSchema` is `document|asset`) —\n * so this `CollectionDef` exists only to drive the portable list/edit surfaces\n * over the users {@link CollectionViewSource}. The rail's *user-ness* comes from\n * the source's capabilities (admin-only, no publish/versions/create), never the\n * schema `kind`.\n *\n * Identity fields (`name`, `email`) are **auth-owned** and read-only here — an\n * admin manages a member's *role* (and, later, project-declared custom fields\n * over the `meta` bag, mirroring assets); the person edits their own name/email\n * via account settings. A v1 default; a project may extend the field set later.\n */\n// Derived from the ONE role policy (EC-240c) — no hardcoded duplicate of the role\n// set. Reflects the shipped default today; a project's custom policy flows through.\nexport const USER_ROLE_OPTIONS = roleOptions()\n\nexport const usersCollectionDef: CollectionDef = collectionDefSchema.parse({\n id: 'user',\n // Users aren't a CMS `kind`; 'document' just satisfies the lens schema. The\n // source's capabilities (adminOnly, no publish/versions/create) carry the semantics.\n kind: 'document',\n // EC-396: name → email → id (never the raw id when an email exists).\n titleField: userTitleFallback,\n // EC-241: built-in rail labels resolve through the message catalog (msgLabel) — a\n // consumer's translation catalog localizes them without a per-rail override.\n form: { label: msgLabel('users.rail.label'), labelSingular: msgLabel('users.rail.labelSingular') },\n fields: [\n { name: 'name', type: 'text', form: { label: msgLabel('users.field.name.label'), readOnly: true }, filterable: true },\n { name: 'email', type: 'text', form: { label: msgLabel('users.field.email.label'), readOnly: true }, filterable: true },\n {\n name: 'role',\n type: 'select',\n options: [...USER_ROLE_OPTIONS],\n filterable: true,\n form: { label: msgLabel('users.field.role.label') },\n },\n ],\n})\n"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@elytracms/studio",
3
3
  "author": "finaldream <hi@finaldream.de>",
4
- "version": "0.0.61",
4
+ "version": "0.0.62",
5
5
  "private": false,
6
6
  "description": "The Elytra studio engine (UI + lib + routes + data-only Convex schema) as an importable package. Extraction staged in EXTRACTION.md (EC-223); Stages 1-2 = UI primitives + router-free lib leaves.",
7
7
  "license": "MIT",
@@ -142,7 +142,7 @@
142
142
  "lucide-react": "^1.23.0",
143
143
  "tailwind-merge": "^3.6.0",
144
144
  "zod": "^4.4.3",
145
- "@elytracms/core": "^0.0.61"
145
+ "@elytracms/core": "^0.0.62"
146
146
  },
147
147
  "peerDependencies": {
148
148
  "@tanstack/react-router": "^1.170.0",