@elytracms/studio 0.0.94 → 0.0.95
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/convex/asset-alt-migration.test.ts +2 -2
- package/convex/asset-meta-patch.test.ts +218 -0
- package/convex/asset-replace-delete-race.test.ts +127 -0
- package/convex/assets.ts +27 -2
- package/convex/delivery-redirects.test.ts +167 -0
- package/convex/delivery.ts +15 -5
- package/convex/rail-write-parity.test.ts +6 -6
- package/convex/redirects.ts +1 -1
- package/convex/schema.ts +6 -0
- package/dist/addon.d.ts +1 -1
- package/dist/addon.js.map +1 -1
- package/dist/components/assets/asset-detail.js +26 -11
- package/dist/components/assets/asset-detail.js.map +1 -1
- package/dist/components/assets/asset-locale.d.ts +9 -0
- package/dist/components/assets/asset-locale.js +16 -0
- package/dist/components/assets/asset-locale.js.map +1 -0
- package/dist/components/assets/asset-meta-label.d.ts +12 -0
- package/dist/components/assets/asset-meta-label.js +15 -0
- package/dist/components/assets/asset-meta-label.js.map +1 -0
- package/dist/components/assets/asset-metadata.d.ts +69 -15
- package/dist/components/assets/asset-metadata.js +99 -11
- package/dist/components/assets/asset-metadata.js.map +1 -1
- package/dist/components/assets/asset-search.js +8 -2
- package/dist/components/assets/asset-search.js.map +1 -1
- package/dist/components/assets/asset-thumb.js +6 -2
- package/dist/components/assets/asset-thumb.js.map +1 -1
- package/dist/components/assets/assets-collection-view-source.d.ts +23 -9
- package/dist/components/assets/assets-collection-view-source.js +78 -26
- package/dist/components/assets/assets-collection-view-source.js.map +1 -1
- package/dist/components/assets/assets-store.js +17 -4
- package/dist/components/assets/assets-store.js.map +1 -1
- package/dist/components/builder/builder-store.d.ts +21 -0
- package/dist/components/builder/builder-store.js +22 -0
- package/dist/components/builder/builder-store.js.map +1 -1
- package/dist/components/builder/canvas-commit-boundary.d.ts +22 -0
- package/dist/components/builder/canvas-commit-boundary.js +27 -0
- package/dist/components/builder/canvas-commit-boundary.js.map +1 -0
- package/dist/components/builder/canvas-rebase.d.ts +77 -0
- package/dist/components/builder/canvas-rebase.js +169 -0
- package/dist/components/builder/canvas-rebase.js.map +1 -0
- package/dist/components/builder/canvas-render-context.d.ts +6 -3
- package/dist/components/builder/canvas-render-context.js +17 -9
- package/dist/components/builder/canvas-render-context.js.map +1 -1
- package/dist/components/builder/composition-field-editor.d.ts +20 -2
- package/dist/components/builder/composition-field-editor.js +45 -27
- package/dist/components/builder/composition-field-editor.js.map +1 -1
- package/dist/components/builder/node-factory.js +15 -3
- package/dist/components/builder/node-factory.js.map +1 -1
- package/dist/components/builder/property-inspector.js +3 -1
- package/dist/components/builder/property-inspector.js.map +1 -1
- package/dist/components/builder/run-inspector.js +10 -6
- package/dist/components/builder/run-inspector.js.map +1 -1
- package/dist/components/cms/asset-field.d.ts +5 -2
- package/dist/components/cms/asset-field.js +34 -17
- package/dist/components/cms/asset-field.js.map +1 -1
- package/dist/components/cms/asset-picker.js +3 -1
- package/dist/components/cms/asset-picker.js.map +1 -1
- package/dist/components/cms/canvas-commit.d.ts +56 -0
- package/dist/components/cms/canvas-commit.js +35 -0
- package/dist/components/cms/canvas-commit.js.map +1 -0
- package/dist/components/cms/collection-table.js +23 -7
- package/dist/components/cms/collection-table.js.map +1 -1
- package/dist/components/cms/commit-policy.d.ts +6 -2
- package/dist/components/cms/commit-policy.js +1 -1
- package/dist/components/cms/commit-policy.js.map +1 -1
- package/dist/components/cms/detail-editor.js +11 -1
- package/dist/components/cms/detail-editor.js.map +1 -1
- package/dist/components/cms/use-document-draft.d.ts +62 -4
- package/dist/components/cms/use-document-draft.js +102 -32
- package/dist/components/cms/use-document-draft.js.map +1 -1
- package/dist/components/cms/use-live-url.d.ts +6 -1
- package/dist/components/cms/use-live-url.js +12 -9
- package/dist/components/cms/use-live-url.js.map +1 -1
- package/dist/components/collection-view/cms-collection-view-source.js +9 -5
- package/dist/components/collection-view/cms-collection-view-source.js.map +1 -1
- package/dist/components/deployment-setup-gate.js +10 -1
- package/dist/components/deployment-setup-gate.js.map +1 -1
- package/dist/components/records/record-lens.js +3 -8
- package/dist/components/records/record-lens.js.map +1 -1
- package/dist/lib/assets/upload.d.ts +25 -7
- package/dist/lib/assets/upload.js +30 -21
- package/dist/lib/assets/upload.js.map +1 -1
- package/dist/lib/cms/published-documents.d.ts +24 -6
- package/dist/lib/cms/published-documents.js +39 -6
- package/dist/lib/cms/published-documents.js.map +1 -1
- package/dist/lib/cms-views/view-model.d.ts +14 -101
- package/dist/lib/cms-views/view-model.js +20 -101
- package/dist/lib/cms-views/view-model.js.map +1 -1
- package/dist/lib/collection-view/collection-view-source.d.ts +6 -1
- package/dist/lib/collection-view/collection-view-source.js.map +1 -1
- package/dist/lib/engine-version.d.ts +1 -1
- package/dist/lib/engine-version.js +1 -1
- package/dist/lib/engine-version.js.map +1 -1
- package/dist/lib/i18n/messages.de.js +7 -3
- package/dist/lib/i18n/messages.de.js.map +1 -1
- package/dist/lib/i18n/messages.en.d.ts +5 -1
- package/dist/lib/i18n/messages.en.js +8 -4
- package/dist/lib/i18n/messages.en.js.map +1 -1
- package/dist/lib/operations/config-manifest-lookup.d.ts +1 -1
- package/dist/lib/operations/config-manifest-lookup.js +3 -3
- package/dist/lib/operations/config-manifest-lookup.js.map +1 -1
- package/dist/lib/persistence/deployment-readiness.js +14 -11
- package/dist/lib/persistence/deployment-readiness.js.map +1 -1
- package/dist/lib/persona/labels.js +1 -0
- package/dist/lib/persona/labels.js.map +1 -1
- package/dist/lib/query/index.d.ts +2 -2
- package/dist/lib/query/index.js +1 -1
- package/dist/lib/query/index.js.map +1 -1
- package/dist/lib/query/operations-query.d.ts +26 -0
- package/dist/lib/query/operations-query.js +12 -0
- package/dist/lib/query/operations-query.js.map +1 -1
- package/dist/lib/registry/index.js +2 -2
- package/dist/lib/registry/index.js.map +1 -1
- package/dist/lib/registry/preview.d.ts +10 -0
- package/dist/lib/registry/preview.js +13 -4
- package/dist/lib/registry/preview.js.map +1 -1
- package/dist/lib/routes/route-issues.js +6 -3
- package/dist/lib/routes/route-issues.js.map +1 -1
- package/dist/lib/workspace/workspace-source.js +6 -7
- package/dist/lib/workspace/workspace-source.js.map +1 -1
- package/dist/previews/entry-node.d.ts +14 -0
- package/dist/previews/entry-node.js +22 -0
- package/dist/previews/entry-node.js.map +1 -0
- package/dist/previews/harness.js +2 -16
- package/dist/previews/harness.js.map +1 -1
- package/package.json +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assets-collection-view-source.js","sourceRoot":"","sources":["../../../src/components/assets/assets-collection-view-source.ts"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useMemo } from 'react'\nimport type { CmsDocument, CollectionDef, DocumentRef, Locale, LocaleConfig } from '@elytracms/core/cms-core'\nimport { createCollectionRegistry, validateDocument } from '@elytracms/core/cms-core'\nimport { assetAlt } from '@elytracms/core/persistence'\nimport type { AssetRecord, ReferenceTarget } from '@elytracms/core/persistence'\nimport type { OperationError } from '@elytracms/core/operations'\nimport { studioPath } from '@elytracms/studio/router'\nimport type { DeleteAssetResult } from '../../lib/assets'\nimport type { CollectionViewSource, CollectionViewCapabilities } from '../../lib/collection-view/collection-view-source'\nimport { useAssets } from './assets-store'\nimport { SYSTEM_ASSET_FIELDS, pruneMeta } from './asset-metadata'\nimport { metaBagProjection, normalizeOkResult, useRailViewSource } from '../rail-view-source'\nimport { parseLocaleConfig } from '@elytracms/core/cms-core'\n\n/** The assets rail is not localized in v1 (EC-301: asset metadata is non-localized). */\nconst ASSETS_LOCALE: Locale = 'en'\nconst ASSETS_LOCALE_CONFIG: LocaleConfig = parseLocaleConfig({\n default: ASSETS_LOCALE,\n locales: [ASSETS_LOCALE],\n})\n\n/**\n * The capability profile of the assets rail (EC-319, EC-433). Hoisted to a\n * module-level constant (no prop/state dependency) so it's both the value the\n * hook returns AND independently unit-testable.\n *\n * `create` is off — a new asset comes from an upload, never a blank draft.\n * `remove` + `bulk` are ON since EC-433: the list's row/bulk delete used to be\n * off deliberately because the generic `ReferenceConfirmDialog` enumerated\n * inbound references as `{ type: 'document' }` targets — the wrong index shape\n * for an asset — which would have shown \"not referenced\" for an asset that IS\n * referenced. That pre-flight now queries the shape THIS source declares via\n * {@link assetReferenceTarget} (`{ type: 'asset' }`, the same lookup the\n * detail surface runs), and the confirmed delete dispatches the SAME checked\n * store delete the detail surface uses (`deleteAsset`: original + EC-400 thumb\n * blobs, then the record — EC-420 lqips ride on the record). Nothing else\n * applies to assets: no publish/versions/locales/inline-edit/live-URL/\n * hierarchy/import-export.\n */\nexport const ASSET_COLLECTION_CAPABILITIES: CollectionViewCapabilities = {\n publish: false,\n versions: false,\n locales: false,\n inlineEdit: false,\n create: false,\n duplicate: false, // EC-442: blob duplication is a different feature, out of scope\n remove: true,\n bulk: true,\n liveUrl: false,\n hierarchy: false,\n importExport: false,\n adminOnly: false,\n}\n\n/** Read one of the record's fixed, upload-managed columns by field name. */\nfunction systemFieldValue(record: AssetRecord, name: string): unknown {\n switch (name) {\n case 'filename':\n return record.filename\n case 'url':\n return record.url\n case 'contentType':\n return record.contentType\n case 'byteSize':\n return record.byteSize\n case 'width':\n return record.width\n case 'height':\n return record.height\n case 'storageKey':\n return record.storageKey\n case 'title':\n return record.title\n default:\n return undefined\n }\n}\n\n/**\n * Project a stored asset record into the lens's `values` shape (EC-319) — the\n * value-keyed projection the redirects/users rails use (EC-577, RESIDUE-3d): EVERY\n * key of the record's open `meta` bag (EC-301) rides in `values`, declared by the\n * schema or not, so a save hands the bag back whole (D22). Declared fields then\n * take their canonical reading on top — a column never loses to a same-named meta\n * key: a system column (`filename`, `url`, dimensions, …) reads straight off the\n * record, and `alt` reads through THE reader (`assetAlt`, EC-587 — `meta.alt`, with a\n * not-yet-migrated legacy column lifted in), exactly what delivery and the image\n * field's options dialog show.\n */\nexport function assetToDocument(collection: CollectionDef, record: AssetRecord): CmsDocument {\n const values: Record<string, unknown> = { ...(record.meta ?? {}) }\n for (const field of collection.fields) {\n if (field.name === 'alt') {\n const alt = assetAlt(record)\n if (alt !== null) values.alt = alt\n else delete values.alt\n } else if (SYSTEM_ASSET_FIELDS.has(field.name)) {\n values[field.name] = systemFieldValue(record, field.name)\n }\n }\n return { collection: collection.id, id: record.id, values }\n}\n\n/**\n * The inverse of {@link assetToDocument}: the meta bag a save writes back — every\n * value outside the system set (EC-301/EC-311), declared or not, through the ONE\n * value-keyed projection the users/redirects rails share (EC-577, RESIDUE-3d).\n * `meta` is written whole (it replaces the stored bag), so a key the lens never\n * declared must be in it: the schema-driven projection this replaced dropped them\n * on every rail save (D22), while the options dialog and asset detail kept them.\n * System columns are upload-managed and never written here.\n */\nexport function documentToAssetMetaPatch(doc: CmsDocument): Record<string, unknown> {\n return pruneMeta(metaBagProjection(doc.values ?? {}, SYSTEM_ASSET_FIELDS))\n}\n\n/** `create` when `capabilities.create === false` — never reached from the UI\n * (list/detail never call it); an explicit throw rather than a silent no-op. */\nfunction createUnsupported(): DocumentRef {\n throw new Error('assets are created by upload, not the assets rail')\n}\n\n/** Assets' `remove()` returns an `outcome`-discriminated {@link DeleteAssetResult}\n * (not the `{ ok }` shape users/redirects use, EC-327). `'error'` surfaces as-is;\n * `'blocked'` (the unconfirmed usage gate fired — nothing was removed) surfaces\n * as an explicit failure too (EC-433): a delete that left the asset in place\n * must NEVER look like success. It is unreachable from this rail's UI — every\n * delete path confirms through the reference dialog and dispatches with\n * `confirmInUse` — but stays honest for any direct caller. `'not-found'` maps\n * to success (idempotent delete: the desired end state — gone — holds). */\nexport function normalizeAssetRemoveResult(result: DeleteAssetResult): OperationError | null {\n if (result.outcome === 'error') return { code: 'unknown', message: result.message }\n if (result.outcome === 'blocked') {\n return {\n code: 'unknown',\n message: `Asset is referenced in ${result.usageCount} ${result.usageCount === 1 ? 'place' : 'places'} and was not deleted.`,\n }\n }\n return null\n}\n\n/**\n * How an asset row is addressed in the EC-155 reference index (EC-433):\n * `{ type: 'asset' }`, never the document shape — so the delete confirmation's\n * inbound pre-flight counts the SAME references `loadInboundAssetReferenceViews`\n * (the detail surface's check) does.\n */\nexport function assetReferenceTarget(ref: DocumentRef): ReferenceTarget {\n return { type: 'asset', id: ref.id }\n}\n\n/**\n * Assemble a {@link CollectionViewSource} for the **assets rail** (EC-319) — the\n * portable list surface over the existing asset library store instead of a\n * bespoke listing. `schema` is the DEPLOYMENT-resolved `kind:'asset'` collection\n * (EC-311: a project's own declared fields, or a mount's `nav.assets({ fields })`\n * override merged over it — see `resolveAssetsSchema` in `app-views/default-modules.tsx`),\n * never a hardcoded engine default — so a project's `copyright`/`credit`/… fields\n * (and any EC-311 override) drive the list's columns/filters exactly as they\n * drive the image field's options dialog.\n *\n * Must be mounted under an `<AssetsProvider>` (for {@link useAssets}) — already\n * ambient at the project level (EC-195), so callers rarely mount their own.\n *\n * The shared scaffold (`useRailViewSource`, EC-327) owns `viewState`/`lastError`\n * plumbing and the assembled source; this hook supplies only the asset-store\n * reads/writes, the capability profile, and the projections above.\n */\nexport function useAssetsCollectionViewSource(\n projectId: string,\n schema: CollectionDef,\n): CollectionViewSource {\n const store = useAssets()\n const registry = useMemo(() => createCollectionRegistry([schema]), [schema])\n\n const documents = useCallback(\n () => store.assets.map((record) => assetToDocument(schema, record)),\n [store.assets, schema],\n )\n const get = useCallback(\n (ref: DocumentRef): CmsDocument | undefined => {\n const record = store.assets.find((a) => a.id === ref.id)\n return record ? assetToDocument(schema, record) : undefined\n },\n [store.assets, schema],\n )\n const titleOf = useCallback(\n (ref: DocumentRef): string => {\n const record = store.assets.find((a) => a.id === ref.id)\n return record?.title ?? record?.filename ?? ref.id\n },\n [store.assets],\n )\n const validate = useCallback(\n (doc: CmsDocument) => validateDocument(registry, doc, { localeConfig: ASSETS_LOCALE_CONFIG }),\n [registry],\n )\n const save = useCallback(\n (doc: CmsDocument) => store.updateMetadata(doc.id, { meta: documentToAssetMetaPatch(doc) }),\n [store],\n )\n // The unconfirmed delete (the store's usage gate still applies — a referenced\n // asset comes back `blocked`, surfaced via `lastError`). The UI's delete paths\n // never land here: row/bulk deletes confirm through the reference dialog and\n // dispatch `bulkRemove` below.\n const remove = useCallback((ref: DocumentRef) => store.remove(ref.id), [store])\n // The CONFIRMED delete the bulk/row path dispatches (EC-433): the reference\n // dialog has already enumerated inbound usages (asset-shaped, see\n // `assetReferenceTarget`), so this passes `confirmInUse` exactly like the\n // detail surface's confirmed delete — same blob + thumb + cache handling.\n const bulkRemove = useCallback(\n (ref: DocumentRef) => store.remove(ref.id, { confirmInUse: true }),\n [store],\n )\n\n return useRailViewSource({\n projectId,\n schema,\n capabilities: ASSET_COLLECTION_CAPABILITIES,\n localeConfig: ASSETS_LOCALE_CONFIG,\n documents,\n get,\n titleOf,\n // No `referenceCandidates`/`collections`/`isFieldReadOnly` override: assets carry\n // no relation/asset fields of their own (v1) — nothing cross-collection to\n // resolve — and read-only follows the factory's default `form.readOnly` check.\n detailPath: (id: string) => studioPath.asset(id),\n listPath: studioPath.assets(),\n // EC-433: the delete confirmation must enumerate ASSET-shaped inbound\n // references — the document-shaped default would report \"not referenced\"\n // for a referenced asset.\n referenceTarget: assetReferenceTarget,\n validate,\n create: createUnsupported,\n save,\n remove,\n bulkRemove,\n normalizeSaveResult: normalizeOkResult,\n normalizeRemoveResult: normalizeAssetRemoveResult,\n })\n}\n"]}
|
|
1
|
+
{"version":3,"file":"assets-collection-view-source.js","sourceRoot":"","sources":["../../../src/components/assets/assets-collection-view-source.ts"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useMemo } from 'react'\nimport type { CmsDocument, CollectionDef, DocumentRef, Locale, LocaleConfig } from '@elytracms/core/cms-core'\nimport { createCollectionRegistry, semanticValueEqual, validateDocument } from '@elytracms/core/cms-core'\nimport { assetAlt, assetMetaView } from '@elytracms/core/persistence'\nimport type { AssetReadLocale, AssetRecord, ReferenceTarget } from '@elytracms/core/persistence'\nimport type { OperationError } from '@elytracms/core/operations'\nimport { studioPath } from '@elytracms/studio/router'\nimport type { DeleteAssetResult } from '../../lib/assets'\nimport type { CollectionViewSource, CollectionViewCapabilities } from '../../lib/collection-view/collection-view-source'\nimport { useAssets } from './assets-store'\nimport { SYSTEM_ASSET_FIELDS, assetMetaDraft, assetMetaDraftPatch, pruneMeta, setAssetMetaField } from './asset-metadata'\nimport type { AssetMetadataPatch } from './asset-metadata'\nimport { useAssetReadLocale } from './asset-locale'\nimport { metaBagProjection, normalizeOkResult, useRailViewSource } from '../rail-view-source'\nimport { parseLocaleConfig } from '@elytracms/core/cms-core'\n\n/**\n * The assets rail has no locale switcher of its own (capability `locales: false`): it\n * reads in ONE locale — the studio's (EC-632), a localized field's value in that locale —\n * so its lens is a one-locale document in that locale. Before a project's locales are\n * installed, the engine's neutral `en` (shared metadata only: nothing is localized then).\n */\nconst ASSETS_LOCALE: Locale = 'en'\nfunction railLocaleConfig(at: AssetReadLocale | null): LocaleConfig {\n const locale = at?.locale ?? ASSETS_LOCALE\n return parseLocaleConfig({ default: locale, locales: [locale] })\n}\n\n/**\n * The capability profile of the assets rail (EC-319, EC-433). Hoisted to a\n * module-level constant (no prop/state dependency) so it's both the value the\n * hook returns AND independently unit-testable.\n *\n * `create` is off — a new asset comes from an upload, never a blank draft.\n * `remove` + `bulk` are ON since EC-433: the list's row/bulk delete used to be\n * off deliberately because the generic `ReferenceConfirmDialog` enumerated\n * inbound references as `{ type: 'document' }` targets — the wrong index shape\n * for an asset — which would have shown \"not referenced\" for an asset that IS\n * referenced. That pre-flight now queries the shape THIS source declares via\n * {@link assetReferenceTarget} (`{ type: 'asset' }`, the same lookup the\n * detail surface runs), and the confirmed delete dispatches the SAME checked\n * store delete the detail surface uses (`deleteAsset`: original + EC-400 thumb\n * blobs, then the record — EC-420 lqips ride on the record). Nothing else\n * applies to assets: no publish/versions/locales/inline-edit/live-URL/\n * hierarchy/import-export.\n */\nexport const ASSET_COLLECTION_CAPABILITIES: CollectionViewCapabilities = {\n publish: false,\n versions: false,\n locales: false,\n inlineEdit: false,\n create: false,\n duplicate: false, // EC-442: blob duplication is a different feature, out of scope\n remove: true,\n bulk: true,\n liveUrl: false,\n hierarchy: false,\n importExport: false,\n adminOnly: false,\n}\n\n/** Read one of the record's fixed, upload-managed columns by field name. */\nfunction systemFieldValue(record: AssetRecord, name: string): unknown {\n switch (name) {\n case 'filename':\n return record.filename\n case 'url':\n return record.url\n case 'contentType':\n return record.contentType\n case 'byteSize':\n return record.byteSize\n case 'width':\n return record.width\n case 'height':\n return record.height\n case 'storageKey':\n return record.storageKey\n case 'title':\n return record.title\n default:\n return undefined\n }\n}\n\n/**\n * Project a stored asset record into the lens's `values` shape (EC-319) — the\n * value-keyed projection the redirects/users rails use (EC-577, RESIDUE-3d): EVERY\n * key of the record's open `meta` bag (EC-301) rides in `values`, declared by the\n * schema or not, so a save hands the bag back whole (D22). Declared fields then\n * take their canonical reading on top — a column never loses to a same-named meta\n * key: a system column (`filename`, `url`, dimensions, …) reads straight off the\n * record, and `alt` reads through THE reader (`assetAlt`, EC-587 — `meta.alt`, with a\n * not-yet-migrated legacy column lifted in), exactly what delivery and the image\n * field's options dialog show.\n *\n * EC-632: the values are the metadata's view in `at` (the studio's locale) — a\n * `localized: true` field shows that locale's value, with delivery's fallback, held in\n * the lens's one locale bucket (`localized[at.locale]`, the rail's locale), so the table\n * reads it as that locale's value rather than as a fallback.\n */\nexport function assetToDocument(collection: CollectionDef, record: AssetRecord, at: AssetReadLocale | null): CmsDocument {\n const values: Record<string, unknown> = assetMetaView(record, at)\n for (const field of collection.fields) {\n if (field.name === 'alt') {\n const alt = assetAlt(record, at)\n if (alt !== null) values.alt = alt\n else delete values.alt\n } else if (SYSTEM_ASSET_FIELDS.has(field.name)) {\n values[field.name] = systemFieldValue(record, field.name)\n }\n }\n if (at === null) return { collection: collection.id, id: record.id, values }\n const bucket: Record<string, unknown> = {}\n for (const field of collection.fields) {\n if (field.localized !== true || !Object.prototype.hasOwnProperty.call(values, field.name)) continue\n bucket[field.name] = values[field.name]\n delete values[field.name]\n }\n return Object.keys(bucket).length > 0\n ? { collection: collection.id, id: record.id, values, localized: { [at.locale]: bucket } }\n : { collection: collection.id, id: record.id, values }\n}\n\n/**\n * The inverse of {@link assetToDocument}: the meta bag a lens document holds — every\n * value outside the system set (EC-301/EC-311), declared or not, through the ONE\n * value-keyed projection the users/redirects rails share (EC-577, RESIDUE-3d). A key\n * the lens never declared is in it (the schema-driven projection this replaced dropped\n * them, D22). System columns are upload-managed and never written here.\n */\nexport function documentToAssetMeta(doc: CmsDocument): Record<string, unknown> {\n // EC-632: the lens's one locale bucket (a localized field's value) reads beside `values`.\n const localized = Object.values(doc.localized ?? {}).reduce<Record<string, unknown>>((all, bag) => ({ ...all, ...bag }), {})\n return pruneMeta(metaBagProjection({ ...(doc.values ?? {}), ...localized }, SYSTEM_ASSET_FIELDS))\n}\n\n/**\n * The rail save's metadata write (EC-634 × EC-632): only the keys whose value differs\n * between the document as the editor OPENED it (`base`, forwarded by the edit shell —\n * EC-616) and the one it saves, each written where its field lives — a `localized: true`\n * field into `at`'s bucket, any other into the shared bag (`setAssetMetaField`, the\n * editors' one rule) — as the per-key, per-locale patch against the record as stored. A\n * key this editor did not change is never written. Without a base every key of the saved\n * bag is set and none is removed.\n */\nexport function assetRailMetaPatch(\n collection: CollectionDef,\n record: AssetRecord,\n at: AssetReadLocale | null,\n doc: CmsDocument,\n base?: CmsDocument,\n): Pick<AssetMetadataPatch, 'metaPatch' | 'localizedMetaPatch'> {\n const before = base ? documentToAssetMeta(base) : {}\n const after = documentToAssetMeta(doc)\n const opened = assetMetaDraft(record)\n let edited = opened\n for (const name of new Set([...Object.keys(before), ...Object.keys(after)])) {\n if (semanticValueEqual(before[name], after[name])) continue\n const field = collection.fields.find((f) => f.name === name) ?? { name }\n edited = setAssetMetaField(edited, field, at, after[name])\n }\n return assetMetaDraftPatch(opened, edited)\n}\n\n/** `create` when `capabilities.create === false` — never reached from the UI\n * (list/detail never call it); an explicit throw rather than a silent no-op. */\nfunction createUnsupported(): DocumentRef {\n throw new Error('assets are created by upload, not the assets rail')\n}\n\n/** Assets' `remove()` returns an `outcome`-discriminated {@link DeleteAssetResult}\n * (not the `{ ok }` shape users/redirects use, EC-327). `'error'` surfaces as-is;\n * `'blocked'` (the unconfirmed usage gate fired — nothing was removed) surfaces\n * as an explicit failure too (EC-433): a delete that left the asset in place\n * must NEVER look like success. It is unreachable from this rail's UI — every\n * delete path confirms through the reference dialog and dispatches with\n * `confirmInUse` — but stays honest for any direct caller. `'not-found'` maps\n * to success (idempotent delete: the desired end state — gone — holds). */\nexport function normalizeAssetRemoveResult(result: DeleteAssetResult): OperationError | null {\n if (result.outcome === 'error') return { code: 'unknown', message: result.message }\n if (result.outcome === 'blocked') {\n return {\n code: 'unknown',\n message: `Asset is referenced in ${result.usageCount} ${result.usageCount === 1 ? 'place' : 'places'} and was not deleted.`,\n }\n }\n return null\n}\n\n/**\n * How an asset row is addressed in the EC-155 reference index (EC-433):\n * `{ type: 'asset' }`, never the document shape — so the delete confirmation's\n * inbound pre-flight counts the SAME references `loadInboundAssetReferenceViews`\n * (the detail surface's check) does.\n */\nexport function assetReferenceTarget(ref: DocumentRef): ReferenceTarget {\n return { type: 'asset', id: ref.id }\n}\n\n/**\n * Assemble a {@link CollectionViewSource} for the **assets rail** (EC-319) — the\n * portable list surface over the existing asset library store instead of a\n * bespoke listing. `schema` is the DEPLOYMENT-resolved `kind:'asset'` collection\n * (EC-311: a project's own declared fields, or a mount's `nav.assets({ fields })`\n * override merged over it — see `resolveAssetsSchema` in `app-views/default-modules.tsx`),\n * never a hardcoded engine default — so a project's `copyright`/`credit`/… fields\n * (and any EC-311 override) drive the list's columns/filters exactly as they\n * drive the image field's options dialog.\n *\n * Must be mounted under an `<AssetsProvider>` (for {@link useAssets}) — already\n * ambient at the project level (EC-195), so callers rarely mount their own.\n *\n * The shared scaffold (`useRailViewSource`, EC-327) owns `viewState`/`lastError`\n * plumbing and the assembled source; this hook supplies only the asset-store\n * reads/writes, the capability profile, and the projections above.\n */\nexport function useAssetsCollectionViewSource(\n projectId: string,\n schema: CollectionDef,\n): CollectionViewSource {\n const store = useAssets()\n const registry = useMemo(() => createCollectionRegistry([schema]), [schema])\n // EC-632: the rail reads (and a save writes) a localized field in the studio's locale.\n const at = useAssetReadLocale()\n\n const documents = useCallback(\n () => store.assets.map((record) => assetToDocument(schema, record, at)),\n [store.assets, schema, at],\n )\n const get = useCallback(\n (ref: DocumentRef): CmsDocument | undefined => {\n const record = store.assets.find((a) => a.id === ref.id)\n return record ? assetToDocument(schema, record, at) : undefined\n },\n [store.assets, schema, at],\n )\n const titleOf = useCallback(\n (ref: DocumentRef): string => {\n const record = store.assets.find((a) => a.id === ref.id)\n return record?.title ?? record?.filename ?? ref.id\n },\n [store.assets],\n )\n const localeConfig = useMemo(() => railLocaleConfig(at), [at])\n const validate = useCallback(\n (doc: CmsDocument) => validateDocument(registry, doc, { localeConfig }),\n [registry, localeConfig],\n )\n const save = useCallback(\n async (doc: CmsDocument, base?: CmsDocument) => {\n const record = store.assets.find((a) => a.id === doc.id)\n if (!record) return { ok: false as const, message: `No asset “${doc.id}” is loaded.` }\n return store.updateMetadata(doc.id, assetRailMetaPatch(schema, record, at, doc, base))\n },\n [store, schema, at],\n )\n // The unconfirmed delete (the store's usage gate still applies — a referenced\n // asset comes back `blocked`, surfaced via `lastError`). The UI's delete paths\n // never land here: row/bulk deletes confirm through the reference dialog and\n // dispatch `bulkRemove` below.\n const remove = useCallback((ref: DocumentRef) => store.remove(ref.id), [store])\n // The CONFIRMED delete the bulk/row path dispatches (EC-433): the reference\n // dialog has already enumerated inbound usages (asset-shaped, see\n // `assetReferenceTarget`), so this passes `confirmInUse` exactly like the\n // detail surface's confirmed delete — same blob + thumb + cache handling.\n const bulkRemove = useCallback(\n (ref: DocumentRef) => store.remove(ref.id, { confirmInUse: true }),\n [store],\n )\n\n return useRailViewSource({\n projectId,\n schema,\n capabilities: ASSET_COLLECTION_CAPABILITIES,\n localeConfig,\n documents,\n get,\n titleOf,\n // No `referenceCandidates`/`collections`/`isFieldReadOnly` override: assets carry\n // no relation/asset fields of their own (v1) — nothing cross-collection to\n // resolve — and read-only follows the factory's default `form.readOnly` check.\n detailPath: (id: string) => studioPath.asset(id),\n listPath: studioPath.assets(),\n // EC-433: the delete confirmation must enumerate ASSET-shaped inbound\n // references — the document-shaped default would report \"not referenced\"\n // for a referenced asset.\n referenceTarget: assetReferenceTarget,\n validate,\n create: createUnsupported,\n save,\n remove,\n bulkRemove,\n normalizeSaveResult: normalizeOkResult,\n normalizeRemoveResult: normalizeAssetRemoveResult,\n })\n}\n"]}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useCallback, useContext, useMemo, useRef, useState } from 'react';
|
|
2
|
+
import { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
3
|
import { deleteAsset, runVariantBackfill, runUpload, xhrUploadTransport } from '../../lib/assets/index.js';
|
|
4
|
+
import { configureStudioLocales } from '../../lib/locale/index.js';
|
|
4
5
|
import { loadInboundAssetReferenceViews } from '../../lib/references/index.js';
|
|
5
6
|
import { getStudioOperations, operationsAssetRepository } from '../../lib/operations/index.js';
|
|
6
|
-
import { projectCollections } from '../../lib/operations/project-runtime-config.js';
|
|
7
|
+
import { projectCollections, projectLocaleConfig } from '../../lib/operations/project-runtime-config.js';
|
|
7
8
|
import { listStoreState, useOperationCache, useOperationQuery } from '../../lib/query/index.js';
|
|
8
9
|
import { createRequiredContext } from '../../lib/required-context.js';
|
|
9
10
|
import { updateAssetMetadata } from './asset-metadata.js';
|
|
@@ -31,6 +32,16 @@ export function AssetsProvider({ projectId, children, }) {
|
|
|
31
32
|
// EC-568: the asset collection's fields — their `source` declarations map a file's
|
|
32
33
|
// embedded IPTC facts onto `meta` on every upload and replace. Config, read once.
|
|
33
34
|
const assetFields = useMemo(() => projectCollections(projectId).find((collection) => collection.kind === 'asset')?.fields, [projectId]);
|
|
35
|
+
// EC-632: the project's locales — a localized asset field's embedded values (XMP
|
|
36
|
+
// `xml:lang` alternatives) land in their locale buckets. Config, read once.
|
|
37
|
+
const locales = useMemo(() => projectLocaleConfig(projectId), [projectId]);
|
|
38
|
+
// EC-632: the asset surfaces read and edit a localized field in the studio's ambient
|
|
39
|
+
// locale — install the project's locales (idempotent, as the CMS store does), so the asset
|
|
40
|
+
// library opened before any CMS surface has them too.
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
if (locales)
|
|
43
|
+
configureStudioLocales(locales);
|
|
44
|
+
}, [locales]);
|
|
34
45
|
/** The shared studio operations bundle + the metadata-write facade over it. */
|
|
35
46
|
const studio = useCallback(async () => {
|
|
36
47
|
const bundle = await getStudioOperations();
|
|
@@ -63,6 +74,7 @@ export function AssetsProvider({ projectId, children, }) {
|
|
|
63
74
|
blobs: adapter.blobs,
|
|
64
75
|
assets: assetRepo,
|
|
65
76
|
...(assetFields ? { assetFields } : {}),
|
|
77
|
+
...(locales ? { locales } : {}),
|
|
66
78
|
...(typeof XMLHttpRequest !== 'undefined' ? { transport: xhrUploadTransport } : {}),
|
|
67
79
|
}, {
|
|
68
80
|
projectId,
|
|
@@ -76,7 +88,7 @@ export function AssetsProvider({ projectId, children, }) {
|
|
|
76
88
|
setUploads((prev) => prev.filter((u) => u.id !== uploadId));
|
|
77
89
|
await refresh();
|
|
78
90
|
}
|
|
79
|
-
}, [studio, projectId, refresh, setUploadState, assetFields]);
|
|
91
|
+
}, [studio, projectId, refresh, setUploadState, assetFields, locales]);
|
|
80
92
|
const upload = useCallback((files) => {
|
|
81
93
|
const ids = [];
|
|
82
94
|
for (const file of files) {
|
|
@@ -144,6 +156,7 @@ export function AssetsProvider({ projectId, children, }) {
|
|
|
144
156
|
blobs: adapter.blobs,
|
|
145
157
|
assets: assetRepo,
|
|
146
158
|
...(assetFields ? { assetFields } : {}),
|
|
159
|
+
...(locales ? { locales } : {}),
|
|
147
160
|
...(typeof XMLHttpRequest !== 'undefined' ? { transport: xhrUploadTransport } : {}),
|
|
148
161
|
}, { projectId, file, replace: { assetId } });
|
|
149
162
|
if (!result.ok)
|
|
@@ -151,7 +164,7 @@ export function AssetsProvider({ projectId, children, }) {
|
|
|
151
164
|
const updated = result.asset;
|
|
152
165
|
cache.patch(assetsQueryKey(projectId), (prev) => (prev ?? []).map((a) => (a.id === assetId ? updated : a)));
|
|
153
166
|
return { ok: true, asset: updated };
|
|
154
|
-
}, [studio, projectId, cache, assetFields]);
|
|
167
|
+
}, [studio, projectId, cache, assetFields, locales]);
|
|
155
168
|
const generatePreviews = useCallback(async (handlers) => {
|
|
156
169
|
const { adapter, assetRepo } = await studio();
|
|
157
170
|
// Reads (list) + the variant patch (upsert) go through the operations facade;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assets-store.js","sourceRoot":"","sources":["../../../src/components/assets/assets-store.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useContext, useMemo, useRef, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport type { AssetRecord, AssetRepository } from '@elytracms/core/persistence'\nimport { deleteAsset, runVariantBackfill, runUpload, xhrUploadTransport } from '../../lib/assets'\nimport type {\n BackfillProgress,\n BackfillSignal,\n DeleteAssetResult,\n UploadState,\n UploadableFile,\n} from '../../lib/assets'\nimport { loadInboundAssetReferenceViews } from '../../lib/references'\nimport type { ReferenceEntryView } from '../../lib/references'\nimport { getStudioOperations, operationsAssetRepository } from '../../lib/operations'\nimport { projectCollections } from '../../lib/operations/project-runtime-config'\nimport type { OperationsClient, StudioOperations } from '../../lib/operations'\nimport { listStoreState, useOperationCache, useOperationQuery } from '../../lib/query'\nimport { createRequiredContext } from '../../lib/required-context'\nimport { updateAssetMetadata } from './asset-metadata'\nimport type { AssetMetadataPatch, UpdateAssetMetadataResult } from './asset-metadata'\n\n/**\n * Asset library store (EC-151, EC-141). Asset *metadata* mutations go through\n * the shared operations client (`assets.*` commands — validate → authorize →\n * mutate → event); blob *bytes* ride on `adapter.blobs`, which is transport,\n * not domain. The UI works identically against the in-memory seed and a live\n * Convex deployment.\n */\n\nexport interface UploadEntry {\n id: string\n filename: string\n state: UploadState\n}\n\nexport interface AssetsStore {\n /** The active project (e.g. for reference deep links, EC-202). */\n projectId: string\n status: 'loading' | 'ready' | 'error'\n errorMessage?: string\n assets: readonly AssetRecord[]\n uploads: readonly UploadEntry[]\n /**\n * Queue files for upload. Returns the upload-queue id of each file, in order, so a\n * caller can follow exactly ITS uploads (EC-588 A7) — see {@link landedAssetId}.\n */\n upload(files: Iterable<UploadableFile>): readonly string[]\n /**\n * The asset id an upload landed as, once its record is confirmed (EC-588 A7);\n * `undefined` while it runs, after it failed, or for an id this store never issued.\n * A completed upload leaves the queue, so this is where its outcome stays readable.\n */\n landedAssetId(uploadId: string): string | undefined\n retryUpload(uploadId: string): void\n dismissUpload(uploadId: string): void\n /**\n * Index-accurate inbound references of an asset (EC-202): the \"used by\"\n * pre-flight of the block-or-confirm delete dialog, read from the EC-155\n * reference index (not the retired string scan).\n */\n usagesOf(assetId: string): Promise<ReferenceEntryView[]>\n remove(assetId: string, options?: { confirmInUse?: boolean }): Promise<DeleteAssetResult>\n /** Edit alt/title (EC-152); persists through the operations asset facade. */\n updateMetadata(assetId: string, patch: AssetMetadataPatch): Promise<UpdateAssetMetadataResult>\n /**\n * EC-568 — replace the file inside an asset: the same id, the new bytes, everything\n * derived regenerated through THE upload path, the file's embedded metadata over\n * `meta` per key (else kept), the focal point kept.\n */\n replaceFile(assetId: string, file: UploadableFile): Promise<ReplaceFileResult>\n /**\n * Backfill downscaled previews onto existing images (EC-400 AD-1). Sequential,\n * idempotent, cancellable; each patch flows through the operations facade and\n * the shared query cache (AD-5/AD-17). Reports progress via `handlers.onProgress`\n * and stops when `handlers.signal.cancelled` flips.\n */\n generatePreviews(handlers?: {\n onProgress?: (progress: BackfillProgress) => void\n signal?: BackfillSignal\n }): Promise<BackfillProgress>\n refresh(): Promise<void>\n}\n\n/** The outcome of {@link AssetsStore.replaceFile}. */\nexport type ReplaceFileResult = { ok: true; asset: AssetRecord } | { ok: false; message: string }\n\nconst { Context: AssetsContext, useRequired: useAssets } = createRequiredContext<AssetsStore>(\n 'Assets',\n { article: 'an' },\n)\n\nconst EMPTY_ASSETS: readonly AssetRecord[] = []\n\n/** Cache key for a project's asset library (shared by the query and its patches). */\nfunction assetsQueryKey(projectId: string): readonly [string, string] {\n return ['assets', projectId]\n}\n\n/** Load a project's asset library (pure over the operations client). A list\n * failure throws so the query surfaces it as an error status. */\nasync function loadAssets(operations: OperationsClient, projectId: string): Promise<AssetRecord[]> {\n const result = await operations.assets.list({ projectId })\n if (!result.ok) throw new Error(result.error.message)\n return [...result.value]\n}\n\nexport function AssetsProvider({\n projectId,\n children,\n}: {\n projectId: string\n children: ReactNode\n}) {\n const uploadFilesRef = useRef(new Map<string, UploadableFile>())\n // EC-588 A7: upload id → the asset id it landed as. Written BEFORE the entry leaves\n // the queue, so a reader never sees an upload that is neither running nor landed.\n const landedRef = useRef(new Map<string, string>())\n const uploadSeq = useRef(0)\n const [uploads, setUploads] = useState<readonly UploadEntry[]>([])\n\n // EC-568: the asset collection's fields — their `source` declarations map a file's\n // embedded IPTC facts onto `meta` on every upload and replace. Config, read once.\n const assetFields = useMemo(\n () => projectCollections(projectId).find((collection) => collection.kind === 'asset')?.fields,\n [projectId],\n )\n\n /** The shared studio operations bundle + the metadata-write facade over it. */\n const studio = useCallback(async (): Promise<\n StudioOperations & { assetRepo: AssetRepository }\n > => {\n const bundle = await getStudioOperations()\n return { ...bundle, assetRepo: operationsAssetRepository(bundle.operations) }\n }, [])\n\n // The library list, read through the one operations-backed query cache\n // (EC-308/AD-17). No `invalidateOn`: this provider is the sole writer of its\n // project's assets, so its own mutations patch the cache in place (the EC-301\n // rule — a metadata or focal-point edit must never trigger a full re-list).\n const query = useOperationQuery<AssetRecord[]>({\n queryKey: assetsQueryKey(projectId),\n loader: (operations) => loadAssets(operations, projectId),\n })\n const cache = useOperationCache()\n const refetch = query.refetch\n const assets = query.data ?? EMPTY_ASSETS\n const { status, errorMessage } = listStoreState(query)\n\n /** Re-list from the backend (the public escape hatch + post-upload reconcile). */\n const refresh = useCallback(async () => {\n await refetch()\n }, [refetch])\n\n const setUploadState = useCallback((uploadId: string, state: UploadState) => {\n setUploads((prev) => prev.map((u) => (u.id === uploadId ? { ...u, state } : u)))\n }, [])\n\n const runEntry = useCallback(\n async (uploadId: string, file: UploadableFile, previous?: UploadState) => {\n const { adapter, assetRepo } = await studio()\n const result = await runUpload(\n {\n // Bytes ride on blob storage (transport); the metadata record is\n // confirmed through the `assets.upsert` operation command.\n blobs: adapter.blobs,\n assets: assetRepo,\n ...(assetFields ? { assetFields } : {}),\n ...(typeof XMLHttpRequest !== 'undefined' ? { transport: xhrUploadTransport } : {}),\n },\n {\n projectId,\n file,\n onState: (state) => setUploadState(uploadId, state),\n },\n previous,\n )\n if (result.ok) {\n // Completed uploads leave the queue; the asset appears in the library.\n landedRef.current.set(uploadId, result.asset.id)\n uploadFilesRef.current.delete(uploadId)\n setUploads((prev) => prev.filter((u) => u.id !== uploadId))\n await refresh()\n }\n },\n [studio, projectId, refresh, setUploadState, assetFields],\n )\n\n const upload = useCallback(\n (files: Iterable<UploadableFile>): readonly string[] => {\n const ids: string[] = []\n for (const file of files) {\n uploadSeq.current += 1\n const uploadId = `upload-${uploadSeq.current}`\n ids.push(uploadId)\n uploadFilesRef.current.set(uploadId, file)\n setUploads((prev) => [\n ...prev,\n { id: uploadId, filename: file.name, state: { phase: 'idle', progress: 0, attempt: 0 } },\n ])\n void runEntry(uploadId, file)\n }\n return ids\n },\n [runEntry],\n )\n\n const landedAssetId = useCallback((uploadId: string) => landedRef.current.get(uploadId), [])\n\n const retryUpload = useCallback(\n (uploadId: string) => {\n const file = uploadFilesRef.current.get(uploadId)\n const entry = uploads.find((u) => u.id === uploadId)\n if (!file || !entry || entry.state.phase !== 'failed') return\n void runEntry(uploadId, file, entry.state)\n },\n [runEntry, uploads],\n )\n\n const dismissUpload = useCallback((uploadId: string) => {\n uploadFilesRef.current.delete(uploadId)\n setUploads((prev) => prev.filter((u) => u.id !== uploadId))\n }, [])\n\n const usagesOf = useCallback(\n // Index-accurate (EC-202): inbound references from the EC-155 index via the\n // operations queries — the same machinery the References panel uses.\n async (assetId: string) => loadInboundAssetReferenceViews(projectId, assetId),\n [projectId],\n )\n\n const remove = useCallback(\n async (assetId: string, options?: { confirmInUse?: boolean }) => {\n const { adapter, assetRepo } = await studio()\n // Metadata removal goes through the `assets.remove` command (via the\n // facade); blob byte deletion stays on the adapter; the usage gate reads\n // the EC-155 reference index (`adapter.references`, EC-202).\n const result = await deleteAsset(\n { assets: assetRepo, blobs: adapter.blobs, references: adapter.references },\n projectId,\n assetId,\n options,\n )\n // Drop the one removed record from the cache rather than re-listing the whole\n // library (EC-301 perf, mirrors updateMetadata) — a delete must not trigger a full\n // `assets.list` round-trip on top of the deletion itself.\n if (result.outcome === 'deleted' || result.outcome === 'not-found') {\n cache.patch<AssetRecord[]>(assetsQueryKey(projectId), (prev) =>\n (prev ?? []).filter((a) => a.id !== assetId),\n )\n }\n return result\n },\n [studio, projectId, cache],\n )\n\n const updateMetadata = useCallback(\n async (assetId: string, patch: AssetMetadataPatch) => {\n // Metadata writes go through the `assets.upsert` command via the\n // operations facade (EC-141) — never straight at a repository.\n const { assetRepo } = await studio()\n const result = await updateAssetMetadata({ assets: assetRepo }, projectId, assetId, patch)\n if (result.ok) {\n // Patch the one updated record into the cache rather than re-listing the\n // whole library (EC-301). A focal-point or alt edit must not trigger a full\n // `assets.list` round-trip — that re-list (plus the marker only moving once\n // it returned) was the focal-point picker's lag and query storm.\n const updated = result.asset\n cache.patch<AssetRecord[]>(assetsQueryKey(projectId), (prev) =>\n (prev ?? []).map((a) => (a.id === assetId ? updated : a)),\n )\n }\n return result\n },\n [studio, projectId, cache],\n )\n\n const replaceFile = useCallback(\n async (assetId: string, file: UploadableFile): Promise<ReplaceFileResult> => {\n const { adapter, assetRepo } = await studio()\n // EC-568: THE upload path, in replace mode — not a second uploader.\n const result = await runUpload(\n {\n blobs: adapter.blobs,\n assets: assetRepo,\n ...(assetFields ? { assetFields } : {}),\n ...(typeof XMLHttpRequest !== 'undefined' ? { transport: xhrUploadTransport } : {}),\n },\n { projectId, file, replace: { assetId } },\n )\n if (!result.ok) return { ok: false, message: result.failure.message }\n const updated = result.asset\n cache.patch<AssetRecord[]>(assetsQueryKey(projectId), (prev) =>\n (prev ?? []).map((a) => (a.id === assetId ? updated : a)),\n )\n return { ok: true, asset: updated }\n },\n [studio, projectId, cache, assetFields],\n )\n\n const generatePreviews = useCallback(\n async (handlers?: {\n onProgress?: (progress: BackfillProgress) => void\n signal?: BackfillSignal\n }) => {\n const { adapter, assetRepo } = await studio()\n // Reads (list) + the variant patch (upsert) go through the operations facade;\n // bytes ride on `adapter.blobs`. The backfill is per-variant (EC-420 AD-4):\n // one fetch of each original fills whichever of thumb/lqip is missing. Each\n // generated record is patched into the shared cache in place (EC-301 perf,\n // AD-17) so the grid updates live without a full re-list.\n return runVariantBackfill(\n {\n assets: assetRepo,\n blobs: adapter.blobs,\n onGenerated: (updated) =>\n cache.patch<AssetRecord[]>(assetsQueryKey(projectId), (prev) =>\n (prev ?? []).map((a) => (a.id === updated.id ? updated : a)),\n ),\n ...(handlers?.onProgress ? { onProgress: handlers.onProgress } : {}),\n ...(handlers?.signal ? { signal: handlers.signal } : {}),\n },\n projectId,\n )\n },\n [studio, projectId, cache],\n )\n\n const store = useMemo<AssetsStore>(\n () => ({\n projectId,\n status,\n ...(errorMessage !== undefined ? { errorMessage } : {}),\n assets,\n uploads,\n upload,\n landedAssetId,\n retryUpload,\n dismissUpload,\n usagesOf,\n remove,\n updateMetadata,\n replaceFile,\n generatePreviews,\n refresh,\n }),\n [\n projectId,\n status,\n errorMessage,\n assets,\n uploads,\n upload,\n landedAssetId,\n retryUpload,\n dismissUpload,\n usagesOf,\n remove,\n updateMetadata,\n replaceFile,\n generatePreviews,\n refresh,\n ],\n )\n\n return <AssetsContext.Provider value={store}>{children}</AssetsContext.Provider>\n}\n\nexport { useAssets }\n\n/**\n * The assets store when an `<AssetsProvider>` is mounted above, else `null`.\n * Lets surfaces outside the Assets section (the CMS asset picker, EC-152)\n * reuse an ambient provider or mount their own.\n */\nexport function useOptionalAssets(): AssetsStore | null {\n return useContext(AssetsContext)\n}\n"]}
|
|
1
|
+
{"version":3,"file":"assets-store.js","sourceRoot":"","sources":["../../../src/components/assets/assets-store.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport type { AssetRecord, AssetRepository } from '@elytracms/core/persistence'\nimport { deleteAsset, runVariantBackfill, runUpload, xhrUploadTransport } from '../../lib/assets'\nimport type {\n BackfillProgress,\n BackfillSignal,\n DeleteAssetResult,\n UploadState,\n UploadableFile,\n} from '../../lib/assets'\nimport { configureStudioLocales } from '../../lib/locale'\nimport { loadInboundAssetReferenceViews } from '../../lib/references'\nimport type { ReferenceEntryView } from '../../lib/references'\nimport { getStudioOperations, operationsAssetRepository } from '../../lib/operations'\nimport { projectCollections, projectLocaleConfig } from '../../lib/operations/project-runtime-config'\nimport type { OperationsClient, StudioOperations } from '../../lib/operations'\nimport { listStoreState, useOperationCache, useOperationQuery } from '../../lib/query'\nimport { createRequiredContext } from '../../lib/required-context'\nimport { updateAssetMetadata } from './asset-metadata'\nimport type { AssetMetadataPatch, UpdateAssetMetadataResult } from './asset-metadata'\n\n/**\n * Asset library store (EC-151, EC-141). Asset *metadata* mutations go through\n * the shared operations client (`assets.*` commands — validate → authorize →\n * mutate → event); blob *bytes* ride on `adapter.blobs`, which is transport,\n * not domain. The UI works identically against the in-memory seed and a live\n * Convex deployment.\n */\n\nexport interface UploadEntry {\n id: string\n filename: string\n state: UploadState\n}\n\nexport interface AssetsStore {\n /** The active project (e.g. for reference deep links, EC-202). */\n projectId: string\n status: 'loading' | 'ready' | 'error'\n errorMessage?: string\n assets: readonly AssetRecord[]\n uploads: readonly UploadEntry[]\n /**\n * Queue files for upload. Returns the upload-queue id of each file, in order, so a\n * caller can follow exactly ITS uploads (EC-588 A7) — see {@link landedAssetId}.\n */\n upload(files: Iterable<UploadableFile>): readonly string[]\n /**\n * The asset id an upload landed as, once its record is confirmed (EC-588 A7);\n * `undefined` while it runs, after it failed, or for an id this store never issued.\n * A completed upload leaves the queue, so this is where its outcome stays readable.\n */\n landedAssetId(uploadId: string): string | undefined\n retryUpload(uploadId: string): void\n dismissUpload(uploadId: string): void\n /**\n * Index-accurate inbound references of an asset (EC-202): the \"used by\"\n * pre-flight of the block-or-confirm delete dialog, read from the EC-155\n * reference index (not the retired string scan).\n */\n usagesOf(assetId: string): Promise<ReferenceEntryView[]>\n remove(assetId: string, options?: { confirmInUse?: boolean }): Promise<DeleteAssetResult>\n /** Edit alt/title (EC-152); persists through the operations asset facade. */\n updateMetadata(assetId: string, patch: AssetMetadataPatch): Promise<UpdateAssetMetadataResult>\n /**\n * EC-568 — replace the file inside an asset: the same id, the new bytes, everything\n * derived regenerated through THE upload path, the file's embedded metadata over\n * `meta` per key (else kept), the focal point kept.\n */\n replaceFile(assetId: string, file: UploadableFile): Promise<ReplaceFileResult>\n /**\n * Backfill downscaled previews onto existing images (EC-400 AD-1). Sequential,\n * idempotent, cancellable; each patch flows through the operations facade and\n * the shared query cache (AD-5/AD-17). Reports progress via `handlers.onProgress`\n * and stops when `handlers.signal.cancelled` flips.\n */\n generatePreviews(handlers?: {\n onProgress?: (progress: BackfillProgress) => void\n signal?: BackfillSignal\n }): Promise<BackfillProgress>\n refresh(): Promise<void>\n}\n\n/** The outcome of {@link AssetsStore.replaceFile}. */\nexport type ReplaceFileResult = { ok: true; asset: AssetRecord } | { ok: false; message: string }\n\nconst { Context: AssetsContext, useRequired: useAssets } = createRequiredContext<AssetsStore>(\n 'Assets',\n { article: 'an' },\n)\n\nconst EMPTY_ASSETS: readonly AssetRecord[] = []\n\n/** Cache key for a project's asset library (shared by the query and its patches). */\nfunction assetsQueryKey(projectId: string): readonly [string, string] {\n return ['assets', projectId]\n}\n\n/** Load a project's asset library (pure over the operations client). A list\n * failure throws so the query surfaces it as an error status. */\nasync function loadAssets(operations: OperationsClient, projectId: string): Promise<AssetRecord[]> {\n const result = await operations.assets.list({ projectId })\n if (!result.ok) throw new Error(result.error.message)\n return [...result.value]\n}\n\nexport function AssetsProvider({\n projectId,\n children,\n}: {\n projectId: string\n children: ReactNode\n}) {\n const uploadFilesRef = useRef(new Map<string, UploadableFile>())\n // EC-588 A7: upload id → the asset id it landed as. Written BEFORE the entry leaves\n // the queue, so a reader never sees an upload that is neither running nor landed.\n const landedRef = useRef(new Map<string, string>())\n const uploadSeq = useRef(0)\n const [uploads, setUploads] = useState<readonly UploadEntry[]>([])\n\n // EC-568: the asset collection's fields — their `source` declarations map a file's\n // embedded IPTC facts onto `meta` on every upload and replace. Config, read once.\n const assetFields = useMemo(\n () => projectCollections(projectId).find((collection) => collection.kind === 'asset')?.fields,\n [projectId],\n )\n // EC-632: the project's locales — a localized asset field's embedded values (XMP\n // `xml:lang` alternatives) land in their locale buckets. Config, read once.\n const locales = useMemo(() => projectLocaleConfig(projectId), [projectId])\n // EC-632: the asset surfaces read and edit a localized field in the studio's ambient\n // locale — install the project's locales (idempotent, as the CMS store does), so the asset\n // library opened before any CMS surface has them too.\n useEffect(() => {\n if (locales) configureStudioLocales(locales)\n }, [locales])\n\n /** The shared studio operations bundle + the metadata-write facade over it. */\n const studio = useCallback(async (): Promise<\n StudioOperations & { assetRepo: AssetRepository }\n > => {\n const bundle = await getStudioOperations()\n return { ...bundle, assetRepo: operationsAssetRepository(bundle.operations) }\n }, [])\n\n // The library list, read through the one operations-backed query cache\n // (EC-308/AD-17). No `invalidateOn`: this provider is the sole writer of its\n // project's assets, so its own mutations patch the cache in place (the EC-301\n // rule — a metadata or focal-point edit must never trigger a full re-list).\n const query = useOperationQuery<AssetRecord[]>({\n queryKey: assetsQueryKey(projectId),\n loader: (operations) => loadAssets(operations, projectId),\n })\n const cache = useOperationCache()\n const refetch = query.refetch\n const assets = query.data ?? EMPTY_ASSETS\n const { status, errorMessage } = listStoreState(query)\n\n /** Re-list from the backend (the public escape hatch + post-upload reconcile). */\n const refresh = useCallback(async () => {\n await refetch()\n }, [refetch])\n\n const setUploadState = useCallback((uploadId: string, state: UploadState) => {\n setUploads((prev) => prev.map((u) => (u.id === uploadId ? { ...u, state } : u)))\n }, [])\n\n const runEntry = useCallback(\n async (uploadId: string, file: UploadableFile, previous?: UploadState) => {\n const { adapter, assetRepo } = await studio()\n const result = await runUpload(\n {\n // Bytes ride on blob storage (transport); the metadata record is\n // confirmed through the `assets.upsert` operation command.\n blobs: adapter.blobs,\n assets: assetRepo,\n ...(assetFields ? { assetFields } : {}),\n ...(locales ? { locales } : {}),\n ...(typeof XMLHttpRequest !== 'undefined' ? { transport: xhrUploadTransport } : {}),\n },\n {\n projectId,\n file,\n onState: (state) => setUploadState(uploadId, state),\n },\n previous,\n )\n if (result.ok) {\n // Completed uploads leave the queue; the asset appears in the library.\n landedRef.current.set(uploadId, result.asset.id)\n uploadFilesRef.current.delete(uploadId)\n setUploads((prev) => prev.filter((u) => u.id !== uploadId))\n await refresh()\n }\n },\n [studio, projectId, refresh, setUploadState, assetFields, locales],\n )\n\n const upload = useCallback(\n (files: Iterable<UploadableFile>): readonly string[] => {\n const ids: string[] = []\n for (const file of files) {\n uploadSeq.current += 1\n const uploadId = `upload-${uploadSeq.current}`\n ids.push(uploadId)\n uploadFilesRef.current.set(uploadId, file)\n setUploads((prev) => [\n ...prev,\n { id: uploadId, filename: file.name, state: { phase: 'idle', progress: 0, attempt: 0 } },\n ])\n void runEntry(uploadId, file)\n }\n return ids\n },\n [runEntry],\n )\n\n const landedAssetId = useCallback((uploadId: string) => landedRef.current.get(uploadId), [])\n\n const retryUpload = useCallback(\n (uploadId: string) => {\n const file = uploadFilesRef.current.get(uploadId)\n const entry = uploads.find((u) => u.id === uploadId)\n if (!file || !entry || entry.state.phase !== 'failed') return\n void runEntry(uploadId, file, entry.state)\n },\n [runEntry, uploads],\n )\n\n const dismissUpload = useCallback((uploadId: string) => {\n uploadFilesRef.current.delete(uploadId)\n setUploads((prev) => prev.filter((u) => u.id !== uploadId))\n }, [])\n\n const usagesOf = useCallback(\n // Index-accurate (EC-202): inbound references from the EC-155 index via the\n // operations queries — the same machinery the References panel uses.\n async (assetId: string) => loadInboundAssetReferenceViews(projectId, assetId),\n [projectId],\n )\n\n const remove = useCallback(\n async (assetId: string, options?: { confirmInUse?: boolean }) => {\n const { adapter, assetRepo } = await studio()\n // Metadata removal goes through the `assets.remove` command (via the\n // facade); blob byte deletion stays on the adapter; the usage gate reads\n // the EC-155 reference index (`adapter.references`, EC-202).\n const result = await deleteAsset(\n { assets: assetRepo, blobs: adapter.blobs, references: adapter.references },\n projectId,\n assetId,\n options,\n )\n // Drop the one removed record from the cache rather than re-listing the whole\n // library (EC-301 perf, mirrors updateMetadata) — a delete must not trigger a full\n // `assets.list` round-trip on top of the deletion itself.\n if (result.outcome === 'deleted' || result.outcome === 'not-found') {\n cache.patch<AssetRecord[]>(assetsQueryKey(projectId), (prev) =>\n (prev ?? []).filter((a) => a.id !== assetId),\n )\n }\n return result\n },\n [studio, projectId, cache],\n )\n\n const updateMetadata = useCallback(\n async (assetId: string, patch: AssetMetadataPatch) => {\n // Metadata writes go through the `assets.upsert` command via the\n // operations facade (EC-141) — never straight at a repository.\n const { assetRepo } = await studio()\n const result = await updateAssetMetadata({ assets: assetRepo }, projectId, assetId, patch)\n if (result.ok) {\n // Patch the one updated record into the cache rather than re-listing the\n // whole library (EC-301). A focal-point or alt edit must not trigger a full\n // `assets.list` round-trip — that re-list (plus the marker only moving once\n // it returned) was the focal-point picker's lag and query storm.\n const updated = result.asset\n cache.patch<AssetRecord[]>(assetsQueryKey(projectId), (prev) =>\n (prev ?? []).map((a) => (a.id === assetId ? updated : a)),\n )\n }\n return result\n },\n [studio, projectId, cache],\n )\n\n const replaceFile = useCallback(\n async (assetId: string, file: UploadableFile): Promise<ReplaceFileResult> => {\n const { adapter, assetRepo } = await studio()\n // EC-568: THE upload path, in replace mode — not a second uploader.\n const result = await runUpload(\n {\n blobs: adapter.blobs,\n assets: assetRepo,\n ...(assetFields ? { assetFields } : {}),\n ...(locales ? { locales } : {}),\n ...(typeof XMLHttpRequest !== 'undefined' ? { transport: xhrUploadTransport } : {}),\n },\n { projectId, file, replace: { assetId } },\n )\n if (!result.ok) return { ok: false, message: result.failure.message }\n const updated = result.asset\n cache.patch<AssetRecord[]>(assetsQueryKey(projectId), (prev) =>\n (prev ?? []).map((a) => (a.id === assetId ? updated : a)),\n )\n return { ok: true, asset: updated }\n },\n [studio, projectId, cache, assetFields, locales],\n )\n\n const generatePreviews = useCallback(\n async (handlers?: {\n onProgress?: (progress: BackfillProgress) => void\n signal?: BackfillSignal\n }) => {\n const { adapter, assetRepo } = await studio()\n // Reads (list) + the variant patch (upsert) go through the operations facade;\n // bytes ride on `adapter.blobs`. The backfill is per-variant (EC-420 AD-4):\n // one fetch of each original fills whichever of thumb/lqip is missing. Each\n // generated record is patched into the shared cache in place (EC-301 perf,\n // AD-17) so the grid updates live without a full re-list.\n return runVariantBackfill(\n {\n assets: assetRepo,\n blobs: adapter.blobs,\n onGenerated: (updated) =>\n cache.patch<AssetRecord[]>(assetsQueryKey(projectId), (prev) =>\n (prev ?? []).map((a) => (a.id === updated.id ? updated : a)),\n ),\n ...(handlers?.onProgress ? { onProgress: handlers.onProgress } : {}),\n ...(handlers?.signal ? { signal: handlers.signal } : {}),\n },\n projectId,\n )\n },\n [studio, projectId, cache],\n )\n\n const store = useMemo<AssetsStore>(\n () => ({\n projectId,\n status,\n ...(errorMessage !== undefined ? { errorMessage } : {}),\n assets,\n uploads,\n upload,\n landedAssetId,\n retryUpload,\n dismissUpload,\n usagesOf,\n remove,\n updateMetadata,\n replaceFile,\n generatePreviews,\n refresh,\n }),\n [\n projectId,\n status,\n errorMessage,\n assets,\n uploads,\n upload,\n landedAssetId,\n retryUpload,\n dismissUpload,\n usagesOf,\n remove,\n updateMetadata,\n replaceFile,\n generatePreviews,\n refresh,\n ],\n )\n\n return <AssetsContext.Provider value={store}>{children}</AssetsContext.Provider>\n}\n\nexport { useAssets }\n\n/**\n * The assets store when an `<AssetsProvider>` is mounted above, else `null`.\n * Lets surfaces outside the Assets section (the CMS asset picker, EC-152)\n * reuse an ambient provider or mount their own.\n */\nexport function useOptionalAssets(): AssetsStore | null {\n return useContext(AssetsContext)\n}\n"]}
|
|
@@ -122,6 +122,18 @@ export type BuilderAction = {
|
|
|
122
122
|
run: number;
|
|
123
123
|
content: readonly unknown[];
|
|
124
124
|
coalesceKey?: string;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* EC-630 — the field's value changed UNDERNEATH the canvas (a colleague's patch by
|
|
128
|
+
* subscription, a write's canonical row, a resolved conflict), from `from` to `to` (the
|
|
129
|
+
* draft's values before and after): the present absorbs the change and the history and
|
|
130
|
+
* selection rebase onto it (`canvas-rebase.ts`) — never a remount. Not an edit: it adds
|
|
131
|
+
* no history entry, and the canvas writes nothing for it.
|
|
132
|
+
*/
|
|
133
|
+
| {
|
|
134
|
+
type: 'rebase';
|
|
135
|
+
from: unknown;
|
|
136
|
+
to: unknown;
|
|
125
137
|
};
|
|
126
138
|
/** The structural actions the placement judge rules on (EC-584): every node that enters or moves. */
|
|
127
139
|
export type PlacingAction = Extract<BuilderAction, {
|
|
@@ -186,6 +198,12 @@ export interface BuilderState {
|
|
|
186
198
|
* action changes no tree, so it adds no history entry and emits no canvas patch.
|
|
187
199
|
*/
|
|
188
200
|
refusal: PlacementRefusal | null;
|
|
201
|
+
/**
|
|
202
|
+
* EC-630 — how many `rebase` actions the store has applied (external changes of its
|
|
203
|
+
* field). The canvas sync reads it to tell a store value from before a rebase it
|
|
204
|
+
* dispatched from one after it (`createCanvasSync`).
|
|
205
|
+
*/
|
|
206
|
+
rebased: number;
|
|
189
207
|
}
|
|
190
208
|
type Action = BuilderAction;
|
|
191
209
|
/**
|
|
@@ -251,6 +269,9 @@ preserved?: readonly PreservedItem[]): BuilderState;
|
|
|
251
269
|
* persistence layer: it is a pure in-memory editor. The block-field editor lifts
|
|
252
270
|
* its committed composition into the open CMS document via `onChange`
|
|
253
271
|
* (`composition-field-editor`); the document's own Save persists it as content.
|
|
272
|
+
* EC-630: a change of the field made underneath the store (a colleague's patch) is
|
|
273
|
+
* absorbed by the present AND every snapshot (`rebase`, `canvas-rebase.ts`), so an undo
|
|
274
|
+
* never reverts it.
|
|
254
275
|
*/
|
|
255
276
|
export declare function BuilderProvider({ composition, preserved, field, components, layout, idFactory, children, }: {
|
|
256
277
|
/** The composition root to edit (the composition-field editor's synthetic root). */
|
|
@@ -4,6 +4,7 @@ import { DEFAULT_SLOT, cloneWithNewIds, duplicateNode, getNode, getParentInfo, g
|
|
|
4
4
|
import { createUuidIdFactory } from '@elytracms/core/persistence';
|
|
5
5
|
import { WRAP_GRID_OPTIONS, applyWrap } from '../../lib/dnd/drop-rules.js';
|
|
6
6
|
import { replaceRun, runByKey } from './canvas-runs.js';
|
|
7
|
+
import { rebaseCanvas } from './canvas-rebase.js';
|
|
7
8
|
import { createRequiredContext } from '../../lib/required-context.js';
|
|
8
9
|
const HISTORY_LIMIT = 100;
|
|
9
10
|
/**
|
|
@@ -137,6 +138,23 @@ function transition(state, action, placement) {
|
|
|
137
138
|
return state;
|
|
138
139
|
return restore(state, next, [...state.past, snapshotOf(state)], state.future.slice(1));
|
|
139
140
|
}
|
|
141
|
+
case 'rebase': {
|
|
142
|
+
// EC-630: undo never reverts the external change — every snapshot absorbs it. The
|
|
143
|
+
// next edit starts a new undo step (the step it would coalesce into may have been
|
|
144
|
+
// dropped by the rebase, and an edit must never land in a step it cannot undo).
|
|
145
|
+
const rebased = rebaseCanvas(state, action.from, action.to);
|
|
146
|
+
// Counted even when it changes nothing: the canvas sync waits for every rebase it dispatched.
|
|
147
|
+
if (!rebased)
|
|
148
|
+
return { ...state, rebased: state.rebased + 1 };
|
|
149
|
+
return {
|
|
150
|
+
...state,
|
|
151
|
+
...rebased,
|
|
152
|
+
past: [...rebased.past],
|
|
153
|
+
future: [...rebased.future],
|
|
154
|
+
lastCoalesceKey: null,
|
|
155
|
+
rebased: state.rebased + 1,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
140
158
|
case 'insert': {
|
|
141
159
|
const node = action.minted ? action.node : cloneWithNewIds(action.node, state.newId);
|
|
142
160
|
const verdict = subtreePlacementRefusal(root, node, landingOf(action.target), placement);
|
|
@@ -279,6 +297,7 @@ preserved = []) {
|
|
|
279
297
|
lastCoalesceKey: null,
|
|
280
298
|
layout,
|
|
281
299
|
refusal: null,
|
|
300
|
+
rebased: 0,
|
|
282
301
|
};
|
|
283
302
|
}
|
|
284
303
|
/**
|
|
@@ -288,6 +307,9 @@ preserved = []) {
|
|
|
288
307
|
* persistence layer: it is a pure in-memory editor. The block-field editor lifts
|
|
289
308
|
* its committed composition into the open CMS document via `onChange`
|
|
290
309
|
* (`composition-field-editor`); the document's own Save persists it as content.
|
|
310
|
+
* EC-630: a change of the field made underneath the store (a colleague's patch) is
|
|
311
|
+
* absorbed by the present AND every snapshot (`rebase`, `canvas-rebase.ts`), so an undo
|
|
312
|
+
* never reverts it.
|
|
291
313
|
*/
|
|
292
314
|
export function BuilderProvider({ composition, preserved, field, components, layout, idFactory, children, }) {
|
|
293
315
|
const [newId] = useState(() => idFactory ?? createUuidIdFactory());
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builder-store.js","sourceRoot":"","sources":["../../../src/components/builder/builder-store.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useMemo, useReducer, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport {\n DEFAULT_SLOT,\n cloneWithNewIds,\n duplicateNode,\n getNode,\n getParentInfo,\n gridContainerNode,\n insertNode,\n moveNode,\n normalizeContainers,\n removeNode,\n removeNodeProp,\n reorderNode,\n replaceNode,\n replacePlacementRefusal,\n setNodeProp,\n subtreePlacementRefusal,\n} from '@elytracms/core/project-graph'\nimport type {\n AllowList,\n ComponentLookup,\n ComponentNode,\n InsertTarget,\n LayoutContainerIds,\n PlacementScope,\n PlacementVerdict,\n PropValue,\n} from '@elytracms/core/project-graph'\nimport { createUuidIdFactory } from '@elytracms/core/persistence'\nimport type { IdFactory } from '@elytracms/core/persistence'\nimport { WRAP_GRID_OPTIONS, applyWrap } from '../../lib/dnd/drop-rules'\nimport { replaceRun, runByKey } from './canvas-runs'\nimport type { PreservedItem } from './canvas-runs'\nimport { createRequiredContext } from '../../lib/required-context'\n\nconst HISTORY_LIMIT = 100\n\n/**\n * A canvas preview-width key (EC-250): `'full'` (unconstrained) or a configured\n * breakpoint name (`'sm'`, `'md'`, …). Dynamic because the set is sourced from\n * the consuming site's breakpoints, so it is a string, not a fixed union — the\n * switcher resolves the available options via {@link resolveCanvasViewports}.\n */\nexport type Viewport = string\n\n/**\n * The builder store's action union. EC-287 retired the DB-resident project graph\n * and its persistence mirror, so these actions are pure in-memory editor state —\n * no action maps to a persisted `graph.*` command anymore. The block-field editor\n * lifts committed edits into the CMS document via `onChange`\n * (`composition-field-editor`); nothing persists a graph.\n */\nexport type BuilderAction =\n | { type: 'select'; id: string | null }\n | { type: 'hover'; id: string | null }\n | { type: 'viewport'; viewport: Viewport }\n | { type: 'undo' }\n | { type: 'redo' }\n /**\n * Insert a node (its subtree re-id'd from the store's id factory). `minted` — the\n * node ALREADY carries fresh ids from the same factory (the paste path mints first\n * so its issue deep links name exactly the ids that land); the reducer then inserts\n * it as given.\n */\n | { type: 'insert'; target: InsertTarget; node: ComponentNode; select?: boolean; minted?: true }\n | { type: 'remove'; id: string }\n | { type: 'duplicate'; id: string }\n | { type: 'reorder'; id: string; delta: -1 | 1 }\n | { type: 'move'; id: string; target: InsertTarget }\n /**\n * EC-282 reuse gesture: replace a node with zero or more nodes at its\n * position (clone-with-fresh-ids, like `insert`). Promote → 1 embed node;\n * detach → the referenced element's inline blocks. One atomic structural\n * edit, one undo step.\n */\n | { type: 'replace'; id: string; nodes: ComponentNode[]; select?: boolean }\n /**\n * EC-162 layout gesture: wrap the reference node and the dragged subject in\n * a two-column Grid container as ONE atomic structural edit (one undo step).\n */\n | {\n type: 'wrap'\n referenceId: string\n side: 'left' | 'right'\n subject: { kind: 'node'; nodeId: string } | { kind: 'component'; node: ComponentNode }\n }\n | { type: 'setProp'; id: string; name: string; value: PropValue; coalesceKey?: string }\n | { type: 'removeProp'; id: string; name: string }\n /** EC-584: the editor dismissed the visible refusal ({@link BuilderState.refusal}). */\n | { type: 'clearRefusal' }\n /**\n * EC-549 — select a typography RUN by its key (`canvas-runs.ts`: the index of its\n * first preserved item); `null` clears it. A run and a node are never both selected.\n */\n | { type: 'selectRun'; run: number | null }\n /** EC-549 — delete a typography run: a real, undoable edit of the preserved list. */\n | { type: 'removeRun'; run: number }\n /**\n * EC-549 — replace a run's typography with `content` (the inspector's rich-text\n * editor on that run; consecutive same-key edits coalesce into one undo step). Empty\n * `content` deletes the run.\n */\n | { type: 'setRun'; run: number; content: readonly unknown[]; coalesceKey?: string }\n\n/** The structural actions the placement judge rules on (EC-584): every node that enters or moves. */\nexport type PlacingAction = Extract<BuilderAction, { type: 'insert' | 'replace' | 'move' | 'wrap' | 'duplicate' }>\n\n/** A refused placement (EC-584): the judge's verdict and the action it refused. */\nexport interface PlacementRefusal {\n action: PlacingAction['type']\n verdict: PlacementVerdict\n}\n\n/**\n * One history entry (EC-549): the component tree AND the preserved typography — so a\n * run's deletion or edit is undone exactly like a block's.\n */\nexport interface CanvasSnapshot {\n root: ComponentNode\n preserved: readonly PreservedItem[]\n}\n\nexport interface BuilderState {\n /**\n * The edited composition: one root-bearing component tree (EC-287). The canvas\n * wraps a page document's `body` blocks into a synthetic root and edits it\n * directly — no page/layout wrapper. Undo/redo snapshot it with the preserved\n * typography ({@link CanvasSnapshot}).\n */\n past: CanvasSnapshot[]\n present: ComponentNode\n future: CanvasSnapshot[]\n /**\n * EC-549 — the field value's typography items (no ids, EC-500 D1), each anchored by\n * how many component roots preceded it (`componentPrefix`); the field value is\n * `mergeBlockRoots(present's roots, preserved)`. In the store, so selecting,\n * deleting and editing a run is a real action with truthful undo.\n */\n preserved: readonly PreservedItem[]\n selectedId: string | null\n /** EC-549 — the selected typography run's key, or `null` (exclusive with {@link selectedId}). */\n selectedRun: number | null\n hoveredId: string | null\n viewport: Viewport\n /**\n * EC-578 / EC-595 A2 — THE node-id factory (injected; UUIDs in the studio, a\n * sequential factory in fixtures). It replaced the per-session `node-<n>` counter,\n * which two sessions editing one page advanced identically — both minted the same\n * next id, a `duplicate-node-id` on the second save. Stored ids are untouched.\n */\n newId: IdFactory\n /** Coalescing key of the last edit (consecutive same-key edits don't stack history). */\n lastCoalesceKey: string | null\n /**\n * The components filling the layout roles (EC-591, `ComponentRegistry.layout`),\n * fixed for the store's lifetime: which nodes container normalization treats as\n * Stack/Grid, and the Grid the wrap gesture builds. No hardcoded engine id.\n */\n layout: LayoutContainerIds\n /**\n * EC-584 — the last REFUSED placement, with its reason: the reducer judged an\n * `insert`/`replace`/`move`/`wrap`/`duplicate` against the field and slot\n * vocabularies and canvas eligibility (the core placement judge) and left the\n * composition unchanged. Transient — the next edit, selection, undo or redo clears\n * it — and never silent: the canvas shows it (`PlacementRefusalNotice`). A refused\n * action changes no tree, so it adds no history entry and emits no canvas patch.\n */\n refusal: PlacementRefusal | null\n}\n\ntype Action = BuilderAction\n\n/**\n * Push a new graph onto history, coalescing consecutive edits with the same key.\n *\n * Every commit normalizes layout containers (EC-162 ledger fix): the\n * operations layer normalizes every persisted `graph.*` write (EC-161), so\n * the optimistic working graph must apply the same `normalizeContainers` pass\n * or the UI state diverges from the persisted revision after\n * container-emptying moves/removes. The mirrored command stays un-normalized\n * (`graphInvocationFor` sees the raw action); both sides normalize\n * identically, so they land on the same graph.\n */\nfunction commit(\n state: BuilderState,\n root: ComponentNode,\n opts: {\n coalesceKey?: string\n selectedId?: string | null\n preserved?: readonly PreservedItem[]\n selectedRun?: number | null\n } = {},\n): BuilderState {\n const normalized = normalizeContainers(root, state.layout)\n const present = normalized\n const preserved = opts.preserved ?? state.preserved\n const coalesce = opts.coalesceKey != null && opts.coalesceKey === state.lastCoalesceKey\n // Normalization can dissolve the node a caller wanted selected (e.g. the\n // parent container of a removed child) — selection never points at a ghost.\n const desired = opts.selectedId !== undefined ? opts.selectedId : state.selectedId\n const selectedId = desired !== null && getNode(normalized, desired) ? desired : null\n const desiredRun = opts.selectedRun !== undefined ? opts.selectedRun : state.selectedRun\n return {\n ...state,\n past: coalesce ? state.past : [...state.past, snapshotOf(state)].slice(-HISTORY_LIMIT),\n present,\n preserved,\n future: [],\n lastCoalesceKey: opts.coalesceKey ?? null,\n selectedId,\n selectedRun: liveRun(present, preserved, desiredRun),\n refusal: null,\n }\n}\n\nfunction snapshotOf(state: BuilderState): CanvasSnapshot {\n return { root: state.present, preserved: state.preserved }\n}\n\n/** The run key if that run still exists in this state, else `null` (never a ghost selection). */\nfunction liveRun(root: ComponentNode, preserved: readonly PreservedItem[], run: number | null): number | null {\n if (run === null) return null\n return runByKey(rootCount(root), preserved, run) ? run : null\n}\n\n/** How many component roots the composition has (the synthetic root's children). */\nfunction rootCount(root: ComponentNode): number {\n return root.slots?.[DEFAULT_SLOT]?.length ?? 0\n}\n\n/** Restore a history entry (undo/redo), keeping a selection only where it still exists. */\nfunction restore(state: BuilderState, to: CanvasSnapshot, past: CanvasSnapshot[], future: CanvasSnapshot[]): BuilderState {\n return {\n ...state,\n past,\n present: to.root,\n preserved: to.preserved,\n future,\n lastCoalesceKey: null,\n selectedId: getNode(to.root, state.selectedId ?? '') ? state.selectedId : null,\n selectedRun: liveRun(to.root, to.preserved, state.selectedRun),\n refusal: null,\n }\n}\n\n/** Leave the composition untouched and say why (EC-584 — never a silent no-op). */\nfunction refuse(state: BuilderState, action: PlacingAction, verdict: PlacementVerdict): BuilderState {\n return { ...state, refusal: { action: action.type, verdict } }\n}\n\n/**\n * The builder's pure state transition, CONSTRUCTED with the placement scope it judges\n * by (EC-584, owner-ratified 4a): the field's effective top-level vocabulary and the\n * manifest lookup. The scope is static per store and deliberately not reducer STATE\n * (it is the declaration the tree is judged against, not part of the tree) — the\n * reducer RECEIVES it, so there is no `(state, action)` to call without it.\n *\n * Every structural action that places a node — `insert`, `replace`, `move`, `wrap`,\n * `duplicate` — runs the ONE core judge (`subtreePlacementRefusal`) over every node\n * that enters or moves, BEFORE committing. A refusal returns the composition\n * unchanged, with the verdict in {@link BuilderState.refusal}: so no gesture — a\n * forgotten pre-check, promote, detach, a fixture's `slotChildren`, a server-only\n * paste — can place what the field, a slot or canvas eligibility forbids, and the\n * publish gate (which calls the same judge) cannot disagree with the canvas.\n *\n * Exported (with `initialBuilderState`) for unit tests — gesture determinism and\n * normalization lockstep are reducer-level properties, not render behavior.\n */\nexport function createBuilderReducer(\n placement: PlacementScope,\n): (state: BuilderState, action: Action) => BuilderState {\n return (state, action) => transition(state, action, placement)\n}\n\nfunction transition(state: BuilderState, action: Action, placement: PlacementScope): BuilderState {\n const root = state.present\n switch (action.type) {\n case 'select':\n return { ...state, selectedId: action.id, selectedRun: action.id === null ? state.selectedRun : null, refusal: null }\n case 'selectRun':\n return {\n ...state,\n selectedRun: liveRun(root, state.preserved, action.run),\n selectedId: action.run === null ? state.selectedId : null,\n refusal: null,\n }\n case 'removeRun': {\n const run = runByKey(rootCount(root), state.preserved, action.run)\n if (!run) return state\n return commit(state, root, { preserved: replaceRun(state.preserved, run, []), selectedRun: null })\n }\n case 'setRun': {\n const run = runByKey(rootCount(root), state.preserved, action.run)\n if (!run) return state\n return commit(state, root, {\n preserved: replaceRun(state.preserved, run, action.content),\n coalesceKey: action.coalesceKey,\n selectedRun: action.content.length > 0 ? action.run : null,\n })\n }\n case 'hover':\n return state.hoveredId === action.id ? state : { ...state, hoveredId: action.id }\n case 'viewport':\n return { ...state, viewport: action.viewport }\n case 'clearRefusal':\n return state.refusal === null ? state : { ...state, refusal: null }\n\n case 'undo': {\n const prev = state.past.at(-1)\n if (!prev) return state\n return restore(state, prev, state.past.slice(0, -1), [snapshotOf(state), ...state.future])\n }\n case 'redo': {\n const next = state.future[0]\n if (!next) return state\n return restore(state, next, [...state.past, snapshotOf(state)], state.future.slice(1))\n }\n\n case 'insert': {\n const node = action.minted ? action.node : cloneWithNewIds(action.node, state.newId)\n const verdict = subtreePlacementRefusal(root, node, landingOf(action.target), placement)\n if (verdict) return refuse(state, action, verdict)\n const next = insertNode(root, action.target, node)\n return commit(state, next, {\n selectedId: action.select === false ? state.selectedId : node.id,\n })\n }\n case 'remove': {\n const parent = getParentInfo(root, action.id)\n const next = removeNode(root, action.id)\n const selectedId =\n state.selectedId === action.id ? (parent?.parent.id ?? null) : state.selectedId\n return commit(state, next, { selectedId })\n }\n case 'duplicate': {\n // The copy (and every node of its subtree) ENTERS beside the original.\n const info = getParentInfo(root, action.id)\n const source = getNode(root, action.id)\n if (info && source) {\n const verdict = subtreePlacementRefusal(root, source, { parentId: info.parent.id, slot: info.slot }, placement)\n if (verdict) return refuse(state, action, verdict)\n }\n const { root: next, newId } = duplicateNode(root, action.id, state.newId)\n return commit(state, next, { selectedId: newId ?? state.selectedId })\n }\n case 'reorder':\n // Same parent, same slot: the placement does not change — nothing to judge.\n return commit(state, reorderNode(root, action.id, action.delta))\n case 'move': {\n // A node that changes parent or slot lands somewhere new (its descendants keep\n // their own slots, so only it is judged); a move within its own slot is a\n // reorder and leaves the placement as it was.\n const info = getParentInfo(root, action.id)\n const node = getNode(root, action.id)\n const relocates = !info || info.parent.id !== action.target.parentId || info.slot !== action.target.slot\n if (node && relocates) {\n const verdict = subtreePlacementRefusal(root, withoutSlots(node), landingOf(action.target), placement)\n if (verdict) return refuse(state, action, verdict)\n }\n return commit(state, moveNode(root, action.id, action.target))\n }\n case 'replace': {\n // Mint fresh ids for the incoming nodes (page-local namespace) so a detached\n // copy never collides with the page, and a promoted block's source subtree\n // (kept for the new reusable document) stays untouched.\n const cloned = action.nodes.map((n) => cloneWithNewIds(n, state.newId))\n // EC-560: promote (one embed) and detach (the reusable's content) land where the\n // replaced node was — judged there like any insert (the reuse preflight asks the\n // same `replacePlacementRefusal` before any side effect).\n const verdict = replacePlacementRefusal(root, action.id, cloned, placement)\n if (verdict) return refuse(state, action, verdict)\n const next = replaceNode(root, action.id, cloned)\n if (next === root) return state // root or absent id — no-op\n return commit(state, next, {\n selectedId: action.select === false ? state.selectedId : (cloned[0]?.id ?? state.selectedId),\n })\n }\n case 'wrap': {\n // Layout gesture (EC-162): the reference and the dragged subject become\n // the two columns of a new Grid container — ONE structural edit, one\n // history entry, one mirrored command, so a single undo dissolves the\n // whole gesture.\n const partner =\n action.subject.kind === 'node'\n ? { kind: 'node' as const, nodeId: action.subject.nodeId }\n : { kind: 'subtree' as const, node: cloneWithNewIds(action.subject.node, state.newId) }\n // EC-591: the gesture builds the `layout.grid` component; with none, it is off\n // (the drop rules refuse it before dispatch — this guard keeps the reducer total).\n const gridComponentId = state.layout.grid\n if (gridComponentId === undefined) return state\n // EC-584: the Grid lands where the reference is; the reference and the partner\n // land in the Grid's column slot. A moved partner keeps its own subtree (only it\n // relocates); a new one (a blocks-panel tile) enters whole.\n const reference = getNode(root, action.referenceId)\n const referenceInfo = getParentInfo(root, action.referenceId)\n const partnerNode = partner.kind === 'node' ? getNode(root, partner.nodeId) : partner.node\n if (reference && referenceInfo && partnerNode) {\n const columns = [withoutSlots(reference), partner.kind === 'node' ? withoutSlots(partnerNode) : partnerNode]\n const probe = gridContainerNode(gridComponentId, 'wrap', WRAP_GRID_OPTIONS, columns)\n const verdict = subtreePlacementRefusal(\n root,\n probe,\n { parentId: referenceInfo.parent.id, slot: referenceInfo.slot },\n placement,\n )\n if (verdict) return refuse(state, action, verdict)\n }\n const containerId = state.newId()\n const wrapped = applyWrap({\n root,\n gridComponentId,\n referenceId: action.referenceId,\n side: action.side,\n partner,\n containerId,\n })\n if (!wrapped) return state\n return commit(state, wrapped.root, { selectedId: containerId })\n }\n case 'setProp':\n return commit(state, setNodeProp(root, action.id, action.name, action.value), {\n coalesceKey: action.coalesceKey,\n })\n case 'removeProp':\n return commit(state, removeNodeProp(root, action.id, action.name))\n default:\n return state\n }\n}\n\nfunction landingOf(target: InsertTarget): { parentId: string; slot?: string } {\n return { parentId: target.parentId, slot: target.slot }\n}\n\n/** The node alone — a move relocates it; its descendants keep their own slots. */\nfunction withoutSlots(node: ComponentNode): ComponentNode {\n const { slots: _slots, ...rest } = node\n return rest\n}\n\n/**\n * The composition FIELD whose value the store edits (EC-554). Static for the\n * store's lifetime and deliberately NOT reducer state: it is the declaration the\n * edited tree is judged against, not part of the tree. It travels WITH the store\n * so every placement surface under the provider — the canvas and page-tree\n * drag-and-drop, paste (canvas, tree, keyboard), and the insert offer — reads the\n * same value from the one context it already requires; none can be mounted\n * without it, and `BuilderProvider` cannot be mounted without declaring it.\n */\nexport interface CompositionFieldScope {\n /** The field's display label — what a refusal notice names (e.g. \"Canvas\"). */\n label: string\n /**\n * The field's EFFECTIVE top-level vocabulary (EC-188; EC-584 — `fieldAllowList(field)`:\n * its `allow`, else the `blocks` type's default). The judge applies it only to\n * direct children of the composition root (`allowAt`, core `placement.ts`).\n */\n allow: AllowList\n}\n\nexport interface BuilderStore extends BuilderState {\n canUndo: boolean\n canRedo: boolean\n selectedNode: ComponentNode | null\n /** The composition field being edited (EC-554) — see {@link CompositionFieldScope}. */\n field: CompositionFieldScope\n /**\n * EC-584 — what the reducer judges every placement by: the manifest lookup and the\n * field's vocabulary. The gestures' own pre-checks (drag-and-drop, paste, the insert\n * offer, the reuse preflight) read the SAME scope, so an indicator and the sink agree.\n */\n placement: PlacementScope\n dispatch: (action: Action) => void\n}\n\nconst { Context: BuilderContext, useRequired: useBuilder } =\n createRequiredContext<BuilderStore>('Builder')\n\nexport function initialBuilderState(\n composition: ComponentNode,\n layout: LayoutContainerIds,\n newId: IdFactory,\n /** EC-549 — the field value's typography items with their anchors (`splitBlockRoots`). */\n preserved: readonly PreservedItem[] = [],\n): BuilderState {\n // Start container-normal (idempotent on healthy trees) so the session and the\n // lifted baseline agree from the first edit.\n const present = normalizeContainers(composition, layout)\n return {\n past: [],\n present,\n future: [],\n preserved,\n selectedId: present.id,\n selectedRun: null,\n hoveredId: null,\n viewport: 'full',\n // EC-578: fresh ids come from the injected factory — never a counter seeded past\n // the tree's `node-<n>` ids (EC-203), which two sessions advanced identically.\n newId,\n lastCoalesceKey: null,\n layout,\n refusal: null,\n }\n}\n\n/**\n * Undo/redo design (EC-141): the history stack is *UI state* — an in-memory\n * stack of composition-root snapshots. EC-287 retired the DB-resident project\n * graph and its append-only revision log, so the store no longer mirrors edits onto a\n * persistence layer: it is a pure in-memory editor. The block-field editor lifts\n * its committed composition into the open CMS document via `onChange`\n * (`composition-field-editor`); the document's own Save persists it as content.\n */\nexport function BuilderProvider({\n composition,\n preserved,\n field,\n components,\n layout,\n idFactory,\n children,\n}: {\n /** The composition root to edit (the composition-field editor's synthetic root). */\n composition: ComponentNode\n /** EC-549 — the field value's typography items (`splitBlockRoots(value).preserved`). */\n preserved?: readonly PreservedItem[]\n /** The field the composition is the value of (EC-554) — required, never defaulted. */\n field: CompositionFieldScope\n /** The manifest lookup the placement judge reads (EC-584, `registry.lookup`) — required. */\n components: ComponentLookup\n /** The layout-role component ids (EC-591, `ComponentRegistry.layout`) — required. */\n layout: LayoutContainerIds\n /**\n * EC-578 — the node-id factory. Defaults to UUIDs (`createUuidIdFactory`, one per\n * store); fixtures inject a deterministic factory.\n */\n idFactory?: IdFactory\n children: ReactNode\n}) {\n const [newId] = useState<IdFactory>(() => idFactory ?? createUuidIdFactory())\n // EC-584: the reducer is constructed ONCE with the scope it judges by — the field\n // and the lookup are static for the store's lifetime (a remount re-reads them).\n const [placement] = useState<PlacementScope>(() => ({ components, fieldAllow: field.allow }))\n const [reducer] = useState(() => createBuilderReducer(placement))\n // EC-287: nothing persists a graph — committed edits are lifted into the CMS\n // document by the composition-field editor's `onChange`. `useReducer` drives the\n // pure reducer (consecutive dispatches in one gesture apply in sequence); the\n // store below decorates the state with the derived undo/redo + selection flags.\n const [state, dispatch] = useReducer(reducer, { composition, layout, newId, preserved }, (init) =>\n initialBuilderState(init.composition, init.layout, init.newId, init.preserved),\n )\n\n const store = useMemo<BuilderStore>(() => {\n const selectedNode = state.selectedId\n ? (getNode(state.present, state.selectedId) ?? null)\n : null\n return {\n ...state,\n canUndo: state.past.length > 0,\n canRedo: state.future.length > 0,\n selectedNode,\n field,\n placement,\n dispatch,\n }\n }, [state, field, placement, dispatch])\n\n return <BuilderContext.Provider value={store}>{children}</BuilderContext.Provider>\n}\n\nexport { useBuilder }\n"]}
|
|
1
|
+
{"version":3,"file":"builder-store.js","sourceRoot":"","sources":["../../../src/components/builder/builder-store.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useMemo, useReducer, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport {\n DEFAULT_SLOT,\n cloneWithNewIds,\n duplicateNode,\n getNode,\n getParentInfo,\n gridContainerNode,\n insertNode,\n moveNode,\n normalizeContainers,\n removeNode,\n removeNodeProp,\n reorderNode,\n replaceNode,\n replacePlacementRefusal,\n setNodeProp,\n subtreePlacementRefusal,\n} from '@elytracms/core/project-graph'\nimport type {\n AllowList,\n ComponentLookup,\n ComponentNode,\n InsertTarget,\n LayoutContainerIds,\n PlacementScope,\n PlacementVerdict,\n PropValue,\n} from '@elytracms/core/project-graph'\nimport { createUuidIdFactory } from '@elytracms/core/persistence'\nimport type { IdFactory } from '@elytracms/core/persistence'\nimport { WRAP_GRID_OPTIONS, applyWrap } from '../../lib/dnd/drop-rules'\nimport { replaceRun, runByKey } from './canvas-runs'\nimport type { PreservedItem } from './canvas-runs'\nimport { rebaseCanvas } from './canvas-rebase'\nimport { createRequiredContext } from '../../lib/required-context'\n\nconst HISTORY_LIMIT = 100\n\n/**\n * A canvas preview-width key (EC-250): `'full'` (unconstrained) or a configured\n * breakpoint name (`'sm'`, `'md'`, …). Dynamic because the set is sourced from\n * the consuming site's breakpoints, so it is a string, not a fixed union — the\n * switcher resolves the available options via {@link resolveCanvasViewports}.\n */\nexport type Viewport = string\n\n/**\n * The builder store's action union. EC-287 retired the DB-resident project graph\n * and its persistence mirror, so these actions are pure in-memory editor state —\n * no action maps to a persisted `graph.*` command anymore. The block-field editor\n * lifts committed edits into the CMS document via `onChange`\n * (`composition-field-editor`); nothing persists a graph.\n */\nexport type BuilderAction =\n | { type: 'select'; id: string | null }\n | { type: 'hover'; id: string | null }\n | { type: 'viewport'; viewport: Viewport }\n | { type: 'undo' }\n | { type: 'redo' }\n /**\n * Insert a node (its subtree re-id'd from the store's id factory). `minted` — the\n * node ALREADY carries fresh ids from the same factory (the paste path mints first\n * so its issue deep links name exactly the ids that land); the reducer then inserts\n * it as given.\n */\n | { type: 'insert'; target: InsertTarget; node: ComponentNode; select?: boolean; minted?: true }\n | { type: 'remove'; id: string }\n | { type: 'duplicate'; id: string }\n | { type: 'reorder'; id: string; delta: -1 | 1 }\n | { type: 'move'; id: string; target: InsertTarget }\n /**\n * EC-282 reuse gesture: replace a node with zero or more nodes at its\n * position (clone-with-fresh-ids, like `insert`). Promote → 1 embed node;\n * detach → the referenced element's inline blocks. One atomic structural\n * edit, one undo step.\n */\n | { type: 'replace'; id: string; nodes: ComponentNode[]; select?: boolean }\n /**\n * EC-162 layout gesture: wrap the reference node and the dragged subject in\n * a two-column Grid container as ONE atomic structural edit (one undo step).\n */\n | {\n type: 'wrap'\n referenceId: string\n side: 'left' | 'right'\n subject: { kind: 'node'; nodeId: string } | { kind: 'component'; node: ComponentNode }\n }\n | { type: 'setProp'; id: string; name: string; value: PropValue; coalesceKey?: string }\n | { type: 'removeProp'; id: string; name: string }\n /** EC-584: the editor dismissed the visible refusal ({@link BuilderState.refusal}). */\n | { type: 'clearRefusal' }\n /**\n * EC-549 — select a typography RUN by its key (`canvas-runs.ts`: the index of its\n * first preserved item); `null` clears it. A run and a node are never both selected.\n */\n | { type: 'selectRun'; run: number | null }\n /** EC-549 — delete a typography run: a real, undoable edit of the preserved list. */\n | { type: 'removeRun'; run: number }\n /**\n * EC-549 — replace a run's typography with `content` (the inspector's rich-text\n * editor on that run; consecutive same-key edits coalesce into one undo step). Empty\n * `content` deletes the run.\n */\n | { type: 'setRun'; run: number; content: readonly unknown[]; coalesceKey?: string }\n /**\n * EC-630 — the field's value changed UNDERNEATH the canvas (a colleague's patch by\n * subscription, a write's canonical row, a resolved conflict), from `from` to `to` (the\n * draft's values before and after): the present absorbs the change and the history and\n * selection rebase onto it (`canvas-rebase.ts`) — never a remount. Not an edit: it adds\n * no history entry, and the canvas writes nothing for it.\n */\n | { type: 'rebase'; from: unknown; to: unknown }\n\n/** The structural actions the placement judge rules on (EC-584): every node that enters or moves. */\nexport type PlacingAction = Extract<BuilderAction, { type: 'insert' | 'replace' | 'move' | 'wrap' | 'duplicate' }>\n\n/** A refused placement (EC-584): the judge's verdict and the action it refused. */\nexport interface PlacementRefusal {\n action: PlacingAction['type']\n verdict: PlacementVerdict\n}\n\n/**\n * One history entry (EC-549): the component tree AND the preserved typography — so a\n * run's deletion or edit is undone exactly like a block's.\n */\nexport interface CanvasSnapshot {\n root: ComponentNode\n preserved: readonly PreservedItem[]\n}\n\nexport interface BuilderState {\n /**\n * The edited composition: one root-bearing component tree (EC-287). The canvas\n * wraps a page document's `body` blocks into a synthetic root and edits it\n * directly — no page/layout wrapper. Undo/redo snapshot it with the preserved\n * typography ({@link CanvasSnapshot}).\n */\n past: CanvasSnapshot[]\n present: ComponentNode\n future: CanvasSnapshot[]\n /**\n * EC-549 — the field value's typography items (no ids, EC-500 D1), each anchored by\n * how many component roots preceded it (`componentPrefix`); the field value is\n * `mergeBlockRoots(present's roots, preserved)`. In the store, so selecting,\n * deleting and editing a run is a real action with truthful undo.\n */\n preserved: readonly PreservedItem[]\n selectedId: string | null\n /** EC-549 — the selected typography run's key, or `null` (exclusive with {@link selectedId}). */\n selectedRun: number | null\n hoveredId: string | null\n viewport: Viewport\n /**\n * EC-578 / EC-595 A2 — THE node-id factory (injected; UUIDs in the studio, a\n * sequential factory in fixtures). It replaced the per-session `node-<n>` counter,\n * which two sessions editing one page advanced identically — both minted the same\n * next id, a `duplicate-node-id` on the second save. Stored ids are untouched.\n */\n newId: IdFactory\n /** Coalescing key of the last edit (consecutive same-key edits don't stack history). */\n lastCoalesceKey: string | null\n /**\n * The components filling the layout roles (EC-591, `ComponentRegistry.layout`),\n * fixed for the store's lifetime: which nodes container normalization treats as\n * Stack/Grid, and the Grid the wrap gesture builds. No hardcoded engine id.\n */\n layout: LayoutContainerIds\n /**\n * EC-584 — the last REFUSED placement, with its reason: the reducer judged an\n * `insert`/`replace`/`move`/`wrap`/`duplicate` against the field and slot\n * vocabularies and canvas eligibility (the core placement judge) and left the\n * composition unchanged. Transient — the next edit, selection, undo or redo clears\n * it — and never silent: the canvas shows it (`PlacementRefusalNotice`). A refused\n * action changes no tree, so it adds no history entry and emits no canvas patch.\n */\n refusal: PlacementRefusal | null\n /**\n * EC-630 — how many `rebase` actions the store has applied (external changes of its\n * field). The canvas sync reads it to tell a store value from before a rebase it\n * dispatched from one after it (`createCanvasSync`).\n */\n rebased: number\n}\n\ntype Action = BuilderAction\n\n/**\n * Push a new graph onto history, coalescing consecutive edits with the same key.\n *\n * Every commit normalizes layout containers (EC-162 ledger fix): the\n * operations layer normalizes every persisted `graph.*` write (EC-161), so\n * the optimistic working graph must apply the same `normalizeContainers` pass\n * or the UI state diverges from the persisted revision after\n * container-emptying moves/removes. The mirrored command stays un-normalized\n * (`graphInvocationFor` sees the raw action); both sides normalize\n * identically, so they land on the same graph.\n */\nfunction commit(\n state: BuilderState,\n root: ComponentNode,\n opts: {\n coalesceKey?: string\n selectedId?: string | null\n preserved?: readonly PreservedItem[]\n selectedRun?: number | null\n } = {},\n): BuilderState {\n const normalized = normalizeContainers(root, state.layout)\n const present = normalized\n const preserved = opts.preserved ?? state.preserved\n const coalesce = opts.coalesceKey != null && opts.coalesceKey === state.lastCoalesceKey\n // Normalization can dissolve the node a caller wanted selected (e.g. the\n // parent container of a removed child) — selection never points at a ghost.\n const desired = opts.selectedId !== undefined ? opts.selectedId : state.selectedId\n const selectedId = desired !== null && getNode(normalized, desired) ? desired : null\n const desiredRun = opts.selectedRun !== undefined ? opts.selectedRun : state.selectedRun\n return {\n ...state,\n past: coalesce ? state.past : [...state.past, snapshotOf(state)].slice(-HISTORY_LIMIT),\n present,\n preserved,\n future: [],\n lastCoalesceKey: opts.coalesceKey ?? null,\n selectedId,\n selectedRun: liveRun(present, preserved, desiredRun),\n refusal: null,\n }\n}\n\nfunction snapshotOf(state: BuilderState): CanvasSnapshot {\n return { root: state.present, preserved: state.preserved }\n}\n\n/** The run key if that run still exists in this state, else `null` (never a ghost selection). */\nfunction liveRun(root: ComponentNode, preserved: readonly PreservedItem[], run: number | null): number | null {\n if (run === null) return null\n return runByKey(rootCount(root), preserved, run) ? run : null\n}\n\n/** How many component roots the composition has (the synthetic root's children). */\nfunction rootCount(root: ComponentNode): number {\n return root.slots?.[DEFAULT_SLOT]?.length ?? 0\n}\n\n/** Restore a history entry (undo/redo), keeping a selection only where it still exists. */\nfunction restore(state: BuilderState, to: CanvasSnapshot, past: CanvasSnapshot[], future: CanvasSnapshot[]): BuilderState {\n return {\n ...state,\n past,\n present: to.root,\n preserved: to.preserved,\n future,\n lastCoalesceKey: null,\n selectedId: getNode(to.root, state.selectedId ?? '') ? state.selectedId : null,\n selectedRun: liveRun(to.root, to.preserved, state.selectedRun),\n refusal: null,\n }\n}\n\n/** Leave the composition untouched and say why (EC-584 — never a silent no-op). */\nfunction refuse(state: BuilderState, action: PlacingAction, verdict: PlacementVerdict): BuilderState {\n return { ...state, refusal: { action: action.type, verdict } }\n}\n\n/**\n * The builder's pure state transition, CONSTRUCTED with the placement scope it judges\n * by (EC-584, owner-ratified 4a): the field's effective top-level vocabulary and the\n * manifest lookup. The scope is static per store and deliberately not reducer STATE\n * (it is the declaration the tree is judged against, not part of the tree) — the\n * reducer RECEIVES it, so there is no `(state, action)` to call without it.\n *\n * Every structural action that places a node — `insert`, `replace`, `move`, `wrap`,\n * `duplicate` — runs the ONE core judge (`subtreePlacementRefusal`) over every node\n * that enters or moves, BEFORE committing. A refusal returns the composition\n * unchanged, with the verdict in {@link BuilderState.refusal}: so no gesture — a\n * forgotten pre-check, promote, detach, a fixture's `slotChildren`, a server-only\n * paste — can place what the field, a slot or canvas eligibility forbids, and the\n * publish gate (which calls the same judge) cannot disagree with the canvas.\n *\n * Exported (with `initialBuilderState`) for unit tests — gesture determinism and\n * normalization lockstep are reducer-level properties, not render behavior.\n */\nexport function createBuilderReducer(\n placement: PlacementScope,\n): (state: BuilderState, action: Action) => BuilderState {\n return (state, action) => transition(state, action, placement)\n}\n\nfunction transition(state: BuilderState, action: Action, placement: PlacementScope): BuilderState {\n const root = state.present\n switch (action.type) {\n case 'select':\n return { ...state, selectedId: action.id, selectedRun: action.id === null ? state.selectedRun : null, refusal: null }\n case 'selectRun':\n return {\n ...state,\n selectedRun: liveRun(root, state.preserved, action.run),\n selectedId: action.run === null ? state.selectedId : null,\n refusal: null,\n }\n case 'removeRun': {\n const run = runByKey(rootCount(root), state.preserved, action.run)\n if (!run) return state\n return commit(state, root, { preserved: replaceRun(state.preserved, run, []), selectedRun: null })\n }\n case 'setRun': {\n const run = runByKey(rootCount(root), state.preserved, action.run)\n if (!run) return state\n return commit(state, root, {\n preserved: replaceRun(state.preserved, run, action.content),\n coalesceKey: action.coalesceKey,\n selectedRun: action.content.length > 0 ? action.run : null,\n })\n }\n case 'hover':\n return state.hoveredId === action.id ? state : { ...state, hoveredId: action.id }\n case 'viewport':\n return { ...state, viewport: action.viewport }\n case 'clearRefusal':\n return state.refusal === null ? state : { ...state, refusal: null }\n\n case 'undo': {\n const prev = state.past.at(-1)\n if (!prev) return state\n return restore(state, prev, state.past.slice(0, -1), [snapshotOf(state), ...state.future])\n }\n case 'redo': {\n const next = state.future[0]\n if (!next) return state\n return restore(state, next, [...state.past, snapshotOf(state)], state.future.slice(1))\n }\n case 'rebase': {\n // EC-630: undo never reverts the external change — every snapshot absorbs it. The\n // next edit starts a new undo step (the step it would coalesce into may have been\n // dropped by the rebase, and an edit must never land in a step it cannot undo).\n const rebased = rebaseCanvas(state, action.from, action.to)\n // Counted even when it changes nothing: the canvas sync waits for every rebase it dispatched.\n if (!rebased) return { ...state, rebased: state.rebased + 1 }\n return {\n ...state,\n ...rebased,\n past: [...rebased.past],\n future: [...rebased.future],\n lastCoalesceKey: null,\n rebased: state.rebased + 1,\n }\n }\n\n case 'insert': {\n const node = action.minted ? action.node : cloneWithNewIds(action.node, state.newId)\n const verdict = subtreePlacementRefusal(root, node, landingOf(action.target), placement)\n if (verdict) return refuse(state, action, verdict)\n const next = insertNode(root, action.target, node)\n return commit(state, next, {\n selectedId: action.select === false ? state.selectedId : node.id,\n })\n }\n case 'remove': {\n const parent = getParentInfo(root, action.id)\n const next = removeNode(root, action.id)\n const selectedId =\n state.selectedId === action.id ? (parent?.parent.id ?? null) : state.selectedId\n return commit(state, next, { selectedId })\n }\n case 'duplicate': {\n // The copy (and every node of its subtree) ENTERS beside the original.\n const info = getParentInfo(root, action.id)\n const source = getNode(root, action.id)\n if (info && source) {\n const verdict = subtreePlacementRefusal(root, source, { parentId: info.parent.id, slot: info.slot }, placement)\n if (verdict) return refuse(state, action, verdict)\n }\n const { root: next, newId } = duplicateNode(root, action.id, state.newId)\n return commit(state, next, { selectedId: newId ?? state.selectedId })\n }\n case 'reorder':\n // Same parent, same slot: the placement does not change — nothing to judge.\n return commit(state, reorderNode(root, action.id, action.delta))\n case 'move': {\n // A node that changes parent or slot lands somewhere new (its descendants keep\n // their own slots, so only it is judged); a move within its own slot is a\n // reorder and leaves the placement as it was.\n const info = getParentInfo(root, action.id)\n const node = getNode(root, action.id)\n const relocates = !info || info.parent.id !== action.target.parentId || info.slot !== action.target.slot\n if (node && relocates) {\n const verdict = subtreePlacementRefusal(root, withoutSlots(node), landingOf(action.target), placement)\n if (verdict) return refuse(state, action, verdict)\n }\n return commit(state, moveNode(root, action.id, action.target))\n }\n case 'replace': {\n // Mint fresh ids for the incoming nodes (page-local namespace) so a detached\n // copy never collides with the page, and a promoted block's source subtree\n // (kept for the new reusable document) stays untouched.\n const cloned = action.nodes.map((n) => cloneWithNewIds(n, state.newId))\n // EC-560: promote (one embed) and detach (the reusable's content) land where the\n // replaced node was — judged there like any insert (the reuse preflight asks the\n // same `replacePlacementRefusal` before any side effect).\n const verdict = replacePlacementRefusal(root, action.id, cloned, placement)\n if (verdict) return refuse(state, action, verdict)\n const next = replaceNode(root, action.id, cloned)\n if (next === root) return state // root or absent id — no-op\n return commit(state, next, {\n selectedId: action.select === false ? state.selectedId : (cloned[0]?.id ?? state.selectedId),\n })\n }\n case 'wrap': {\n // Layout gesture (EC-162): the reference and the dragged subject become\n // the two columns of a new Grid container — ONE structural edit, one\n // history entry, one mirrored command, so a single undo dissolves the\n // whole gesture.\n const partner =\n action.subject.kind === 'node'\n ? { kind: 'node' as const, nodeId: action.subject.nodeId }\n : { kind: 'subtree' as const, node: cloneWithNewIds(action.subject.node, state.newId) }\n // EC-591: the gesture builds the `layout.grid` component; with none, it is off\n // (the drop rules refuse it before dispatch — this guard keeps the reducer total).\n const gridComponentId = state.layout.grid\n if (gridComponentId === undefined) return state\n // EC-584: the Grid lands where the reference is; the reference and the partner\n // land in the Grid's column slot. A moved partner keeps its own subtree (only it\n // relocates); a new one (a blocks-panel tile) enters whole.\n const reference = getNode(root, action.referenceId)\n const referenceInfo = getParentInfo(root, action.referenceId)\n const partnerNode = partner.kind === 'node' ? getNode(root, partner.nodeId) : partner.node\n if (reference && referenceInfo && partnerNode) {\n const columns = [withoutSlots(reference), partner.kind === 'node' ? withoutSlots(partnerNode) : partnerNode]\n const probe = gridContainerNode(gridComponentId, 'wrap', WRAP_GRID_OPTIONS, columns)\n const verdict = subtreePlacementRefusal(\n root,\n probe,\n { parentId: referenceInfo.parent.id, slot: referenceInfo.slot },\n placement,\n )\n if (verdict) return refuse(state, action, verdict)\n }\n const containerId = state.newId()\n const wrapped = applyWrap({\n root,\n gridComponentId,\n referenceId: action.referenceId,\n side: action.side,\n partner,\n containerId,\n })\n if (!wrapped) return state\n return commit(state, wrapped.root, { selectedId: containerId })\n }\n case 'setProp':\n return commit(state, setNodeProp(root, action.id, action.name, action.value), {\n coalesceKey: action.coalesceKey,\n })\n case 'removeProp':\n return commit(state, removeNodeProp(root, action.id, action.name))\n default:\n return state\n }\n}\n\nfunction landingOf(target: InsertTarget): { parentId: string; slot?: string } {\n return { parentId: target.parentId, slot: target.slot }\n}\n\n/** The node alone — a move relocates it; its descendants keep their own slots. */\nfunction withoutSlots(node: ComponentNode): ComponentNode {\n const { slots: _slots, ...rest } = node\n return rest\n}\n\n/**\n * The composition FIELD whose value the store edits (EC-554). Static for the\n * store's lifetime and deliberately NOT reducer state: it is the declaration the\n * edited tree is judged against, not part of the tree. It travels WITH the store\n * so every placement surface under the provider — the canvas and page-tree\n * drag-and-drop, paste (canvas, tree, keyboard), and the insert offer — reads the\n * same value from the one context it already requires; none can be mounted\n * without it, and `BuilderProvider` cannot be mounted without declaring it.\n */\nexport interface CompositionFieldScope {\n /** The field's display label — what a refusal notice names (e.g. \"Canvas\"). */\n label: string\n /**\n * The field's EFFECTIVE top-level vocabulary (EC-188; EC-584 — `fieldAllowList(field)`:\n * its `allow`, else the `blocks` type's default). The judge applies it only to\n * direct children of the composition root (`allowAt`, core `placement.ts`).\n */\n allow: AllowList\n}\n\nexport interface BuilderStore extends BuilderState {\n canUndo: boolean\n canRedo: boolean\n selectedNode: ComponentNode | null\n /** The composition field being edited (EC-554) — see {@link CompositionFieldScope}. */\n field: CompositionFieldScope\n /**\n * EC-584 — what the reducer judges every placement by: the manifest lookup and the\n * field's vocabulary. The gestures' own pre-checks (drag-and-drop, paste, the insert\n * offer, the reuse preflight) read the SAME scope, so an indicator and the sink agree.\n */\n placement: PlacementScope\n dispatch: (action: Action) => void\n}\n\nconst { Context: BuilderContext, useRequired: useBuilder } =\n createRequiredContext<BuilderStore>('Builder')\n\nexport function initialBuilderState(\n composition: ComponentNode,\n layout: LayoutContainerIds,\n newId: IdFactory,\n /** EC-549 — the field value's typography items with their anchors (`splitBlockRoots`). */\n preserved: readonly PreservedItem[] = [],\n): BuilderState {\n // Start container-normal (idempotent on healthy trees) so the session and the\n // lifted baseline agree from the first edit.\n const present = normalizeContainers(composition, layout)\n return {\n past: [],\n present,\n future: [],\n preserved,\n selectedId: present.id,\n selectedRun: null,\n hoveredId: null,\n viewport: 'full',\n // EC-578: fresh ids come from the injected factory — never a counter seeded past\n // the tree's `node-<n>` ids (EC-203), which two sessions advanced identically.\n newId,\n lastCoalesceKey: null,\n layout,\n refusal: null,\n rebased: 0,\n }\n}\n\n/**\n * Undo/redo design (EC-141): the history stack is *UI state* — an in-memory\n * stack of composition-root snapshots. EC-287 retired the DB-resident project\n * graph and its append-only revision log, so the store no longer mirrors edits onto a\n * persistence layer: it is a pure in-memory editor. The block-field editor lifts\n * its committed composition into the open CMS document via `onChange`\n * (`composition-field-editor`); the document's own Save persists it as content.\n * EC-630: a change of the field made underneath the store (a colleague's patch) is\n * absorbed by the present AND every snapshot (`rebase`, `canvas-rebase.ts`), so an undo\n * never reverts it.\n */\nexport function BuilderProvider({\n composition,\n preserved,\n field,\n components,\n layout,\n idFactory,\n children,\n}: {\n /** The composition root to edit (the composition-field editor's synthetic root). */\n composition: ComponentNode\n /** EC-549 — the field value's typography items (`splitBlockRoots(value).preserved`). */\n preserved?: readonly PreservedItem[]\n /** The field the composition is the value of (EC-554) — required, never defaulted. */\n field: CompositionFieldScope\n /** The manifest lookup the placement judge reads (EC-584, `registry.lookup`) — required. */\n components: ComponentLookup\n /** The layout-role component ids (EC-591, `ComponentRegistry.layout`) — required. */\n layout: LayoutContainerIds\n /**\n * EC-578 — the node-id factory. Defaults to UUIDs (`createUuidIdFactory`, one per\n * store); fixtures inject a deterministic factory.\n */\n idFactory?: IdFactory\n children: ReactNode\n}) {\n const [newId] = useState<IdFactory>(() => idFactory ?? createUuidIdFactory())\n // EC-584: the reducer is constructed ONCE with the scope it judges by — the field\n // and the lookup are static for the store's lifetime (a remount re-reads them).\n const [placement] = useState<PlacementScope>(() => ({ components, fieldAllow: field.allow }))\n const [reducer] = useState(() => createBuilderReducer(placement))\n // EC-287: nothing persists a graph — committed edits are lifted into the CMS\n // document by the composition-field editor's `onChange`. `useReducer` drives the\n // pure reducer (consecutive dispatches in one gesture apply in sequence); the\n // store below decorates the state with the derived undo/redo + selection flags.\n const [state, dispatch] = useReducer(reducer, { composition, layout, newId, preserved }, (init) =>\n initialBuilderState(init.composition, init.layout, init.newId, init.preserved),\n )\n\n const store = useMemo<BuilderStore>(() => {\n const selectedNode = state.selectedId\n ? (getNode(state.present, state.selectedId) ?? null)\n : null\n return {\n ...state,\n canUndo: state.past.length > 0,\n canRedo: state.future.length > 0,\n selectedNode,\n field,\n placement,\n dispatch,\n }\n }, [state, field, placement, dispatch])\n\n return <BuilderContext.Provider value={store}>{children}</BuilderContext.Provider>\n}\n\nexport { useBuilder }\n"]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { CommitPolicy } from '../cms/commit-policy.js';
|
|
3
|
+
import type { CanvasControlPosition } from '../cms/canvas-commit.js';
|
|
4
|
+
/**
|
|
5
|
+
* EC-630 — the canvas inspector's commit boundary. The document editor hands the canvas a
|
|
6
|
+
* channel to its committer ({@link CanvasCommitChannel}); every inspector control that
|
|
7
|
+
* types (a prop's control, a typography run's editor) stands inside a boundary naming the
|
|
8
|
+
* position it writes and its kind's policy (`commitPolicyOf`), so a burst of typing is
|
|
9
|
+
* committed on blur / Enter / the idle pause like a form field's — never per keystroke.
|
|
10
|
+
* Without a channel (a surface with no document — the component preview) it is inert.
|
|
11
|
+
*/
|
|
12
|
+
export interface CanvasCommitChannel {
|
|
13
|
+
focused(position: CanvasControlPosition, policy: CommitPolicy): void;
|
|
14
|
+
blurred(position: CanvasControlPosition): void;
|
|
15
|
+
entered(position: CanvasControlPosition): void;
|
|
16
|
+
}
|
|
17
|
+
export declare const CanvasCommitProvider: import("react").Provider<CanvasCommitChannel | null>;
|
|
18
|
+
export declare function CanvasCommitBoundary({ position, policy, children, }: {
|
|
19
|
+
position: CanvasControlPosition;
|
|
20
|
+
policy: CommitPolicy;
|
|
21
|
+
children: ReactNode;
|
|
22
|
+
}): import("react").JSX.Element;
|