@elytracms/studio 0.0.87 → 0.0.90

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (164) hide show
  1. package/convex/asset-revalidate.test.ts +35 -42
  2. package/convex/delivery.ts +8 -11
  3. package/convex/document-revalidate.test.ts +175 -0
  4. package/convex/redirect-revalidate.test.ts +36 -36
  5. package/convex/revalidate.ts +18 -30
  6. package/convex/triggers.ts +47 -38
  7. package/convex/webhooks.ts +77 -62
  8. package/dist/components/account/self-account-view-source.js +2 -1
  9. package/dist/components/account/self-account-view-source.js.map +1 -1
  10. package/dist/components/ai/ai-chat-panel.js +9 -2
  11. package/dist/components/ai/ai-chat-panel.js.map +1 -1
  12. package/dist/components/app-views/default-modules.js +3 -3
  13. package/dist/components/app-views/default-modules.js.map +1 -1
  14. package/dist/components/assets/asset-grid.js +1 -1
  15. package/dist/components/assets/asset-grid.js.map +1 -1
  16. package/dist/components/assets/assets-collection-view-source.js +3 -2
  17. package/dist/components/assets/assets-collection-view-source.js.map +1 -1
  18. package/dist/components/assets/assets-store.d.ts +11 -1
  19. package/dist/components/assets/assets-store.js +10 -0
  20. package/dist/components/assets/assets-store.js.map +1 -1
  21. package/dist/components/assets/focal-point.d.ts +0 -7
  22. package/dist/components/assets/focal-point.js +0 -10
  23. package/dist/components/assets/focal-point.js.map +1 -1
  24. package/dist/components/builder/canvas-render-context.d.ts +12 -1
  25. package/dist/components/builder/canvas-render-context.js +20 -16
  26. package/dist/components/builder/canvas-render-context.js.map +1 -1
  27. package/dist/components/builder/reuse-context.js +2 -2
  28. package/dist/components/builder/reuse-context.js.map +1 -1
  29. package/dist/components/builder/reuse.d.ts +6 -2
  30. package/dist/components/builder/reuse.js +12 -7
  31. package/dist/components/builder/reuse.js.map +1 -1
  32. package/dist/components/cms/asset-field.d.ts +2 -1
  33. package/dist/components/cms/asset-field.js +27 -59
  34. package/dist/components/cms/asset-field.js.map +1 -1
  35. package/dist/components/cms/asset-picker-core.d.ts +77 -40
  36. package/dist/components/cms/asset-picker-core.js +73 -46
  37. package/dist/components/cms/asset-picker-core.js.map +1 -1
  38. package/dist/components/cms/asset-picker.d.ts +48 -19
  39. package/dist/components/cms/asset-picker.js +69 -30
  40. package/dist/components/cms/asset-picker.js.map +1 -1
  41. package/dist/components/cms/cell-edit.d.ts +3 -7
  42. package/dist/components/cms/cell-edit.js +7 -25
  43. package/dist/components/cms/cell-edit.js.map +1 -1
  44. package/dist/components/cms/cms-store.d.ts +3 -2
  45. package/dist/components/cms/cms-store.js +38 -47
  46. package/dist/components/cms/cms-store.js.map +1 -1
  47. package/dist/components/cms/collection-table.js +14 -4
  48. package/dist/components/cms/collection-table.js.map +1 -1
  49. package/dist/components/cms/detail-editor.js +20 -21
  50. package/dist/components/cms/detail-editor.js.map +1 -1
  51. package/dist/components/cms/duplicate-document.d.ts +3 -2
  52. package/dist/components/cms/duplicate-document.js +5 -5
  53. package/dist/components/cms/duplicate-document.js.map +1 -1
  54. package/dist/components/cms/editable-cell.js +3 -6
  55. package/dist/components/cms/editable-cell.js.map +1 -1
  56. package/dist/components/cms/field-control.js +7 -6
  57. package/dist/components/cms/field-control.js.map +1 -1
  58. package/dist/components/cms/gate-validation.d.ts +27 -2
  59. package/dist/components/cms/gate-validation.js +19 -1
  60. package/dist/components/cms/gate-validation.js.map +1 -1
  61. package/dist/components/cms/publish-clean.d.ts +13 -6
  62. package/dist/components/cms/publish-clean.js +14 -6
  63. package/dist/components/cms/publish-clean.js.map +1 -1
  64. package/dist/components/cms/rich-text/editor.js +2 -2
  65. package/dist/components/cms/rich-text/editor.js.map +1 -1
  66. package/dist/components/cms/rich-text/insert.d.ts +7 -0
  67. package/dist/components/cms/rich-text/insert.js +7 -0
  68. package/dist/components/cms/rich-text/insert.js.map +1 -1
  69. package/dist/components/cms/use-publish-controller.js +3 -2
  70. package/dist/components/cms/use-publish-controller.js.map +1 -1
  71. package/dist/components/components/transfer-dialog.js +1 -0
  72. package/dist/components/components/transfer-dialog.js.map +1 -1
  73. package/dist/components/components/transfer.d.ts +10 -4
  74. package/dist/components/components/transfer.js +11 -9
  75. package/dist/components/components/transfer.js.map +1 -1
  76. package/dist/components/delivery-health-banner.d.ts +3 -2
  77. package/dist/components/delivery-health-banner.js +3 -2
  78. package/dist/components/delivery-health-banner.js.map +1 -1
  79. package/dist/components/nav-issues.d.ts +23 -3
  80. package/dist/components/nav-issues.js +38 -2
  81. package/dist/components/nav-issues.js.map +1 -1
  82. package/dist/components/redirects/redirects-collection-view-source.js +3 -2
  83. package/dist/components/redirects/redirects-collection-view-source.js.map +1 -1
  84. package/dist/components/section-nav.js +26 -2
  85. package/dist/components/section-nav.js.map +1 -1
  86. package/dist/components/users/users-collection-view-source.js +2 -1
  87. package/dist/components/users/users-collection-view-source.js.map +1 -1
  88. package/dist/lib/ai/apply-content.js +32 -28
  89. package/dist/lib/ai/apply-content.js.map +1 -1
  90. package/dist/lib/ai/tools.d.ts +10 -4
  91. package/dist/lib/ai/tools.js +12 -4
  92. package/dist/lib/ai/tools.js.map +1 -1
  93. package/dist/lib/ai/translate-document.js +11 -7
  94. package/dist/lib/ai/translate-document.js.map +1 -1
  95. package/dist/lib/ai/types.d.ts +9 -5
  96. package/dist/lib/ai/types.js.map +1 -1
  97. package/dist/lib/ai/update-fields.js +10 -29
  98. package/dist/lib/ai/update-fields.js.map +1 -1
  99. package/dist/lib/ai/vite.d.ts +22 -2
  100. package/dist/lib/ai/vite.js +12 -3
  101. package/dist/lib/ai/vite.js.map +1 -1
  102. package/dist/lib/cms/published-documents.d.ts +14 -3
  103. package/dist/lib/cms/published-documents.js +12 -3
  104. package/dist/lib/cms/published-documents.js.map +1 -1
  105. package/dist/lib/cms-views/view-model.d.ts +4 -4
  106. package/dist/lib/cms-views/view-model.js +16 -13
  107. package/dist/lib/cms-views/view-model.js.map +1 -1
  108. package/dist/lib/data/collection-content.d.ts +22 -1
  109. package/dist/lib/data/collection-content.js +51 -14
  110. package/dist/lib/data/collection-content.js.map +1 -1
  111. package/dist/lib/delivery/instant-publish.d.ts +0 -3
  112. package/dist/lib/delivery/instant-publish.js +0 -1
  113. package/dist/lib/delivery/instant-publish.js.map +1 -1
  114. package/dist/lib/engine-version.d.ts +3 -3
  115. package/dist/lib/engine-version.js +3 -3
  116. package/dist/lib/engine-version.js.map +1 -1
  117. package/dist/lib/i18n/messages.de.js +5 -0
  118. package/dist/lib/i18n/messages.de.js.map +1 -1
  119. package/dist/lib/i18n/messages.en.d.ts +4 -0
  120. package/dist/lib/i18n/messages.en.js +6 -0
  121. package/dist/lib/i18n/messages.en.js.map +1 -1
  122. package/dist/lib/locale/fallback.d.ts +5 -19
  123. package/dist/lib/locale/fallback.js +18 -39
  124. package/dist/lib/locale/fallback.js.map +1 -1
  125. package/dist/lib/locale/locale-store.js +2 -1
  126. package/dist/lib/locale/locale-store.js.map +1 -1
  127. package/dist/lib/operations/session-hydration.d.ts +24 -26
  128. package/dist/lib/operations/session-hydration.js +15 -53
  129. package/dist/lib/operations/session-hydration.js.map +1 -1
  130. package/dist/lib/operations/studio-operations.js +4 -6
  131. package/dist/lib/operations/studio-operations.js.map +1 -1
  132. package/dist/lib/persistence/backend-mode.d.ts +5 -25
  133. package/dist/lib/persistence/backend-mode.js +13 -30
  134. package/dist/lib/persistence/backend-mode.js.map +1 -1
  135. package/dist/lib/references/reference-model.d.ts +17 -3
  136. package/dist/lib/references/reference-model.js +17 -23
  137. package/dist/lib/references/reference-model.js.map +1 -1
  138. package/dist/lib/references/references-store.js +7 -0
  139. package/dist/lib/references/references-store.js.map +1 -1
  140. package/dist/lib/workspace/config-issues.d.ts +6 -0
  141. package/dist/lib/workspace/config-issues.js +8 -4
  142. package/dist/lib/workspace/config-issues.js.map +1 -1
  143. package/dist/lib/workspace/index.d.ts +1 -8
  144. package/dist/lib/workspace/index.js +1 -10
  145. package/dist/lib/workspace/index.js.map +1 -1
  146. package/dist/router/navigate-once.d.ts +19 -4
  147. package/dist/router/navigate-once.js +22 -5
  148. package/dist/router/navigate-once.js.map +1 -1
  149. package/package.json +3 -7
  150. package/dist/lib/webhooks/config.d.ts +0 -15
  151. package/dist/lib/webhooks/config.js +0 -65
  152. package/dist/lib/webhooks/config.js.map +0 -1
  153. package/dist/lib/webhooks/dispatcher.d.ts +0 -70
  154. package/dist/lib/webhooks/dispatcher.js +0 -61
  155. package/dist/lib/webhooks/dispatcher.js.map +0 -1
  156. package/dist/lib/webhooks/index.d.ts +0 -11
  157. package/dist/lib/webhooks/index.js +0 -10
  158. package/dist/lib/webhooks/index.js.map +0 -1
  159. package/dist/lib/webhooks/signing.d.ts +0 -15
  160. package/dist/lib/webhooks/signing.js +0 -24
  161. package/dist/lib/webhooks/signing.js.map +0 -1
  162. package/dist/lib/webhooks/tags.d.ts +0 -25
  163. package/dist/lib/webhooks/tags.js +0 -289
  164. package/dist/lib/webhooks/tags.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"reuse.js","sourceRoot":"","sources":["../../../src/components/builder/reuse.ts"],"names":[],"mappings":"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","sourcesContent":["import { manifestPropFields } from '@elytracms/core/component-registry'\nimport type { ComponentManifest } from '@elytracms/core/component-registry'\nimport { readDocumentRef, seedDefaults, titleFieldOf } from '@elytracms/core/cms-core'\nimport type { CollectionDef, DocumentRef } from '@elytracms/core/cms-core'\nimport { editBlockRoots, isStaticProp } from '@elytracms/core/project-graph'\nimport type { ComponentNode } from '@elytracms/core/project-graph'\nimport type { CustomizationIssue, CustomizationMessageRef } from '@elytracms/core/studio-core'\nimport { slugify } from '@elytracms/studio/lib/utils'\n\n/**\n * The reuse capability (EC-282): the engine-generic description of how a project\n * models reusable elements. **AD-a (EC-467): reusable elements are SINGULAR per\n * project** — exactly one embed component and exactly one reusable collection.\n * Multiple reuse collections are deliberately not supported, which is why this is\n * one value and never a list.\n *\n * The capability is normally DERIVED from config (manifests + collections) — no\n * declaration needed, so a consumer that already has a transcluding embed gets the\n * in-canvas gestures for free (the EC-296 \"works automatically\" pattern). The\n * derivation predicate for a \"reusable embed\" component: it contributes no slots of\n * its own and has exactly ONE `relation` prop (cardinality `one`) whose target\n * collection holds a `blocks` field — i.e. it embeds a single document and renders\n * that document's composition by transclusion (EC-254/EC-281). For Konditorei that\n * is uniquely `project.Reusable` → `element` → `reusable.content`.\n *\n * EC-462 — the predicate additionally DISQUALIFIES a candidate whose own manifest\n * explicitly declares its relation prop to be something other than an embed\n * pointer. The shape test alone proved unsound the moment a relation prop's\n * target collection gains a `blocks` field of its own (a category/page with a\n * canvas — a completely natural content shape): the EC-276 sprint's ProductGrid\n * and the demo's OptionBox were both claimed as \"the embed\", so their ordinary\n * relation picks rendered the reusable-element placeholder, grew Detach/Make-\n * reusable gestures, and fed embed-worded publish-integrity issues. Two\n * explicit-declaration disqualifiers close the observed class:\n *\n * - a manifest with a `listing` declaration is a QUERY host (EC-255) — its\n * relation prop is the query's filter source (`listing.filter.fromProp`),\n * never a transclusion pointer;\n * - a relation prop with `form.hidden` (predicate or `true`) is conditional/\n * auxiliary — EC-394 strips a hidden relation's ref at publish, so it cannot\n * coherently be what the component transcludes.\n *\n * Both read only the CANDIDATE manifest's own declarations (never the target's\n * contents) and can only EXCLUDE, so a genuine pure embed (which declares\n * neither) is always kept. EC-462's residual — a bare unconditional\n * single-relation display component (e.g. a page teaser) is at rest\n * indistinguishable from a genuine embed — is closed by EC-467's optional\n * {@link StudioReuseConfig} declaration: a project that hits ambiguity declares\n * its way out, and the ambiguity is LOUD instead of silently disabling the\n * gestures. See {@link resolveReuse}.\n */\nexport interface ReuseCapability {\n /** The embed component, e.g. `project.Reusable`. */\n embedComponentId: string\n /** The embed's relation prop that points at the reusable document, e.g. `element`. */\n relationProp: string\n /** The collection reusable documents live in, e.g. `reusable`. */\n collectionId: string\n /** The collection's composition field the embed transcludes, e.g. `content`. */\n contentField: string\n /** Whether that field holds a single block or a list. */\n contentCardinality: 'one' | 'many'\n}\n\n/**\n * EC-467 (AD-b) — the OPTIONAL studio-config declaration of the reuse embed:\n * `defineStudio({ reuse: { component: 'project.Reusable' } })`. See\n * {@link import('../app-views/studio-config').StudioConfig.reuse} for the\n * consumer-facing contract; this is the shape {@link resolveReuse} consumes.\n *\n * One identifier is enough because the rest DERIVES from it (its single-document\n * relation prop → that prop's target collection → that collection's `blocks`\n * field), and because reusable elements are singular per project (AD-a).\n */\nexport interface StudioReuseConfig {\n /** The embed component's registry id, e.g. `'project.Reusable'`. */\n component: string\n /**\n * Which relation prop points at the reusable document. Needed ONLY when the\n * declared component legitimately has more than one single-document relation\n * prop — otherwise the sole one is derived.\n */\n relationProp?: string\n}\n\n/**\n * EC-467 — the resolution of the project's reuse capability: the capability (or\n * `null`), plus the config issues a developer must SEE.\n *\n * The issues ride the EC-236 {@link CustomizationIssue} surface (`point: 'reuse'`),\n * exactly like the sidebar's and the field renderers' — so they render at the\n * studio rail foot on every surface, which is the studio's home for\n * project-CONFIGURATION problems (the document-scoped Issues pane is the wrong\n * home: this is not a content problem in one document).\n *\n * EC-478: each issue carries a `messageRef` (catalog key + identifier values)\n * alongside its prebuilt English, so the rail foot renders it in the active UI\n * locale. This module stays pure — it names a key, it never calls `translate`;\n * `lib/persona/labels.ts` resolves it at the render site. That is what makes the\n * whole list localize together, which is the mixed-language outcome EC-467\n * declined to create by localizing only its own half.\n */\nexport interface ReuseResolution {\n capability: ReuseCapability | null\n /** Empty whenever the capability is unambiguous — including \"this project has none\". */\n issues: CustomizationIssue[]\n}\n\n/** The customization point name stamped onto every reuse issue (EC-236 locator). */\nconst REUSE_POINT = 'reuse'\n\n/** A `relation` prop of a manifest, normalized for both resolution paths. */\ninterface RelationProp {\n name: string\n /** The target collection id; `undefined` when the manifest declares none. */\n target: string | undefined\n multiple: boolean\n /** EC-462: `form.hidden` present and not literally `false` (predicate or `true`). */\n conditional: boolean\n}\n\n/** Every `relation` prop of a manifest, in declaration order. */\nfunction relationPropsOf(manifest: ComponentManifest): RelationProp[] {\n const props: RelationProp[] = []\n for (const field of manifestPropFields(manifest)) {\n if (field.type !== 'relation') continue\n const hidden = field.form?.hidden\n props.push({\n name: field.name,\n target: field.target,\n multiple: field.multiple === true,\n conditional: hidden !== undefined && hidden !== false,\n })\n }\n return props\n}\n\n/**\n * The relation props a DECLARED embed could plausibly transclude through: targeted\n * and cardinality-one. A cardinality-`many` (or targetless) relation is excluded\n * structurally, not heuristically — an embed transcludes exactly one document and\n * {@link makeEmbedNode} writes exactly one `{collection,id}`, which a list-valued\n * or target-less prop cannot hold.\n */\nfunction embeddableRelationProps(manifest: ComponentManifest): TargetedRelationProp[] {\n return relationPropsOf(manifest).filter(\n (r): r is TargetedRelationProp => r.target !== undefined && !r.multiple,\n )\n}\ntype TargetedRelationProp = RelationProp & { target: string }\n\n/** The collection's first composition field — what an embed of it transcludes. */\nfunction blocksFieldOf(collection: CollectionDef) {\n const field = collection.fields.find((f) => f.type === 'blocks')\n return field?.type === 'blocks' ? field : undefined\n}\n\n/**\n * The catalog keys for the reuse config issues (EC-478), in the `builder.reuse.config.*`\n * namespace. Named here so the producer and the catalog-parity test agree on one list.\n */\nconst REUSE_KEYS = {\n unknownComponent: 'builder.reuse.config.unknownComponent',\n noRelationProp: 'builder.reuse.config.noRelationProp',\n unknownRelationProp: 'builder.reuse.config.unknownRelationProp',\n ambiguousRelationProp: 'builder.reuse.config.ambiguousRelationProp',\n unknownCollection: 'builder.reuse.config.unknownCollection',\n noBlocksField: 'builder.reuse.config.noBlocksField',\n ambiguousInference: 'builder.reuse.config.ambiguousInference',\n} as const\n\nfunction reuseIssue(\n code: CustomizationIssue['code'],\n severity: CustomizationIssue['severity'],\n message: string,\n messageRef: CustomizationMessageRef,\n id?: string,\n): CustomizationIssue {\n return {\n code,\n severity,\n message,\n messageRef,\n point: REUSE_POINT,\n ...(id === undefined ? {} : { id }),\n }\n}\n\n/** `\"a\", \"b\"` — a readable list of prop/component ids for an issue message. */\nfunction quotedList(names: readonly string[]): string {\n return names.map((name) => `\"${name}\"`).join(', ')\n}\n\n/**\n * The components whose SHAPE makes them plausible reuse embeds (the zero-config\n * heuristic + EC-462's disqualifiers), in manifest order. Exported for the tests\n * and for issue copy; the resolution rule lives in {@link resolveReuse}.\n */\nexport function inferredEmbedCandidates(\n manifests: readonly ComponentManifest[],\n collections: readonly CollectionDef[],\n): ReuseCapability[] {\n const matches: ReuseCapability[] = []\n for (const manifest of manifests) {\n if (manifest.slots.length > 0) continue\n // EC-462: a `listing` manifest is a query host by its own declaration — its\n // relation prop is the filter source, never an embed pointer.\n if (manifest.listing) continue\n // The heuristic requires the relation to be UNAMBIGUOUS within the manifest:\n // exactly ONE relation prop of any kind, which must then be targeted,\n // cardinality-one and unconditional. (Counting all relation props — not just\n // the embeddable ones — is deliberate: a component with a second, list-valued\n // relation is not the single-purpose shape the heuristic is allowed to claim.)\n const relations = relationPropsOf(manifest)\n const relation = relations.length === 1 ? relations[0]! : undefined\n if (!relation || relation.target === undefined || relation.multiple || relation.conditional)\n continue\n const collection = collections.find((c) => c.id === relation.target)\n if (!collection) continue\n const blocksField = blocksFieldOf(collection)\n if (!blocksField) continue\n matches.push({\n embedComponentId: manifest.id,\n relationProp: relation.name,\n collectionId: collection.id,\n contentField: blocksField.name,\n contentCardinality: blocksField.multiple === false ? 'one' : 'many',\n })\n }\n return matches\n}\n\n/**\n * Resolve the DECLARED embed (EC-467, AD-b). An explicit declaration is the\n * developer's authority, so the inference disqualifiers are NOT applied here: a\n * declared component may have slots, may declare `listing`, and its relation prop\n * may be `form.hidden` — those exist to stop the engine GUESSING, not to overrule\n * a stated intent. Only structural impossibilities are issues (see AD-c row 2):\n * an unknown component id, no usable relation prop (or an ambiguous set with no\n * `relationProp`), an undefined target collection, or a target with no `blocks`\n * field — each of which would leave nothing coherent to transclude.\n */\nfunction resolveDeclaredReuse(\n declaration: StudioReuseConfig,\n manifests: readonly ComponentManifest[],\n collections: readonly CollectionDef[],\n): ReuseResolution {\n const { component, relationProp } = declaration\n const fail = (message: string, messageRef: CustomizationMessageRef): ReuseResolution => ({\n capability: null,\n issues: [reuseIssue('invalid-override', 'error', message, messageRef, component)],\n })\n\n const manifest = manifests.find((m) => m.id === component)\n if (!manifest) {\n return {\n capability: null,\n issues: [\n reuseIssue(\n 'unknown-override-target',\n 'error',\n `Reusable elements: studio config declares reuse.component \"${component}\", which is not a registered component. Fix the id, or remove the reuse block to let the studio infer the embed.`,\n { key: REUSE_KEYS.unknownComponent, vars: { component } },\n component,\n ),\n ],\n }\n }\n\n const relations = embeddableRelationProps(manifest)\n if (relations.length === 0) {\n return fail(\n `Reusable elements: the declared embed \"${component}\" has no single-document relation prop, so it cannot point at a reusable document. An embed needs one relation prop with cardinality one.`,\n { key: REUSE_KEYS.noRelationProp, vars: { component } },\n )\n }\n const names = relations.map((r) => r.name)\n let relation: TargetedRelationProp\n if (relationProp !== undefined) {\n const named = relations.find((r) => r.name === relationProp)\n if (!named) {\n return fail(\n `Reusable elements: the declared embed \"${component}\" has no single-document relation prop named \"${relationProp}\". Its relation props are: ${quotedList(names)}.`,\n {\n key: REUSE_KEYS.unknownRelationProp,\n vars: { component, relationProp, props: quotedList(names) },\n },\n )\n }\n relation = named\n } else if (relations.length === 1) {\n relation = relations[0]!\n } else {\n return fail(\n `Reusable elements: the declared embed \"${component}\" has ${relations.length} single-document relation props (${quotedList(names)}) — add reuse.relationProp to say which one points at the reusable document.`,\n {\n key: REUSE_KEYS.ambiguousRelationProp,\n vars: { component, count: relations.length, props: quotedList(names) },\n },\n )\n }\n\n const collection = collections.find((c) => c.id === relation.target)\n if (!collection) {\n return fail(\n `Reusable elements: \"${component}.${relation.name}\" targets the collection \"${relation.target}\", which this project does not define.`,\n {\n key: REUSE_KEYS.unknownCollection,\n vars: { component, relationProp: relation.name, collection: relation.target },\n },\n )\n }\n const blocksField = blocksFieldOf(collection)\n if (!blocksField) {\n return fail(\n `Reusable elements: the collection \"${collection.id}\" behind \"${component}.${relation.name}\" has no blocks (composition) field, so there is nothing for the embed to transclude.`,\n {\n key: REUSE_KEYS.noBlocksField,\n vars: { component, relationProp: relation.name, collection: collection.id },\n },\n )\n }\n\n return {\n capability: {\n embedComponentId: manifest.id,\n relationProp: relation.name,\n collectionId: collection.id,\n contentField: blocksField.name,\n contentCardinality: blocksField.multiple === false ? 'one' : 'many',\n },\n issues: [],\n }\n}\n\n/**\n * Resolve the project's ONE reuse capability (AD-a) plus any config issues —\n * EC-467's full matrix (AD-c):\n *\n * | Declaration | Candidates | Result |\n * | --- | --- | --- |\n * | present, resolves | any | the declared capability, no issue |\n * | present, does not resolve | any | `null` + an ERROR issue (misconfiguration) |\n * | absent | exactly 1 | that candidate, no issue (the zero-config default) |\n * | absent | 2 or more | `null` + a WARNING issue naming every candidate |\n * | absent | 0 | `null`, **no issue** — a project need not use reusable elements |\n *\n * Severity is the honest split: a declaration that cannot resolve is broken\n * config the developer asked for (error); inference ambiguity is nothing broken —\n * the engine declines to guess and asks for one line (warning). Both disable the\n * gestures, and both render identically in the rail's issue list.\n *\n * The zero-candidate row is deliberately silent: crying wolf at every project\n * that has no reusable elements would train developers to ignore the surface.\n */\nexport function resolveReuse(\n manifests: readonly ComponentManifest[],\n collections: readonly CollectionDef[],\n declaration?: StudioReuseConfig,\n): ReuseResolution {\n if (declaration) return resolveDeclaredReuse(declaration, manifests, collections)\n\n const candidates = inferredEmbedCandidates(manifests, collections)\n if (candidates.length === 1) return { capability: candidates[0]!, issues: [] }\n if (candidates.length === 0) return { capability: null, issues: [] }\n return {\n capability: null,\n issues: [\n reuseIssue(\n 'ambiguous-inference',\n 'warning',\n `Reusable elements: ${candidates.length} components have the reusable-embed shape (${quotedList(\n candidates.map((c) => c.embedComponentId),\n )}). A project has exactly one reusable-element embed, so the studio will not guess — declare it in studio.config as reuse: { component: '<id>' }. Until then the reuse gestures are off.`,\n {\n key: REUSE_KEYS.ambiguousInference,\n vars: {\n count: candidates.length,\n components: quotedList(candidates.map((c) => c.embedComponentId)),\n },\n },\n ),\n ],\n }\n}\n\n/**\n * The project's reuse capability alone, or `null` when it is absent, ambiguous, or\n * misdeclared — for callers that act on the capability and do not surface config\n * issues themselves (the publish-integrity walk). The issues are surfaced ONCE, by\n * the shell rail; see {@link resolveReuse}.\n */\nexport function resolveReuseCapability(\n manifests: readonly ComponentManifest[],\n collections: readonly CollectionDef[],\n declaration?: StudioReuseConfig,\n): ReuseCapability | null {\n return resolveReuse(manifests, collections, declaration).capability\n}\n\n/** A slug for `name`, suffixed `-2`/`-3`/… until it is free among `taken`. */\nexport function uniqueSlug(name: string, taken: ReadonlySet<string>): string {\n const base = slugify(name) || 'element'\n let id = base\n for (let n = 2; taken.has(id); n += 1) id = `${base}-${n}`\n return id\n}\n\n/**\n * The values for a new reusable document promoted from `node` (EC-282): seeded\n * defaults, the given `name` on the title field, the node on the content field,\n * and a collision-safe slug on any UNIQUE text field (e.g. Konditorei's\n * `internalId`) so the created document is publish-valid without hardcoding that\n * field's name. Other required fields, if any, are completed by the editor when\n * they open the new reusable.\n */\nexport function buildReusableValues(\n collection: CollectionDef,\n capability: ReuseCapability,\n name: string,\n node: ComponentNode,\n existing: readonly { values?: Record<string, unknown> }[],\n): Record<string, unknown> {\n const values: Record<string, unknown> = { ...seedDefaults(collection) }\n const titleField = titleFieldOf(collection)?.name\n if (titleField) values[titleField] = name\n for (const field of collection.fields) {\n if (\n field.type === 'text' &&\n field.validation?.unique === true &&\n field.name !== titleField &&\n field.name !== capability.contentField\n ) {\n const taken = new Set(\n existing\n .map((d) => d.values?.[field.name])\n .filter((v): v is string => typeof v === 'string'),\n )\n values[field.name] = uniqueSlug(name, taken)\n }\n }\n // EC-576: the converged v2 sequence holding the tagged node, through the core\n // write path — never the pre-0.0.84 bare node/array. Cardinality is a\n // constraint over that one shape, not a second shape (EC-500 D2).\n values[capability.contentField] = editBlockRoots(undefined, () => [node])\n return values\n}\n\n/**\n * The document a reuse-embed `node` targets (EC-300), or null when `node` is not the\n * embed component or has no static pick yet. Lets the canvas detect a HARD reference\n * whose target doesn't resolve (→ placeholder) without reaching into prop internals.\n */\nexport function reuseEmbedRef(node: ComponentNode, capability: ReuseCapability): DocumentRef | null {\n if (node.componentId !== capability.embedComponentId) return null\n const prop = node.props?.[capability.relationProp]\n const ref = isStaticProp(prop) ? readDocumentRef(prop.value) : undefined\n return ref ?? null\n}\n\n/**\n * Every reuse-embed target ref in a composition `blocks` value, at any depth\n * (EC-300) — the hard references a publish-integrity check walks to find\n * unpublished targets. Recurses slots; order is document order. Duplicates are\n * kept (the caller dedupes by key if needed).\n */\nexport function collectEmbedRefs(\n blocks: readonly ComponentNode[],\n capability: ReuseCapability,\n): DocumentRef[] {\n const refs: DocumentRef[] = []\n const visit = (node: ComponentNode): void => {\n const ref = reuseEmbedRef(node, capability)\n if (ref) refs.push(ref)\n for (const children of Object.values(node.slots ?? {})) for (const child of children) visit(child)\n }\n for (const node of blocks) visit(node)\n return refs\n}\n\n/** The `Reusable` embed node referencing the new document (id minted by the action). */\nexport function makeEmbedNode(capability: ReuseCapability, documentId: string): ComponentNode {\n return {\n id: 'reuse-embed',\n componentId: capability.embedComponentId,\n props: {\n [capability.relationProp]: {\n kind: 'static',\n value: { collection: capability.collectionId, id: documentId },\n },\n },\n }\n}\n"]}
1
+ {"version":3,"file":"reuse.js","sourceRoot":"","sources":["../../../src/components/builder/reuse.ts"],"names":[],"mappings":"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","sourcesContent":["import { manifestPropFields } from '@elytracms/core/component-registry'\nimport type { ComponentManifest } from '@elytracms/core/component-registry'\nimport { readDocumentRef, seedDefaults, titleFieldOf, writeField } from '@elytracms/core/cms-core'\nimport type { CmsDocument, CollectionDef, DocumentRef, LocaleConfig } from '@elytracms/core/cms-core'\nimport { editBlockRoots, isStaticProp } from '@elytracms/core/project-graph'\nimport type { ComponentNode } from '@elytracms/core/project-graph'\nimport type { CustomizationIssue, CustomizationMessageRef } from '@elytracms/core/studio-core'\nimport { slugify } from '@elytracms/studio/lib/utils'\n\n/**\n * The reuse capability (EC-282): the engine-generic description of how a project\n * models reusable elements. **AD-a (EC-467): reusable elements are SINGULAR per\n * project** — exactly one embed component and exactly one reusable collection.\n * Multiple reuse collections are deliberately not supported, which is why this is\n * one value and never a list.\n *\n * The capability is normally DERIVED from config (manifests + collections) — no\n * declaration needed, so a consumer that already has a transcluding embed gets the\n * in-canvas gestures for free (the EC-296 \"works automatically\" pattern). The\n * derivation predicate for a \"reusable embed\" component: it contributes no slots of\n * its own and has exactly ONE `relation` prop (cardinality `one`) whose target\n * collection holds a `blocks` field — i.e. it embeds a single document and renders\n * that document's composition by transclusion (EC-254/EC-281). For Konditorei that\n * is uniquely `project.Reusable` → `element` → `reusable.content`.\n *\n * EC-462 — the predicate additionally DISQUALIFIES a candidate whose own manifest\n * explicitly declares its relation prop to be something other than an embed\n * pointer. The shape test alone proved unsound the moment a relation prop's\n * target collection gains a `blocks` field of its own (a category/page with a\n * canvas — a completely natural content shape): the EC-276 sprint's ProductGrid\n * and the demo's OptionBox were both claimed as \"the embed\", so their ordinary\n * relation picks rendered the reusable-element placeholder, grew Detach/Make-\n * reusable gestures, and fed embed-worded publish-integrity issues. Two\n * explicit-declaration disqualifiers close the observed class:\n *\n * - a manifest with a `listing` declaration is a QUERY host (EC-255) — its\n * relation prop is the query's filter source (`listing.filter.fromProp`),\n * never a transclusion pointer;\n * - a relation prop with `form.hidden` (predicate or `true`) is conditional/\n * auxiliary — EC-394 strips a hidden relation's ref at publish, so it cannot\n * coherently be what the component transcludes.\n *\n * Both read only the CANDIDATE manifest's own declarations (never the target's\n * contents) and can only EXCLUDE, so a genuine pure embed (which declares\n * neither) is always kept. EC-462's residual — a bare unconditional\n * single-relation display component (e.g. a page teaser) is at rest\n * indistinguishable from a genuine embed — is closed by EC-467's optional\n * {@link StudioReuseConfig} declaration: a project that hits ambiguity declares\n * its way out, and the ambiguity is LOUD instead of silently disabling the\n * gestures. See {@link resolveReuse}.\n */\nexport interface ReuseCapability {\n /** The embed component, e.g. `project.Reusable`. */\n embedComponentId: string\n /** The embed's relation prop that points at the reusable document, e.g. `element`. */\n relationProp: string\n /** The collection reusable documents live in, e.g. `reusable`. */\n collectionId: string\n /** The collection's composition field the embed transcludes, e.g. `content`. */\n contentField: string\n /** Whether that field holds a single block or a list. */\n contentCardinality: 'one' | 'many'\n}\n\n/**\n * EC-467 (AD-b) — the OPTIONAL studio-config declaration of the reuse embed:\n * `defineStudio({ reuse: { component: 'project.Reusable' } })`. See\n * {@link import('../app-views/studio-config').StudioConfig.reuse} for the\n * consumer-facing contract; this is the shape {@link resolveReuse} consumes.\n *\n * One identifier is enough because the rest DERIVES from it (its single-document\n * relation prop → that prop's target collection → that collection's `blocks`\n * field), and because reusable elements are singular per project (AD-a).\n */\nexport interface StudioReuseConfig {\n /** The embed component's registry id, e.g. `'project.Reusable'`. */\n component: string\n /**\n * Which relation prop points at the reusable document. Needed ONLY when the\n * declared component legitimately has more than one single-document relation\n * prop — otherwise the sole one is derived.\n */\n relationProp?: string\n}\n\n/**\n * EC-467 — the resolution of the project's reuse capability: the capability (or\n * `null`), plus the config issues a developer must SEE.\n *\n * The issues ride the EC-236 {@link CustomizationIssue} surface (`point: 'reuse'`),\n * exactly like the sidebar's and the field renderers' — so they render at the\n * studio rail foot on every surface, which is the studio's home for\n * project-CONFIGURATION problems (the document-scoped Issues pane is the wrong\n * home: this is not a content problem in one document).\n *\n * EC-478: each issue carries a `messageRef` (catalog key + identifier values)\n * alongside its prebuilt English, so the rail foot renders it in the active UI\n * locale. This module stays pure — it names a key, it never calls `translate`;\n * `lib/persona/labels.ts` resolves it at the render site. That is what makes the\n * whole list localize together, which is the mixed-language outcome EC-467\n * declined to create by localizing only its own half.\n */\nexport interface ReuseResolution {\n capability: ReuseCapability | null\n /** Empty whenever the capability is unambiguous — including \"this project has none\". */\n issues: CustomizationIssue[]\n}\n\n/** The customization point name stamped onto every reuse issue (EC-236 locator). */\nconst REUSE_POINT = 'reuse'\n\n/** A `relation` prop of a manifest, normalized for both resolution paths. */\ninterface RelationProp {\n name: string\n /** The target collection id; `undefined` when the manifest declares none. */\n target: string | undefined\n multiple: boolean\n /** EC-462: `form.hidden` present and not literally `false` (predicate or `true`). */\n conditional: boolean\n}\n\n/** Every `relation` prop of a manifest, in declaration order. */\nfunction relationPropsOf(manifest: ComponentManifest): RelationProp[] {\n const props: RelationProp[] = []\n for (const field of manifestPropFields(manifest)) {\n if (field.type !== 'relation') continue\n const hidden = field.form?.hidden\n props.push({\n name: field.name,\n target: field.target,\n multiple: field.multiple === true,\n conditional: hidden !== undefined && hidden !== false,\n })\n }\n return props\n}\n\n/**\n * The relation props a DECLARED embed could plausibly transclude through: targeted\n * and cardinality-one. A cardinality-`many` (or targetless) relation is excluded\n * structurally, not heuristically — an embed transcludes exactly one document and\n * {@link makeEmbedNode} writes exactly one `{collection,id}`, which a list-valued\n * or target-less prop cannot hold.\n */\nfunction embeddableRelationProps(manifest: ComponentManifest): TargetedRelationProp[] {\n return relationPropsOf(manifest).filter(\n (r): r is TargetedRelationProp => r.target !== undefined && !r.multiple,\n )\n}\ntype TargetedRelationProp = RelationProp & { target: string }\n\n/** The collection's first composition field — what an embed of it transcludes. */\nfunction blocksFieldOf(collection: CollectionDef) {\n const field = collection.fields.find((f) => f.type === 'blocks')\n return field?.type === 'blocks' ? field : undefined\n}\n\n/**\n * The catalog keys for the reuse config issues (EC-478), in the `builder.reuse.config.*`\n * namespace. Named here so the producer and the catalog-parity test agree on one list.\n */\nconst REUSE_KEYS = {\n unknownComponent: 'builder.reuse.config.unknownComponent',\n noRelationProp: 'builder.reuse.config.noRelationProp',\n unknownRelationProp: 'builder.reuse.config.unknownRelationProp',\n ambiguousRelationProp: 'builder.reuse.config.ambiguousRelationProp',\n unknownCollection: 'builder.reuse.config.unknownCollection',\n noBlocksField: 'builder.reuse.config.noBlocksField',\n ambiguousInference: 'builder.reuse.config.ambiguousInference',\n} as const\n\nfunction reuseIssue(\n code: CustomizationIssue['code'],\n severity: CustomizationIssue['severity'],\n message: string,\n messageRef: CustomizationMessageRef,\n id?: string,\n): CustomizationIssue {\n return {\n code,\n severity,\n message,\n messageRef,\n point: REUSE_POINT,\n ...(id === undefined ? {} : { id }),\n }\n}\n\n/** `\"a\", \"b\"` — a readable list of prop/component ids for an issue message. */\nfunction quotedList(names: readonly string[]): string {\n return names.map((name) => `\"${name}\"`).join(', ')\n}\n\n/**\n * The components whose SHAPE makes them plausible reuse embeds (the zero-config\n * heuristic + EC-462's disqualifiers), in manifest order. Exported for the tests\n * and for issue copy; the resolution rule lives in {@link resolveReuse}.\n */\nexport function inferredEmbedCandidates(\n manifests: readonly ComponentManifest[],\n collections: readonly CollectionDef[],\n): ReuseCapability[] {\n const matches: ReuseCapability[] = []\n for (const manifest of manifests) {\n if (manifest.slots.length > 0) continue\n // EC-462: a `listing` manifest is a query host by its own declaration — its\n // relation prop is the filter source, never an embed pointer.\n if (manifest.listing) continue\n // The heuristic requires the relation to be UNAMBIGUOUS within the manifest:\n // exactly ONE relation prop of any kind, which must then be targeted,\n // cardinality-one and unconditional. (Counting all relation props — not just\n // the embeddable ones — is deliberate: a component with a second, list-valued\n // relation is not the single-purpose shape the heuristic is allowed to claim.)\n const relations = relationPropsOf(manifest)\n const relation = relations.length === 1 ? relations[0]! : undefined\n if (!relation || relation.target === undefined || relation.multiple || relation.conditional)\n continue\n const collection = collections.find((c) => c.id === relation.target)\n if (!collection) continue\n const blocksField = blocksFieldOf(collection)\n if (!blocksField) continue\n matches.push({\n embedComponentId: manifest.id,\n relationProp: relation.name,\n collectionId: collection.id,\n contentField: blocksField.name,\n contentCardinality: blocksField.multiple === false ? 'one' : 'many',\n })\n }\n return matches\n}\n\n/**\n * Resolve the DECLARED embed (EC-467, AD-b). An explicit declaration is the\n * developer's authority, so the inference disqualifiers are NOT applied here: a\n * declared component may have slots, may declare `listing`, and its relation prop\n * may be `form.hidden` — those exist to stop the engine GUESSING, not to overrule\n * a stated intent. Only structural impossibilities are issues (see AD-c row 2):\n * an unknown component id, no usable relation prop (or an ambiguous set with no\n * `relationProp`), an undefined target collection, or a target with no `blocks`\n * field — each of which would leave nothing coherent to transclude.\n */\nfunction resolveDeclaredReuse(\n declaration: StudioReuseConfig,\n manifests: readonly ComponentManifest[],\n collections: readonly CollectionDef[],\n): ReuseResolution {\n const { component, relationProp } = declaration\n const fail = (message: string, messageRef: CustomizationMessageRef): ReuseResolution => ({\n capability: null,\n issues: [reuseIssue('invalid-override', 'error', message, messageRef, component)],\n })\n\n const manifest = manifests.find((m) => m.id === component)\n if (!manifest) {\n return {\n capability: null,\n issues: [\n reuseIssue(\n 'unknown-override-target',\n 'error',\n `Reusable elements: studio config declares reuse.component \"${component}\", which is not a registered component. Fix the id, or remove the reuse block to let the studio infer the embed.`,\n { key: REUSE_KEYS.unknownComponent, vars: { component } },\n component,\n ),\n ],\n }\n }\n\n const relations = embeddableRelationProps(manifest)\n if (relations.length === 0) {\n return fail(\n `Reusable elements: the declared embed \"${component}\" has no single-document relation prop, so it cannot point at a reusable document. An embed needs one relation prop with cardinality one.`,\n { key: REUSE_KEYS.noRelationProp, vars: { component } },\n )\n }\n const names = relations.map((r) => r.name)\n let relation: TargetedRelationProp\n if (relationProp !== undefined) {\n const named = relations.find((r) => r.name === relationProp)\n if (!named) {\n return fail(\n `Reusable elements: the declared embed \"${component}\" has no single-document relation prop named \"${relationProp}\". Its relation props are: ${quotedList(names)}.`,\n {\n key: REUSE_KEYS.unknownRelationProp,\n vars: { component, relationProp, props: quotedList(names) },\n },\n )\n }\n relation = named\n } else if (relations.length === 1) {\n relation = relations[0]!\n } else {\n return fail(\n `Reusable elements: the declared embed \"${component}\" has ${relations.length} single-document relation props (${quotedList(names)}) — add reuse.relationProp to say which one points at the reusable document.`,\n {\n key: REUSE_KEYS.ambiguousRelationProp,\n vars: { component, count: relations.length, props: quotedList(names) },\n },\n )\n }\n\n const collection = collections.find((c) => c.id === relation.target)\n if (!collection) {\n return fail(\n `Reusable elements: \"${component}.${relation.name}\" targets the collection \"${relation.target}\", which this project does not define.`,\n {\n key: REUSE_KEYS.unknownCollection,\n vars: { component, relationProp: relation.name, collection: relation.target },\n },\n )\n }\n const blocksField = blocksFieldOf(collection)\n if (!blocksField) {\n return fail(\n `Reusable elements: the collection \"${collection.id}\" behind \"${component}.${relation.name}\" has no blocks (composition) field, so there is nothing for the embed to transclude.`,\n {\n key: REUSE_KEYS.noBlocksField,\n vars: { component, relationProp: relation.name, collection: collection.id },\n },\n )\n }\n\n return {\n capability: {\n embedComponentId: manifest.id,\n relationProp: relation.name,\n collectionId: collection.id,\n contentField: blocksField.name,\n contentCardinality: blocksField.multiple === false ? 'one' : 'many',\n },\n issues: [],\n }\n}\n\n/**\n * Resolve the project's ONE reuse capability (AD-a) plus any config issues —\n * EC-467's full matrix (AD-c):\n *\n * | Declaration | Candidates | Result |\n * | --- | --- | --- |\n * | present, resolves | any | the declared capability, no issue |\n * | present, does not resolve | any | `null` + an ERROR issue (misconfiguration) |\n * | absent | exactly 1 | that candidate, no issue (the zero-config default) |\n * | absent | 2 or more | `null` + a WARNING issue naming every candidate |\n * | absent | 0 | `null`, **no issue** — a project need not use reusable elements |\n *\n * Severity is the honest split: a declaration that cannot resolve is broken\n * config the developer asked for (error); inference ambiguity is nothing broken —\n * the engine declines to guess and asks for one line (warning). Both disable the\n * gestures, and both render identically in the rail's issue list.\n *\n * The zero-candidate row is deliberately silent: crying wolf at every project\n * that has no reusable elements would train developers to ignore the surface.\n */\nexport function resolveReuse(\n manifests: readonly ComponentManifest[],\n collections: readonly CollectionDef[],\n declaration?: StudioReuseConfig,\n): ReuseResolution {\n if (declaration) return resolveDeclaredReuse(declaration, manifests, collections)\n\n const candidates = inferredEmbedCandidates(manifests, collections)\n if (candidates.length === 1) return { capability: candidates[0]!, issues: [] }\n if (candidates.length === 0) return { capability: null, issues: [] }\n return {\n capability: null,\n issues: [\n reuseIssue(\n 'ambiguous-inference',\n 'warning',\n `Reusable elements: ${candidates.length} components have the reusable-embed shape (${quotedList(\n candidates.map((c) => c.embedComponentId),\n )}). A project has exactly one reusable-element embed, so the studio will not guess — declare it in studio.config as reuse: { component: '<id>' }. Until then the reuse gestures are off.`,\n {\n key: REUSE_KEYS.ambiguousInference,\n vars: {\n count: candidates.length,\n components: quotedList(candidates.map((c) => c.embedComponentId)),\n },\n },\n ),\n ],\n }\n}\n\n/**\n * The project's reuse capability alone, or `null` when it is absent, ambiguous, or\n * misdeclared — for callers that act on the capability and do not surface config\n * issues themselves (the publish-integrity walk). The issues are surfaced ONCE, by\n * the shell rail; see {@link resolveReuse}.\n */\nexport function resolveReuseCapability(\n manifests: readonly ComponentManifest[],\n collections: readonly CollectionDef[],\n declaration?: StudioReuseConfig,\n): ReuseCapability | null {\n return resolveReuse(manifests, collections, declaration).capability\n}\n\n/** A slug for `name`, suffixed `-2`/`-3`/… until it is free among `taken`. */\nexport function uniqueSlug(name: string, taken: ReadonlySet<string>): string {\n const base = slugify(name) || 'element'\n let id = base\n for (let n = 2; taken.has(id); n += 1) id = `${base}-${n}`\n return id\n}\n\n/**\n * The values for a new reusable document promoted from `node` (EC-282): seeded\n * defaults, the given `name` on the title field, the node on the content field,\n * and a collision-safe slug on any UNIQUE text field (e.g. Konditorei's\n * `internalId`) so the created document is publish-valid without hardcoding that\n * field's name. Other required fields, if any, are completed by the editor when\n * they open the new reusable.\n *\n * EC-580: every write goes through core's `writeField` in the project's DEFAULT\n * locale — the new document is created in its default bucket, so a localized title\n * or content field lands under `localized[default]`, never on the shared bag.\n */\nexport function buildReusableValues(\n collection: CollectionDef,\n capability: ReuseCapability,\n name: string,\n node: ComponentNode,\n existing: readonly { values?: Record<string, unknown> }[],\n locales: LocaleConfig,\n): Pick<CmsDocument, 'values' | 'localized'> {\n const at = locales.default\n let doc: CmsDocument = { collection: collection.id, id: '', ...seedDefaults(collection, locales) }\n const titleField = titleFieldOf(collection)?.name\n if (titleField) doc = writeField(collection, doc, titleField, at, name)\n for (const field of collection.fields) {\n if (\n field.type === 'text' &&\n field.validation?.unique === true &&\n field.name !== titleField &&\n field.name !== capability.contentField\n ) {\n const taken = new Set(\n existing\n .map((d) => d.values?.[field.name])\n .filter((v): v is string => typeof v === 'string'),\n )\n doc = writeField(collection, doc, field.name, at, uniqueSlug(name, taken))\n }\n }\n // EC-576: the converged v2 sequence holding the tagged node, through the core\n // write path — never the pre-0.0.84 bare node/array. Cardinality is a\n // constraint over that one shape, not a second shape (EC-500 D2).\n doc = writeField(collection, doc, capability.contentField, at, editBlockRoots(undefined, () => [node]))\n return { values: doc.values, ...(doc.localized !== undefined ? { localized: doc.localized } : {}) }\n}\n\n/**\n * The document a reuse-embed `node` targets (EC-300), or null when `node` is not the\n * embed component or has no static pick yet. Lets the canvas detect a HARD reference\n * whose target doesn't resolve (→ placeholder) without reaching into prop internals.\n */\nexport function reuseEmbedRef(node: ComponentNode, capability: ReuseCapability): DocumentRef | null {\n if (node.componentId !== capability.embedComponentId) return null\n const prop = node.props?.[capability.relationProp]\n const ref = isStaticProp(prop) ? readDocumentRef(prop.value) : undefined\n return ref ?? null\n}\n\n/**\n * Every reuse-embed target ref in a composition `blocks` value, at any depth\n * (EC-300) — the hard references a publish-integrity check walks to find\n * unpublished targets. Recurses slots; order is document order. Duplicates are\n * kept (the caller dedupes by key if needed).\n */\nexport function collectEmbedRefs(\n blocks: readonly ComponentNode[],\n capability: ReuseCapability,\n): DocumentRef[] {\n const refs: DocumentRef[] = []\n const visit = (node: ComponentNode): void => {\n const ref = reuseEmbedRef(node, capability)\n if (ref) refs.push(ref)\n for (const children of Object.values(node.slots ?? {})) for (const child of children) visit(child)\n }\n for (const node of blocks) visit(node)\n return refs\n}\n\n/** The `Reusable` embed node referencing the new document (id minted by the action). */\nexport function makeEmbedNode(capability: ReuseCapability, documentId: string): ComponentNode {\n return {\n id: 'reuse-embed',\n componentId: capability.embedComponentId,\n props: {\n [capability.relationProp]: {\n kind: 'static',\n value: { collection: capability.collectionId, id: documentId },\n },\n },\n }\n}\n"]}
@@ -11,7 +11,8 @@ import type { AssetFieldDef } from '@elytracms/core/cms-core';
11
11
  * a focal-point-aware preview, uploads directly from the field (reusing the EC-152
12
12
  * upload path — no parallel uploader), and edits the selected image's alt + focal
13
13
  * point inline (persisted on the asset document, where delivery reads them, EC-230).
14
- * With no `<AssetsProvider>` (fixtures/playground) it degrades to the labelled-badge
14
+ * With no `<AssetsProvider>` (a fixture-only session — every project, the playground
15
+ * included, mounts one, `studio-project-screen.tsx`) it degrades to the labelled-badge
15
16
  * picker — no preview/upload — so it stays usable without a real library.
16
17
  */
17
18
  export declare function AssetField({ field, value, onChange, readOnly, }: {
@@ -1,19 +1,19 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { AlertTriangle, ImagePlus, RotateCcw, SlidersHorizontal, Trash2, Upload, X } from 'lucide-react';
2
+ import { ImagePlus, RotateCcw, SlidersHorizontal, Trash2, Upload, X } from 'lucide-react';
3
3
  import { useEffect, useMemo, useRef, useState } from 'react';
4
- import { assetAcceptAttr, assetMatchesAccept, assetNode, assetSubfieldsOf, describeAccept, readAssetId, } from '@elytracms/core/cms-core';
4
+ import { assetNode, assetSubfieldsOf, readAssetId } from '@elytracms/core/cms-core';
5
5
  import { Button, Card, Code, Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, Field, Row, Spacer, Stack, Text, } from '@elytracms/studio/ui';
6
6
  import { cn } from '@elytracms/studio/lib/utils';
7
7
  import { localizedLabel, translate } from '@elytracms/studio/lib/i18n';
8
8
  import { appendAssetSelections, applyAssetSelection, assetIdsOf, assetPickForm, singleAssetIdOf, } from '../assets/asset-selection.js';
9
9
  import { useOptionalAssets } from '../assets/assets-store.js';
10
10
  import { AssetThumb, FocalCropPreviews, FocalPointPicker, useAssetRecord } from '../assets/asset-thumb.js';
11
- import { uploadBatchProgress } from './asset-picker-core.js';
11
+ import { assetPickerBinding, splitByAccept, uploadBatchProgress } from './asset-picker-core.js';
12
12
  import { assetMetaFieldsOf, pruneMeta } from '../assets/asset-metadata.js';
13
13
  import { fieldLabel } from './cms-labels.js';
14
14
  import { useCollectionView } from '../collection-view/collection-view-context.js';
15
15
  import { useFieldDensity } from './field-density.js';
16
- import { AssetPicker } from './asset-picker.js';
16
+ import { AssetPicker, AssetRejectedNotice } from './asset-picker.js';
17
17
  import { FieldControl } from './field-control.js';
18
18
  import { ObjectItemForm } from './object-array-field.js';
19
19
  /**
@@ -28,7 +28,8 @@ import { ObjectItemForm } from './object-array-field.js';
28
28
  * a focal-point-aware preview, uploads directly from the field (reusing the EC-152
29
29
  * upload path — no parallel uploader), and edits the selected image's alt + focal
30
30
  * point inline (persisted on the asset document, where delivery reads them, EC-230).
31
- * With no `<AssetsProvider>` (fixtures/playground) it degrades to the labelled-badge
31
+ * With no `<AssetsProvider>` (a fixture-only session — every project, the playground
32
+ * included, mounts one, `studio-project-screen.tsx`) it degrades to the labelled-badge
32
33
  * picker — no preview/upload — so it stays usable without a real library.
33
34
  */
34
35
  export function AssetField({ field, value, onChange, readOnly, }) {
@@ -42,9 +43,9 @@ export function AssetField({ field, value, onChange, readOnly, }) {
42
43
  // Names of files a drop/pick rejected against the field's `accept` (EC-301) — shown
43
44
  // as a transient inline issue, cleared on the next valid upload. Never a silent drop.
44
45
  const [rejected, setRejected] = useState([]);
45
- // The active upload BATCH (EC-460): the pre-upload library snapshot + how many
46
- // files the drop submitted, so the effect below attaches the WHOLE batch (the
47
- // `upload()` API returns nothing — the library diff is the signal).
46
+ // The active upload BATCH (EC-460): exactly the upload ids this field's drop
47
+ // started (EC-588 A7), so the effect below attaches the WHOLE batch and nothing
48
+ // another tab or a sibling field uploaded meanwhile.
48
49
  const pendingRef = useRef(null);
49
50
  const assetCollectionId = source.assetCollectionId ?? 'asset';
50
51
  const many = field.multiple;
@@ -61,12 +62,6 @@ export function AssetField({ field, value, onChange, readOnly, }) {
61
62
  // The raw `many` entries (envelopes or bare ids), index-aligned with `ids`, so each
62
63
  // gallery item can read/write its own sub-fields.
63
64
  const entries = many && Array.isArray(value) ? value : [];
64
- const libraryIds = useMemo(() => (assets?.status === 'ready' ? assets.assets.map((a) => a.id) : []), [assets]);
65
- // Uploads still running (failed entries are terminal — visible in the queue
66
- // for retry; they must not keep a batch waiting forever).
67
- const uploadsInFlight = assets
68
- ? assets.uploads.filter((u) => u.state.phase !== 'failed').length
69
- : 0;
70
65
  // Auto-select what an upload FROM this field added once it lands in the
71
66
  // library. EC-460: a `many` field attaches EVERY batch arrival as it lands
72
67
  // (the batch stays armed until it settles), so dropping N files attaches all
@@ -74,9 +69,9 @@ export function AssetField({ field, value, onChange, readOnly, }) {
74
69
  // rest in the library. A `one` field keeps the first-to-land selection.
75
70
  useEffect(() => {
76
71
  const batch = pendingRef.current;
77
- if (!batch)
72
+ if (!batch || !assets)
78
73
  return;
79
- const progress = uploadBatchProgress(batch, libraryIds, uploadsInFlight);
74
+ const progress = uploadBatchProgress(batch, assets, field.accept);
80
75
  if (progress.added.length === 0) {
81
76
  if (progress.settled)
82
77
  pendingRef.current = null; // everything failed — disarm
@@ -94,47 +89,28 @@ export function AssetField({ field, value, onChange, readOnly, }) {
94
89
  const next = applyAssetSelection(value, progress.added[0], false, form);
95
90
  if (next !== value)
96
91
  onChange(next);
97
- // Reacts to the library set growing (an upload landing) and the upload
98
- // queue draining (a batch settling) — `value`/`onChange` are read from the
99
- // closure on purpose.
100
- }, [libraryIds, uploadsInFlight, many, form, onChange, value]);
92
+ // Reacts to the assets store changing (an upload landing in the library, the
93
+ // queue draining) — `value`/`onChange` are read from the closure on purpose.
94
+ }, [assets, field.accept, many, form, onChange, value]);
101
95
  function uploadFiles(files) {
102
96
  if (!assets || files.length === 0)
103
97
  return;
104
- // EC-301: a drag-drop bypasses the file dialog's `accept` filter — re-check each
105
- // file against the field's `accept` and surface a visible issue for the rejects
106
- // instead of silently uploading the wrong kind.
107
- const ok = [];
108
- const bad = [];
109
- for (const file of files) {
110
- if (assetMatchesAccept({ contentType: file.type, filename: file.name }, field.accept))
111
- ok.push(file);
112
- else
113
- bad.push(file.name);
114
- }
115
- setRejected(bad);
116
- if (ok.length === 0)
98
+ // EC-301 → EC-588: the drop rule the picker applies too — a drag-drop bypasses
99
+ // the file dialog's `accept` filter, so re-check each file and name the rejects in
100
+ // a visible notice instead of silently uploading the wrong kind.
101
+ const { accepted, rejected: refused } = splitByAccept(files, field.accept);
102
+ setRejected(refused);
103
+ if (accepted.length === 0)
117
104
  return;
118
- // Remember the library snapshot + the batch size so the effect above can
119
- // attach every upload of this batch as it lands (the upload API returns nothing).
120
- pendingRef.current = { before: new Set(libraryIds), expected: ok.length };
121
- assets.upload(ok);
105
+ pendingRef.current = { uploadIds: assets.upload(accepted) };
122
106
  }
123
- // Apply a picked asset id through the pick operation the table cell also commits
124
- // (EC-558), so a placement's per-use sub-fields (EC-301) survive a swap or an append.
125
- function applyPicked(picked) {
126
- const next = applyAssetSelection(value, picked, many, form);
107
+ // The picker is configured from the field — the SAME binding the table cell uses
108
+ // (EC-570 #1 → EC-588): `accept`, `multiple`, and a pick through the carry rule
109
+ // (EC-558) so a placement's per-use sub-fields (EC-301) survive a swap or an append.
110
+ const pickerBinding = assetPickerBinding(field, value, (next) => {
127
111
  if (next !== value)
128
112
  onChange(next);
129
- }
130
- // EC-460: attach a whole settled in-picker upload batch to a `many` field —
131
- // every id not already attached, in arrival order (envelopes when the field
132
- // declares per-use sub-fields). Only wired for `many` fields (see the picker).
133
- function applyPickedMany(picked) {
134
- const next = appendAssetSelections(value, picked, form);
135
- if (next !== value)
136
- onChange(next);
137
- }
113
+ });
138
114
  const canUpload = !readOnly && assets !== null;
139
115
  // Drag-to-upload onto the field (EC-301): a depth counter keeps the overlay
140
116
  // steady as the pointer crosses child element boundaries. `dragenter`/`dragleave`
@@ -188,15 +164,7 @@ export function AssetField({ field, value, onChange, readOnly, }) {
188
164
  nextEntries[i] = assetNode(aid, next);
189
165
  onChange(nextEntries);
190
166
  }, onRemove: readOnly ? undefined : () => onChange(entries.filter((_, j) => j !== i)) }, `${aid}-${i}`));
191
- }) })) : (_jsx(EmptyAssetState, { compact: compact, canUpload: canUpload, readOnly: readOnly, onSelect: () => setOpen(true) })), _jsx(Row, { gap: "2", wrap: true, align: "center", children: selectButton })] })) : many ? (_jsxs(Stack, { gap: "2", children: [ids.length > 0 ? (_jsx("div", { className: cn('grid gap-2', compact ? 'grid-cols-2' : 'grid-cols-3 sm:grid-cols-4'), children: ids.map((aid, i) => (_jsx(AssetChip, { assetId: aid, collectionId: assetCollectionId, onRemove: readOnly ? undefined : () => onChange(ids.filter((x) => x !== aid)) }, `${aid}-${i}`))) })) : (_jsx(EmptyAssetState, { compact: compact, canUpload: canUpload, readOnly: readOnly, onSelect: () => setOpen(true) })), _jsx(Row, { gap: "2", wrap: true, align: "center", children: selectButton })] })) : single && hasSub ? (_jsx(Card, { className: "border-l-2 border-l-border p-3", children: _jsxs(Stack, { gap: "3", children: [_jsx(AssetSinglePreview, { assetId: single, compact: compact, readOnly: readOnly, hasLibrary: assets !== null, fallbackTitle: source.titleOf({ collection: assetCollectionId, id: single }), selectButton: selectButton, onEdit: () => setEditing(true), onRemove: () => onChange(applyAssetSelection(value, null, many, form)) }), _jsx("div", { className: "border-t border-border/60 pt-3", children: _jsx(ObjectItemForm, { fields: subFields, value: assetSubfieldsOf(value), readOnly: readOnly, idPrefix: "asset-sub", onChange: (next) => onChange(assetNode(single, next)) }) })] }) })) : single ? (_jsx(AssetSinglePreview, { assetId: single, compact: compact, readOnly: readOnly, hasLibrary: assets !== null, fallbackTitle: source.titleOf({ collection: assetCollectionId, id: single }), selectButton: selectButton, onEdit: () => setEditing(true), onRemove: () => onChange(applyAssetSelection(value, null, many, form)) })) : (_jsxs(Stack, { gap: "2", children: [_jsx(EmptyAssetState, { compact: compact, canUpload: canUpload, readOnly: readOnly, onSelect: () => setOpen(true) }), _jsx(Row, { gap: "2", wrap: true, align: "center", children: selectButton })] })), !many && single && assets ? (_jsx(AssetOptionsDialog, { assetId: single, readOnly: readOnly, open: editing, onOpenChange: setEditing })) : null, _jsx(AssetPicker, { value: single, open: open, onOpenChange: setOpen, accept: assetAcceptAttr(field.accept), onChange: applyPicked, multiple: many, ...(many ? { onChangeMany: applyPickedMany } : {}) }), rejected.length > 0 ? (_jsxs(Row, { gap: "2", align: "center", children: [_jsx(AlertTriangle, { className: "size-4 shrink-0 text-warning" }), _jsx(Text, { size: "xs", tone: "muted", children: rejected.length === 1
192
- ? translate('cms.asset.rejected.one', {
193
- name: rejected[0],
194
- types: describeAccept(field.accept),
195
- })
196
- : translate('cms.asset.rejected.other', {
197
- count: rejected.length,
198
- types: describeAccept(field.accept),
199
- }) })] })) : null, dragging ? (_jsx("div", { className: "pointer-events-none absolute inset-0 z-10 grid place-items-center rounded-lg border-2 border-dashed border-primary bg-background/85", children: _jsxs(Row, { gap: "2", align: "center", children: [_jsx(Upload, { className: "size-4 text-primary" }), _jsx(Text, { size: "sm", className: "font-medium", children: translate('cms.asset.dropToUpload') })] }) })) : null] }));
167
+ }) })) : (_jsx(EmptyAssetState, { compact: compact, canUpload: canUpload, readOnly: readOnly, onSelect: () => setOpen(true) })), _jsx(Row, { gap: "2", wrap: true, align: "center", children: selectButton })] })) : many ? (_jsxs(Stack, { gap: "2", children: [ids.length > 0 ? (_jsx("div", { className: cn('grid gap-2', compact ? 'grid-cols-2' : 'grid-cols-3 sm:grid-cols-4'), children: ids.map((aid, i) => (_jsx(AssetChip, { assetId: aid, collectionId: assetCollectionId, onRemove: readOnly ? undefined : () => onChange(ids.filter((x) => x !== aid)) }, `${aid}-${i}`))) })) : (_jsx(EmptyAssetState, { compact: compact, canUpload: canUpload, readOnly: readOnly, onSelect: () => setOpen(true) })), _jsx(Row, { gap: "2", wrap: true, align: "center", children: selectButton })] })) : single && hasSub ? (_jsx(Card, { className: "border-l-2 border-l-border p-3", children: _jsxs(Stack, { gap: "3", children: [_jsx(AssetSinglePreview, { assetId: single, compact: compact, readOnly: readOnly, hasLibrary: assets !== null, fallbackTitle: source.titleOf({ collection: assetCollectionId, id: single }), selectButton: selectButton, onEdit: () => setEditing(true), onRemove: () => onChange(applyAssetSelection(value, null, many, form)) }), _jsx("div", { className: "border-t border-border/60 pt-3", children: _jsx(ObjectItemForm, { fields: subFields, value: assetSubfieldsOf(value), readOnly: readOnly, idPrefix: "asset-sub", onChange: (next) => onChange(assetNode(single, next)) }) })] }) })) : single ? (_jsx(AssetSinglePreview, { assetId: single, compact: compact, readOnly: readOnly, hasLibrary: assets !== null, fallbackTitle: source.titleOf({ collection: assetCollectionId, id: single }), selectButton: selectButton, onEdit: () => setEditing(true), onRemove: () => onChange(applyAssetSelection(value, null, many, form)) })) : (_jsxs(Stack, { gap: "2", children: [_jsx(EmptyAssetState, { compact: compact, canUpload: canUpload, readOnly: readOnly, onSelect: () => setOpen(true) }), _jsx(Row, { gap: "2", wrap: true, align: "center", children: selectButton })] })), !many && single && assets ? (_jsx(AssetOptionsDialog, { assetId: single, readOnly: readOnly, open: editing, onOpenChange: setEditing })) : null, _jsx(AssetPicker, { open: open, onOpenChange: setOpen, ...pickerBinding }), _jsx(AssetRejectedNotice, { rejected: rejected, accept: field.accept }), dragging ? (_jsx("div", { className: "pointer-events-none absolute inset-0 z-10 grid place-items-center rounded-lg border-2 border-dashed border-primary bg-background/85", children: _jsxs(Row, { gap: "2", align: "center", children: [_jsx(Upload, { className: "size-4 text-primary" }), _jsx(Text, { size: "sm", className: "font-medium", children: translate('cms.asset.dropToUpload') })] }) })) : null] }));
200
168
  }
201
169
  /**
202
170
  * Empty-state placeholder shown when an asset field has no selection (EC-301).
@@ -1 +1 @@
1
- {"version":3,"file":"asset-field.js","sourceRoot":"","sources":["../../../src/components/cms/asset-field.tsx"],"names":[],"mappings":"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","sourcesContent":["import { AlertTriangle, ImagePlus, RotateCcw, SlidersHorizontal, Trash2, Upload, X } from 'lucide-react'\nimport { useEffect, useMemo, useRef, useState } from 'react'\nimport type { AssetFieldDef, FieldDef } from '@elytracms/core/cms-core'\nimport {\n assetAcceptAttr,\n assetMatchesAccept,\n assetNode,\n assetSubfieldsOf,\n describeAccept,\n readAssetId,\n} from '@elytracms/core/cms-core'\nimport {\n Button,\n Card,\n Code,\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n Field,\n Row,\n Spacer,\n Stack,\n Text,\n} from '@elytracms/studio/ui'\nimport { cn } from '@elytracms/studio/lib/utils'\nimport { localizedLabel, translate } from '@elytracms/studio/lib/i18n'\nimport {\n appendAssetSelections,\n applyAssetSelection,\n assetIdsOf,\n assetPickForm,\n singleAssetIdOf,\n} from '../assets/asset-selection'\nimport { useOptionalAssets } from '../assets/assets-store'\nimport { AssetThumb, FocalCropPreviews, FocalPointPicker, useAssetRecord } from '../assets/asset-thumb'\nimport { uploadBatchProgress } from './asset-picker-core'\nimport type { UploadBatch } from './asset-picker-core'\nimport { assetMetaFieldsOf, pruneMeta } from '../assets/asset-metadata'\nimport { fieldLabel } from './cms-labels'\nimport { useCollectionView } from '../collection-view/collection-view-context'\nimport { useFieldDensity } from './field-density'\nimport { AssetPicker } from './asset-picker'\nimport { FieldControl } from './field-control'\nimport { ObjectItemForm } from './object-array-field'\n\n/**\n * The asset-field family (EC-152 + EC-248 + EC-301) — split from\n * `field-control.tsx` (EC-330): the {@link AssetField} control plus its\n * preview/gallery/options sub-components. Per-use sub-fields and the options\n * dialog's metadata form render through the shared object item form /\n * `FieldControl`, so asset metadata stays schema-driven.\n */\n\n/**\n * The asset field (EC-152 + EC-248). Beyond picking from the library it now shows\n * a focal-point-aware preview, uploads directly from the field (reusing the EC-152\n * upload path — no parallel uploader), and edits the selected image's alt + focal\n * point inline (persisted on the asset document, where delivery reads them, EC-230).\n * With no `<AssetsProvider>` (fixtures/playground) it degrades to the labelled-badge\n * picker — no preview/upload — so it stays usable without a real library.\n */\nexport function AssetField({\n field,\n value,\n onChange,\n readOnly,\n}: {\n field: AssetFieldDef\n value: unknown\n onChange: (value: unknown) => void\n readOnly: boolean\n}) {\n const source = useCollectionView()\n const assets = useOptionalAssets()\n const density = useFieldDensity()\n const compact = density === 'compact'\n const [open, setOpen] = useState(false)\n const [editing, setEditing] = useState(false)\n const [dragging, setDragging] = useState(false)\n // Names of files a drop/pick rejected against the field's `accept` (EC-301) — shown\n // as a transient inline issue, cleared on the next valid upload. Never a silent drop.\n const [rejected, setRejected] = useState<string[]>([])\n // The active upload BATCH (EC-460): the pre-upload library snapshot + how many\n // files the drop submitted, so the effect below attaches the WHOLE batch (the\n // `upload()` API returns nothing — the library diff is the signal).\n const pendingRef = useRef<UploadBatch | null>(null)\n const assetCollectionId = source.assetCollectionId ?? 'asset'\n const many = field.multiple\n const ids = many ? assetIdsOf(value) : []\n const single = !many ? singleAssetIdOf(value) : null\n // EC-301 per-use sub-fields: with `field.fields` the value stores asset ENVELOPES\n // carrying the sub-field siblings (`{ type:'asset', id, caption }`); without them it\n // stays bare ids (self-typed on save). Picks/edits preserve a placement's sub-fields.\n const subFields = field.fields ?? []\n const hasSub = subFields.length > 0\n // How a pick writes a NEW placement: an envelope when the field has sub-fields,\n // else a bare id. The table cell reads the same decision (EC-558).\n const form = assetPickForm(field)\n // The raw `many` entries (envelopes or bare ids), index-aligned with `ids`, so each\n // gallery item can read/write its own sub-fields.\n const entries = many && Array.isArray(value) ? (value as unknown[]) : []\n\n const libraryIds = useMemo(\n () => (assets?.status === 'ready' ? assets.assets.map((a) => a.id) : []),\n [assets],\n )\n // Uploads still running (failed entries are terminal — visible in the queue\n // for retry; they must not keep a batch waiting forever).\n const uploadsInFlight = assets\n ? assets.uploads.filter((u) => u.state.phase !== 'failed').length\n : 0\n\n // Auto-select what an upload FROM this field added once it lands in the\n // library. EC-460: a `many` field attaches EVERY batch arrival as it lands\n // (the batch stays armed until it settles), so dropping N files attaches all\n // N — the old flow disarmed on the FIRST arrival and silently stranded the\n // rest in the library. A `one` field keeps the first-to-land selection.\n useEffect(() => {\n const batch = pendingRef.current\n if (!batch) return\n const progress = uploadBatchProgress(batch, libraryIds, uploadsInFlight)\n if (progress.added.length === 0) {\n if (progress.settled) pendingRef.current = null // everything failed — disarm\n return\n }\n if (many) {\n const next = appendAssetSelections(value, progress.added, form)\n if (next !== value) onChange(next)\n if (progress.settled) pendingRef.current = null\n return\n }\n pendingRef.current = null\n const next = applyAssetSelection(value, progress.added[0]!, false, form)\n if (next !== value) onChange(next)\n // Reacts to the library set growing (an upload landing) and the upload\n // queue draining (a batch settling) — `value`/`onChange` are read from the\n // closure on purpose.\n }, [libraryIds, uploadsInFlight, many, form, onChange, value])\n\n function uploadFiles(files: File[]) {\n if (!assets || files.length === 0) return\n // EC-301: a drag-drop bypasses the file dialog's `accept` filter — re-check each\n // file against the field's `accept` and surface a visible issue for the rejects\n // instead of silently uploading the wrong kind.\n const ok: File[] = []\n const bad: string[] = []\n for (const file of files) {\n if (assetMatchesAccept({ contentType: file.type, filename: file.name }, field.accept)) ok.push(file)\n else bad.push(file.name)\n }\n setRejected(bad)\n if (ok.length === 0) return\n // Remember the library snapshot + the batch size so the effect above can\n // attach every upload of this batch as it lands (the upload API returns nothing).\n pendingRef.current = { before: new Set(libraryIds), expected: ok.length }\n assets.upload(ok)\n }\n\n // Apply a picked asset id through the pick operation the table cell also commits\n // (EC-558), so a placement's per-use sub-fields (EC-301) survive a swap or an append.\n function applyPicked(picked: string | null) {\n const next = applyAssetSelection(value, picked, many, form)\n if (next !== value) onChange(next)\n }\n\n // EC-460: attach a whole settled in-picker upload batch to a `many` field —\n // every id not already attached, in arrival order (envelopes when the field\n // declares per-use sub-fields). Only wired for `many` fields (see the picker).\n function applyPickedMany(picked: string[]) {\n const next = appendAssetSelections(value, picked, form)\n if (next !== value) onChange(next)\n }\n\n const canUpload = !readOnly && assets !== null\n\n // Drag-to-upload onto the field (EC-301): a depth counter keeps the overlay\n // steady as the pointer crosses child element boundaries. `dragenter`/`dragleave`\n // fire per descendant, so toggling on a bare `dragleave` blinks constantly; we\n // only hide the overlay when the counter returns to zero (the pointer has truly\n // left the field). The overlay is pointer-events-none, so it never fires its own\n // enter/leave to confuse the count.\n const dragDepth = useRef(0)\n function onDragEnter(e: React.DragEvent) {\n if (!canUpload) return\n e.preventDefault()\n dragDepth.current += 1\n if (!dragging) setDragging(true)\n }\n function onDragOver(e: React.DragEvent) {\n if (!canUpload) return\n e.preventDefault() // required so a drop is allowed\n }\n function onDragLeave() {\n if (!canUpload) return\n dragDepth.current -= 1\n if (dragDepth.current <= 0) {\n dragDepth.current = 0\n setDragging(false)\n }\n }\n function onDrop(e: React.DragEvent) {\n if (!canUpload) return\n e.preventDefault()\n dragDepth.current = 0\n setDragging(false)\n uploadFiles(Array.from(e.dataTransfer.files))\n }\n // While the picker is open the field is behind the modal, so any drop is happening\n // INSIDE the dialog — detach the field's drag handlers entirely so a drop there can\n // only be handled by the picker, never double-counted by the field too (EC-301). This\n // is the belt to the picker's stopPropagation braces: the field cannot react at all.\n const dragProps = canUpload && !open ? { onDragEnter, onDragOver, onDragLeave, onDrop } : {}\n\n const selectButton = !readOnly ? (\n <Button variant=\"outline\" size=\"sm\" onClick={() => setOpen(true)}>\n <ImagePlus /> {translate('common.select')}\n </Button>\n ) : null\n\n return (\n <Stack\n gap=\"2\"\n className={cn(\n 'relative rounded-lg transition-shadow',\n dragging && 'ring-2 ring-primary ring-offset-2 ring-offset-background',\n )}\n {...dragProps}\n >\n {many && hasSub ? (\n // Gallery with per-use sub-fields (EC-301): a card per placement — thumbnail +\n // its own sub-field form — reusing the object/repeater item rendering.\n <Stack gap=\"2\">\n {entries.length > 0 ? (\n <Stack gap=\"2\">\n {entries.map((entry, i) => {\n const aid = readAssetId(entry)\n if (!aid) return null\n return (\n <AssetGalleryItem\n key={`${aid}-${i}`}\n assetId={aid}\n collectionId={assetCollectionId}\n fields={subFields}\n value={assetSubfieldsOf(entry)}\n readOnly={readOnly}\n idPrefix={`asset-${i}`}\n onChangeSubs={(next) => {\n const nextEntries = entries.slice()\n nextEntries[i] = assetNode(aid, next)\n onChange(nextEntries)\n }}\n onRemove={readOnly ? undefined : () => onChange(entries.filter((_, j) => j !== i))}\n />\n )\n })}\n </Stack>\n ) : (\n <EmptyAssetState\n compact={compact}\n canUpload={canUpload}\n readOnly={readOnly}\n onSelect={() => setOpen(true)}\n />\n )}\n <Row gap=\"2\" wrap align=\"center\">\n {selectButton}\n </Row>\n </Stack>\n ) : many ? (\n // Gallery: a grid of focal-aware previews + the single Select entry.\n <Stack gap=\"2\">\n {ids.length > 0 ? (\n <div\n className={cn(\n 'grid gap-2',\n compact ? 'grid-cols-2' : 'grid-cols-3 sm:grid-cols-4',\n )}\n >\n {ids.map((aid, i) => (\n <AssetChip\n key={`${aid}-${i}`}\n assetId={aid}\n collectionId={assetCollectionId}\n onRemove={readOnly ? undefined : () => onChange(ids.filter((x) => x !== aid))}\n />\n ))}\n </div>\n ) : (\n <EmptyAssetState\n compact={compact}\n canUpload={canUpload}\n readOnly={readOnly}\n onSelect={() => setOpen(true)}\n />\n )}\n <Row gap=\"2\" wrap align=\"center\">\n {selectButton}\n </Row>\n </Stack>\n ) : single && hasSub ? (\n // With per-use sub-fields the asset + its fields read as ONE grouped unit\n // (like an object), so it's clear they belong together (EC-301 feedback).\n <Card className=\"border-l-2 border-l-border p-3\">\n <Stack gap=\"3\">\n <AssetSinglePreview\n assetId={single}\n compact={compact}\n readOnly={readOnly}\n hasLibrary={assets !== null}\n fallbackTitle={source.titleOf({ collection: assetCollectionId, id: single })}\n selectButton={selectButton}\n onEdit={() => setEditing(true)}\n onRemove={() => onChange(applyAssetSelection(value, null, many, form))}\n />\n <div className=\"border-t border-border/60 pt-3\">\n <ObjectItemForm\n fields={subFields}\n value={assetSubfieldsOf(value)}\n readOnly={readOnly}\n idPrefix=\"asset-sub\"\n onChange={(next) => onChange(assetNode(single, next))}\n />\n </div>\n </Stack>\n </Card>\n ) : single ? (\n <AssetSinglePreview\n assetId={single}\n compact={compact}\n readOnly={readOnly}\n hasLibrary={assets !== null}\n fallbackTitle={source.titleOf({ collection: assetCollectionId, id: single })}\n selectButton={selectButton}\n onEdit={() => setEditing(true)}\n onRemove={() => onChange(applyAssetSelection(value, null, many, form))}\n />\n ) : (\n <Stack gap=\"2\">\n <EmptyAssetState\n compact={compact}\n canUpload={canUpload}\n readOnly={readOnly}\n onSelect={() => setOpen(true)}\n />\n <Row gap=\"2\" wrap align=\"center\">\n {selectButton}\n </Row>\n </Stack>\n )}\n\n {/* Image options (focal point + metadata) for a single selected asset, in a\n dialog rather than an inline disclosure (EC-301). Shared across every use. */}\n {!many && single && assets ? (\n <AssetOptionsDialog\n assetId={single}\n readOnly={readOnly}\n open={editing}\n onOpenChange={setEditing}\n />\n ) : null}\n\n <AssetPicker\n value={single}\n open={open}\n onOpenChange={setOpen}\n accept={assetAcceptAttr(field.accept)}\n onChange={applyPicked}\n multiple={many}\n {...(many ? { onChangeMany: applyPickedMany } : {})}\n />\n\n {rejected.length > 0 ? (\n <Row gap=\"2\" align=\"center\">\n <AlertTriangle className=\"size-4 shrink-0 text-warning\" />\n <Text size=\"xs\" tone=\"muted\">\n {rejected.length === 1\n ? translate('cms.asset.rejected.one', {\n name: rejected[0]!,\n types: describeAccept(field.accept),\n })\n : translate('cms.asset.rejected.other', {\n count: rejected.length,\n types: describeAccept(field.accept),\n })}\n </Text>\n </Row>\n ) : null}\n\n {dragging ? (\n <div className=\"pointer-events-none absolute inset-0 z-10 grid place-items-center rounded-lg border-2 border-dashed border-primary bg-background/85\">\n <Row gap=\"2\" align=\"center\">\n <Upload className=\"size-4 text-primary\" />\n <Text size=\"sm\" className=\"font-medium\">\n {translate('cms.asset.dropToUpload')}\n </Text>\n </Row>\n </div>\n ) : null}\n </Stack>\n )\n}\n\n/**\n * Empty-state placeholder shown when an asset field has no selection (EC-301).\n *\n * EC-395: this was the one asset-field affordance that ignored `readOnly` — every\n * sibling control (`selectButton`, the per-item remove/change actions) already\n * degraded, but the empty-state tile stayed a clickable `<button>` that opened the\n * picker regardless. A locked, empty asset field must be as inert as a filled one:\n * read-only renders a plain (non-interactive, non-focusable) `div` with the same\n * frame — no `onClick`, no hover affordance — instead of a `<button>`.\n */\nfunction EmptyAssetState({\n compact,\n canUpload,\n readOnly,\n onSelect,\n}: {\n compact: boolean\n canUpload: boolean\n readOnly: boolean\n onSelect: () => void\n}) {\n const frame = cn(\n 'grid w-full place-items-center gap-1 rounded-lg border border-dashed border-border bg-muted/30 text-center text-muted-foreground',\n compact ? 'aspect-video' : 'aspect-[5/2]',\n )\n if (readOnly) {\n return <div className={frame}>{emptyAssetStateBody(canUpload)}</div>\n }\n return (\n <button type=\"button\" onClick={onSelect} className={cn(frame, 'transition-colors hover:bg-muted/60')}>\n {emptyAssetStateBody(canUpload)}\n </button>\n )\n}\n\nfunction emptyAssetStateBody(canUpload: boolean) {\n return (\n <>\n <ImagePlus className=\"size-6\" aria-hidden />\n <Text size=\"xs\" tone=\"muted\">\n {canUpload ? translate('cms.asset.selectOrDrop') : translate('cms.asset.selectAsset')}\n </Text>\n </>\n )\n}\n\n/** A large focal-aware preview of the selected asset (EC-301). */\nfunction AssetPreview({ assetId }: { assetId: string }) {\n const record = useAssetRecord(assetId)\n return <AssetThumb record={record} className=\"aspect-[4/3] w-full\" />\n}\n\n/** Filename + dimensions/type for the selected asset — the id is never shown (EC-301). */\nfunction AssetInfo({ assetId, fallbackTitle }: { assetId: string; fallbackTitle?: string }) {\n const record = useAssetRecord(assetId)\n const name = record?.title ?? record?.filename ?? fallbackTitle ?? assetId\n const dims = record?.width && record?.height ? `${record.width}×${record.height}` : null\n const kind = record?.contentType?.split('/')[1]?.toUpperCase()\n const meta = [dims, kind].filter(Boolean).join(' · ')\n return (\n <Stack gap=\"1\">\n <Text size=\"sm\" className=\"truncate font-medium\" title={name}>\n {name}\n </Text>\n {meta ? (\n <Text size=\"xs\" tone=\"subtle\">\n {meta}\n </Text>\n ) : null}\n </Stack>\n )\n}\n\n/**\n * The selected image's alt text shown in the field (EC-301) — what an editor most\n * wants at a glance; edited in the options dialog. Reads `meta.alt` with the legacy\n * `alt` column as fallback, matching delivery.\n */\nfunction AssetAltLine({ assetId }: { assetId: string }) {\n const record = useAssetRecord(assetId)\n const metaAlt = typeof record?.meta?.alt === 'string' ? record.meta.alt : undefined\n const alt = metaAlt ?? record?.alt\n return (\n <Row gap=\"1\" align=\"baseline\" className=\"min-w-0\">\n <Text size=\"xs\" tone=\"subtle\" className=\"shrink-0\">\n {translate('cms.asset.altLabel')}\n </Text>\n <Text\n size=\"xs\"\n tone={alt ? 'muted' : 'subtle'}\n className={cn('min-w-0 truncate', !alt && 'italic')}\n title={alt ?? undefined}\n >\n {alt || translate('cms.asset.notSet')}\n </Text>\n </Row>\n )\n}\n\n/** A single asset reference: focal-aware thumbnail + title, with an optional remove. */\nfunction AssetChip({\n assetId,\n collectionId,\n onRemove,\n}: {\n assetId: string\n collectionId: string\n onRemove?: () => void\n}) {\n const source = useCollectionView()\n const record = useAssetRecord(assetId)\n const title = source.titleOf({ collection: collectionId, id: assetId })\n return (\n <div className=\"group relative overflow-hidden rounded-lg border border-border bg-muted/40\">\n <AssetThumb record={record} alt={title} className=\"aspect-square w-full rounded-none border-0\" />\n <Row gap=\"1\" align=\"center\" className=\"px-2 py-1\">\n <Text size=\"xs\" tone=\"subtle\" className=\"min-w-0 flex-1 truncate\" title={title}>\n {title}\n </Text>\n {onRemove ? (\n <button\n type=\"button\"\n aria-label={translate('common.remove')}\n onClick={onRemove}\n className=\"text-muted-foreground hover:text-destructive\"\n >\n <X className=\"size-3.5\" />\n </button>\n ) : null}\n </Row>\n </div>\n )\n}\n\n/**\n * The selected single asset's preview row (EC-301): large focal-aware preview + info\n * column (filename/dimensions, Select/Edit/Remove, alt). Extracted so it renders either\n * bare (a plain asset field) or inside the grouping card when the field carries per-use\n * sub-fields — the asset + its fields then read as one unit.\n */\nfunction AssetSinglePreview({\n assetId,\n compact,\n readOnly,\n hasLibrary,\n fallbackTitle,\n selectButton,\n onEdit,\n onRemove,\n}: {\n assetId: string\n compact: boolean\n readOnly: boolean\n hasLibrary: boolean\n fallbackTitle?: string\n selectButton: React.ReactNode\n onEdit: () => void\n onRemove: () => void\n}) {\n return (\n <div className={compact ? 'flex flex-col gap-3' : 'flex gap-4'}>\n <div className={compact ? 'w-full' : 'w-1/2 shrink-0'}>\n <AssetPreview assetId={assetId} />\n </div>\n <Stack gap=\"2\" className={cn('min-w-0', !compact && 'flex-1')}>\n <AssetInfo assetId={assetId} fallbackTitle={fallbackTitle} />\n {!readOnly ? (\n <Row gap=\"2\" wrap align=\"center\">\n {selectButton}\n {hasLibrary ? (\n <Button variant=\"outline\" size=\"sm\" onClick={onEdit}>\n <SlidersHorizontal /> {translate('common.edit')}\n </Button>\n ) : null}\n <Button variant=\"ghost\" size=\"sm\" onClick={onRemove}>\n <Trash2 /> {translate('common.remove')}\n </Button>\n </Row>\n ) : null}\n <AssetAltLine assetId={assetId} />\n </Stack>\n </div>\n )\n}\n\n/**\n * One gallery placement when a `many` asset field declares per-use sub-fields (EC-301):\n * a thumbnail + filename + remove header over the placement's own sub-field form,\n * modelled on a repeater item card so a gallery's placements read as grouped units.\n */\nfunction AssetGalleryItem({\n assetId,\n collectionId,\n fields,\n value,\n onChangeSubs,\n onRemove,\n readOnly,\n idPrefix,\n}: {\n assetId: string\n collectionId: string\n fields: readonly FieldDef[]\n value: Record<string, unknown>\n onChangeSubs: (value: Record<string, unknown>) => void\n onRemove?: () => void\n readOnly: boolean\n idPrefix: string\n}) {\n const source = useCollectionView()\n const record = useAssetRecord(assetId)\n const title = source.titleOf({ collection: collectionId, id: assetId })\n return (\n <Card className=\"border-l-2 border-l-border p-0\">\n <Row gap=\"2\" align=\"center\" className=\"border-b border-border/60 bg-muted/30 px-2 py-1.5\">\n <AssetThumb record={record} alt={title} className=\"size-10 shrink-0\" />\n <Text size=\"sm\" className=\"min-w-0 flex-1 truncate font-medium\" title={title}>\n {title}\n </Text>\n {onRemove ? (\n <Button variant=\"ghost\" size=\"sm\" aria-label={translate('common.remove')} onClick={onRemove}>\n <Trash2 className=\"size-3.5\" />\n </Button>\n ) : null}\n </Row>\n <div className=\"px-3 pb-3\">\n <ObjectItemForm\n fields={fields}\n value={value}\n onChange={onChangeSubs}\n readOnly={readOnly}\n idPrefix={idPrefix}\n />\n </div>\n </Card>\n )\n}\n\n/**\n * The image options dialog (EC-301): the focal point (engine-special visual\n * control) + the asset's **schema-driven** metadata, rendered from the\n * `kind:'asset'` collection's `fields` through the same generic field engine\n * documents use — so a project declares asset metadata (alt, copyright, credit, …)\n * in code, not hardcoded here. Edits persist on the asset, shared across every use.\n * Metadata is buffered locally and committed once on close (one write); the focal\n * point commits immediately (it drives a live visual). No-ops without a library.\n */\nfunction AssetOptionsDialog({\n assetId,\n readOnly,\n open,\n onOpenChange,\n}: {\n assetId: string\n readOnly: boolean\n open: boolean\n onOpenChange: (open: boolean) => void\n}) {\n const source = useCollectionView()\n const assets = useOptionalAssets()\n const record = useAssetRecord(assetId)\n const metaFields = useMemo(() => {\n const assetCollectionId = source.assetCollectionId ?? 'asset'\n const collection = source.collections.find((c) => c.id === assetCollectionId)\n return collection ? assetMetaFieldsOf(collection) : []\n }, [source])\n\n // Local edit buffers, seeded once per open from the record (so an external edit\n // still wins between opens). Metadata is non-localized in v1 (the asset\n // resolution path is locale-agnostic) — localized asset metadata is a follow-up.\n const [meta, setMeta] = useState<Record<string, unknown>>({})\n const [focal, setFocal] = useState<{ x: number; y: number } | undefined>(undefined)\n const dirty = useRef(false)\n const seeded = useRef(false)\n useEffect(() => {\n if (!open) {\n seeded.current = false\n return\n }\n if (seeded.current || !record) return\n // Seed from the meta bag, but lift the legacy `alt` column in when meta lacks\n // it (EC-301 transition): a not-yet-migrated asset still shows its alt, and\n // saving writes it into meta — a lazy migration-on-edit, matching delivery's\n // `meta.alt ?? record.alt` read fallback.\n const seedMeta = { ...(record.alt !== undefined ? { alt: record.alt } : {}), ...(record.meta ?? {}) }\n setMeta(seedMeta)\n setFocal(record.focalPoint)\n dirty.current = false\n seeded.current = true\n }, [open, record])\n\n function handleOpenChange(next: boolean) {\n // Commit the buffered metadata once, on close (one write, not per keystroke).\n if (!next && !readOnly && assets && dirty.current) {\n void assets.updateMetadata(assetId, { meta: pruneMeta(meta) })\n dirty.current = false\n }\n onOpenChange(next)\n }\n\n function setField(name: string, value: unknown) {\n dirty.current = true\n setMeta((m) => ({ ...m, [name]: value }))\n }\n\n // The focal point drives a live crop preview → commit immediately (optimistic\n // marker), not deferred to close.\n function commitFocal(point: { x: number; y: number } | null) {\n if (readOnly || !assets) return\n setFocal(point ?? undefined)\n void assets.updateMetadata(assetId, { focalPoint: point })\n }\n\n return (\n <Dialog open={open} onOpenChange={handleOpenChange}>\n <DialogContent className=\"max-w-lg\">\n <DialogHeader>\n <DialogTitle>{translate('cms.asset.imageOptions.title')}</DialogTitle>\n <DialogDescription>\n {translate('cms.asset.imageOptions.description')}\n </DialogDescription>\n </DialogHeader>\n <Stack gap=\"3\">\n <Stack gap=\"2\">\n <Row align=\"center\">\n <Text size=\"sm\" className=\"font-medium\">\n {translate('cms.asset.focalPoint')}\n </Text>\n <Spacer />\n {focal && !readOnly ? (\n <Button variant=\"ghost\" size=\"sm\" onClick={() => commitFocal(null)}>\n <RotateCcw /> {translate('cms.asset.resetToCentre')}\n </Button>\n ) : null}\n </Row>\n <FocalPointPicker\n record={record}\n point={focal}\n onChange={commitFocal}\n onPreview={setFocal}\n disabled={readOnly}\n />\n <Text size=\"xs\" tone=\"subtle\">\n {translate('cms.asset.focalPointHint')}\n </Text>\n <FocalCropPreviews record={record} point={focal} />\n </Stack>\n {metaFields.map((f) => (\n <Field key={f.name} label={fieldLabel(f)} hint={localizedLabel(f.form?.description) || undefined}>\n <FieldControl\n id={`asset-meta-${f.name}`}\n field={f}\n value={meta[f.name]}\n readOnly={readOnly}\n onChange={(v) => setField(f.name, v)}\n />\n </Field>\n ))}\n {metaFields.length === 0 ? (\n <Text size=\"sm\" tone=\"subtle\">\n No editable metadata fields are declared on the asset collection. Add fields (e.g.\n <Code tone=\"muted\"> alt</Code>, <Code tone=\"muted\">copyright</Code>) to it in code.\n </Text>\n ) : null}\n </Stack>\n </DialogContent>\n </Dialog>\n )\n}\n"]}
1
+ {"version":3,"file":"asset-field.js","sourceRoot":"","sources":["../../../src/components/cms/asset-field.tsx"],"names":[],"mappings":"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","sourcesContent":["import { ImagePlus, RotateCcw, SlidersHorizontal, Trash2, Upload, X } from 'lucide-react'\nimport { useEffect, useMemo, useRef, useState } from 'react'\nimport type { AssetFieldDef, FieldDef } from '@elytracms/core/cms-core'\nimport { assetNode, assetSubfieldsOf, readAssetId } from '@elytracms/core/cms-core'\nimport {\n Button,\n Card,\n Code,\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n Field,\n Row,\n Spacer,\n Stack,\n Text,\n} from '@elytracms/studio/ui'\nimport { cn } from '@elytracms/studio/lib/utils'\nimport { localizedLabel, translate } from '@elytracms/studio/lib/i18n'\nimport {\n appendAssetSelections,\n applyAssetSelection,\n assetIdsOf,\n assetPickForm,\n singleAssetIdOf,\n} from '../assets/asset-selection'\nimport { useOptionalAssets } from '../assets/assets-store'\nimport { AssetThumb, FocalCropPreviews, FocalPointPicker, useAssetRecord } from '../assets/asset-thumb'\nimport { assetPickerBinding, splitByAccept, uploadBatchProgress } from './asset-picker-core'\nimport type { UploadBatch } from './asset-picker-core'\nimport { assetMetaFieldsOf, pruneMeta } from '../assets/asset-metadata'\nimport { fieldLabel } from './cms-labels'\nimport { useCollectionView } from '../collection-view/collection-view-context'\nimport { useFieldDensity } from './field-density'\nimport { AssetPicker, AssetRejectedNotice } from './asset-picker'\nimport { FieldControl } from './field-control'\nimport { ObjectItemForm } from './object-array-field'\n\n/**\n * The asset-field family (EC-152 + EC-248 + EC-301) — split from\n * `field-control.tsx` (EC-330): the {@link AssetField} control plus its\n * preview/gallery/options sub-components. Per-use sub-fields and the options\n * dialog's metadata form render through the shared object item form /\n * `FieldControl`, so asset metadata stays schema-driven.\n */\n\n/**\n * The asset field (EC-152 + EC-248). Beyond picking from the library it now shows\n * a focal-point-aware preview, uploads directly from the field (reusing the EC-152\n * upload path — no parallel uploader), and edits the selected image's alt + focal\n * point inline (persisted on the asset document, where delivery reads them, EC-230).\n * With no `<AssetsProvider>` (a fixture-only session — every project, the playground\n * included, mounts one, `studio-project-screen.tsx`) it degrades to the labelled-badge\n * picker — no preview/upload — so it stays usable without a real library.\n */\nexport function AssetField({\n field,\n value,\n onChange,\n readOnly,\n}: {\n field: AssetFieldDef\n value: unknown\n onChange: (value: unknown) => void\n readOnly: boolean\n}) {\n const source = useCollectionView()\n const assets = useOptionalAssets()\n const density = useFieldDensity()\n const compact = density === 'compact'\n const [open, setOpen] = useState(false)\n const [editing, setEditing] = useState(false)\n const [dragging, setDragging] = useState(false)\n // Names of files a drop/pick rejected against the field's `accept` (EC-301) — shown\n // as a transient inline issue, cleared on the next valid upload. Never a silent drop.\n const [rejected, setRejected] = useState<string[]>([])\n // The active upload BATCH (EC-460): exactly the upload ids this field's drop\n // started (EC-588 A7), so the effect below attaches the WHOLE batch and nothing\n // another tab or a sibling field uploaded meanwhile.\n const pendingRef = useRef<UploadBatch | null>(null)\n const assetCollectionId = source.assetCollectionId ?? 'asset'\n const many = field.multiple\n const ids = many ? assetIdsOf(value) : []\n const single = !many ? singleAssetIdOf(value) : null\n // EC-301 per-use sub-fields: with `field.fields` the value stores asset ENVELOPES\n // carrying the sub-field siblings (`{ type:'asset', id, caption }`); without them it\n // stays bare ids (self-typed on save). Picks/edits preserve a placement's sub-fields.\n const subFields = field.fields ?? []\n const hasSub = subFields.length > 0\n // How a pick writes a NEW placement: an envelope when the field has sub-fields,\n // else a bare id. The table cell reads the same decision (EC-558).\n const form = assetPickForm(field)\n // The raw `many` entries (envelopes or bare ids), index-aligned with `ids`, so each\n // gallery item can read/write its own sub-fields.\n const entries = many && Array.isArray(value) ? (value as unknown[]) : []\n\n // Auto-select what an upload FROM this field added once it lands in the\n // library. EC-460: a `many` field attaches EVERY batch arrival as it lands\n // (the batch stays armed until it settles), so dropping N files attaches all\n // N — the old flow disarmed on the FIRST arrival and silently stranded the\n // rest in the library. A `one` field keeps the first-to-land selection.\n useEffect(() => {\n const batch = pendingRef.current\n if (!batch || !assets) return\n const progress = uploadBatchProgress(batch, assets, field.accept)\n if (progress.added.length === 0) {\n if (progress.settled) pendingRef.current = null // everything failed — disarm\n return\n }\n if (many) {\n const next = appendAssetSelections(value, progress.added, form)\n if (next !== value) onChange(next)\n if (progress.settled) pendingRef.current = null\n return\n }\n pendingRef.current = null\n const next = applyAssetSelection(value, progress.added[0]!, false, form)\n if (next !== value) onChange(next)\n // Reacts to the assets store changing (an upload landing in the library, the\n // queue draining) — `value`/`onChange` are read from the closure on purpose.\n }, [assets, field.accept, many, form, onChange, value])\n\n function uploadFiles(files: File[]) {\n if (!assets || files.length === 0) return\n // EC-301 → EC-588: the drop rule the picker applies too — a drag-drop bypasses\n // the file dialog's `accept` filter, so re-check each file and name the rejects in\n // a visible notice instead of silently uploading the wrong kind.\n const { accepted, rejected: refused } = splitByAccept(files, field.accept)\n setRejected(refused)\n if (accepted.length === 0) return\n pendingRef.current = { uploadIds: assets.upload(accepted) }\n }\n\n // The picker is configured from the field — the SAME binding the table cell uses\n // (EC-570 #1 → EC-588): `accept`, `multiple`, and a pick through the carry rule\n // (EC-558) so a placement's per-use sub-fields (EC-301) survive a swap or an append.\n const pickerBinding = assetPickerBinding(field, value, (next) => {\n if (next !== value) onChange(next)\n })\n\n const canUpload = !readOnly && assets !== null\n\n // Drag-to-upload onto the field (EC-301): a depth counter keeps the overlay\n // steady as the pointer crosses child element boundaries. `dragenter`/`dragleave`\n // fire per descendant, so toggling on a bare `dragleave` blinks constantly; we\n // only hide the overlay when the counter returns to zero (the pointer has truly\n // left the field). The overlay is pointer-events-none, so it never fires its own\n // enter/leave to confuse the count.\n const dragDepth = useRef(0)\n function onDragEnter(e: React.DragEvent) {\n if (!canUpload) return\n e.preventDefault()\n dragDepth.current += 1\n if (!dragging) setDragging(true)\n }\n function onDragOver(e: React.DragEvent) {\n if (!canUpload) return\n e.preventDefault() // required so a drop is allowed\n }\n function onDragLeave() {\n if (!canUpload) return\n dragDepth.current -= 1\n if (dragDepth.current <= 0) {\n dragDepth.current = 0\n setDragging(false)\n }\n }\n function onDrop(e: React.DragEvent) {\n if (!canUpload) return\n e.preventDefault()\n dragDepth.current = 0\n setDragging(false)\n uploadFiles(Array.from(e.dataTransfer.files))\n }\n // While the picker is open the field is behind the modal, so any drop is happening\n // INSIDE the dialog — detach the field's drag handlers entirely so a drop there can\n // only be handled by the picker, never double-counted by the field too (EC-301). This\n // is the belt to the picker's stopPropagation braces: the field cannot react at all.\n const dragProps = canUpload && !open ? { onDragEnter, onDragOver, onDragLeave, onDrop } : {}\n\n const selectButton = !readOnly ? (\n <Button variant=\"outline\" size=\"sm\" onClick={() => setOpen(true)}>\n <ImagePlus /> {translate('common.select')}\n </Button>\n ) : null\n\n return (\n <Stack\n gap=\"2\"\n className={cn(\n 'relative rounded-lg transition-shadow',\n dragging && 'ring-2 ring-primary ring-offset-2 ring-offset-background',\n )}\n {...dragProps}\n >\n {many && hasSub ? (\n // Gallery with per-use sub-fields (EC-301): a card per placement — thumbnail +\n // its own sub-field form — reusing the object/repeater item rendering.\n <Stack gap=\"2\">\n {entries.length > 0 ? (\n <Stack gap=\"2\">\n {entries.map((entry, i) => {\n const aid = readAssetId(entry)\n if (!aid) return null\n return (\n <AssetGalleryItem\n key={`${aid}-${i}`}\n assetId={aid}\n collectionId={assetCollectionId}\n fields={subFields}\n value={assetSubfieldsOf(entry)}\n readOnly={readOnly}\n idPrefix={`asset-${i}`}\n onChangeSubs={(next) => {\n const nextEntries = entries.slice()\n nextEntries[i] = assetNode(aid, next)\n onChange(nextEntries)\n }}\n onRemove={readOnly ? undefined : () => onChange(entries.filter((_, j) => j !== i))}\n />\n )\n })}\n </Stack>\n ) : (\n <EmptyAssetState\n compact={compact}\n canUpload={canUpload}\n readOnly={readOnly}\n onSelect={() => setOpen(true)}\n />\n )}\n <Row gap=\"2\" wrap align=\"center\">\n {selectButton}\n </Row>\n </Stack>\n ) : many ? (\n // Gallery: a grid of focal-aware previews + the single Select entry.\n <Stack gap=\"2\">\n {ids.length > 0 ? (\n <div\n className={cn(\n 'grid gap-2',\n compact ? 'grid-cols-2' : 'grid-cols-3 sm:grid-cols-4',\n )}\n >\n {ids.map((aid, i) => (\n <AssetChip\n key={`${aid}-${i}`}\n assetId={aid}\n collectionId={assetCollectionId}\n onRemove={readOnly ? undefined : () => onChange(ids.filter((x) => x !== aid))}\n />\n ))}\n </div>\n ) : (\n <EmptyAssetState\n compact={compact}\n canUpload={canUpload}\n readOnly={readOnly}\n onSelect={() => setOpen(true)}\n />\n )}\n <Row gap=\"2\" wrap align=\"center\">\n {selectButton}\n </Row>\n </Stack>\n ) : single && hasSub ? (\n // With per-use sub-fields the asset + its fields read as ONE grouped unit\n // (like an object), so it's clear they belong together (EC-301 feedback).\n <Card className=\"border-l-2 border-l-border p-3\">\n <Stack gap=\"3\">\n <AssetSinglePreview\n assetId={single}\n compact={compact}\n readOnly={readOnly}\n hasLibrary={assets !== null}\n fallbackTitle={source.titleOf({ collection: assetCollectionId, id: single })}\n selectButton={selectButton}\n onEdit={() => setEditing(true)}\n onRemove={() => onChange(applyAssetSelection(value, null, many, form))}\n />\n <div className=\"border-t border-border/60 pt-3\">\n <ObjectItemForm\n fields={subFields}\n value={assetSubfieldsOf(value)}\n readOnly={readOnly}\n idPrefix=\"asset-sub\"\n onChange={(next) => onChange(assetNode(single, next))}\n />\n </div>\n </Stack>\n </Card>\n ) : single ? (\n <AssetSinglePreview\n assetId={single}\n compact={compact}\n readOnly={readOnly}\n hasLibrary={assets !== null}\n fallbackTitle={source.titleOf({ collection: assetCollectionId, id: single })}\n selectButton={selectButton}\n onEdit={() => setEditing(true)}\n onRemove={() => onChange(applyAssetSelection(value, null, many, form))}\n />\n ) : (\n <Stack gap=\"2\">\n <EmptyAssetState\n compact={compact}\n canUpload={canUpload}\n readOnly={readOnly}\n onSelect={() => setOpen(true)}\n />\n <Row gap=\"2\" wrap align=\"center\">\n {selectButton}\n </Row>\n </Stack>\n )}\n\n {/* Image options (focal point + metadata) for a single selected asset, in a\n dialog rather than an inline disclosure (EC-301). Shared across every use. */}\n {!many && single && assets ? (\n <AssetOptionsDialog\n assetId={single}\n readOnly={readOnly}\n open={editing}\n onOpenChange={setEditing}\n />\n ) : null}\n\n <AssetPicker open={open} onOpenChange={setOpen} {...pickerBinding} />\n\n <AssetRejectedNotice rejected={rejected} accept={field.accept} />\n\n {dragging ? (\n <div className=\"pointer-events-none absolute inset-0 z-10 grid place-items-center rounded-lg border-2 border-dashed border-primary bg-background/85\">\n <Row gap=\"2\" align=\"center\">\n <Upload className=\"size-4 text-primary\" />\n <Text size=\"sm\" className=\"font-medium\">\n {translate('cms.asset.dropToUpload')}\n </Text>\n </Row>\n </div>\n ) : null}\n </Stack>\n )\n}\n\n/**\n * Empty-state placeholder shown when an asset field has no selection (EC-301).\n *\n * EC-395: this was the one asset-field affordance that ignored `readOnly` — every\n * sibling control (`selectButton`, the per-item remove/change actions) already\n * degraded, but the empty-state tile stayed a clickable `<button>` that opened the\n * picker regardless. A locked, empty asset field must be as inert as a filled one:\n * read-only renders a plain (non-interactive, non-focusable) `div` with the same\n * frame — no `onClick`, no hover affordance — instead of a `<button>`.\n */\nfunction EmptyAssetState({\n compact,\n canUpload,\n readOnly,\n onSelect,\n}: {\n compact: boolean\n canUpload: boolean\n readOnly: boolean\n onSelect: () => void\n}) {\n const frame = cn(\n 'grid w-full place-items-center gap-1 rounded-lg border border-dashed border-border bg-muted/30 text-center text-muted-foreground',\n compact ? 'aspect-video' : 'aspect-[5/2]',\n )\n if (readOnly) {\n return <div className={frame}>{emptyAssetStateBody(canUpload)}</div>\n }\n return (\n <button type=\"button\" onClick={onSelect} className={cn(frame, 'transition-colors hover:bg-muted/60')}>\n {emptyAssetStateBody(canUpload)}\n </button>\n )\n}\n\nfunction emptyAssetStateBody(canUpload: boolean) {\n return (\n <>\n <ImagePlus className=\"size-6\" aria-hidden />\n <Text size=\"xs\" tone=\"muted\">\n {canUpload ? translate('cms.asset.selectOrDrop') : translate('cms.asset.selectAsset')}\n </Text>\n </>\n )\n}\n\n/** A large focal-aware preview of the selected asset (EC-301). */\nfunction AssetPreview({ assetId }: { assetId: string }) {\n const record = useAssetRecord(assetId)\n return <AssetThumb record={record} className=\"aspect-[4/3] w-full\" />\n}\n\n/** Filename + dimensions/type for the selected asset — the id is never shown (EC-301). */\nfunction AssetInfo({ assetId, fallbackTitle }: { assetId: string; fallbackTitle?: string }) {\n const record = useAssetRecord(assetId)\n const name = record?.title ?? record?.filename ?? fallbackTitle ?? assetId\n const dims = record?.width && record?.height ? `${record.width}×${record.height}` : null\n const kind = record?.contentType?.split('/')[1]?.toUpperCase()\n const meta = [dims, kind].filter(Boolean).join(' · ')\n return (\n <Stack gap=\"1\">\n <Text size=\"sm\" className=\"truncate font-medium\" title={name}>\n {name}\n </Text>\n {meta ? (\n <Text size=\"xs\" tone=\"subtle\">\n {meta}\n </Text>\n ) : null}\n </Stack>\n )\n}\n\n/**\n * The selected image's alt text shown in the field (EC-301) — what an editor most\n * wants at a glance; edited in the options dialog. Reads `meta.alt` with the legacy\n * `alt` column as fallback, matching delivery.\n */\nfunction AssetAltLine({ assetId }: { assetId: string }) {\n const record = useAssetRecord(assetId)\n const metaAlt = typeof record?.meta?.alt === 'string' ? record.meta.alt : undefined\n const alt = metaAlt ?? record?.alt\n return (\n <Row gap=\"1\" align=\"baseline\" className=\"min-w-0\">\n <Text size=\"xs\" tone=\"subtle\" className=\"shrink-0\">\n {translate('cms.asset.altLabel')}\n </Text>\n <Text\n size=\"xs\"\n tone={alt ? 'muted' : 'subtle'}\n className={cn('min-w-0 truncate', !alt && 'italic')}\n title={alt ?? undefined}\n >\n {alt || translate('cms.asset.notSet')}\n </Text>\n </Row>\n )\n}\n\n/** A single asset reference: focal-aware thumbnail + title, with an optional remove. */\nfunction AssetChip({\n assetId,\n collectionId,\n onRemove,\n}: {\n assetId: string\n collectionId: string\n onRemove?: () => void\n}) {\n const source = useCollectionView()\n const record = useAssetRecord(assetId)\n const title = source.titleOf({ collection: collectionId, id: assetId })\n return (\n <div className=\"group relative overflow-hidden rounded-lg border border-border bg-muted/40\">\n <AssetThumb record={record} alt={title} className=\"aspect-square w-full rounded-none border-0\" />\n <Row gap=\"1\" align=\"center\" className=\"px-2 py-1\">\n <Text size=\"xs\" tone=\"subtle\" className=\"min-w-0 flex-1 truncate\" title={title}>\n {title}\n </Text>\n {onRemove ? (\n <button\n type=\"button\"\n aria-label={translate('common.remove')}\n onClick={onRemove}\n className=\"text-muted-foreground hover:text-destructive\"\n >\n <X className=\"size-3.5\" />\n </button>\n ) : null}\n </Row>\n </div>\n )\n}\n\n/**\n * The selected single asset's preview row (EC-301): large focal-aware preview + info\n * column (filename/dimensions, Select/Edit/Remove, alt). Extracted so it renders either\n * bare (a plain asset field) or inside the grouping card when the field carries per-use\n * sub-fields — the asset + its fields then read as one unit.\n */\nfunction AssetSinglePreview({\n assetId,\n compact,\n readOnly,\n hasLibrary,\n fallbackTitle,\n selectButton,\n onEdit,\n onRemove,\n}: {\n assetId: string\n compact: boolean\n readOnly: boolean\n hasLibrary: boolean\n fallbackTitle?: string\n selectButton: React.ReactNode\n onEdit: () => void\n onRemove: () => void\n}) {\n return (\n <div className={compact ? 'flex flex-col gap-3' : 'flex gap-4'}>\n <div className={compact ? 'w-full' : 'w-1/2 shrink-0'}>\n <AssetPreview assetId={assetId} />\n </div>\n <Stack gap=\"2\" className={cn('min-w-0', !compact && 'flex-1')}>\n <AssetInfo assetId={assetId} fallbackTitle={fallbackTitle} />\n {!readOnly ? (\n <Row gap=\"2\" wrap align=\"center\">\n {selectButton}\n {hasLibrary ? (\n <Button variant=\"outline\" size=\"sm\" onClick={onEdit}>\n <SlidersHorizontal /> {translate('common.edit')}\n </Button>\n ) : null}\n <Button variant=\"ghost\" size=\"sm\" onClick={onRemove}>\n <Trash2 /> {translate('common.remove')}\n </Button>\n </Row>\n ) : null}\n <AssetAltLine assetId={assetId} />\n </Stack>\n </div>\n )\n}\n\n/**\n * One gallery placement when a `many` asset field declares per-use sub-fields (EC-301):\n * a thumbnail + filename + remove header over the placement's own sub-field form,\n * modelled on a repeater item card so a gallery's placements read as grouped units.\n */\nfunction AssetGalleryItem({\n assetId,\n collectionId,\n fields,\n value,\n onChangeSubs,\n onRemove,\n readOnly,\n idPrefix,\n}: {\n assetId: string\n collectionId: string\n fields: readonly FieldDef[]\n value: Record<string, unknown>\n onChangeSubs: (value: Record<string, unknown>) => void\n onRemove?: () => void\n readOnly: boolean\n idPrefix: string\n}) {\n const source = useCollectionView()\n const record = useAssetRecord(assetId)\n const title = source.titleOf({ collection: collectionId, id: assetId })\n return (\n <Card className=\"border-l-2 border-l-border p-0\">\n <Row gap=\"2\" align=\"center\" className=\"border-b border-border/60 bg-muted/30 px-2 py-1.5\">\n <AssetThumb record={record} alt={title} className=\"size-10 shrink-0\" />\n <Text size=\"sm\" className=\"min-w-0 flex-1 truncate font-medium\" title={title}>\n {title}\n </Text>\n {onRemove ? (\n <Button variant=\"ghost\" size=\"sm\" aria-label={translate('common.remove')} onClick={onRemove}>\n <Trash2 className=\"size-3.5\" />\n </Button>\n ) : null}\n </Row>\n <div className=\"px-3 pb-3\">\n <ObjectItemForm\n fields={fields}\n value={value}\n onChange={onChangeSubs}\n readOnly={readOnly}\n idPrefix={idPrefix}\n />\n </div>\n </Card>\n )\n}\n\n/**\n * The image options dialog (EC-301): the focal point (engine-special visual\n * control) + the asset's **schema-driven** metadata, rendered from the\n * `kind:'asset'` collection's `fields` through the same generic field engine\n * documents use — so a project declares asset metadata (alt, copyright, credit, …)\n * in code, not hardcoded here. Edits persist on the asset, shared across every use.\n * Metadata is buffered locally and committed once on close (one write); the focal\n * point commits immediately (it drives a live visual). No-ops without a library.\n */\nfunction AssetOptionsDialog({\n assetId,\n readOnly,\n open,\n onOpenChange,\n}: {\n assetId: string\n readOnly: boolean\n open: boolean\n onOpenChange: (open: boolean) => void\n}) {\n const source = useCollectionView()\n const assets = useOptionalAssets()\n const record = useAssetRecord(assetId)\n const metaFields = useMemo(() => {\n const assetCollectionId = source.assetCollectionId ?? 'asset'\n const collection = source.collections.find((c) => c.id === assetCollectionId)\n return collection ? assetMetaFieldsOf(collection) : []\n }, [source])\n\n // Local edit buffers, seeded once per open from the record (so an external edit\n // still wins between opens). Metadata is non-localized in v1 (the asset\n // resolution path is locale-agnostic) — localized asset metadata is a follow-up.\n const [meta, setMeta] = useState<Record<string, unknown>>({})\n const [focal, setFocal] = useState<{ x: number; y: number } | undefined>(undefined)\n const dirty = useRef(false)\n const seeded = useRef(false)\n useEffect(() => {\n if (!open) {\n seeded.current = false\n return\n }\n if (seeded.current || !record) return\n // Seed from the meta bag, but lift the legacy `alt` column in when meta lacks\n // it (EC-301 transition): a not-yet-migrated asset still shows its alt, and\n // saving writes it into meta — a lazy migration-on-edit, matching delivery's\n // `meta.alt ?? record.alt` read fallback.\n const seedMeta = { ...(record.alt !== undefined ? { alt: record.alt } : {}), ...(record.meta ?? {}) }\n setMeta(seedMeta)\n setFocal(record.focalPoint)\n dirty.current = false\n seeded.current = true\n }, [open, record])\n\n function handleOpenChange(next: boolean) {\n // Commit the buffered metadata once, on close (one write, not per keystroke).\n if (!next && !readOnly && assets && dirty.current) {\n void assets.updateMetadata(assetId, { meta: pruneMeta(meta) })\n dirty.current = false\n }\n onOpenChange(next)\n }\n\n function setField(name: string, value: unknown) {\n dirty.current = true\n setMeta((m) => ({ ...m, [name]: value }))\n }\n\n // The focal point drives a live crop preview → commit immediately (optimistic\n // marker), not deferred to close.\n function commitFocal(point: { x: number; y: number } | null) {\n if (readOnly || !assets) return\n setFocal(point ?? undefined)\n void assets.updateMetadata(assetId, { focalPoint: point })\n }\n\n return (\n <Dialog open={open} onOpenChange={handleOpenChange}>\n <DialogContent className=\"max-w-lg\">\n <DialogHeader>\n <DialogTitle>{translate('cms.asset.imageOptions.title')}</DialogTitle>\n <DialogDescription>\n {translate('cms.asset.imageOptions.description')}\n </DialogDescription>\n </DialogHeader>\n <Stack gap=\"3\">\n <Stack gap=\"2\">\n <Row align=\"center\">\n <Text size=\"sm\" className=\"font-medium\">\n {translate('cms.asset.focalPoint')}\n </Text>\n <Spacer />\n {focal && !readOnly ? (\n <Button variant=\"ghost\" size=\"sm\" onClick={() => commitFocal(null)}>\n <RotateCcw /> {translate('cms.asset.resetToCentre')}\n </Button>\n ) : null}\n </Row>\n <FocalPointPicker\n record={record}\n point={focal}\n onChange={commitFocal}\n onPreview={setFocal}\n disabled={readOnly}\n />\n <Text size=\"xs\" tone=\"subtle\">\n {translate('cms.asset.focalPointHint')}\n </Text>\n <FocalCropPreviews record={record} point={focal} />\n </Stack>\n {metaFields.map((f) => (\n <Field key={f.name} label={fieldLabel(f)} hint={localizedLabel(f.form?.description) || undefined}>\n <FieldControl\n id={`asset-meta-${f.name}`}\n field={f}\n value={meta[f.name]}\n readOnly={readOnly}\n onChange={(v) => setField(f.name, v)}\n />\n </Field>\n ))}\n {metaFields.length === 0 ? (\n <Text size=\"sm\" tone=\"subtle\">\n No editable metadata fields are declared on the asset collection. Add fields (e.g.\n <Code tone=\"muted\"> alt</Code>, <Code tone=\"muted\">copyright</Code>) to it in code.\n </Text>\n ) : null}\n </Stack>\n </DialogContent>\n </Dialog>\n )\n}\n"]}