@elytracms/studio 0.0.95 → 0.0.97
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/convex/cms.ts +272 -16
- package/convex/delivery-folded-drift.test.ts +104 -0
- package/convex/delivery.ts +24 -8
- package/convex/depth-codec-lookalike.test.ts +192 -0
- package/convex/fixture-accounts.test.ts +305 -0
- package/convex/fixtureAccounts.ts +298 -0
- package/convex/incarnation.test.ts +149 -0
- package/convex/ingest.test.ts +145 -3
- package/convex/ingest.ts +123 -17
- package/convex/receipt-races.test.ts +184 -0
- package/convex/records-zod-copy.test.ts +76 -0
- package/convex/schema.ts +20 -1
- package/convex/strict-delete.test.ts +129 -0
- package/convex/strict-reference-race.test.ts +139 -0
- package/dist/components/ai/ai-chat-panel.js +15 -2
- package/dist/components/ai/ai-chat-panel.js.map +1 -1
- package/dist/components/ai/translate-document-action.d.ts +6 -0
- package/dist/components/ai/translate-document-action.js.map +1 -1
- package/dist/components/ai/translate-document-dialog.d.ts +1 -1
- package/dist/components/ai/translate-document-dialog.js +5 -5
- package/dist/components/ai/translate-document-dialog.js.map +1 -1
- package/dist/components/assets/asset-detail.js +5 -2
- package/dist/components/assets/asset-detail.js.map +1 -1
- package/dist/components/assets/asset-locale.d.ts +7 -0
- package/dist/components/assets/asset-locale.js +16 -2
- package/dist/components/assets/asset-locale.js.map +1 -1
- package/dist/components/assets/asset-meta-drift-notice.d.ts +13 -0
- package/dist/components/assets/asset-meta-drift-notice.js +22 -0
- package/dist/components/assets/asset-meta-drift-notice.js.map +1 -0
- package/dist/components/assets/asset-meta-label.d.ts +2 -1
- package/dist/components/assets/asset-meta-label.js +4 -2
- package/dist/components/assets/asset-meta-label.js.map +1 -1
- package/dist/components/assets/asset-metadata.d.ts +33 -13
- package/dist/components/assets/asset-metadata.js +46 -22
- package/dist/components/assets/asset-metadata.js.map +1 -1
- package/dist/components/assets/asset-search.d.ts +6 -2
- package/dist/components/assets/asset-search.js +15 -5
- package/dist/components/assets/asset-search.js.map +1 -1
- package/dist/components/assets/assets-collection-view-source.d.ts +5 -3
- package/dist/components/assets/assets-collection-view-source.js +7 -5
- package/dist/components/assets/assets-collection-view-source.js.map +1 -1
- package/dist/components/builder/canvas-rebase.d.ts +36 -12
- package/dist/components/builder/canvas-rebase.js +13 -3
- package/dist/components/builder/canvas-rebase.js.map +1 -1
- package/dist/components/builder/canvas-render-context.js +5 -4
- package/dist/components/builder/canvas-render-context.js.map +1 -1
- package/dist/components/builder/composition-field-editor.d.ts +12 -6
- package/dist/components/builder/composition-field-editor.js +6 -5
- package/dist/components/builder/composition-field-editor.js.map +1 -1
- package/dist/components/cms/asset-picker-core.d.ts +2 -2
- package/dist/components/cms/asset-picker-core.js +2 -2
- package/dist/components/cms/asset-picker-core.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 +6 -0
- package/dist/components/cms/canvas-commit.js +5 -1
- package/dist/components/cms/canvas-commit.js.map +1 -1
- package/dist/components/cms/cms-store.d.ts +24 -8
- package/dist/components/cms/cms-store.js +21 -14
- package/dist/components/cms/cms-store.js.map +1 -1
- package/dist/components/cms/collection-table.js +4 -11
- package/dist/components/cms/collection-table.js.map +1 -1
- package/dist/components/cms/detail-editor.js +2 -2
- package/dist/components/cms/detail-editor.js.map +1 -1
- package/dist/components/cms/draft-commit.d.ts +16 -3
- package/dist/components/cms/draft-commit.js +3 -2
- package/dist/components/cms/draft-commit.js.map +1 -1
- package/dist/components/cms/use-document-draft.d.ts +107 -10
- package/dist/components/cms/use-document-draft.js +157 -42
- package/dist/components/cms/use-document-draft.js.map +1 -1
- package/dist/components/collection-view/cms-collection-view-source.js +6 -4
- package/dist/components/collection-view/cms-collection-view-source.js.map +1 -1
- package/dist/components/components/transfer-dialog.js +6 -4
- package/dist/components/components/transfer-dialog.js.map +1 -1
- package/dist/lib/ai/chat-state.d.ts +7 -0
- package/dist/lib/ai/chat-state.js.map +1 -1
- package/dist/lib/ai/tools.d.ts +8 -0
- package/dist/lib/ai/tools.js +4 -0
- package/dist/lib/ai/tools.js.map +1 -1
- package/dist/lib/cms-views/view-model.d.ts +15 -8
- package/dist/lib/cms-views/view-model.js +19 -5
- package/dist/lib/cms-views/view-model.js.map +1 -1
- package/dist/lib/collection-view/__fixtures__/document-writes.js +1 -0
- package/dist/lib/collection-view/__fixtures__/document-writes.js.map +1 -1
- package/dist/lib/collection-view/collection-view-source.d.ts +18 -7
- package/dist/lib/collection-view/collection-view-source.js.map +1 -1
- package/dist/lib/data/collection-content.js +5 -0
- package/dist/lib/data/collection-content.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 +15 -1
- package/dist/lib/i18n/messages.de.js.map +1 -1
- package/dist/lib/i18n/messages.en.d.ts +13 -0
- package/dist/lib/i18n/messages.en.js +16 -1
- package/dist/lib/i18n/messages.en.js.map +1 -1
- package/dist/lib/operations/cms-persistence.d.ts +119 -35
- package/dist/lib/operations/cms-persistence.js +441 -91
- package/dist/lib/operations/cms-persistence.js.map +1 -1
- package/dist/lib/persistence/convex-adapter.js +80 -8
- package/dist/lib/persistence/convex-adapter.js.map +1 -1
- package/dist/lib/persona/labels.js +2 -0
- package/dist/lib/persona/labels.js.map +1 -1
- package/package.json +4 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-document-draft.js","sourceRoot":"","sources":["../../../src/components/cms/use-document-draft.ts"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport {\n PRESENCE_TTL_MS,\n livePresence,\n} from '@elytracms/core/persistence'\nimport type { PresenceEntry } from '@elytracms/core/persistence'\nimport {\n applyDocumentPatches,\n formatValuePath,\n readValueAt,\n semanticValueEqual,\n writeField,\n} from '@elytracms/core/cms-core'\nimport type {\n CmsDocument,\n CollectionDef,\n DocumentPatch,\n DocumentRef,\n FieldDef,\n Locale,\n PropFieldLookup,\n ValuePath,\n} from '@elytracms/core/cms-core'\nimport type { CollectionViewSource } from '../../lib/collection-view/collection-view-source'\nimport { fieldPositionPath } from '../../lib/cms/field-patch'\nimport { compositionPatches } from '../builder/composition-patches'\nimport type { CanvasFieldChange } from '../builder/canvas-rebase'\nimport { commitPolicyOf } from './commit-policy'\nimport type { CommitPolicy } from './commit-policy'\nimport { canvasControl, routeCanvasWrite } from './canvas-commit'\nimport type { CanvasControl, CanvasControlPosition } from './canvas-commit'\nimport { browserScheduler, createDraftCommitter } from './draft-commit'\nimport type { DraftCommitter, Scheduler, UncommittedPosition } from './draft-commit'\n\n/**\n * EC-595 A2 — the detail editor's draft over the revisioned document (and, for the\n * explicit-save rails, over a record).\n *\n * **Document write model** (the CMS): the draft changes per keystroke (live validation\n * reads it); a field's edit is COMMITTED per its control kind's policy\n * ({@link commitPolicyOf} / {@link createDraftCommitter}) as the patch of the position it\n * wrote — never a document. The canvas commits each action's node patches at once\n * (`compositionPatches`); a burst of typing in one of its inspector controls is that\n * control's uncommitted position, committed on its kind's policy like a form field\n * (EC-630, `canvas-commit.ts`). The draft REBASES whenever the session's document changes\n * (a write result, a colleague's change by subscription, a resolved conflict): it is the\n * session's document with this editor's uncommitted positions laid over it — local wins\n * until committed, then the per-path check decides (EC-595 point 7).\n *\n * **Forced flushes** (the ruling: before unmount, tab hide, publish, preview, an AI\n * proposal, duplicate, transfer): {@link DocumentDraft.flush} commits every uncommitted\n * position and awaits the persister. Unmount and tab hide run it here; the editor's\n * publish, duplicate and the AI tap call it.\n *\n * **Thin presence** (Stage A): a heartbeat naming the focused field's path on focus\n * change and every {@link PRESENCE_HEARTBEAT_MS} (under the 30 s TTL), cleared on\n * unmount; the other sessions' live heartbeats arrive with the subscription.\n *\n * **Record write model** (users, redirects, assets, the account): the draft is edited\n * in place and written whole on the editor's Save button (EC-240) — unchanged.\n */\n\n/** The presence heartbeat interval — under {@link PRESENCE_TTL_MS} so a present editor never lapses. */\nexport const PRESENCE_HEARTBEAT_MS = 10_000\n\n/** An injectable visibility source (the tab-hide flush; tests drive it). */\nexport interface VisibilitySource {\n /** Call `onHidden` whenever the tab becomes hidden; returns the unsubscribe. */\n onHidden(onHidden: () => void): () => void\n}\n\nexport const documentVisibility: VisibilitySource = {\n onHidden(onHidden) {\n if (typeof document === 'undefined') return () => {}\n const listener = () => {\n if (document.visibilityState === 'hidden') onHidden()\n }\n document.addEventListener('visibilitychange', listener)\n return () => document.removeEventListener('visibilitychange', listener)\n },\n}\n\n/** The patch that writes the value `doc` holds at `path` (unset when absent). */\nexport function positionPatch(doc: CmsDocument, path: ValuePath): DocumentPatch {\n const value = readValueAt(doc, path)\n return value === undefined ? { op: 'unset', path } : { op: 'set', path, value }\n}\n\n/**\n * THE rebase (pure): the session's document with the editor's uncommitted positions laid\n * over it, values from the editor's current draft.\n */\nexport function rebaseDraft(\n sessionDoc: CmsDocument,\n draft: CmsDocument,\n uncommitted: readonly UncommittedPosition[],\n): CmsDocument {\n if (uncommitted.length === 0) return sessionDoc\n const patches = uncommitted.map((position) => positionPatch(draft, position.path))\n return applyDocumentPatches({ document: sessionDoc, patches, since: [] }).document\n}\n\n/**\n * EC-630 — the canvas's write into the draft (pure). The canvas writer's node patches,\n * keyed by node id, are the canvas's OWN delta: from `base`, the value it last agreed on\n * with the draft, to `value` — never a document diff, and never measured against the\n * draft's CURRENT value (a colleague's change that reached the draft a moment before the\n * edit lifted would read as part of the edit, and its patches would revert it). The draft\n * takes the delta over whatever it holds now (normally `base` itself) and canonicalises the\n * field through the write lens. `undefined` when the canvas changed nothing.\n */\nexport function canvasWrite(input: {\n collection: CollectionDef\n draft: CmsDocument\n field: string\n locale: Locale\n value: unknown\n base: unknown\n lookupManifest: PropFieldLookup\n}): { next: CmsDocument; patches: DocumentPatch[] } | undefined {\n const { collection, draft, field, locale, lookupManifest } = input\n const path = fieldPositionPath(collection, field, locale)\n const localized = path.length > 1 ? locale : undefined\n const patches = compositionPatches(input.base, input.value, field, localized)\n if (patches.length === 0) return undefined\n const applied = applyDocumentPatches({ document: draft, patches, since: [] }).document\n return { next: writeField(collection, draft, field, locale, readValueAt(applied, path), { lookupManifest }), patches }\n}\n\n/**\n * EC-630 — the change of the canvas field made UNDERNEATH the canvas when the draft\n * rebased from `previous` to `next` (pure), numbered after `last`; `undefined` when the\n * field did not move. The canvas's own edits are in both values (the session's view holds\n * every patch it committed, the draft overlays every position it has not), so the change\n * is someone else's: a colleague's patch, a write's canonical row, a resolved conflict.\n */\nexport function canvasFieldChange(\n previous: CmsDocument,\n next: CmsDocument,\n path: ValuePath,\n last: CanvasFieldChange | undefined,\n): CanvasFieldChange | undefined {\n const from = readValueAt(previous, path)\n const to = readValueAt(next, path)\n return semanticValueEqual(from, to) ? undefined : { from, to, seq: (last?.seq ?? 0) + 1 }\n}\n\n/**\n * The reader's \"now\" for presence liveness: THIS session's own latest heartbeat `at`\n * (written by the same store clock as everyone else's — the playground store runs a\n * deterministic sequential clock, Convex the writers' wall clocks), falling back to the\n * injected clock before the first heartbeat lands. Comparing others' `at` against a\n * different time base would call every colleague stale (measured live in the\n * playground: the store's clock is months behind the browser's).\n */\nexport function presenceReaderNow(\n entries: readonly PresenceEntry[],\n sessionId: string,\n fallback: string,\n): string {\n const own = entries.filter((entry) => entry.sessionId === sessionId).map((entry) => entry.at)\n return own.length > 0 ? own.sort().at(-1)! : fallback\n}\n\nexport interface DocumentDraft {\n draft: CmsDocument\n /** Uncommitted (document model) / unsaved (record model) edits exist. */\n dirty: boolean\n /** The record model: the editor renders a Save button + navigation guard. */\n explicitSave: boolean\n /** A field's control wrote `value` (the active locale for a localized field). */\n setFieldValue(field: FieldDef, value: unknown): void\n /** A writer that transforms ONE field position (fill / reset from base). */\n editField(field: FieldDef, update: (draft: CmsDocument) => CmsDocument): void\n /**\n * The canvas emitted a new value for its field (node patches, committed at once). `base`\n * is the value the canvas last agreed on with the draft: the canvas writer's delta is\n * `base` → `value`, applied over whatever the draft holds now (EC-630 — a change that\n * landed underneath meanwhile is never reverted by the canvas's own write).\n */\n setCanvasValue(field: FieldDef, value: unknown, base: unknown): void\n /** The slot alias (a document-level key: a whole-document write at the held revision). */\n setAlias(alias: string | undefined): void\n /** Field boundary events (focus / blur / Enter) — the commit policy's triggers. */\n focused(field: FieldDef): void\n blurred(field: FieldDef): void\n entered(field: FieldDef): void\n /**\n * EC-630 — the canvas inspector's boundary events: a control writing `position` of the\n * canvas field `field`, committing on `policy` (THE declaration for its kind). A burst of\n * typing in it is its uncommitted position until blur / Enter / the idle pause.\n */\n canvasFocused(field: FieldDef, position: CanvasControlPosition, policy: CommitPolicy): void\n canvasBlurred(field: FieldDef, position: CanvasControlPosition): void\n canvasEntered(field: FieldDef, position: CanvasControlPosition): void\n /** The forced commit (sync; the persister serialises what follows) / the record model's Save. */\n save(): void\n /** The FORCED FLUSH: commit everything uncommitted and await persistence. */\n flush(): Promise<void>\n /** Adopt a draft from outside (a restored version). */\n replaceDraft(next: CmsDocument): void\n /**\n * Bumped when the canvas must remount from the draft: a restored version replaced the\n * document, or a locale switch pointed the canvas at another bucket. (A change made\n * underneath the canvas is NOT a remount — {@link canvasChange}.)\n */\n canvasNonce: number\n /**\n * EC-630 — the latest change of the canvas field's value made UNDERNEATH the canvas (a\n * colleague's patch by subscription, a write's canonical row, a resolved conflict): the\n * draft's value before and after it. The canvas REBASES onto it — the change lands, its\n * undo history and selection survive — where it used to remount and lose both.\n */\n canvasChange: CanvasFieldChange | undefined\n /** The OTHER sessions present on this document, live by the reader's clock. */\n presence: readonly PresenceEntry[]\n}\n\nexport interface UseDocumentDraftInput {\n source: CollectionViewSource\n collection: CollectionDef\n doc: CmsDocument\n locale: Locale\n lookupManifest: PropFieldLookup\n /** The composition field the canvas edits on this screen, if any. */\n canvasField?: FieldDef\n scheduler?: Scheduler\n visibility?: VisibilitySource\n /** The reader's clock for presence liveness (ISO); `new Date().toISOString()` by default. */\n now?: () => string\n}\n\n/**\n * EC-616 — the base a record save is diffed against: the source's value whenever the draft\n * equals it (the record as opened, or as a save came back), else the previous base (the\n * editor has unsaved changes; a change from another writer meanwhile is not theirs). Pure.\n */\nexport function recordSaveBase(previous: CmsDocument, draft: CmsDocument, doc: CmsDocument): CmsDocument {\n return semanticValueEqual(draft, doc) ? doc : previous\n}\n\nexport function useDocumentDraft(input: UseDocumentDraftInput): DocumentDraft {\n const { source, collection, doc, locale, lookupManifest, canvasField } = input\n const writes = source.writes\n const documentModel = writes.kind === 'document' ? writes : undefined\n const ref: DocumentRef = useMemo(() => ({ collection: doc.collection, id: doc.id }), [doc.collection, doc.id])\n const [draft, setDraftState] = useState<CmsDocument>(doc)\n const draftRef = useRef(draft)\n // EC-616: the record as the editor opened it (or as of their last successful save) — the\n // BASE a record rail diffs a save against, so it writes only the editor's changes. It moves\n // only while the draft equals the source's value (at open, and once a save is reflected\n // back): another writer's change arriving mid-edit never becomes the editor's base.\n const baseRef = useRef<CmsDocument>(doc)\n baseRef.current = recordSaveBase(baseRef.current, draftRef.current, doc)\n const setDraft = useCallback((next: CmsDocument) => {\n draftRef.current = next\n setDraftState(next)\n }, [])\n const scheduler = input.scheduler ?? browserScheduler\n const now = input.now ?? (() => new Date().toISOString())\n const fieldsByName = useMemo(() => new Map(collection.fields.map((f) => [f.name, f])), [collection])\n\n // --- Committing --------------------------------------------------------------------\n const writesRef = useRef(documentModel)\n writesRef.current = documentModel\n /** EC-630 — the canvas inspector controls that took focus, by committer key (their position + policy). */\n const canvasControls = useRef(new Map<string, CanvasControl>())\n const committer = useMemo<DraftCommitter>(\n () =>\n createDraftCommitter({\n policyOf: (name) => {\n const field = fieldsByName.get(name)\n if (field) return commitPolicyOf(field)\n return canvasControls.current.get(name)?.policy ?? 'blur-or-idle'\n },\n commit: (positions) => {\n const target = writesRef.current\n if (!target) return\n void target.commit(\n ref,\n positions.map((position) => positionPatch(draftRef.current, position.path)),\n positions.map((position) => position.base),\n )\n },\n baseOf: () => writesRef.current?.revisionOf(ref),\n scheduler,\n }),\n // One committer per open document (the editor is keyed by it).\n [ref],\n )\n\n // --- The canvas's value: its remount and its rebase ------------------------------------\n const canvasPath = canvasField ? fieldPositionPath(collection, canvasField.name, locale) : undefined\n const canvasPathKey = canvasPath ? formatValuePath(canvasPath) : ''\n const [canvasNonce, setCanvasNonce] = useState(0)\n const [canvasChange, setCanvasChange] = useState<CanvasFieldChange | undefined>(undefined)\n // A locale switch points the canvas at another bucket: remount from it.\n const lastCanvasPathKey = useRef(canvasPathKey)\n useEffect(() => {\n if (lastCanvasPathKey.current === canvasPathKey) return\n lastCanvasPathKey.current = canvasPathKey\n setCanvasNonce((n) => n + 1)\n }, [canvasPathKey])\n\n // --- The rebase (document model) -------------------------------------------------------\n const firstDoc = useRef(true)\n useEffect(() => {\n if (firstDoc.current) {\n firstDoc.current = false\n return\n }\n if (!documentModel) return\n const previous = draftRef.current\n const next = rebaseDraft(doc, previous, committer.uncommitted())\n setDraft(next)\n // EC-630: the canvas field moved underneath the canvas — the canvas rebases onto the\n // change (its own edits are in both values: the session's view holds every patch it\n // committed, and the draft overlays every position it has not).\n if (!canvasPath) return\n setCanvasChange((last) => canvasFieldChange(previous, next, canvasPath, last) ?? last)\n }, [doc])\n\n // --- Writers ----------------------------------------------------------------------------\n const setFieldValue = useCallback(\n (field: FieldDef, value: unknown) => {\n const next = writeField(collection, draftRef.current, field.name, locale, value, { lookupManifest })\n setDraft(next)\n if (documentModel) committer.edited(field.name, fieldPositionPath(collection, field.name, locale))\n },\n [collection, locale, lookupManifest, setDraft, documentModel, committer],\n )\n const editField = useCallback(\n (field: FieldDef, update: (current: CmsDocument) => CmsDocument) => {\n setDraft(update(draftRef.current))\n if (documentModel) committer.edited(field.name, fieldPositionPath(collection, field.name, locale))\n },\n [collection, locale, setDraft, documentModel, committer],\n )\n const setCanvasValue = useCallback(\n (field: FieldDef, value: unknown, base: unknown) => {\n const write = canvasWrite({ collection, draft: draftRef.current, field: field.name, locale, value, base, lookupManifest })\n if (!write) return\n setDraft(write.next)\n if (!documentModel) return\n // EC-630: a burst of typing in a focused inspector control is that control's\n // uncommitted position (its policy commits it); a canvas action commits now.\n const focus = committer.focusedField()\n routeCanvasWrite({\n patches: write.patches,\n focused: focus === undefined ? undefined : canvasControls.current.get(focus),\n committer,\n commitNow: (patches) => void documentModel.commit(ref, patches),\n })\n },\n [collection, locale, lookupManifest, setDraft, documentModel, ref, committer],\n )\n const setAlias = useCallback(\n (alias: string | undefined) => {\n const next = { ...draftRef.current }\n if (alias === undefined) delete next.alias\n else next.alias = alias\n setDraft(next)\n if (!documentModel) return\n committer.commitAll()\n documentModel.replace(next)\n },\n [setDraft, documentModel, committer],\n )\n\n // --- Forced flush ----------------------------------------------------------------------\n const save = useCallback(() => {\n if (documentModel) committer.commitAll()\n else if (writes.kind === 'record') writes.save(draftRef.current, baseRef.current)\n }, [documentModel, committer, writes])\n const flush = useCallback(async () => {\n save()\n if (documentModel) await documentModel.flush(ref)\n }, [save, documentModel, ref])\n\n // Tab hide: commit and flush. Unmount: the same, and leave the document.\n const visibility = input.visibility ?? documentVisibility\n useEffect(() => {\n if (!documentModel) return\n return visibility.onHidden(() => {\n committer.commitAll()\n void documentModel.flush(ref)\n })\n }, [ref])\n useEffect(\n () => () => {\n const target = writesRef.current\n if (!target) return\n committer.commitAll()\n committer.dispose()\n void target.flush(ref)\n target.clearPresence(ref)\n },\n [ref],\n )\n\n // --- Subscription + presence ------------------------------------------------------------\n const [presenceEntries, setPresenceEntries] = useState<readonly PresenceEntry[]>([])\n const [clock, setClock] = useState(now)\n useEffect(() => {\n if (!documentModel) return\n return documentModel.subscribe(ref, setPresenceEntries)\n }, [ref])\n const focusedPathRef = useRef<ValuePath | undefined>(undefined)\n const heartbeat = useCallback(() => {\n writesRef.current?.reportPresence(ref, focusedPathRef.current)\n setClock(now())\n }, [ref])\n useEffect(() => {\n if (!documentModel) return\n heartbeat()\n let handle: unknown\n const tick = () => {\n heartbeat()\n handle = scheduler.setTimeout(tick, PRESENCE_HEARTBEAT_MS)\n }\n handle = scheduler.setTimeout(tick, PRESENCE_HEARTBEAT_MS)\n return () => scheduler.clearTimeout(handle)\n }, [ref])\n const presence = useMemo(\n () =>\n documentModel\n ? livePresence(\n presenceEntries,\n presenceReaderNow(presenceEntries, documentModel.presenceSessionId, clock),\n PRESENCE_TTL_MS,\n ).filter((entry) => entry.sessionId !== documentModel.presenceSessionId)\n : [],\n [presenceEntries, clock, documentModel],\n )\n\n const focused = useCallback(\n (field: FieldDef) => {\n if (!documentModel) return\n committer.focused(field.name)\n focusedPathRef.current = fieldPositionPath(collection, field.name, locale)\n heartbeat()\n },\n [documentModel, committer, collection, locale, heartbeat],\n )\n const blurred = useCallback(\n (field: FieldDef) => {\n if (!documentModel) return\n committer.blurred(field.name)\n focusedPathRef.current = undefined\n heartbeat()\n },\n [documentModel, committer, heartbeat],\n )\n const entered = useCallback(\n (field: FieldDef) => {\n if (documentModel) committer.entered(field.name)\n },\n [documentModel, committer],\n )\n\n // EC-630 — the canvas inspector's commit boundary: a control at a position of the canvas\n // field gained focus, lost it, or saw Enter. Its policy is THE declaration's for its kind.\n const canvasControlOf = useCallback(\n (field: FieldDef, position: CanvasControlPosition, policy: CommitPolicy) =>\n canvasControl(fieldPositionPath(collection, field.name, locale), position, policy),\n [collection, locale],\n )\n const canvasFocused = useCallback(\n (field: FieldDef, position: CanvasControlPosition, policy: CommitPolicy) => {\n if (!documentModel) return\n const control = canvasControlOf(field, position, policy)\n canvasControls.current.set(control.key, control)\n committer.focused(control.key)\n focusedPathRef.current = control.path\n heartbeat()\n },\n [documentModel, committer, canvasControlOf, heartbeat],\n )\n const canvasBlurred = useCallback(\n (field: FieldDef, position: CanvasControlPosition) => {\n if (!documentModel) return\n const key = canvasControlOf(field, position, 'change').key\n committer.blurred(key)\n canvasControls.current.delete(key)\n focusedPathRef.current = undefined\n heartbeat()\n },\n [documentModel, committer, canvasControlOf, heartbeat],\n )\n const canvasEntered = useCallback(\n (field: FieldDef, position: CanvasControlPosition) => {\n if (documentModel) committer.entered(canvasControlOf(field, position, 'change').key)\n },\n [documentModel, committer, canvasControlOf],\n )\n\n const replaceDraft = useCallback(\n (next: CmsDocument) => {\n // A restored version replaces the document: when it moved the canvas field, the\n // canvas remounts from it (its history belonged to the replaced value).\n const moved = canvasPath && !semanticValueEqual(readValueAt(draftRef.current, canvasPath), readValueAt(next, canvasPath))\n setDraft(next)\n if (moved) setCanvasNonce((n) => n + 1)\n },\n // The path is keyed by its string form.\n [setDraft, canvasPathKey],\n )\n\n const dirty = useMemo(() => !semanticValueEqual(draft, doc), [draft, doc])\n\n return {\n draft,\n dirty,\n explicitSave: writes.kind === 'record',\n setFieldValue,\n editField,\n setCanvasValue,\n setAlias,\n focused,\n blurred,\n entered,\n canvasFocused,\n canvasBlurred,\n canvasEntered,\n save,\n flush,\n replaceDraft,\n canvasNonce,\n canvasChange,\n presence,\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"use-document-draft.js","sourceRoot":"","sources":["../../../src/components/cms/use-document-draft.ts"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport {\n PRESENCE_TTL_MS,\n livePresence,\n} from '@elytracms/core/persistence'\nimport type { PresenceEntry } from '@elytracms/core/persistence'\nimport {\n applyDocumentPatches,\n formatValuePath,\n readValueAt,\n semanticValueEqual,\n writeField,\n} from '@elytracms/core/cms-core'\nimport type {\n CmsDocument,\n CollectionDef,\n DocumentPatch,\n DocumentRef,\n FieldDef,\n Locale,\n PropFieldLookup,\n ValuePath,\n} from '@elytracms/core/cms-core'\nimport type { CollectionViewSource, DocumentWrites } from '../../lib/collection-view/collection-view-source'\nimport { fieldPositionPath } from '../../lib/cms/field-patch'\nimport { compositionPatches } from '../builder/composition-patches'\nimport type { CanvasFieldChange, CanvasLift } from '../builder/canvas-rebase'\nimport { commitPolicyOf } from './commit-policy'\nimport type { CommitPolicy } from './commit-policy'\nimport { canvasControl, routeCanvasWrite } from './canvas-commit'\nimport type { CanvasControl, CanvasControlPosition } from './canvas-commit'\nimport { browserScheduler, createDraftCommitter } from './draft-commit'\nimport type { DraftCommitter, Scheduler, UncommittedPosition } from './draft-commit'\n\n/**\n * EC-595 A2 — the detail editor's draft over the revisioned document (and, for the\n * explicit-save rails, over a record).\n *\n * **Document write model** (the CMS): the draft changes per keystroke (live validation\n * reads it); a field's edit is COMMITTED per its control kind's policy\n * ({@link commitPolicyOf} / {@link createDraftCommitter}) as the patch of the position it\n * wrote — never a document. The canvas commits each action's node patches at once\n * (`compositionPatches`); a burst of typing in one of its inspector controls is that\n * control's uncommitted position, committed on its kind's policy like a form field\n * (EC-630, `canvas-commit.ts`). The draft REBASES whenever the session's document changes\n * (a write result, a colleague's change by subscription, a resolved conflict): it is the\n * session's document with this editor's uncommitted positions laid over it — local wins\n * until committed, then the per-path check decides (EC-595 point 7).\n *\n * **Forced flushes** (the ruling: before unmount, tab hide, publish, preview, an AI\n * proposal, duplicate, transfer): {@link DocumentDraft.flush} commits every uncommitted\n * position and awaits the persister. Unmount and tab hide run it here; the editor's\n * publish, duplicate and the AI tap call it.\n *\n * **Thin presence** (Stage A): a heartbeat naming the focused field's path on focus\n * change and every {@link PRESENCE_HEARTBEAT_MS} (under the 30 s TTL), cleared on\n * unmount; the other sessions' live heartbeats arrive with the subscription.\n *\n * **Record write model** (users, redirects, assets, the account): the draft is edited\n * in place and written whole on the editor's Save button (EC-240) — unchanged.\n */\n\n/** The presence heartbeat interval — under {@link PRESENCE_TTL_MS} so a present editor never lapses. */\nexport const PRESENCE_HEARTBEAT_MS = 10_000\n\n/** An injectable visibility source (the tab-hide flush; tests drive it). */\nexport interface VisibilitySource {\n /** Call `onHidden` whenever the tab becomes hidden; returns the unsubscribe. */\n onHidden(onHidden: () => void): () => void\n}\n\nexport const documentVisibility: VisibilitySource = {\n onHidden(onHidden) {\n if (typeof document === 'undefined') return () => {}\n const listener = () => {\n if (document.visibilityState === 'hidden') onHidden()\n }\n document.addEventListener('visibilitychange', listener)\n return () => document.removeEventListener('visibilitychange', listener)\n },\n}\n\n/**\n * EC-647 — the editor's draft and the revision of the session view it was last rebased\n * onto, as ONE value: every edit made on the draft was authored against that revision (an\n * uncommitted position keeps the revision its first keystroke saw). `undefined`: the view\n * had no revision (a fixture session, a record rail, a document still being created).\n *\n * EC-667 — that session view is a COMPOSITE while it shows a pending write (Codex F18):\n * `revision` is its whole-document token (the EARLIEST authoring revision of any position\n * — what a whole-document writer, the slot alias or the translation, writes at), and\n * `shown` is the view itself, the token a position's edit reads ITS provenance from at\n * the first keystroke ({@link positionBase}).\n */\nexport interface DraftView {\n readonly document: CmsDocument\n readonly revision: number | undefined\n /** EC-667 — the session view the draft was last rebased onto (absent: none — a fixture, a record rail). */\n readonly shown?: CmsDocument\n}\n\n/** What a draft reads its tokens from: the write model's view registry (EC-647 / EC-667). */\nexport type ViewTokens = Pick<DocumentWrites, 'revisionOfView'>\n\n/** EC-667 — the draft of a session view as it is shown (pure): its value, its whole-document token, the view itself. */\nexport function sessionDraftView(doc: CmsDocument, tokens: ViewTokens | undefined): DraftView {\n return { document: doc, revision: tokens?.revisionOfView(doc), shown: doc }\n}\n\n/**\n * EC-667 — the base an edit of `path` that STARTS now on `draft` is authored against\n * (pure; the committer captures it at the first keystroke): the revision the content at\n * that position of the shown session view was authored against — under a pending\n * whole-document snapshot the snapshot's revision, under a pending own write that write's\n * base — never the revision of a row adopted underneath it. Captured at the start, it\n * survives the pending write's refusal or failure (looking it up when the commit arrives\n * would be too late). Without a view token: the draft's whole-document revision.\n */\nexport function positionBase(draft: DraftView, path: ValuePath, tokens: ViewTokens | undefined): number | undefined {\n return tokens && draft.shown ? tokens.revisionOfView(draft.shown, path) : draft.revision\n}\n\n/**\n * THE draft rebase as one value (pure — what the editor runs when the session's document\n * changes): the session view with the uncommitted positions laid over it ({@link\n * rebaseDraft}), its whole-document token, the view itself (EC-647 → EC-667: the rebase\n * moves the value AND its tokens), and — when the screen has a canvas — the token of the\n * canvas field's position, which the canvas's next edit is authored at.\n */\nexport function rebaseDraftView(\n doc: CmsDocument,\n previous: DraftView,\n uncommitted: readonly UncommittedPosition[],\n tokens: ViewTokens,\n canvasPath?: ValuePath,\n): { view: DraftView; canvasRevision: number | undefined } {\n return {\n view: { document: rebaseDraft(doc, previous.document, uncommitted), revision: tokens.revisionOfView(doc), shown: doc },\n canvasRevision: canvasPath ? tokens.revisionOfView(doc, canvasPath) : undefined,\n }\n}\n\n/**\n * EC-647 — the slot alias write (pure): the draft with `alias` set (or removed), written\n * whole AT THE DRAFT'S REVISION — never the session's tracked one, which may already hold\n * a colleague's change the draft has not absorbed (Codex F10). There is no identity-\n * metadata operation for the alias (the value-position grammar has no position for it,\n * measured on the base), so it stays a whole-document `replace` with its precondition.\n */\nexport function aliasWrite(\n draft: DraftView,\n alias: string | undefined,\n writes: { replace(doc: CmsDocument, revision: number | undefined): void },\n): CmsDocument {\n const next = { ...draft.document }\n if (alias === undefined) delete next.alias\n else next.alias = alias\n writes.replace(next, draft.revision)\n return next\n}\n\n/** The patch that writes the value `doc` holds at `path` (unset when absent). */\nexport function positionPatch(doc: CmsDocument, path: ValuePath): DocumentPatch {\n const value = readValueAt(doc, path)\n return value === undefined ? { op: 'unset', path } : { op: 'set', path, value }\n}\n\n/**\n * THE rebase (pure): the session's document with the editor's uncommitted positions laid\n * over it, values from the editor's current draft.\n */\nexport function rebaseDraft(\n sessionDoc: CmsDocument,\n draft: CmsDocument,\n uncommitted: readonly UncommittedPosition[],\n): CmsDocument {\n if (uncommitted.length === 0) return sessionDoc\n const patches = uncommitted.map((position) => positionPatch(draft, position.path))\n return applyDocumentPatches({ document: sessionDoc, patches, since: [] }).document\n}\n\n/**\n * EC-630 — the canvas's write into the draft (pure). The canvas writer's node patches,\n * keyed by node id, are the canvas's OWN delta: from `base`, the value it last agreed on\n * with the draft, to `value` — never a document diff, and never measured against the\n * draft's CURRENT value (a colleague's change that reached the draft a moment before the\n * edit lifted would read as part of the edit, and its patches would revert it). The draft\n * takes the delta over whatever it holds now (normally `base` itself) and canonicalises the\n * field through the write lens. `undefined` when the canvas changed nothing.\n */\nexport function canvasWrite(input: {\n collection: CollectionDef\n draft: CmsDocument\n field: string\n locale: Locale\n value: unknown\n base: unknown\n lookupManifest: PropFieldLookup\n}): { next: CmsDocument; patches: DocumentPatch[] } | undefined {\n const { collection, draft, field, locale, lookupManifest } = input\n const path = fieldPositionPath(collection, field, locale)\n const localized = path.length > 1 ? locale : undefined\n const patches = compositionPatches(input.base, input.value, field, localized)\n if (patches.length === 0) return undefined\n const applied = applyDocumentPatches({ document: draft, patches, since: [] }).document\n return { next: writeField(collection, draft, field, locale, readValueAt(applied, path), { lookupManifest }), patches }\n}\n\n/**\n * EC-630 — the change of the canvas field made UNDERNEATH the canvas when the draft\n * rebased from `previous` to `next` (pure), numbered after `last`; `undefined` when the\n * field did not move. The canvas's own edits are in both values (the session's view holds\n * every patch it committed, the draft overlays every position it has not), so the change\n * is someone else's: a colleague's patch, a write's canonical row, a resolved conflict.\n *\n * EC-647: it carries `revision`, the revision the draft is at after the rebase, and is\n * reported when only THAT moved too (`from` ≡ `to`): the canvas's agreed value is valid\n * at the newer revision, so its next edit is authored there — the revision follows the\n * value, never runs ahead of it.\n *\n * EC-667: `revision` is the token of the canvas FIELD's position in the shown view\n * ({@link rebaseDraftView}) — under a pending snapshot the snapshot's revision. The canvas\n * mounts at the draft's whole-document token (the earliest, possibly earlier than its\n * field's), so the first rebase with a token reports it even when nothing moved.\n */\nexport function canvasFieldChange(\n previous: CmsDocument,\n next: CmsDocument,\n path: ValuePath,\n last: CanvasFieldChange | undefined,\n revision: number | undefined,\n): CanvasFieldChange | undefined {\n const from = readValueAt(previous, path)\n const to = readValueAt(next, path)\n const tokenHeld = last === undefined ? revision === undefined : last.revision === revision\n if (semanticValueEqual(from, to) && tokenHeld) return undefined\n return { from, to, seq: (last?.seq ?? 0) + 1, revision }\n}\n\n/**\n * EC-647 — the canvas's write path (pure; what {@link DocumentDraft.setCanvasValue} runs):\n * the canvas writer's delta into the draft ({@link canvasWrite}, handed to `apply`), then\n * routed ({@link routeCanvasWrite}) — committed at once, or held as a focused inspector\n * control's uncommitted position — and in both cases paired with the revision of the\n * canvas value the edit was authored on (`lift.revision`), never the draft's: the draft\n * may already hold a colleague's change the canvas has not been rebased onto (Codex F12),\n * and the store must check the edit from the canvas's revision to refuse it where it\n * overlaps that change. `commit` absent → a draft without a revisioned write model (the\n * draft moves, nothing is committed). `undefined` when the canvas changed nothing.\n */\nexport function canvasCommit(input: {\n collection: CollectionDef\n draft: CmsDocument\n field: string\n locale: Locale\n lift: CanvasLift\n lookupManifest: PropFieldLookup\n /** The focused canvas inspector control, if focus is on one. */\n focused: CanvasControl | undefined\n committer: DraftCommitter\n /** The draft takes the canvas's write (before it is routed — a forced commit reads it). */\n apply: (next: CmsDocument) => void\n commit?: (patches: readonly DocumentPatch[], bases: readonly (number | undefined)[]) => void\n}): 'deferred' | 'committed' | 'draft' | undefined {\n const { lift, commit } = input\n const write = canvasWrite({\n collection: input.collection,\n draft: input.draft,\n field: input.field,\n locale: input.locale,\n value: lift.value,\n base: lift.base,\n lookupManifest: input.lookupManifest,\n })\n if (!write) return undefined\n input.apply(write.next)\n if (!commit) return 'draft'\n // EC-630: a burst of typing in a focused inspector control is that control's\n // uncommitted position (its policy commits it); a canvas action commits now.\n return routeCanvasWrite({\n patches: write.patches,\n focused: input.focused,\n committer: input.committer,\n revision: lift.revision,\n commitNow: (patches) => commit(patches, patches.map(() => lift.revision)),\n })\n}\n\n/**\n * The reader's \"now\" for presence liveness: THIS session's own latest heartbeat `at`\n * (written by the same store clock as everyone else's — the playground store runs a\n * deterministic sequential clock, Convex the writers' wall clocks), falling back to the\n * injected clock before the first heartbeat lands. Comparing others' `at` against a\n * different time base would call every colleague stale (measured live in the\n * playground: the store's clock is months behind the browser's).\n */\nexport function presenceReaderNow(\n entries: readonly PresenceEntry[],\n sessionId: string,\n fallback: string,\n): string {\n const own = entries.filter((entry) => entry.sessionId === sessionId).map((entry) => entry.at)\n return own.length > 0 ? own.sort().at(-1)! : fallback\n}\n\nexport interface DocumentDraft {\n draft: CmsDocument\n /**\n * EC-647 — the revision of the session view the draft was last rebased onto (see\n * {@link DraftView}): a writer that authors from {@link draft} (the translation) writes\n * at this revision. EC-667: the view's WHOLE-DOCUMENT token — the earliest authoring\n * revision of any position it shows (a field's own edit reads its position's token).\n */\n revision: number | undefined\n /** Uncommitted (document model) / unsaved (record model) edits exist. */\n dirty: boolean\n /** The record model: the editor renders a Save button + navigation guard. */\n explicitSave: boolean\n /** A field's control wrote `value` (the active locale for a localized field). */\n setFieldValue(field: FieldDef, value: unknown): void\n /** A writer that transforms ONE field position (fill / reset from base). */\n editField(field: FieldDef, update: (draft: CmsDocument) => CmsDocument): void\n /**\n * The canvas emitted a new value for its field (node patches, committed at once). The\n * lift's `base` is the value the canvas last agreed on with the draft: the canvas\n * writer's delta is `base` → `value`, applied over whatever the draft holds now (EC-630 —\n * a change that landed underneath meanwhile is never reverted by the canvas's own\n * write); its `revision` is the revision that value was agreed at — the revision the\n * edit is committed at (EC-647, {@link canvasCommit}).\n */\n setCanvasValue(field: FieldDef, lift: CanvasLift): void\n /** The slot alias (a document-level key: a whole-document write at the held revision). */\n setAlias(alias: string | undefined): void\n /** Field boundary events (focus / blur / Enter) — the commit policy's triggers. */\n focused(field: FieldDef): void\n blurred(field: FieldDef): void\n entered(field: FieldDef): void\n /**\n * EC-630 — the canvas inspector's boundary events: a control writing `position` of the\n * canvas field `field`, committing on `policy` (THE declaration for its kind). A burst of\n * typing in it is its uncommitted position until blur / Enter / the idle pause.\n */\n canvasFocused(field: FieldDef, position: CanvasControlPosition, policy: CommitPolicy): void\n canvasBlurred(field: FieldDef, position: CanvasControlPosition): void\n canvasEntered(field: FieldDef, position: CanvasControlPosition): void\n /** The forced commit (sync; the persister serialises what follows) / the record model's Save. */\n save(): void\n /** The FORCED FLUSH: commit everything uncommitted and await persistence. */\n flush(): Promise<void>\n /** Adopt a draft from outside (a restored version). */\n replaceDraft(next: CmsDocument): void\n /**\n * Bumped when the canvas must remount from the draft: a restored version replaced the\n * document, or a locale switch pointed the canvas at another bucket. (A change made\n * underneath the canvas is NOT a remount — {@link canvasChange}.)\n */\n canvasNonce: number\n /**\n * EC-630 — the latest change of the canvas field's value made UNDERNEATH the canvas (a\n * colleague's patch by subscription, a write's canonical row, a resolved conflict): the\n * draft's value before and after it. The canvas REBASES onto it — the change lands, its\n * undo history and selection survive — where it used to remount and lose both.\n */\n canvasChange: CanvasFieldChange | undefined\n /** The OTHER sessions present on this document, live by the reader's clock. */\n presence: readonly PresenceEntry[]\n}\n\nexport interface UseDocumentDraftInput {\n source: CollectionViewSource\n collection: CollectionDef\n doc: CmsDocument\n locale: Locale\n lookupManifest: PropFieldLookup\n /** The composition field the canvas edits on this screen, if any. */\n canvasField?: FieldDef\n scheduler?: Scheduler\n visibility?: VisibilitySource\n /** The reader's clock for presence liveness (ISO); `new Date().toISOString()` by default. */\n now?: () => string\n}\n\n/**\n * EC-616 — the base a record save is diffed against: the source's value whenever the draft\n * equals it (the record as opened, or as a save came back), else the previous base (the\n * editor has unsaved changes; a change from another writer meanwhile is not theirs). Pure.\n */\nexport function recordSaveBase(previous: CmsDocument, draft: CmsDocument, doc: CmsDocument): CmsDocument {\n return semanticValueEqual(draft, doc) ? doc : previous\n}\n\nexport function useDocumentDraft(input: UseDocumentDraftInput): DocumentDraft {\n const { source, collection, doc, locale, lookupManifest, canvasField } = input\n const writes = source.writes\n const documentModel = writes.kind === 'document' ? writes : undefined\n const ref: DocumentRef = useMemo(() => ({ collection: doc.collection, id: doc.id }), [doc.collection, doc.id])\n // EC-647: the draft and the revision of the view it rebased onto travel as ONE value\n // (EC-667: with the view itself — the per-position token).\n const [view, setViewState] = useState<DraftView>(() => sessionDraftView(doc, documentModel))\n const draft = view.document\n const draftRef = useRef(view)\n // EC-616: the record as the editor opened it (or as of their last successful save) — the\n // BASE a record rail diffs a save against, so it writes only the editor's changes. It moves\n // only while the draft equals the source's value (at open, and once a save is reflected\n // back): another writer's change arriving mid-edit never becomes the editor's base.\n const baseRef = useRef<CmsDocument>(doc)\n baseRef.current = recordSaveBase(baseRef.current, draftRef.current.document, doc)\n const setView = useCallback((next: DraftView) => {\n draftRef.current = next\n setViewState(next)\n }, [])\n /** An edit of the draft: its value moves, the tokens it is authored against do not. */\n const setDraft = useCallback(\n (next: CmsDocument) => setView({ ...draftRef.current, document: next }),\n [setView],\n )\n const scheduler = input.scheduler ?? browserScheduler\n const now = input.now ?? (() => new Date().toISOString())\n const fieldsByName = useMemo(() => new Map(collection.fields.map((f) => [f.name, f])), [collection])\n\n // --- Committing --------------------------------------------------------------------\n const writesRef = useRef(documentModel)\n writesRef.current = documentModel\n /** EC-630 — the canvas inspector controls that took focus, by committer key (their position + policy). */\n const canvasControls = useRef(new Map<string, CanvasControl>())\n const committer = useMemo<DraftCommitter>(\n () =>\n createDraftCommitter({\n policyOf: (name) => {\n const field = fieldsByName.get(name)\n if (field) return commitPolicyOf(field)\n return canvasControls.current.get(name)?.policy ?? 'blur-or-idle'\n },\n commit: (positions) => {\n const target = writesRef.current\n if (!target) return\n void target.commit(\n ref,\n positions.map((position) => positionPatch(draftRef.current.document, position.path)),\n positions.map((position) => position.base),\n )\n },\n // EC-647: a position's base is the revision of the DRAFT its first keystroke was\n // made on — never the session's tracked revision, which may already hold a\n // colleague's change the draft has not rebased onto yet. EC-667: the form's\n // writers name the POSITION's token ({@link positionBase}, passed to `edited`);\n // this whole-document token (the earliest) is the fallback.\n baseOf: () => draftRef.current.revision,\n scheduler,\n }),\n // One committer per open document (the editor is keyed by it).\n [ref],\n )\n\n // --- The canvas's value: its remount and its rebase ------------------------------------\n const canvasPath = canvasField ? fieldPositionPath(collection, canvasField.name, locale) : undefined\n const canvasPathKey = canvasPath ? formatValuePath(canvasPath) : ''\n const [canvasNonce, setCanvasNonce] = useState(0)\n const [canvasChange, setCanvasChange] = useState<CanvasFieldChange | undefined>(undefined)\n // A locale switch points the canvas at another bucket: remount from it.\n const lastCanvasPathKey = useRef(canvasPathKey)\n useEffect(() => {\n if (lastCanvasPathKey.current === canvasPathKey) return\n lastCanvasPathKey.current = canvasPathKey\n setCanvasNonce((n) => n + 1)\n }, [canvasPathKey])\n\n // --- The rebase (document model) -------------------------------------------------------\n const firstDoc = useRef(true)\n useEffect(() => {\n if (firstDoc.current) {\n firstDoc.current = false\n return\n }\n if (!documentModel) return\n const previous = draftRef.current.document\n // EC-647: the rebase moves the value AND the revision — the session view's own\n // (EC-667: its tokens, whole-document and per position).\n const { view: next, canvasRevision } = rebaseDraftView(doc, draftRef.current, committer.uncommitted(), documentModel, canvasPath)\n setView(next)\n // EC-630: the canvas field moved underneath the canvas — the canvas rebases onto the\n // change (its own edits are in both values: the session's view holds every patch it\n // committed, and the draft overlays every position it has not). EC-647: the change\n // carries the revision — EC-667: the canvas FIELD's token — so the canvas's next edit\n // is authored where its value is.\n if (!canvasPath) return\n setCanvasChange((last) => canvasFieldChange(previous, next.document, canvasPath, last, canvasRevision) ?? last)\n }, [doc])\n\n // --- Writers ----------------------------------------------------------------------------\n const setFieldValue = useCallback(\n (field: FieldDef, value: unknown) => {\n // EC-667: the position's token is read BEFORE the draft moves — from the view this\n // keystroke was made on (a re-edit keeps its first keystroke's either way).\n const path = fieldPositionPath(collection, field.name, locale)\n const authored = { base: positionBase(draftRef.current, path, documentModel) }\n const next = writeField(collection, draftRef.current.document, field.name, locale, value, { lookupManifest })\n setDraft(next)\n if (documentModel) committer.edited(field.name, path, authored)\n },\n [collection, locale, lookupManifest, setDraft, documentModel, committer],\n )\n const editField = useCallback(\n (field: FieldDef, update: (current: CmsDocument) => CmsDocument) => {\n const path = fieldPositionPath(collection, field.name, locale)\n const authored = { base: positionBase(draftRef.current, path, documentModel) }\n setDraft(update(draftRef.current.document))\n if (documentModel) committer.edited(field.name, path, authored)\n },\n [collection, locale, setDraft, documentModel, committer],\n )\n const setCanvasValue = useCallback(\n (field: FieldDef, lift: CanvasLift) => {\n const focus = committer.focusedField()\n canvasCommit({\n collection,\n draft: draftRef.current.document,\n field: field.name,\n locale,\n lift,\n lookupManifest,\n focused: focus === undefined ? undefined : canvasControls.current.get(focus),\n committer,\n apply: setDraft,\n ...(documentModel\n ? { commit: (patches: readonly DocumentPatch[], bases: readonly (number | undefined)[]) => void documentModel.commit(ref, patches, bases) }\n : {}),\n })\n },\n [collection, locale, lookupManifest, setDraft, documentModel, ref, committer],\n )\n const setAlias = useCallback(\n (alias: string | undefined) => {\n if (!documentModel) {\n aliasWrite(draftRef.current, alias, { replace: (next) => setDraft(next) })\n return\n }\n // The uncommitted positions first (the snapshot holds them), then the snapshot AT\n // THE DRAFT'S REVISION (EC-647 — Codex F10).\n committer.commitAll()\n setDraft(aliasWrite(draftRef.current, alias, documentModel))\n },\n [setDraft, documentModel, committer],\n )\n\n // --- Forced flush ----------------------------------------------------------------------\n const save = useCallback(() => {\n if (documentModel) committer.commitAll()\n else if (writes.kind === 'record') writes.save(draftRef.current.document, baseRef.current)\n }, [documentModel, committer, writes])\n const flush = useCallback(async () => {\n save()\n if (documentModel) await documentModel.flush(ref)\n }, [save, documentModel, ref])\n\n // Tab hide: commit and flush. Unmount: the same, and leave the document.\n const visibility = input.visibility ?? documentVisibility\n useEffect(() => {\n if (!documentModel) return\n return visibility.onHidden(() => {\n committer.commitAll()\n void documentModel.flush(ref)\n })\n }, [ref])\n useEffect(\n () => () => {\n const target = writesRef.current\n if (!target) return\n committer.commitAll()\n committer.dispose()\n void target.flush(ref)\n target.clearPresence(ref)\n },\n [ref],\n )\n\n // --- Subscription + presence ------------------------------------------------------------\n const [presenceEntries, setPresenceEntries] = useState<readonly PresenceEntry[]>([])\n const [clock, setClock] = useState(now)\n useEffect(() => {\n if (!documentModel) return\n return documentModel.subscribe(ref, setPresenceEntries)\n }, [ref])\n const focusedPathRef = useRef<ValuePath | undefined>(undefined)\n const heartbeat = useCallback(() => {\n writesRef.current?.reportPresence(ref, focusedPathRef.current)\n setClock(now())\n }, [ref])\n useEffect(() => {\n if (!documentModel) return\n heartbeat()\n let handle: unknown\n const tick = () => {\n heartbeat()\n handle = scheduler.setTimeout(tick, PRESENCE_HEARTBEAT_MS)\n }\n handle = scheduler.setTimeout(tick, PRESENCE_HEARTBEAT_MS)\n return () => scheduler.clearTimeout(handle)\n }, [ref])\n const presence = useMemo(\n () =>\n documentModel\n ? livePresence(\n presenceEntries,\n presenceReaderNow(presenceEntries, documentModel.presenceSessionId, clock),\n PRESENCE_TTL_MS,\n ).filter((entry) => entry.sessionId !== documentModel.presenceSessionId)\n : [],\n [presenceEntries, clock, documentModel],\n )\n\n const focused = useCallback(\n (field: FieldDef) => {\n if (!documentModel) return\n committer.focused(field.name)\n focusedPathRef.current = fieldPositionPath(collection, field.name, locale)\n heartbeat()\n },\n [documentModel, committer, collection, locale, heartbeat],\n )\n const blurred = useCallback(\n (field: FieldDef) => {\n if (!documentModel) return\n committer.blurred(field.name)\n focusedPathRef.current = undefined\n heartbeat()\n },\n [documentModel, committer, heartbeat],\n )\n const entered = useCallback(\n (field: FieldDef) => {\n if (documentModel) committer.entered(field.name)\n },\n [documentModel, committer],\n )\n\n // EC-630 — the canvas inspector's commit boundary: a control at a position of the canvas\n // field gained focus, lost it, or saw Enter. Its policy is THE declaration's for its kind.\n const canvasControlOf = useCallback(\n (field: FieldDef, position: CanvasControlPosition, policy: CommitPolicy) =>\n canvasControl(fieldPositionPath(collection, field.name, locale), position, policy),\n [collection, locale],\n )\n const canvasFocused = useCallback(\n (field: FieldDef, position: CanvasControlPosition, policy: CommitPolicy) => {\n if (!documentModel) return\n const control = canvasControlOf(field, position, policy)\n canvasControls.current.set(control.key, control)\n committer.focused(control.key)\n focusedPathRef.current = control.path\n heartbeat()\n },\n [documentModel, committer, canvasControlOf, heartbeat],\n )\n const canvasBlurred = useCallback(\n (field: FieldDef, position: CanvasControlPosition) => {\n if (!documentModel) return\n const key = canvasControlOf(field, position, 'change').key\n committer.blurred(key)\n canvasControls.current.delete(key)\n focusedPathRef.current = undefined\n heartbeat()\n },\n [documentModel, committer, canvasControlOf, heartbeat],\n )\n const canvasEntered = useCallback(\n (field: FieldDef, position: CanvasControlPosition) => {\n if (documentModel) committer.entered(canvasControlOf(field, position, 'change').key)\n },\n [documentModel, committer, canvasControlOf],\n )\n\n const replaceDraft = useCallback(\n (next: CmsDocument) => {\n // A restored version replaces the document: when it moved the canvas field, the\n // canvas remounts from it (its history belonged to the replaced value).\n const moved = canvasPath && !semanticValueEqual(readValueAt(draftRef.current.document, canvasPath), readValueAt(next, canvasPath))\n // EC-647: the restored value comes with the revision the session showed it at.\n setView(sessionDraftView(next, documentModel))\n if (moved) setCanvasNonce((n) => n + 1)\n },\n // The path is keyed by its string form.\n [setView, documentModel, canvasPathKey],\n )\n\n const dirty = useMemo(() => !semanticValueEqual(draft, doc), [draft, doc])\n\n return {\n draft,\n revision: view.revision,\n dirty,\n explicitSave: writes.kind === 'record',\n setFieldValue,\n editField,\n setCanvasValue,\n setAlias,\n focused,\n blurred,\n entered,\n canvasFocused,\n canvasBlurred,\n canvasEntered,\n save,\n flush,\n replaceDraft,\n canvasNonce,\n canvasChange,\n presence,\n }\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useCallback, useMemo } from 'react';
|
|
2
2
|
import { isHierarchical } from '@elytracms/core/cms-core';
|
|
3
|
-
import { assetAlt, assetReadLocale } from '@elytracms/core/persistence';
|
|
3
|
+
import { assetAlt, assetMetaLens, assetReadLocale } from '@elytracms/core/persistence';
|
|
4
4
|
import { studioPath } from '@elytracms/studio/router';
|
|
5
5
|
import { resolveDeploymentFrontendUrl, resolveDeploymentRoutes } from '../../lib/workspace/index.js';
|
|
6
6
|
import { useCms } from '../cms/cms-store.js';
|
|
@@ -72,8 +72,9 @@ export function useCmsCollectionViewSource(collectionId) {
|
|
|
72
72
|
const record = assetStore?.assets.find((asset) => asset.id === id);
|
|
73
73
|
if (!record)
|
|
74
74
|
return undefined;
|
|
75
|
-
// EC-587: the one reader — EC-632: in the CMS surface's active locale
|
|
76
|
-
|
|
75
|
+
// EC-587: the one reader — EC-632: in the CMS surface's active locale; EC-659: through
|
|
76
|
+
// the session's asset collection as declared now.
|
|
77
|
+
const alt = assetAlt(record, assetReadLocale(cms.localeConfig, assetMetaLens(cms.collections), cms.activeLocale));
|
|
77
78
|
return {
|
|
78
79
|
...(record.url !== undefined ? { url: record.url } : {}),
|
|
79
80
|
contentType: record.contentType,
|
|
@@ -81,13 +82,14 @@ export function useCmsCollectionViewSource(collectionId) {
|
|
|
81
82
|
filename: record.filename,
|
|
82
83
|
...(alt !== null ? { alt } : {}),
|
|
83
84
|
};
|
|
84
|
-
}, [assetStore, cms.localeConfig, cms.activeLocale]);
|
|
85
|
+
}, [assetStore, cms.localeConfig, cms.collections, cms.activeLocale]);
|
|
85
86
|
const liveUrls = useCallback(() => liveUrlMap, [liveUrlMap]);
|
|
86
87
|
// EC-595 A2: the CMS writes revisioned documents — patches at the source.
|
|
87
88
|
const writes = useMemo(() => ({
|
|
88
89
|
kind: 'document',
|
|
89
90
|
commit: cms.commitPatches,
|
|
90
91
|
revisionOf: cms.revisionOf,
|
|
92
|
+
revisionOfView: cms.revisionOfView,
|
|
91
93
|
insert: cms.insertDocument,
|
|
92
94
|
newId: cms.newDocumentId,
|
|
93
95
|
conflicts: cms.documentConflicts,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cms-collection-view-source.js","sourceRoot":"","sources":["../../../src/components/collection-view/cms-collection-view-source.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,OAAO,CAAA;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAEzD,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAA;AACvE,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAA;AACrD,OAAO,EAAE,4BAA4B,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAA;AAO3F,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AACzC,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAC1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,mCAAmC,CAAA;AAEzE,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAA;AAE5D;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,0BAA0B,CAAC,YAAoB;IAC7D,MAAM,GAAG,GAAG,MAAM,EAAE,CAAA;IACpB,MAAM,UAAU,GAAG,iBAAiB,EAAE,CAAA;IACtC,MAAM,UAAU,GAAG,GAAG,CAAC,aAAa,CAAC,YAAY,CAAC,CAAA;IAClD,MAAM,aAAa,GAAG,qBAAqB,CAAC,GAAG,CAAC,SAAS,EAAE,YAAY,CAAC,CAAA;IAExE,+EAA+E;IAC/E,MAAM,iBAAiB,GAAG,OAAO,CAC/B,GAAG,EAAE,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,EAAE,EAAE,IAAI,OAAO,EACpE,CAAC,GAAG,CAAC,WAAW,CAAC,CAClB,CAAA;IAED,+EAA+E;IAC/E,+EAA+E;IAC/E,MAAM,UAAU,GAAG,OAAO,CACxB,GAAG,EAAE,CAAC,uBAAuB,EAAE,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,EAAE,UAAU,KAAK,YAAY,CAAC,EAC5F,CAAC,YAAY,CAAC,CACf,CAAA;IACD,2EAA2E;IAC3E,uEAAuE;IACvE,8EAA8E;IAC9E,sDAAsD;IACtD,uFAAuF;IACvF,MAAM,UAAU,GAAG,OAAO,CAAC,GAAuD,EAAE;QAClF,IAAI,CAAC,UAAU,IAAI,CAAC,UAAU;YAAE,OAAO,IAAI,CAAA;QAC3C,IAAI,aAAa,CAAC,MAAM,KAAK,OAAO;YAAE,OAAO,aAAa,CAAA;QAC1D,MAAM,QAAQ,GAAG,CAAC,GAAG,aAAa,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAA;QAClD,MAAM,GAAG,GAAG,IAAI,GAAG,EAAuB,CAAA;QAC1C,KAAK,MAAM,SAAS,IAAI,QAAQ,EAAE,CAAC;YACjC,MAAM,GAAG,GAAG,sBAAsB,CAAC;gBACjC,MAAM,EAAE,uBAAuB,EAAE;gBACjC,WAAW,EAAE,4BAA4B,EAAE;gBAC3C,YAAY,EAAE,GAAG,CAAC,YAAY;gBAC9B,UAAU;gBACV,GAAG,EAAE,SAAS;gBACd,QAAQ;gBACR,MAAM,EAAE,GAAG,CAAC,YAAY;aACzB,CAAC,CAAA;YACF,IAAI,GAAG;gBAAE,GAAG,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,EAAE,GAAG,CAAC,CAAA;QACrC,CAAC;QACD,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,CAAA;IACxC,CAAC,EAAE,CAAC,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,GAAG,CAAC,YAAY,EAAE,GAAG,CAAC,YAAY,CAAC,CAAC,CAAA;IAE/E,MAAM,SAAS,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,WAAW,CAAC,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC,CAAA;IACvF,MAAM,SAAS,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC,CAAA;IACxF,MAAM,YAAY,GAAG,WAAW,CAC9B,CAAC,MAA2D,EAAE,EAAE,CAC9D,GAAG,CAAC,aAAa,CAAC,YAAY,EAAE,MAAM,CAAC,EACzC,CAAC,GAAG,EAAE,YAAY,CAAC,CACpB,CAAA;IACD,qFAAqF;IACrF,MAAM,KAAK,GAAG,OAAO,CACnB,GAAG,EAAE,CAAC,CAAC;QACL,IAAI,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,YAAY,CAAC;QACrC,QAAQ,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,YAAY,CAAC,YAAY,CAAC;QAC9C,SAAS,EAAE,CAAC,EAAU,EAAE,EAAE,CAAC,GAAG,CAAC,aAAa,CAAC,YAAY,EAAE,EAAE,CAAC;KAC/D,CAAC,EACF,CAAC,GAAG,EAAE,YAAY,CAAC,CACpB,CAAA;IACD,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,cAAc,CAAC,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC,CAAA;IACvF,MAAM,OAAO,GAAG,OAAO,CACrB,GAAG,EAAE,CAAC,CAAC,EAAE,eAAe,EAAE,CAAC,KAAgB,EAAE,EAAE,CAAC,GAAG,CAAC,eAAe,CAAC,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,EAC3F,CAAC,GAAG,EAAE,YAAY,CAAC,CACpB,CAAA;IACD,MAAM,YAAY,GAAG,WAAW,CAC9B,CAAC,EAAU,EAA4B,EAAE;QACvC,MAAM,MAAM,GAAG,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,CAAC,CAAA;QAClE,IAAI,CAAC,MAAM;YAAE,OAAO,SAAS,CAAA;QAC7B,uEAAuE;QACvE,MAAM,GAAG,GAAG,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC,GAAG,CAAC,YAAY,EAAE,GAAG,CAAC,YAAY,CAAC,CAAC,CAAA;QACjF,OAAO;YACL,GAAG,CAAC,MAAM,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACxD,WAAW,EAAE,MAAM,CAAC,WAAW;YAC/B,GAAG,CAAC,MAAM,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC9D,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,GAAG,CAAC,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACjC,CAAA;IACH,CAAC,EACD,CAAC,UAAU,EAAE,GAAG,CAAC,YAAY,EAAE,GAAG,CAAC,YAAY,CAAC,CACjD,CAAA;IACD,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,UAAU,EAAE,CAAC,UAAU,CAAC,CAAC,CAAA;IAC5D,0EAA0E;IAC1E,MAAM,MAAM,GAAG,OAAO,CACpB,GAAG,EAAE,CAAC,CAAC;QACL,IAAI,EAAE,UAAU;QAChB,MAAM,EAAE,GAAG,CAAC,aAAa;QACzB,UAAU,EAAE,GAAG,CAAC,UAAU;QAC1B,MAAM,EAAE,GAAG,CAAC,cAAc;QAC1B,KAAK,EAAE,GAAG,CAAC,aAAa;QACxB,SAAS,EAAE,GAAG,CAAC,iBAAiB;QAChC,eAAe,EAAE,GAAG,CAAC,eAAe;QACpC,aAAa,EAAE,GAAG,CAAC,qBAAqB;QACxC,KAAK,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,gBAAgB,CAAC,GAAG,CAAC;QACzC,OAAO,EAAE,GAAG,CAAC,eAAe;QAC5B,SAAS,EAAE,GAAG,CAAC,iBAAiB;QAChC,cAAc,EAAE,GAAG,CAAC,cAAc;QAClC,aAAa,EAAE,GAAG,CAAC,aAAa;QAChC,iBAAiB,EAAE,GAAG,CAAC,iBAAiB;KACzC,CAAC,EACF,CAAC,GAAG,CAAC,CACN,CAAA;IAED,OAAO,OAAO,CAAC,GAAG,EAAE;QAClB,IAAI,CAAC,UAAU;YAAE,OAAO,IAAI,CAAA;QAC5B,MAAM,YAAY,GAA+B;YAC/C,OAAO,EAAE,IAAI;YACb,QAAQ,EAAE,IAAI;YACd,OAAO,EAAE,IAAI;YACb,UAAU,EAAE,IAAI;YAChB,MAAM,EAAE,IAAI;YACZ,SAAS,EAAE,IAAI,EAAE,yDAAyD;YAC1E,MAAM,EAAE,IAAI;YACZ,IAAI,EAAE,IAAI;YACV,OAAO,EAAE,UAAU;YACnB,SAAS,EAAE,cAAc,CAAC,UAAU,CAAC;YACrC,YAAY,EAAE,IAAI;SACnB,CAAA;QACD,MAAM,MAAM,GAAyB;YACnC,YAAY;YACZ,MAAM,EAAE,UAAU;YAClB,YAAY;YACZ,GAAG,CAAC,GAAG,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACpE,iBAAiB;YACjB,YAAY,EAAE,GAAG,CAAC,YAAY;YAC9B,YAAY,EAAE,GAAG,CAAC,YAAY;YAC9B,SAAS;YACT,GAAG,EAAE,GAAG,CAAC,WAAW;YACpB,OAAO,EAAE,GAAG,CAAC,OAAO;YACpB,6EAA6E;YAC7E,gFAAgF;YAChF,mBAAmB,EAAE,GAAG,CAAC,WAAW;YACpC,WAAW,EAAE,GAAG,CAAC,WAAW;YAC5B,eAAe,EAAE,GAAG,CAAC,eAAe;YACpC,QAAQ,EAAE,GAAG,CAAC,QAAQ;YACtB,YAAY;YACZ,SAAS,EAAE,GAAG,CAAC,YAAY;YAC3B,cAAc,EAAE,GAAG,CAAC,iBAAiB;YACrC,gEAAgE;YAChE,KAAK,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,gBAAgB,EAAE;YACnC,UAAU,EAAE,CAAC,EAAU,EAAE,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,YAAY,EAAE,EAAE,CAAC;YACpE,QAAQ,EAAE,UAAU,CAAC,aAAa,CAAC,YAAY,CAAC;YAChD,SAAS;YACT,YAAY;YACZ,KAAK;YACL,OAAO;YACP,MAAM;YACN,MAAM;YACN,MAAM,EAAE,GAAG,CAAC,cAAc;YAC1B,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,YAAY,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC;YACjF,UAAU,EAAE;gBACV,WAAW,EAAE,GAAG,CAAC,WAAW;gBAC5B,sEAAsE;gBACtE,2EAA2E;gBAC3E,qBAAqB,EAAE,GAAG,CAAC,6BAA6B;gBACxD,OAAO,EAAE,GAAG,CAAC,eAAe;gBAC5B,SAAS,EAAE,GAAG,CAAC,iBAAiB;gBAChC,QAAQ;aACT;YACD,8EAA8E;YAC9E,2EAA2E;YAC3E,+EAA+E;YAC/E,2EAA2E;YAC3E,UAAU,EAAE;gBACV,SAAS,EAAE,GAAG,CAAC,iBAAiB;gBAChC,WAAW,EAAE,GAAG,CAAC,mBAAmB;aACrC;YACD,UAAU,EAAE;gBACV,OAAO,EAAE,CAAC,GAAgB,EAAE,OAAe,EAAE,KAAK,EAAE,EAAE,CACpD,GAAG,CAAC,sBAAsB,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,CAAC;gBACjD,2EAA2E;gBAC3E,qEAAqE;gBACrE,IAAI,EAAE,KAAK,EAAE,GAAgB,EAAE,EAAE;oBAC/B,MAAM,GAAG,CAAC,gBAAgB,EAAE,CAAA;oBAC5B,OAAO,GAAG,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAA;gBACtC,CAAC;aACF;YACD,aAAa,EAAE;gBACb,WAAW,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,CAAC,eAAe,CAAC,KAAK,CAAC;aACnD;SACF,CAAA;QACD,OAAO,MAAM,CAAA;IACf,CAAC,EAAE;QACD,UAAU;QACV,YAAY;QACZ,GAAG;QACH,UAAU;QACV,SAAS;QACT,SAAS;QACT,YAAY;QACZ,KAAK;QACL,OAAO;QACP,MAAM;QACN,YAAY;QACZ,QAAQ;QACR,iBAAiB;QACjB,MAAM;KACP,CAAC,CAAA;AACJ,CAAC","sourcesContent":["import { useCallback, useMemo } from 'react'\nimport { isHierarchical } from '@elytracms/core/cms-core'\nimport type { DocumentRef, DocumentUrl } from '@elytracms/core/cms-core'\nimport { assetAlt, assetReadLocale } from '@elytracms/core/persistence'\nimport { studioPath } from '@elytracms/studio/router'\nimport { resolveDeploymentFrontendUrl, resolveDeploymentRoutes } from '../../lib/workspace'\nimport type {\n CollectionViewCapabilities,\n CollectionViewSource,\n DocumentWrites,\n AssetDisplay,\n} from '../../lib/collection-view/collection-view-source'\nimport { useCms } from '../cms/cms-store'\nimport { useOptionalAssets } from '../assets/assets-store'\nimport { usePublishedDocuments } from '../../lib/cms/published-documents'\nimport type { ReadState } from '../../lib/query'\nimport { resolveDocumentLiveUrl } from '../cms/use-live-url'\n\n/**\n * Assemble a {@link CollectionViewSource} for a `cmsDocuments` collection from the\n * ambient `CmsStore` + its side contexts (assets, published pool) + the\n * deployment config reads (EC-240 / AD-19). This is the CMS adapter: a\n * behaviour-preserving pass-through so the portable list + edit surfaces render\n * over the source instead of `useCms()` directly, byte-for-byte as before.\n *\n * Returns `null` for an unknown collection id (the caller renders the fallback).\n *\n * Referential stability: collection-parameterised methods are `useCallback`-bound\n * and the source object is `useMemo`-ed, so the surfaces' `useMemo` dependency\n * arrays stay stable across renders (the inline-edit order snapshot depends on it).\n */\nexport function useCmsCollectionViewSource(collectionId: string): CollectionViewSource | null {\n const cms = useCms()\n const assetStore = useOptionalAssets()\n const collection = cms.getCollection(collectionId)\n const publishedDocs = usePublishedDocuments(cms.projectId, collectionId)\n\n // The asset collection id is resolved once from the schema (as the cell does).\n const assetCollectionId = useMemo(\n () => cms.collections.find((c) => c.kind === 'asset')?.id ?? 'asset',\n [cms.collections],\n )\n\n // Live-URL column: the published pool × deployment routes, memoised exactly as\n // the table did — derived from PUBLISHED snapshots, never draft rows (EC-205).\n const isRoutable = useMemo(\n () => resolveDeploymentRoutes().some((route) => route.document?.collection === collectionId),\n [collectionId],\n )\n // EC-384: `resolveDocumentLiveUrl` merges the stored routes with the kit's\n // fallback-home rule (same as the document editor's header affordance,\n // `use-live-url.ts`) — otherwise a fallback-addressed home page showed no URL\n // in this column even though the live site serves it.\n // EC-628: the pool's read state carries through — a failed read is the column's error.\n const liveUrlMap = useMemo((): ReadState<ReadonlyMap<string, DocumentUrl>> | null => {\n if (!collection || !isRoutable) return null\n if (publishedDocs.status !== 'ready') return publishedDocs\n const siblings = [...publishedDocs.value.values()]\n const map = new Map<string, DocumentUrl>()\n for (const published of siblings) {\n const url = resolveDocumentLiveUrl({\n routes: resolveDeploymentRoutes(),\n frontendUrl: resolveDeploymentFrontendUrl(),\n localeConfig: cms.localeConfig,\n collection,\n doc: published,\n siblings,\n locale: cms.activeLocale,\n })\n if (url) map.set(published.id, url)\n }\n return { status: 'ready', value: map }\n }, [collection, isRoutable, publishedDocs, cms.localeConfig, cms.activeLocale])\n\n const documents = useCallback(() => cms.documentsOf(collectionId), [cms, collectionId])\n const viewState = useCallback(() => cms.tableStateOf(collectionId), [cms, collectionId])\n const setViewState = useCallback(\n (update: Parameters<CollectionViewSource['setViewState']>[0]) =>\n cms.setTableState(collectionId, update),\n [cms, collectionId],\n )\n // Named views (EC-246b): config-as-code lenses the header selector switches between.\n const views = useMemo(\n () => ({\n list: () => cms.viewsOf(collectionId),\n activeId: () => cms.activeViewId(collectionId),\n setActive: (id: string) => cms.setActiveView(collectionId, id),\n }),\n [cms, collectionId],\n )\n const create = useCallback(() => cms.createDocument(collectionId), [cms, collectionId])\n const imports = useMemo(\n () => ({ importDocuments: (items: unknown[]) => cms.importDocuments(collectionId, items) }),\n [cms, collectionId],\n )\n const resolveAsset = useCallback(\n (id: string): AssetDisplay | undefined => {\n const record = assetStore?.assets.find((asset) => asset.id === id)\n if (!record) return undefined\n // EC-587: the one reader — EC-632: in the CMS surface's active locale.\n const alt = assetAlt(record, assetReadLocale(cms.localeConfig, cms.activeLocale))\n return {\n ...(record.url !== undefined ? { url: record.url } : {}),\n contentType: record.contentType,\n ...(record.title !== undefined ? { title: record.title } : {}),\n filename: record.filename,\n ...(alt !== null ? { alt } : {}),\n }\n },\n [assetStore, cms.localeConfig, cms.activeLocale],\n )\n const liveUrls = useCallback(() => liveUrlMap, [liveUrlMap])\n // EC-595 A2: the CMS writes revisioned documents — patches at the source.\n const writes = useMemo<DocumentWrites>(\n () => ({\n kind: 'document',\n commit: cms.commitPatches,\n revisionOf: cms.revisionOf,\n insert: cms.insertDocument,\n newId: cms.newDocumentId,\n conflicts: cms.documentConflicts,\n resolveConflict: cms.resolveConflict,\n adoptSnapshot: cms.adoptDocumentSnapshot,\n flush: (ref) => cms.flushPersistence(ref),\n replace: cms.replaceDocument,\n subscribe: cms.subscribeDocument,\n reportPresence: cms.reportPresence,\n clearPresence: cms.clearPresence,\n presenceSessionId: cms.presenceSessionId,\n }),\n [cms],\n )\n\n return useMemo(() => {\n if (!collection) return null\n const capabilities: CollectionViewCapabilities = {\n publish: true,\n versions: true,\n locales: true,\n inlineEdit: true,\n create: true,\n duplicate: true, // EC-442: CMS collections only (owner call — rails skip)\n remove: true,\n bulk: true,\n liveUrl: isRoutable,\n hierarchy: isHierarchical(collection),\n importExport: true,\n }\n const source: CollectionViewSource = {\n collectionId,\n schema: collection,\n capabilities,\n ...(cms.projectId !== undefined ? { projectId: cms.projectId } : {}),\n assetCollectionId,\n localeConfig: cms.localeConfig,\n activeLocale: cms.activeLocale,\n documents,\n get: cms.getDocument,\n titleOf: cms.titleOf,\n // References are collection-based (EC-072): resolve candidates of ANY target\n // collection the schema declares — the workspace doc store already serves them.\n referenceCandidates: cms.documentsOf,\n collections: cms.collections,\n isFieldReadOnly: cms.isFieldReadOnly,\n validate: cms.validate,\n resolveAsset,\n lastError: cms.persistError,\n clearLastError: cms.clearPersistError,\n // Queue flush, so a pre-flight reference-index read is current.\n flush: () => cms.flushPersistence(),\n detailPath: (id: string) => studioPath.cmsDocument(collectionId, id),\n listPath: studioPath.cmsCollection(collectionId),\n viewState,\n setViewState,\n views,\n imports,\n create,\n writes,\n remove: cms.deleteDocument,\n bulk: (ids, action, setField) => cms.runBulk(collectionId, ids, action, setField),\n publishing: {\n isPublished: cms.isPublished,\n // EC-449: the materialized changed flag behind the amber `$state` dot\n // (chokepoint-computed; the store re-stamps it on save/publish/unpublish).\n hasUnpublishedChanges: cms.documentHasUnpublishedChanges,\n publish: cms.publishDocument,\n unpublish: cms.unpublishDocument,\n liveUrls,\n },\n // EC-445/EC-447: per-document timestamps behind the $updatedAt + $publishedAt\n // columns, sourced from the store (hydration seeds them from the persisted\n // `cmsDocuments.updatedAt` / `.publishedAt`; the store re-stamps on the edit +\n // publish/unpublish paths). Both are referentially-stable store accessors.\n timestamps: {\n updatedAt: cms.documentUpdatedAt,\n publishedAt: cms.documentPublishedAt,\n },\n versioning: {\n restore: (ref: DocumentRef, version: number, scope) =>\n cms.restoreDocumentVersion(ref, version, scope),\n // Flush-before-read ordering folds into the loader (EC-224) so any surface\n // that mutated the history still lists an authoritative version set.\n load: async (ref: DocumentRef) => {\n await cms.flushPersistence()\n return cms.loadDocumentVersions(ref)\n },\n },\n inlineEditing: {\n commitField: (input) => cms.commitFieldEdit(input),\n },\n }\n return source\n }, [\n collection,\n collectionId,\n cms,\n isRoutable,\n documents,\n viewState,\n setViewState,\n views,\n imports,\n create,\n resolveAsset,\n liveUrls,\n assetCollectionId,\n writes,\n ])\n}\n"]}
|
|
1
|
+
{"version":3,"file":"cms-collection-view-source.js","sourceRoot":"","sources":["../../../src/components/collection-view/cms-collection-view-source.ts"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useMemo } from 'react'\nimport { isHierarchical } from '@elytracms/core/cms-core'\nimport type { DocumentRef, DocumentUrl } from '@elytracms/core/cms-core'\nimport { assetAlt, assetMetaLens, assetReadLocale } from '@elytracms/core/persistence'\nimport { studioPath } from '@elytracms/studio/router'\nimport { resolveDeploymentFrontendUrl, resolveDeploymentRoutes } from '../../lib/workspace'\nimport type {\n CollectionViewCapabilities,\n CollectionViewSource,\n DocumentWrites,\n AssetDisplay,\n} from '../../lib/collection-view/collection-view-source'\nimport { useCms } from '../cms/cms-store'\nimport { useOptionalAssets } from '../assets/assets-store'\nimport { usePublishedDocuments } from '../../lib/cms/published-documents'\nimport type { ReadState } from '../../lib/query'\nimport { resolveDocumentLiveUrl } from '../cms/use-live-url'\n\n/**\n * Assemble a {@link CollectionViewSource} for a `cmsDocuments` collection from the\n * ambient `CmsStore` + its side contexts (assets, published pool) + the\n * deployment config reads (EC-240 / AD-19). This is the CMS adapter: a\n * behaviour-preserving pass-through so the portable list + edit surfaces render\n * over the source instead of `useCms()` directly, byte-for-byte as before.\n *\n * Returns `null` for an unknown collection id (the caller renders the fallback).\n *\n * Referential stability: collection-parameterised methods are `useCallback`-bound\n * and the source object is `useMemo`-ed, so the surfaces' `useMemo` dependency\n * arrays stay stable across renders (the inline-edit order snapshot depends on it).\n */\nexport function useCmsCollectionViewSource(collectionId: string): CollectionViewSource | null {\n const cms = useCms()\n const assetStore = useOptionalAssets()\n const collection = cms.getCollection(collectionId)\n const publishedDocs = usePublishedDocuments(cms.projectId, collectionId)\n\n // The asset collection id is resolved once from the schema (as the cell does).\n const assetCollectionId = useMemo(\n () => cms.collections.find((c) => c.kind === 'asset')?.id ?? 'asset',\n [cms.collections],\n )\n\n // Live-URL column: the published pool × deployment routes, memoised exactly as\n // the table did — derived from PUBLISHED snapshots, never draft rows (EC-205).\n const isRoutable = useMemo(\n () => resolveDeploymentRoutes().some((route) => route.document?.collection === collectionId),\n [collectionId],\n )\n // EC-384: `resolveDocumentLiveUrl` merges the stored routes with the kit's\n // fallback-home rule (same as the document editor's header affordance,\n // `use-live-url.ts`) — otherwise a fallback-addressed home page showed no URL\n // in this column even though the live site serves it.\n // EC-628: the pool's read state carries through — a failed read is the column's error.\n const liveUrlMap = useMemo((): ReadState<ReadonlyMap<string, DocumentUrl>> | null => {\n if (!collection || !isRoutable) return null\n if (publishedDocs.status !== 'ready') return publishedDocs\n const siblings = [...publishedDocs.value.values()]\n const map = new Map<string, DocumentUrl>()\n for (const published of siblings) {\n const url = resolveDocumentLiveUrl({\n routes: resolveDeploymentRoutes(),\n frontendUrl: resolveDeploymentFrontendUrl(),\n localeConfig: cms.localeConfig,\n collection,\n doc: published,\n siblings,\n locale: cms.activeLocale,\n })\n if (url) map.set(published.id, url)\n }\n return { status: 'ready', value: map }\n }, [collection, isRoutable, publishedDocs, cms.localeConfig, cms.activeLocale])\n\n const documents = useCallback(() => cms.documentsOf(collectionId), [cms, collectionId])\n const viewState = useCallback(() => cms.tableStateOf(collectionId), [cms, collectionId])\n const setViewState = useCallback(\n (update: Parameters<CollectionViewSource['setViewState']>[0]) =>\n cms.setTableState(collectionId, update),\n [cms, collectionId],\n )\n // Named views (EC-246b): config-as-code lenses the header selector switches between.\n const views = useMemo(\n () => ({\n list: () => cms.viewsOf(collectionId),\n activeId: () => cms.activeViewId(collectionId),\n setActive: (id: string) => cms.setActiveView(collectionId, id),\n }),\n [cms, collectionId],\n )\n const create = useCallback(() => cms.createDocument(collectionId), [cms, collectionId])\n const imports = useMemo(\n () => ({ importDocuments: (items: unknown[]) => cms.importDocuments(collectionId, items) }),\n [cms, collectionId],\n )\n const resolveAsset = useCallback(\n (id: string): AssetDisplay | undefined => {\n const record = assetStore?.assets.find((asset) => asset.id === id)\n if (!record) return undefined\n // EC-587: the one reader — EC-632: in the CMS surface's active locale; EC-659: through\n // the session's asset collection as declared now.\n const alt = assetAlt(record, assetReadLocale(cms.localeConfig, assetMetaLens(cms.collections), cms.activeLocale))\n return {\n ...(record.url !== undefined ? { url: record.url } : {}),\n contentType: record.contentType,\n ...(record.title !== undefined ? { title: record.title } : {}),\n filename: record.filename,\n ...(alt !== null ? { alt } : {}),\n }\n },\n [assetStore, cms.localeConfig, cms.collections, cms.activeLocale],\n )\n const liveUrls = useCallback(() => liveUrlMap, [liveUrlMap])\n // EC-595 A2: the CMS writes revisioned documents — patches at the source.\n const writes = useMemo<DocumentWrites>(\n () => ({\n kind: 'document',\n commit: cms.commitPatches,\n revisionOf: cms.revisionOf,\n revisionOfView: cms.revisionOfView,\n insert: cms.insertDocument,\n newId: cms.newDocumentId,\n conflicts: cms.documentConflicts,\n resolveConflict: cms.resolveConflict,\n adoptSnapshot: cms.adoptDocumentSnapshot,\n flush: (ref) => cms.flushPersistence(ref),\n replace: cms.replaceDocument,\n subscribe: cms.subscribeDocument,\n reportPresence: cms.reportPresence,\n clearPresence: cms.clearPresence,\n presenceSessionId: cms.presenceSessionId,\n }),\n [cms],\n )\n\n return useMemo(() => {\n if (!collection) return null\n const capabilities: CollectionViewCapabilities = {\n publish: true,\n versions: true,\n locales: true,\n inlineEdit: true,\n create: true,\n duplicate: true, // EC-442: CMS collections only (owner call — rails skip)\n remove: true,\n bulk: true,\n liveUrl: isRoutable,\n hierarchy: isHierarchical(collection),\n importExport: true,\n }\n const source: CollectionViewSource = {\n collectionId,\n schema: collection,\n capabilities,\n ...(cms.projectId !== undefined ? { projectId: cms.projectId } : {}),\n assetCollectionId,\n localeConfig: cms.localeConfig,\n activeLocale: cms.activeLocale,\n documents,\n get: cms.getDocument,\n titleOf: cms.titleOf,\n // References are collection-based (EC-072): resolve candidates of ANY target\n // collection the schema declares — the workspace doc store already serves them.\n referenceCandidates: cms.documentsOf,\n collections: cms.collections,\n isFieldReadOnly: cms.isFieldReadOnly,\n validate: cms.validate,\n resolveAsset,\n lastError: cms.persistError,\n clearLastError: cms.clearPersistError,\n // Queue flush, so a pre-flight reference-index read is current.\n flush: () => cms.flushPersistence(),\n detailPath: (id: string) => studioPath.cmsDocument(collectionId, id),\n listPath: studioPath.cmsCollection(collectionId),\n viewState,\n setViewState,\n views,\n imports,\n create,\n writes,\n remove: cms.deleteDocument,\n bulk: (ids, action, setField) => cms.runBulk(collectionId, ids, action, setField),\n publishing: {\n isPublished: cms.isPublished,\n // EC-449: the materialized changed flag behind the amber `$state` dot\n // (chokepoint-computed; the store re-stamps it on save/publish/unpublish).\n hasUnpublishedChanges: cms.documentHasUnpublishedChanges,\n publish: cms.publishDocument,\n unpublish: cms.unpublishDocument,\n liveUrls,\n },\n // EC-445/EC-447: per-document timestamps behind the $updatedAt + $publishedAt\n // columns, sourced from the store (hydration seeds them from the persisted\n // `cmsDocuments.updatedAt` / `.publishedAt`; the store re-stamps on the edit +\n // publish/unpublish paths). Both are referentially-stable store accessors.\n timestamps: {\n updatedAt: cms.documentUpdatedAt,\n publishedAt: cms.documentPublishedAt,\n },\n versioning: {\n restore: (ref: DocumentRef, version: number, scope) =>\n cms.restoreDocumentVersion(ref, version, scope),\n // Flush-before-read ordering folds into the loader (EC-224) so any surface\n // that mutated the history still lists an authoritative version set.\n load: async (ref: DocumentRef) => {\n await cms.flushPersistence()\n return cms.loadDocumentVersions(ref)\n },\n },\n inlineEditing: {\n commitField: (input) => cms.commitFieldEdit(input),\n },\n }\n return source\n }, [\n collection,\n collectionId,\n cms,\n isRoutable,\n documents,\n viewState,\n setViewState,\n views,\n imports,\n create,\n resolveAsset,\n liveUrls,\n assetCollectionId,\n writes,\n ])\n}\n"]}
|
|
@@ -76,10 +76,12 @@ export function TransferPicker({ projectId, node, onClose, }) {
|
|
|
76
76
|
* NEW document is one insert-if-absent create; an EXISTING one gets the transfer's
|
|
77
77
|
* patch (one `insert` of the embed node at the end of its field) — never a
|
|
78
78
|
* whole-document write, so a concurrent edit of that document is never overwritten.
|
|
79
|
+
* EC-647: the patch carries the revision of the document value it was appended to.
|
|
79
80
|
*/
|
|
80
|
-
async function persistAndOpen(doc, target,
|
|
81
|
-
if (
|
|
82
|
-
await cms.commitPatches({ collection: doc.collection, id: doc.id }, patches);
|
|
81
|
+
async function persistAndOpen(doc, target, append) {
|
|
82
|
+
if (append) {
|
|
83
|
+
await cms.commitPatches({ collection: doc.collection, id: doc.id }, append.patches, append.patches.map(() => append.revision));
|
|
84
|
+
}
|
|
83
85
|
else
|
|
84
86
|
cms.insertDocument(doc);
|
|
85
87
|
// AWAIT the queued write landing, so a reload of the destination editor — not just
|
|
@@ -127,7 +129,7 @@ export function TransferPicker({ projectId, node, onClose, }) {
|
|
|
127
129
|
locale: cms.activeLocale,
|
|
128
130
|
newId: cms.newDocumentId,
|
|
129
131
|
});
|
|
130
|
-
await persistAndOpen(doc, target, patches);
|
|
132
|
+
await persistAndOpen(doc, target, { patches, revision: cms.revisionOfView(current) });
|
|
131
133
|
}
|
|
132
134
|
finally {
|
|
133
135
|
setPendingKey(null);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transfer-dialog.js","sourceRoot":"","sources":["../../../src/components/components/transfer-dialog.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useMemo, useState } from 'react'\nimport { FilePlus2, FileText, Loader2, Search } from 'lucide-react'\nimport type { ReactNode } from 'react'\nimport type { ComponentManifest } from '@elytracms/core/component-registry'\nimport type { CmsDocument, DocumentPatch } from '@elytracms/core/cms-core'\nimport type { ComponentNode } from '@elytracms/core/project-graph'\nimport { STUDIO_PROJECT_ROUTE, studioPath, useNavigate } from '@elytracms/studio/router'\nimport {\n Button,\n Code,\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n Input,\n Row,\n Stack,\n Text,\n} from '@elytracms/studio/ui'\nimport { cn } from '../../lib/utils'\nimport { getStudioRegistry } from '../../lib/registry'\nimport { localizedLabel, translate } from '../../lib/i18n'\nimport { CmsSessionGate } from '../project-session'\nimport { CmsProvider, useCms } from '../cms/cms-store'\nimport { stageDocument } from '../cms/document-staging'\nimport { resolveCmsInitialCollections } from '../app-views/default-modules'\nimport {\n appendToBlockDocument,\n buildBlockDocument,\n canAppendTo,\n collectionsWithBlocks,\n isComponentAllowed,\n} from './transfer'\nimport type { CollectionBlocksTarget } from './transfer'\n\n/** Existing documents offered per target, beyond which the rest are hidden (filter to find them). */\nconst MAX_EXISTING_TARGETS = 8\n\n/**\n * EC-320 (+ 0.0.44 follow-up) — \"Use in document…\". Transfers the current\n * component combination (the live preview {@link ComponentNode}, edited props\n * and all) into a document's blocks field, then opens that document. Each\n * eligible collection+field target offers a NEW draft AND its existing\n * documents to append into.\n *\n * The dialog mounts a CMS session (so it reuses the store's create/save machinery,\n * collision-free ids and persistence) only while open. On confirm it builds the\n * draft or the append (pure {@link buildBlockDocument} / {@link appendToBlockDocument}),\n * saves it and AWAITS the persisted write landing (belt and braces alongside the\n * STAGE for the next CMS mount, see `document-staging` — both matter: staging\n * covers the very next mount regardless of mode, the await guards any later\n * reload), then navigates to the document — focused on its blocks field so it\n * opens on the canvas.\n */\nexport function TransferToDocumentDialog({\n projectId,\n manifest,\n node,\n open,\n onOpenChange,\n}: {\n projectId: string\n manifest: ComponentManifest\n node: ComponentNode\n open: boolean\n onOpenChange: (open: boolean) => void\n}) {\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent className=\"max-w-md\">\n <DialogHeader>\n <DialogTitle>\n {translate('components.transfer.title', { name: localizedLabel(manifest.title, manifest.id) })}\n </DialogTitle>\n <DialogDescription>{translate('components.transfer.description')}</DialogDescription>\n </DialogHeader>\n {open ? (\n <CmsSessionGate>\n {(cms) => {\n // Delegate to the shared, tested resolver (also used by the CMS/Assets\n // tabs) so this dialog's targets stay in sync with what those tabs show,\n // rather than reimplementing the ladder inline. (EC-479 removed the\n // reason the old inline version was subtly wrong: `CmsProvider` had a\n // `?? sampleFilterableCollections` fixture fallback that an inline `[]`\n // \"suppressed\" — there is no fixture fallback to suppress now, so the\n // distinction between `[]` and `undefined` no longer carries meaning.)\n const collections = resolveCmsInitialCollections(cms)\n return (\n <CmsProvider\n projectId={projectId}\n initialCollections={collections}\n initialDocuments={cms?.documents}\n initialPublishedKeys={cms?.publishedKeys}\n initialRevisions={cms?.revisions}\n // EC-461: hydrated seed (both modes now) supersedes staged docs.\n seedReflectsWrites={cms !== null}\n >\n <TransferPicker projectId={projectId} node={node} onClose={() => onOpenChange(false)} />\n </CmsProvider>\n )\n }}\n </CmsSessionGate>\n ) : null}\n </DialogContent>\n </Dialog>\n )\n}\n\n/** A pending transfer's in-flight target key: `<collectionId>/new` or `<collectionId>/<docId>`. */\nfunction newTargetKey(target: CollectionBlocksTarget): string {\n return `${target.collection.id}/new`\n}\nfunction existingTargetKey(target: CollectionBlocksTarget, doc: CmsDocument): string {\n return `${target.collection.id}/${doc.id}`\n}\n\n/**\n * Exported for the fresh-Convex-project regression test (bug fix,\n * transfer-dialog.test.tsx) — `TransferToDocumentDialog` is otherwise its only\n * caller.\n */\nexport function TransferPicker({\n projectId,\n node,\n onClose,\n}: {\n projectId: string\n node: ComponentNode\n onClose: () => void\n}) {\n const cms = useCms()\n const navigate = useNavigate()\n const [query, setQuery] = useState('')\n // The in-flight target's key while its save is awaited (disables every row\n // + shows a spinner on the clicked one) — `null` when idle.\n const [pendingKey, setPendingKey] = useState<string | null>(null)\n\n // Read the LIVE store schema (`cms.collections`) rather than re-deriving it\n // from the same `resolveCmsInitialCollections` result passed to `CmsProvider`\n // above — `cms.collections` is what the provider actually resolved to, so the\n // targets list can never drift from what the CMS/Assets tabs show for this\n // same session. Allow-gated (0.0.44 follow-up): a target\n // whose field's `allow` vocabulary rejects this component is never offered,\n // new-document or existing-document alike — the validator would flag it.\n const targets = useMemo(\n () =>\n collectionsWithBlocks(cms.collections).filter((t) =>\n isComponentAllowed(t.field, node.componentId, getStudioRegistry().registry.lookup),\n ),\n [cms.collections, node.componentId],\n )\n\n /**\n * Persist (awaited) + stage + navigate — shared by both transfer paths. EC-595 A2: a\n * NEW document is one insert-if-absent create; an EXISTING one gets the transfer's\n * patch (one `insert` of the embed node at the end of its field) — never a\n * whole-document write, so a concurrent edit of that document is never overwritten.\n */\n async function persistAndOpen(\n doc: CmsDocument,\n target: CollectionBlocksTarget,\n patches?: readonly DocumentPatch[],\n ) {\n if (patches) await cms.commitPatches({ collection: doc.collection, id: doc.id }, patches)\n else cms.insertDocument(doc)\n // AWAIT the queued write landing, so a reload of the destination editor — not just\n // its very next mount — finds the document.\n await cms.flushPersistence({ collection: doc.collection, id: doc.id })\n // Cross-mount handoff for the very next `CmsProvider` mount, in EVERY mode (see\n // document-staging.ts): the session's view of the written document.\n stageDocument(projectId, cms.getDocument({ collection: doc.collection, id: doc.id }) ?? doc)\n onClose()\n void navigate({\n to: STUDIO_PROJECT_ROUTE,\n params: { projectId, _splat: studioPath.cmsDocument(target.collection.id, doc.id) },\n search: { field: `values/${target.field.name}` },\n })\n }\n\n async function transferNew(target: CollectionBlocksTarget) {\n const key = newTargetKey(target)\n setPendingKey(key)\n try {\n const doc = buildBlockDocument({\n collection: target.collection,\n blocksField: target.field,\n node,\n newId: cms.newDocumentId,\n locales: cms.localeConfig,\n })\n await persistAndOpen(doc, target)\n } finally {\n setPendingKey(null)\n }\n }\n\n async function transferInto(target: CollectionBlocksTarget, existing: CmsDocument) {\n const key = existingTargetKey(target, existing)\n setPendingKey(key)\n try {\n // The FORCED FLUSH before the transfer reads the document (the ruling).\n await cms.flushPersistence({ collection: existing.collection, id: existing.id })\n const current = cms.getDocument({ collection: existing.collection, id: existing.id }) ?? existing\n const { document: doc, patches } = appendToBlockDocument({\n collection: target.collection,\n document: current,\n blocksField: target.field,\n node,\n locale: cms.activeLocale,\n newId: cms.newDocumentId,\n })\n await persistAndOpen(doc, target, patches)\n } finally {\n setPendingKey(null)\n }\n }\n\n if (targets.length === 0) {\n return (\n <Text size=\"sm\" tone=\"muted\" data-testid=\"transfer-no-targets\">\n {translate('components.transfer.noTargets')}\n </Text>\n )\n }\n\n const trimmedQuery = query.trim().toLowerCase()\n const hasAnyExistingDocs = targets.some((t) => cms.documentsOf(t.collection.id).length > 0)\n\n return (\n <>\n {hasAnyExistingDocs ? (\n <div className=\"relative\">\n <Search className=\"pointer-events-none absolute left-2.5 top-1/2 size-3.5 -translate-y-1/2 text-muted-foreground\" />\n <Input\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n placeholder={translate('common.searchPlaceholder')}\n className=\"pl-8\"\n />\n </div>\n ) : null}\n <Stack gap=\"4\" className=\"max-h-[50vh] overflow-y-auto\">\n {targets.map((target) => {\n const existing = cms\n .documentsOf(target.collection.id)\n .filter((doc) => canAppendTo({ collection: target.collection, document: doc, blocksField: target.field, locale: cms.activeLocale }))\n .filter(\n (doc) =>\n trimmedQuery === '' ||\n cms.titleOf({ collection: target.collection.id, id: doc.id }).toLowerCase().includes(trimmedQuery),\n )\n .slice(0, MAX_EXISTING_TARGETS)\n\n return (\n <Stack key={target.collection.id} gap=\"2\">\n <Stack gap=\"px\">\n <Text size=\"sm\" weight=\"medium\" className=\"truncate\">\n {localizedLabel(target.collection.form?.label, target.collection.id)}\n </Text>\n <Row gap=\"1\" align=\"center\">\n <Text size=\"xs\" tone=\"muted\">\n {translate('components.transfer.intoField')}\n </Text>\n <Code tone=\"muted\">{target.field.name}</Code>\n </Row>\n </Stack>\n <Stack gap=\"1\">\n <TransferTargetRow\n testId=\"transfer-target\"\n icon={<FilePlus2 className=\"size-4\" />}\n label={translate('components.transfer.newDocument', {\n collection: localizedLabel(target.collection.form?.labelSingular, target.collection.id),\n })}\n pending={pendingKey === newTargetKey(target)}\n disabled={pendingKey !== null}\n onClick={() => void transferNew(target)}\n />\n {existing.length > 0 ? (\n <Text size=\"xs\" tone=\"muted\" className=\"px-1 pt-1\">\n {translate('components.transfer.orExisting')}\n </Text>\n ) : null}\n {existing.map((doc) => (\n <TransferTargetRow\n key={doc.id}\n testId=\"transfer-target-existing\"\n icon={<FileText className=\"size-4\" />}\n label={cms.titleOf({ collection: target.collection.id, id: doc.id })}\n pending={pendingKey === existingTargetKey(target, doc)}\n disabled={pendingKey !== null}\n onClick={() => void transferInto(target, doc)}\n />\n ))}\n </Stack>\n </Stack>\n )\n })}\n </Stack>\n <DialogFooter>\n <Button type=\"button\" variant=\"ghost\" onClick={onClose} disabled={pendingKey !== null}>\n {translate('common.cancel')}\n </Button>\n </DialogFooter>\n </>\n )\n}\n\n/** One selectable transfer target row — a new draft, or an existing document to append into. */\nfunction TransferTargetRow({\n testId,\n icon,\n label,\n pending,\n disabled,\n onClick,\n}: {\n testId: string\n icon: ReactNode\n label: string\n pending: boolean\n disabled: boolean\n onClick: () => void\n}) {\n return (\n <button\n type=\"button\"\n data-testid={testId}\n disabled={disabled}\n onClick={onClick}\n className={cn(\n 'flex items-center gap-3 rounded-md border border-border p-3 text-left transition-colors hover:border-primary/40 hover:bg-muted',\n disabled && 'cursor-not-allowed opacity-60',\n )}\n >\n <span className=\"grid size-8 shrink-0 place-items-center rounded-md bg-muted text-muted-foreground\">\n {pending ? <Loader2 className=\"size-4 animate-spin\" /> : icon}\n </span>\n <Text size=\"sm\" weight=\"medium\" className=\"min-w-0 flex-1 truncate\">\n {label}\n </Text>\n </button>\n )\n}\n"]}
|
|
1
|
+
{"version":3,"file":"transfer-dialog.js","sourceRoot":"","sources":["../../../src/components/components/transfer-dialog.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useMemo, useState } from 'react'\nimport { FilePlus2, FileText, Loader2, Search } from 'lucide-react'\nimport type { ReactNode } from 'react'\nimport type { ComponentManifest } from '@elytracms/core/component-registry'\nimport type { CmsDocument, DocumentPatch } from '@elytracms/core/cms-core'\nimport type { ComponentNode } from '@elytracms/core/project-graph'\nimport { STUDIO_PROJECT_ROUTE, studioPath, useNavigate } from '@elytracms/studio/router'\nimport {\n Button,\n Code,\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n Input,\n Row,\n Stack,\n Text,\n} from '@elytracms/studio/ui'\nimport { cn } from '../../lib/utils'\nimport { getStudioRegistry } from '../../lib/registry'\nimport { localizedLabel, translate } from '../../lib/i18n'\nimport { CmsSessionGate } from '../project-session'\nimport { CmsProvider, useCms } from '../cms/cms-store'\nimport { stageDocument } from '../cms/document-staging'\nimport { resolveCmsInitialCollections } from '../app-views/default-modules'\nimport {\n appendToBlockDocument,\n buildBlockDocument,\n canAppendTo,\n collectionsWithBlocks,\n isComponentAllowed,\n} from './transfer'\nimport type { CollectionBlocksTarget } from './transfer'\n\n/** Existing documents offered per target, beyond which the rest are hidden (filter to find them). */\nconst MAX_EXISTING_TARGETS = 8\n\n/**\n * EC-320 (+ 0.0.44 follow-up) — \"Use in document…\". Transfers the current\n * component combination (the live preview {@link ComponentNode}, edited props\n * and all) into a document's blocks field, then opens that document. Each\n * eligible collection+field target offers a NEW draft AND its existing\n * documents to append into.\n *\n * The dialog mounts a CMS session (so it reuses the store's create/save machinery,\n * collision-free ids and persistence) only while open. On confirm it builds the\n * draft or the append (pure {@link buildBlockDocument} / {@link appendToBlockDocument}),\n * saves it and AWAITS the persisted write landing (belt and braces alongside the\n * STAGE for the next CMS mount, see `document-staging` — both matter: staging\n * covers the very next mount regardless of mode, the await guards any later\n * reload), then navigates to the document — focused on its blocks field so it\n * opens on the canvas.\n */\nexport function TransferToDocumentDialog({\n projectId,\n manifest,\n node,\n open,\n onOpenChange,\n}: {\n projectId: string\n manifest: ComponentManifest\n node: ComponentNode\n open: boolean\n onOpenChange: (open: boolean) => void\n}) {\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent className=\"max-w-md\">\n <DialogHeader>\n <DialogTitle>\n {translate('components.transfer.title', { name: localizedLabel(manifest.title, manifest.id) })}\n </DialogTitle>\n <DialogDescription>{translate('components.transfer.description')}</DialogDescription>\n </DialogHeader>\n {open ? (\n <CmsSessionGate>\n {(cms) => {\n // Delegate to the shared, tested resolver (also used by the CMS/Assets\n // tabs) so this dialog's targets stay in sync with what those tabs show,\n // rather than reimplementing the ladder inline. (EC-479 removed the\n // reason the old inline version was subtly wrong: `CmsProvider` had a\n // `?? sampleFilterableCollections` fixture fallback that an inline `[]`\n // \"suppressed\" — there is no fixture fallback to suppress now, so the\n // distinction between `[]` and `undefined` no longer carries meaning.)\n const collections = resolveCmsInitialCollections(cms)\n return (\n <CmsProvider\n projectId={projectId}\n initialCollections={collections}\n initialDocuments={cms?.documents}\n initialPublishedKeys={cms?.publishedKeys}\n initialRevisions={cms?.revisions}\n // EC-461: hydrated seed (both modes now) supersedes staged docs.\n seedReflectsWrites={cms !== null}\n >\n <TransferPicker projectId={projectId} node={node} onClose={() => onOpenChange(false)} />\n </CmsProvider>\n )\n }}\n </CmsSessionGate>\n ) : null}\n </DialogContent>\n </Dialog>\n )\n}\n\n/** A pending transfer's in-flight target key: `<collectionId>/new` or `<collectionId>/<docId>`. */\nfunction newTargetKey(target: CollectionBlocksTarget): string {\n return `${target.collection.id}/new`\n}\nfunction existingTargetKey(target: CollectionBlocksTarget, doc: CmsDocument): string {\n return `${target.collection.id}/${doc.id}`\n}\n\n/**\n * Exported for the fresh-Convex-project regression test (bug fix,\n * transfer-dialog.test.tsx) — `TransferToDocumentDialog` is otherwise its only\n * caller.\n */\nexport function TransferPicker({\n projectId,\n node,\n onClose,\n}: {\n projectId: string\n node: ComponentNode\n onClose: () => void\n}) {\n const cms = useCms()\n const navigate = useNavigate()\n const [query, setQuery] = useState('')\n // The in-flight target's key while its save is awaited (disables every row\n // + shows a spinner on the clicked one) — `null` when idle.\n const [pendingKey, setPendingKey] = useState<string | null>(null)\n\n // Read the LIVE store schema (`cms.collections`) rather than re-deriving it\n // from the same `resolveCmsInitialCollections` result passed to `CmsProvider`\n // above — `cms.collections` is what the provider actually resolved to, so the\n // targets list can never drift from what the CMS/Assets tabs show for this\n // same session. Allow-gated (0.0.44 follow-up): a target\n // whose field's `allow` vocabulary rejects this component is never offered,\n // new-document or existing-document alike — the validator would flag it.\n const targets = useMemo(\n () =>\n collectionsWithBlocks(cms.collections).filter((t) =>\n isComponentAllowed(t.field, node.componentId, getStudioRegistry().registry.lookup),\n ),\n [cms.collections, node.componentId],\n )\n\n /**\n * Persist (awaited) + stage + navigate — shared by both transfer paths. EC-595 A2: a\n * NEW document is one insert-if-absent create; an EXISTING one gets the transfer's\n * patch (one `insert` of the embed node at the end of its field) — never a\n * whole-document write, so a concurrent edit of that document is never overwritten.\n * EC-647: the patch carries the revision of the document value it was appended to.\n */\n async function persistAndOpen(\n doc: CmsDocument,\n target: CollectionBlocksTarget,\n append?: { patches: readonly DocumentPatch[]; revision: number | undefined },\n ) {\n if (append) {\n await cms.commitPatches(\n { collection: doc.collection, id: doc.id },\n append.patches,\n append.patches.map(() => append.revision),\n )\n } else cms.insertDocument(doc)\n // AWAIT the queued write landing, so a reload of the destination editor — not just\n // its very next mount — finds the document.\n await cms.flushPersistence({ collection: doc.collection, id: doc.id })\n // Cross-mount handoff for the very next `CmsProvider` mount, in EVERY mode (see\n // document-staging.ts): the session's view of the written document.\n stageDocument(projectId, cms.getDocument({ collection: doc.collection, id: doc.id }) ?? doc)\n onClose()\n void navigate({\n to: STUDIO_PROJECT_ROUTE,\n params: { projectId, _splat: studioPath.cmsDocument(target.collection.id, doc.id) },\n search: { field: `values/${target.field.name}` },\n })\n }\n\n async function transferNew(target: CollectionBlocksTarget) {\n const key = newTargetKey(target)\n setPendingKey(key)\n try {\n const doc = buildBlockDocument({\n collection: target.collection,\n blocksField: target.field,\n node,\n newId: cms.newDocumentId,\n locales: cms.localeConfig,\n })\n await persistAndOpen(doc, target)\n } finally {\n setPendingKey(null)\n }\n }\n\n async function transferInto(target: CollectionBlocksTarget, existing: CmsDocument) {\n const key = existingTargetKey(target, existing)\n setPendingKey(key)\n try {\n // The FORCED FLUSH before the transfer reads the document (the ruling).\n await cms.flushPersistence({ collection: existing.collection, id: existing.id })\n const current = cms.getDocument({ collection: existing.collection, id: existing.id }) ?? existing\n const { document: doc, patches } = appendToBlockDocument({\n collection: target.collection,\n document: current,\n blocksField: target.field,\n node,\n locale: cms.activeLocale,\n newId: cms.newDocumentId,\n })\n await persistAndOpen(doc, target, { patches, revision: cms.revisionOfView(current) })\n } finally {\n setPendingKey(null)\n }\n }\n\n if (targets.length === 0) {\n return (\n <Text size=\"sm\" tone=\"muted\" data-testid=\"transfer-no-targets\">\n {translate('components.transfer.noTargets')}\n </Text>\n )\n }\n\n const trimmedQuery = query.trim().toLowerCase()\n const hasAnyExistingDocs = targets.some((t) => cms.documentsOf(t.collection.id).length > 0)\n\n return (\n <>\n {hasAnyExistingDocs ? (\n <div className=\"relative\">\n <Search className=\"pointer-events-none absolute left-2.5 top-1/2 size-3.5 -translate-y-1/2 text-muted-foreground\" />\n <Input\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n placeholder={translate('common.searchPlaceholder')}\n className=\"pl-8\"\n />\n </div>\n ) : null}\n <Stack gap=\"4\" className=\"max-h-[50vh] overflow-y-auto\">\n {targets.map((target) => {\n const existing = cms\n .documentsOf(target.collection.id)\n .filter((doc) => canAppendTo({ collection: target.collection, document: doc, blocksField: target.field, locale: cms.activeLocale }))\n .filter(\n (doc) =>\n trimmedQuery === '' ||\n cms.titleOf({ collection: target.collection.id, id: doc.id }).toLowerCase().includes(trimmedQuery),\n )\n .slice(0, MAX_EXISTING_TARGETS)\n\n return (\n <Stack key={target.collection.id} gap=\"2\">\n <Stack gap=\"px\">\n <Text size=\"sm\" weight=\"medium\" className=\"truncate\">\n {localizedLabel(target.collection.form?.label, target.collection.id)}\n </Text>\n <Row gap=\"1\" align=\"center\">\n <Text size=\"xs\" tone=\"muted\">\n {translate('components.transfer.intoField')}\n </Text>\n <Code tone=\"muted\">{target.field.name}</Code>\n </Row>\n </Stack>\n <Stack gap=\"1\">\n <TransferTargetRow\n testId=\"transfer-target\"\n icon={<FilePlus2 className=\"size-4\" />}\n label={translate('components.transfer.newDocument', {\n collection: localizedLabel(target.collection.form?.labelSingular, target.collection.id),\n })}\n pending={pendingKey === newTargetKey(target)}\n disabled={pendingKey !== null}\n onClick={() => void transferNew(target)}\n />\n {existing.length > 0 ? (\n <Text size=\"xs\" tone=\"muted\" className=\"px-1 pt-1\">\n {translate('components.transfer.orExisting')}\n </Text>\n ) : null}\n {existing.map((doc) => (\n <TransferTargetRow\n key={doc.id}\n testId=\"transfer-target-existing\"\n icon={<FileText className=\"size-4\" />}\n label={cms.titleOf({ collection: target.collection.id, id: doc.id })}\n pending={pendingKey === existingTargetKey(target, doc)}\n disabled={pendingKey !== null}\n onClick={() => void transferInto(target, doc)}\n />\n ))}\n </Stack>\n </Stack>\n )\n })}\n </Stack>\n <DialogFooter>\n <Button type=\"button\" variant=\"ghost\" onClick={onClose} disabled={pendingKey !== null}>\n {translate('common.cancel')}\n </Button>\n </DialogFooter>\n </>\n )\n}\n\n/** One selectable transfer target row — a new draft, or an existing document to append into. */\nfunction TransferTargetRow({\n testId,\n icon,\n label,\n pending,\n disabled,\n onClick,\n}: {\n testId: string\n icon: ReactNode\n label: string\n pending: boolean\n disabled: boolean\n onClick: () => void\n}) {\n return (\n <button\n type=\"button\"\n data-testid={testId}\n disabled={disabled}\n onClick={onClick}\n className={cn(\n 'flex items-center gap-3 rounded-md border border-border p-3 text-left transition-colors hover:border-primary/40 hover:bg-muted',\n disabled && 'cursor-not-allowed opacity-60',\n )}\n >\n <span className=\"grid size-8 shrink-0 place-items-center rounded-md bg-muted text-muted-foreground\">\n {pending ? <Loader2 className=\"size-4 animate-spin\" /> : icon}\n </span>\n <Text size=\"sm\" weight=\"medium\" className=\"min-w-0 flex-1 truncate\">\n {label}\n </Text>\n </button>\n )\n}\n"]}
|
|
@@ -54,6 +54,13 @@ export interface ChatProposal {
|
|
|
54
54
|
* revert any change made since the proposal was built. Absent for a create.
|
|
55
55
|
*/
|
|
56
56
|
patches?: readonly DocumentPatch[];
|
|
57
|
+
/**
|
|
58
|
+
* EC-647 — for an update or a translation: the revision of the read the proposal was
|
|
59
|
+
* built from. Applying it writes {@link patches} AT this revision, so a position changed
|
|
60
|
+
* since (by anyone else) is refused on that field, never overwritten. Absent → the apply
|
|
61
|
+
* names no revision and is refused.
|
|
62
|
+
*/
|
|
63
|
+
revision?: number;
|
|
57
64
|
/**
|
|
58
65
|
* EC-593 (ratified 4e) — for a translation: every text position it could NOT
|
|
59
66
|
* translate, shown on the card the editor reviews (never a silent skip).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat-state.js","sourceRoot":"","sources":["../../../src/lib/ai/chat-state.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"chat-state.js","sourceRoot":"","sources":["../../../src/lib/ai/chat-state.ts"],"names":[],"mappings":"AAmFA,MAAM,CAAC,MAAM,sBAAsB,GAAoB;IACrD,SAAS,EAAE,EAAE;IACb,KAAK,EAAE,IAAI;CACZ,CAAA;AAWD,SAAS,UAAU,CACjB,KAAsB,EACtB,UAAkB,EAClB,MAA8B;IAE9B,MAAM,QAAQ,GAAG,KAAK,CAAC,SAAS,CAAC,UAAU,CAAC,CAAA;IAC5C,mEAAmE;IACnE,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,MAAM,KAAK,SAAS;QAAE,OAAO,KAAK,CAAA;IAC5D,OAAO;QACL,GAAG,KAAK;QACR,SAAS,EAAE,EAAE,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC,UAAU,CAAC,EAAE,EAAE,GAAG,QAAQ,EAAE,MAAM,EAAE,EAAE;KACzE,CAAA;AACH,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,KAAsB,EACtB,MAAwB;IAExB,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;QACpB,KAAK,SAAS;YACZ,OAAO;gBACL,GAAG,KAAK;gBACR,SAAS,EAAE;oBACT,GAAG,KAAK,CAAC,SAAS;oBAClB,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,EAAE,GAAG,MAAM,CAAC,QAAQ,EAAE,EAAE,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE;iBACtF;aACF,CAAA;QACH,KAAK,OAAO;YACV,OAAO,UAAU,CAAC,KAAK,EAAE,MAAM,CAAC,UAAU,EAAE,SAAS,CAAC,CAAA;QACxD,KAAK,QAAQ;YACX,OAAO,UAAU,CAAC,KAAK,EAAE,MAAM,CAAC,UAAU,EAAE,UAAU,CAAC,CAAA;QACzD,KAAK,MAAM;YACT,OAAO,KAAK,CAAC,KAAK,KAAK,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,OAAO,EAAE,CAAA;QACrF,KAAK,aAAa;YAChB,OAAO,KAAK,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,CAAA;QACjE,KAAK,OAAO;YACV,OAAO,sBAAsB,CAAA;QAC/B;YACE,OAAO,KAAK,CAAA;IAChB,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAC/B,KAAmB,EACnB,MAA0E;IAE1E,OAAO;QACL,MAAM,EAAE;YACN,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,EAAE,EAAE,KAAK,CAAC,EAAE;YACZ,MAAM,EAAE,MAAM,EAAE,MAAM,IAAI,EAAE;YAC5B,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC9D;QACD,KAAK,EAAE;YACL,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,EAAE,EAAE,KAAK,CAAC,EAAE;YACZ,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC3D;KACF,CAAA;AACH,CAAC","sourcesContent":["/**\n * The chat panel's proposal-review state (EC-199, reshaped by EC-334).\n *\n * `useChat` (the AI SDK) owns the conversation: messages, streaming text, tool\n * parts, busy-status. This reducer keeps ONLY what the SDK does not own:\n *\n * - the proposal review map — the FULL proposals the terminal tools\n * (`createDocument` / `translateDocument`) produce, keyed by `toolCallId`\n * (the model receives only a compact summary as the tool output; the editor\n * reviews the real patch here), each `pending` → `applied` | `rejected`;\n * - the last blocking error (route/provider/step-cap failure), surfaced as a\n * visible note and cleared on the next submit.\n *\n * EC-380 (`clear`): a new conversation resets this state entirely — pending\n * proposals are discarded with REJECT semantics (they were never applied and\n * nothing half-applied survives; an applied draft already lives in the CMS and\n * is untouched). The panel pairs the dispatch with a fresh `useChat` id.\n *\n * House testing pattern (AGENTS.md): the panel's behaviour lives in this\n * deterministic reducer, tested directly; the React component is a thin shell.\n */\nimport type { CmsDocument, CmsValidationIssue, DocumentPatch, FieldValues } from '@elytracms/core/cms-core'\nimport type { AiProvenance, ContentPatch } from './types'\nimport type { TranslateSkip } from './translate-document'\n\n/**\n * A proposal's kind: fresh authoring, a fill-empty translation of an existing\n * doc, or an EC-378 field update of an existing doc (old → new diff).\n */\nexport type ProposalOrigin = 'create' | 'translate' | 'update'\n\n/**\n * The full proposal payload a terminal tool hands to the review UI (via\n * `onProposal` on the executor — see `tools.ts`). The model never sees this;\n * it gets a compact summary as the tool output.\n */\nexport interface ChatProposal {\n origin: ProposalOrigin\n collection: string\n documentId: string\n patch: ContentPatch\n provenance: AiProvenance\n /** Generation attempts made (1 = happy path, >1 = repaired). */\n attempts: number\n /** Non-error issues on the accepted content (warnings). */\n issues: CmsValidationIssue[]\n /** For a translation/update: the existing document values the diff renders \"before\". */\n before?: { values?: FieldValues; localized?: Record<string, FieldValues> }\n /**\n * EC-595 A2 — for an update or a translation: the positions it changes, as patches\n * (a field's bucket, a block prop by node id). Applying the proposal persists exactly\n * these through `documents.patch` — never the merged whole document, which would\n * revert any change made since the proposal was built. Absent for a create.\n */\n patches?: readonly DocumentPatch[]\n /**\n * EC-647 — for an update or a translation: the revision of the read the proposal was\n * built from. Applying it writes {@link patches} AT this revision, so a position changed\n * since (by anyone else) is refused on that field, never overwritten. Absent → the apply\n * names no revision and is refused.\n */\n revision?: number\n /**\n * EC-593 (ratified 4e) — for a translation: every text position it could NOT\n * translate, shown on the card the editor reviews (never a silent skip).\n */\n skipped?: readonly TranslateSkip[]\n}\n\n/** A proposal under review: {@link ChatProposal} + its review status. */\nexport interface ProposalReview extends ChatProposal {\n /** The tool call that produced it (`toolCallId`) — the review key. */\n id: string\n status: 'pending' | 'applied' | 'rejected'\n}\n\nexport interface ChatReviewState {\n /** Proposals keyed by `toolCallId`. */\n proposals: Record<string, ProposalReview>\n /** Last blocking error (route/provider/step-cap), surfaced then cleared on submit. */\n error: string | null\n}\n\nexport const initialChatReviewState: ChatReviewState = {\n proposals: {},\n error: null,\n}\n\nexport type ChatReviewAction =\n | { type: 'propose'; toolCallId: string; proposal: ChatProposal }\n | { type: 'apply'; toolCallId: string }\n | { type: 'reject'; toolCallId: string }\n | { type: 'fail'; message: string }\n | { type: 'clear-error' }\n /** EC-380: new conversation — full reset; pending proposals discarded (reject semantics). */\n | { type: 'clear' }\n\nfunction transition(\n state: ChatReviewState,\n toolCallId: string,\n status: 'applied' | 'rejected',\n): ChatReviewState {\n const existing = state.proposals[toolCallId]\n // Only a PENDING proposal transitions — apply/reject are one-shot.\n if (!existing || existing.status !== 'pending') return state\n return {\n ...state,\n proposals: { ...state.proposals, [toolCallId]: { ...existing, status } },\n }\n}\n\nexport function chatReviewReducer(\n state: ChatReviewState,\n action: ChatReviewAction,\n): ChatReviewState {\n switch (action.type) {\n case 'propose':\n return {\n ...state,\n proposals: {\n ...state.proposals,\n [action.toolCallId]: { ...action.proposal, id: action.toolCallId, status: 'pending' },\n },\n }\n case 'apply':\n return transition(state, action.toolCallId, 'applied')\n case 'reject':\n return transition(state, action.toolCallId, 'rejected')\n case 'fail':\n return state.error === action.message ? state : { ...state, error: action.message }\n case 'clear-error':\n return state.error === null ? state : { ...state, error: null }\n case 'clear':\n return initialChatReviewState\n default:\n return state\n }\n}\n\n/**\n * Turn a proposal patch into the before/after document pair the EC-317 diff\n * engine (`diffDocuments`) compares. For a fresh document `before` is empty\n * (the whole proposal reads as \"added\"); for a translation pass the existing\n * document's values. Pure — the reviewable diff is a straight\n * `diffDocuments(collection, before, after)`.\n */\nexport function proposalDocuments(\n patch: ContentPatch,\n before?: { values?: FieldValues; localized?: Record<string, FieldValues> },\n): { before: CmsDocument; after: CmsDocument } {\n return {\n before: {\n collection: patch.collection,\n id: patch.id,\n values: before?.values ?? {},\n ...(before?.localized ? { localized: before.localized } : {}),\n },\n after: {\n collection: patch.collection,\n id: patch.id,\n values: patch.values,\n ...(patch.localized ? { localized: patch.localized } : {}),\n },\n }\n}\n"]}
|
package/dist/lib/ai/tools.d.ts
CHANGED
|
@@ -68,6 +68,14 @@ export interface ChatToolReaders {
|
|
|
68
68
|
collection: string;
|
|
69
69
|
id: string;
|
|
70
70
|
}): Promise<CmsDocument | null>;
|
|
71
|
+
/**
|
|
72
|
+
* EC-647 — the revision a document {@link getDocument} returned was read at (the
|
|
73
|
+
* returned value is the token). An update or a translation records it on its proposal:
|
|
74
|
+
* applying the proposal writes AT that revision, so a change made since on one of its
|
|
75
|
+
* positions is refused, never overwritten. Optional: absent, a proposal names no
|
|
76
|
+
* revision and its apply is refused.
|
|
77
|
+
*/
|
|
78
|
+
revisionOf?(document: CmsDocument): number | undefined;
|
|
71
79
|
}
|
|
72
80
|
/** Per-search result cap (protects the wire — results become model context). */
|
|
73
81
|
export declare const DEFAULT_SEARCH_LIMIT = 20;
|
package/dist/lib/ai/tools.js
CHANGED
|
@@ -381,12 +381,14 @@ export function createChatToolExecutor(options) {
|
|
|
381
381
|
},
|
|
382
382
|
};
|
|
383
383
|
}
|
|
384
|
+
const revision = options.readers.revisionOf?.(document);
|
|
384
385
|
options.onProposal(toolCallId, {
|
|
385
386
|
origin: 'update',
|
|
386
387
|
collection: collection.id,
|
|
387
388
|
documentId: document.id,
|
|
388
389
|
patch: result.patch,
|
|
389
390
|
patches: result.patches,
|
|
391
|
+
...(revision !== undefined ? { revision } : {}),
|
|
390
392
|
provenance: aiProvenance(options.provider),
|
|
391
393
|
attempts: 1,
|
|
392
394
|
issues: result.issues,
|
|
@@ -519,6 +521,7 @@ export function createChatToolExecutor(options) {
|
|
|
519
521
|
errorText: `The translation did not pass validation: ${issueSummary(result.introducedErrors)}`,
|
|
520
522
|
};
|
|
521
523
|
}
|
|
524
|
+
const revision = options.readers.revisionOf?.(document);
|
|
522
525
|
options.onProposal(toolCallId, {
|
|
523
526
|
origin: 'translate',
|
|
524
527
|
collection: collection.id,
|
|
@@ -530,6 +533,7 @@ export function createChatToolExecutor(options) {
|
|
|
530
533
|
...(result.document.localized ? { localized: result.document.localized } : {}),
|
|
531
534
|
},
|
|
532
535
|
patches: result.patches,
|
|
536
|
+
...(revision !== undefined ? { revision } : {}),
|
|
533
537
|
provenance: result.provenance,
|
|
534
538
|
attempts: 1,
|
|
535
539
|
// Warnings ride onto the proposal like every other path's (EC-496 renders them).
|