@elytracms/studio 0.0.15 → 0.0.17
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/dist/components/app-views/default-modules.d.ts +41 -0
- package/dist/components/app-views/default-modules.js +201 -0
- package/dist/components/app-views/default-modules.js.map +1 -0
- package/dist/components/app-views/module.d.ts +93 -0
- package/dist/components/app-views/module.js +75 -0
- package/dist/components/app-views/module.js.map +1 -0
- package/dist/components/app-views/sidebar.d.ts +95 -0
- package/dist/components/app-views/sidebar.js +153 -0
- package/dist/components/app-views/sidebar.js.map +1 -0
- package/dist/components/builder/builder-store.d.ts +7 -1
- package/dist/components/builder/builder-store.js +1 -1
- package/dist/components/builder/builder-store.js.map +1 -1
- package/dist/components/builder/canvas-breakpoints.d.ts +53 -0
- package/dist/components/builder/canvas-breakpoints.js +55 -0
- package/dist/components/builder/canvas-breakpoints.js.map +1 -0
- package/dist/components/builder/canvas.js +14 -8
- package/dist/components/builder/canvas.js.map +1 -1
- package/dist/components/builder/composition-field-editor.js +18 -17
- package/dist/components/builder/composition-field-editor.js.map +1 -1
- package/dist/components/cms/bulk-actions.d.ts +23 -5
- package/dist/components/cms/bulk-actions.js +7 -3
- package/dist/components/cms/bulk-actions.js.map +1 -1
- package/dist/components/cms/bulk-bar.d.ts +22 -6
- package/dist/components/cms/bulk-bar.js +46 -9
- package/dist/components/cms/bulk-bar.js.map +1 -1
- package/dist/components/cms/cms-index.d.ts +4 -2
- package/dist/components/cms/cms-index.js +6 -4
- package/dist/components/cms/cms-index.js.map +1 -1
- package/dist/components/cms/cms-store.d.ts +6 -5
- package/dist/components/cms/cms-store.js +51 -9
- package/dist/components/cms/cms-store.js.map +1 -1
- package/dist/components/cms/collection-table.js +7 -3
- package/dist/components/cms/collection-table.js.map +1 -1
- package/dist/components/cms/collections-sidebar.d.ts +10 -1
- package/dist/components/cms/collections-sidebar.js +13 -2
- package/dist/components/cms/collections-sidebar.js.map +1 -1
- package/dist/components/cms/field-control.d.ts +11 -2
- package/dist/components/cms/field-control.js +22 -1
- package/dist/components/cms/field-control.js.map +1 -1
- package/dist/components/cms/field-renderer.d.ts +71 -0
- package/dist/components/cms/field-renderer.js +67 -0
- package/dist/components/cms/field-renderer.js.map +1 -0
- package/dist/components/cms/filter-builder.js +6 -28
- package/dist/components/cms/filter-builder.js.map +1 -1
- package/dist/components/cms/scalar-value-editor.d.ts +35 -0
- package/dist/components/cms/scalar-value-editor.js +43 -0
- package/dist/components/cms/scalar-value-editor.js.map +1 -0
- package/dist/components/project-session.js +8 -3
- package/dist/components/project-session.js.map +1 -1
- package/dist/components/section-nav.d.ts +10 -10
- package/dist/components/section-nav.js +48 -25
- package/dist/components/section-nav.js.map +1 -1
- package/dist/components/section-surface.js +2 -3
- package/dist/components/section-surface.js.map +1 -1
- package/dist/components/studio-project-screen.d.ts +3 -4
- package/dist/components/studio-project-screen.js +29 -147
- package/dist/components/studio-project-screen.js.map +1 -1
- package/dist/components/studio-shell.js +13 -9
- package/dist/components/studio-shell.js.map +1 -1
- package/dist/lib/data/collection-content.d.ts +19 -1
- package/dist/lib/data/collection-content.js +16 -0
- package/dist/lib/data/collection-content.js.map +1 -1
- package/dist/lib/workspace/index.d.ts +27 -1
- package/dist/lib/workspace/index.js +34 -0
- package/dist/lib/workspace/index.js.map +1 -1
- package/dist/router/match.d.ts +22 -34
- package/dist/router/match.js +22 -40
- package/dist/router/match.js.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"canvas.js","sourceRoot":"","sources":["../../../src/components/builder/canvas.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport { createPortal } from 'react-dom'\nimport { renderPageComposition } from '@elytracms/core/runtime-renderer'\nimport type {\n RenderContext,\n ResolveAsset,\n ResolveBinding,\n ResolveListing,\n ResolveRelation,\n} from '@elytracms/core/runtime-renderer'\nimport type { ComponentNode, JsonValue } from '@elytracms/core/project-graph'\nimport { useParams } from '@elytracms/studio/router'\nimport { getNode, getParentInfo, staticProp } from '@elytracms/core/project-graph'\nimport {\n canNestInto,\n containerFlowAxis,\n dropIndicatorFor,\n dropPositionForPoint,\n resolveDrop,\n} from '@elytracms/studio/lib/dnd/drop-rules'\nimport type { DropPlan, DropPosition, FlowAxis } from '@elytracms/studio/lib/dnd/drop-rules'\nimport { activeDrag, beginDrag, endDrag, onDragSessionEnd } from './drag-session'\nimport {\n PRESERVE_NODE_ATTR,\n collectPreservedPairs,\n richTextDocFromDom,\n} from '@elytracms/core/rich-text'\nimport type { RtDomElement } from '@elytracms/core/rich-text'\nimport { getStudioRegistry } from '@elytracms/studio/lib/registry'\nimport { useOptionalAssets } from '../assets/assets-store'\nimport { resolveCanvasListing, resolveCanvasRelation, useData } from '@elytracms/studio/lib/data'\nimport { cmsBindingFallback, useStudioLocale } from '@elytracms/studio/lib/locale'\nimport { getInstantiatedKit } from '@elytracms/studio/lib/starters/kit-session'\nimport { getCanvasStylesheetUrl } from './canvas-stylesheet'\nimport { useBuilder } from './builder-store'\nimport type { Viewport } from './builder-store'\nimport {\n ClipboardNoticeOverlay,\n attachClipboardListeners,\n useStudioClipboard,\n} from './clipboard-actions'\nimport {\n createInlineEditSession,\n createInlineRichTextSession,\n findInlineTextHost,\n resolveInlineEditableProp,\n resolveInlineRichTextProp,\n} from './inline-edit'\nimport type { InlineEditSession, InlineRichTextProp } from './inline-edit'\nimport { RichTextToolbar } from './rich-text-toolbar'\nimport { SpacingHandles, hasSpacingHandles } from './spacing-handles'\n\nconst VIEWPORT_WIDTH: Record<Viewport, string> = {\n desktop: '100%',\n tablet: '768px',\n mobile: '390px',\n}\n\n/** Selection/hover outlines, scoped to the per-node editable wrappers. */\nconst OUTLINE_CSS = `\n html { color-scheme: dark; }\n body { margin: 0; padding: 16px; }\n .ec-editable { outline-offset: -1px; cursor: default; }\n .ec-editable[data-hovered]:not([data-selected]) { outline: 1px dashed rgba(130,150,190,0.75); }\n .ec-editable[data-selected] { outline: 2px solid #5b8cff; }\n /* Inline editing (EC-158 text, EC-159 rich text): the rendered element\n itself becomes the editor — no overlay, no replacement input — so\n typography, alignment, and layout are pixel-identical between edit and\n render states. The wrapper keeps the selection outline; the host only\n gets a caret. pre-wrap applies to PLAIN text hosts only — rich-text\n markup must keep its rendered whitespace handling (no layout shift). */\n .ec-editable[data-ec-editing] { outline: 2px solid #5b8cff; }\n .ec-editable [data-ec-edit] { outline: none; cursor: text; }\n .ec-editable [data-ec-edit='text'] { white-space: pre-wrap; }\n /* Preserved atoms during a rich-text session (custom blocks, unknown nodes,\n images, embeds): visible and rendered, but not text-editable. */\n .ec-editable [data-ec-edit='rich'] [${PRESERVE_NODE_ATTR}] { user-select: none; }\n /* Floating mark toolbar (EC-159), portaled into the iframe. */\n .ec-rt-toolbar { color-scheme: dark; }\n .ec-rt-toolbar-row { display: flex; align-items: center; gap: 2px; padding: 3px;\n border: 1px solid rgba(130,150,190,0.4); border-radius: 8px;\n background: rgb(24,26,32); box-shadow: 0 4px 16px rgba(0,0,0,0.4); }\n .ec-rt-toolbar-btn { min-width: 26px; height: 24px; padding: 0 7px; border: 0; border-radius: 5px;\n background: transparent; color: rgb(225,228,236); font: 600 12px/1 ui-sans-serif, system-ui, sans-serif;\n cursor: pointer; }\n .ec-rt-toolbar-btn:hover { background: rgba(130,150,190,0.25); }\n .ec-rt-toolbar-btn[data-active] { background: #5b8cff; color: white; }\n .ec-rt-toolbar-input { width: 200px; height: 24px; padding: 0 7px; border-radius: 5px;\n border: 1px solid rgba(130,150,190,0.4); background: rgb(16,18,22); color: rgb(225,228,236);\n font: 12px/1 ui-sans-serif, system-ui, sans-serif; outline: none; }\n /* Locale-fallback chips (EC-176): a node whose bound content is currently\n served via locale fallback wears a small corner chip naming the locale it\n actually came from — the canvas never silently mixes locales. */\n .ec-editable[data-ec-locale-fallback] { position: relative; }\n .ec-editable[data-ec-locale-fallback]::after {\n content: attr(data-ec-locale-fallback);\n position: absolute; top: 2px; right: 2px; z-index: 5; pointer-events: none;\n padding: 0 4px; border-radius: 4px;\n font: 600 9px/1.5 ui-sans-serif, system-ui, sans-serif;\n letter-spacing: 0.05em; text-transform: uppercase;\n color: #fbbf24; background: rgba(180,83,9,0.25); border: 1px solid rgba(245,158,11,0.45);\n }\n /* Drag & drop states (EC-160/EC-162): eligibility comes from the shared pure\n rules (lib/dnd/drop-rules); these classes only *show* the resolution.\n Rejection is explicit and visible during the drag — never a silent failed\n drop. Before/after bars follow the parent's flow axis (data-ec-drop-axis);\n left/right are the wrap-into-columns gesture (violet, paired highlight). */\n .ec-editable[data-ec-drop='into'] { outline: 2px solid #34d399; outline-offset: -2px; background-color: rgba(52, 211, 153, 0.08); }\n .ec-editable[data-ec-drop='before'] { box-shadow: 0 -3px 0 0 #38bdf8; }\n .ec-editable[data-ec-drop='after'] { box-shadow: 0 3px 0 0 #38bdf8; }\n .ec-editable[data-ec-drop='before'][data-ec-drop-axis='x'] { box-shadow: -3px 0 0 0 #38bdf8; }\n .ec-editable[data-ec-drop='after'][data-ec-drop-axis='x'] { box-shadow: 3px 0 0 0 #38bdf8; }\n .ec-editable[data-ec-drop='left'] { box-shadow: -3px 0 0 0 #a78bfa; background-color: rgba(167, 139, 250, 0.1); }\n .ec-editable[data-ec-drop='right'] { box-shadow: 3px 0 0 0 #a78bfa; background-color: rgba(167, 139, 250, 0.1); }\n .ec-editable[data-ec-drop='blocked'] { outline: 2px solid #ef4444; outline-offset: -2px; background-color: rgba(239, 68, 68, 0.08); cursor: not-allowed; }\n`\n\nfunction nodeIdAt(target: EventTarget | null): string | null {\n return wrapperAt(target)?.getAttribute('data-ec-node-id') ?? null\n}\n\nfunction wrapperAt(target: EventTarget | null): HTMLElement | null {\n const el = target as HTMLElement | null\n return (el?.closest?.('[data-ec-node-id]') as HTMLElement | null) ?? null\n}\n\n/** A live inline-editing session bound to its DOM host (EC-158/EC-159). */\ninterface ActiveInlineEdit {\n nodeId: string\n host: HTMLElement\n /** Finish the session exactly once (later calls are no-ops). */\n finish: (mode: 'commit' | 'cancel') => void\n}\n\n/**\n * The rendered `.rich-text` element inside a node's wrapper — the rich-text\n * editing host (EC-159). Never crosses into a nested node's wrapper.\n */\nfunction findRichTextHost(wrapper: HTMLElement): HTMLElement | null {\n const host = wrapper.querySelector('.rich-text') as HTMLElement | null\n return host && host.closest('[data-ec-node-id]') === wrapper ? host : null\n}\n\n/** Collect every node id under a root (the page subtree). */\nfunction collectIds(node: ComponentNode, out: Set<string> = new Set()): Set<string> {\n out.add(node.id)\n for (const children of Object.values(node.slots ?? {})) {\n for (const child of children) collectIds(child, out)\n }\n return out\n}\n\n/**\n * Iframe-isolated page preview (brief §8.2, EC-061, EC-064). The graph renders\n * into a same-origin iframe via a React portal — true style isolation + true\n * responsive widths. Each node is wrapped with an editable marker so clicks\n * select and hover/selection draw outlines. Native listeners bridge the iframe\n * boundary (React's synthetic events don't cross frames).\n */\nexport function BuilderCanvas() {\n const builder = useBuilder()\n const { dispatch } = builder\n const studio = getStudioRegistry()\n const data = useData()\n\n // The edited composition root, composed for preview. EC-287 retired the project\n // graph and its published-graph preview; the canvas always renders the live,\n // editable draft (content publishing is per document).\n const activeRoot: ComponentNode = builder.present\n const iframeRef = useRef<HTMLIFrameElement>(null)\n const [doc, setDoc] = useState<Document | null>(null)\n\n // Inline editing (EC-158/EC-159): the active session lives in a ref — it is\n // imperative DOM state inside the iframe, not render state. Graph/page refs\n // let the native dblclick handler read the *current* graph without\n // re-subscribing listeners on every edit. The rich-text host additionally\n // mirrors into render state so the floating mark toolbar can mount.\n const editingRef = useRef<ActiveInlineEdit | null>(null)\n const [richEditHost, setRichEditHost] = useState<HTMLElement | null>(null)\n // A rich-text session mutates the rendered DOM behind React's back\n // (contenteditable + mark commands), so finishing one must REMOUNT the\n // node's subtree — bumping its epoch changes the decorate key, React\n // discards the edited DOM wholesale and re-renders from the graph (the\n // committed value, or the untouched original on cancel). Patching the old\n // fiber tree against editor-mutated DOM would leave stale artifacts.\n const [richEditEpochs, setRichEditEpochs] = useState<Record<string, number>>({})\n const presentRef = useRef(builder.present)\n presentRef.current = builder.present\n const selectedIdRef = useRef(builder.selectedId)\n selectedIdRef.current = builder.selectedId\n\n // When the project was created from a starter kit, its brand token palette drives the canvas UI\n // (EC-117) — injected after app.css so the kit's overrides win in the cascade.\n const params = useParams({ strict: false }) as { projectId?: string }\n const kitTokensCss = params.projectId ? getInstantiatedKit(params.projectId)?.tokensCss : undefined\n\n const wireUp = useCallback(() => {\n const d = iframeRef.current?.contentDocument\n if (!d || !d.body) return\n d.documentElement.classList.add('dark')\n const appCss = getCanvasStylesheetUrl()\n if (appCss && !d.getElementById('ec-app-css')) {\n const link = d.createElement('link')\n link.id = 'ec-app-css'\n link.rel = 'stylesheet'\n link.href = appCss\n d.head.appendChild(link)\n }\n if (!d.getElementById('ec-outline-css')) {\n const style = d.createElement('style')\n style.id = 'ec-outline-css'\n style.textContent = OUTLINE_CSS\n d.head.appendChild(style)\n }\n if (kitTokensCss) {\n const existing = d.getElementById('ec-kit-tokens')\n if (existing) existing.remove()\n const style = d.createElement('style')\n style.id = 'ec-kit-tokens'\n style.textContent = kitTokensCss\n d.head.appendChild(style)\n }\n d.body.className = 'bg-background text-foreground font-sans'\n setDoc(d)\n }, [kitTokensCss])\n\n // Handle the already-loaded case (srcDoc may load before onLoad attaches).\n useEffect(() => {\n if (iframeRef.current?.contentDocument?.readyState === 'complete') wireUp()\n }, [wireUp])\n\n // Native selection/hover listeners inside the iframe document.\n useEffect(() => {\n if (!doc) return\n const onClick = (e: Event) => {\n // Clicks inside the active inline editor are caret moves, not selection.\n if (editingRef.current?.host.contains(e.target as Node)) return\n const id = nodeIdAt(e.target)\n if (id) {\n e.preventDefault()\n dispatch({ type: 'select', id })\n }\n }\n const onOver = (e: Event) => dispatch({ type: 'hover', id: nodeIdAt(e.target) })\n const onLeave = () => dispatch({ type: 'hover', id: null })\n doc.addEventListener('click', onClick, true)\n doc.addEventListener('mouseover', onOver)\n doc.addEventListener('mouseleave', onLeave, true)\n return () => {\n doc.removeEventListener('click', onClick, true)\n doc.removeEventListener('mouseover', onOver)\n doc.removeEventListener('mouseleave', onLeave, true)\n }\n }, [doc, dispatch])\n\n // Drag & drop on the canvas (EC-160). Eligibility is the pure shared rule\n // set (`lib/dnd/drop-rules`) — the same functions the page tree consumes —\n // evaluated live during `dragover`; the wrapper under the pointer shows the\n // resolution (`data-ec-drop`: into/before/after, or `blocked` for a visible\n // rejection). A completed drop dispatches exactly one builder action (move\n // or insert) → one operations command → one undo step. Ineligible targets\n // never `preventDefault`, so releasing over one is a native no-op.\n useEffect(() => {\n if (!doc) return\n const getManifest = (id: string) => studio.registry.getManifest(id)\n const currentRoot = () => presentRef.current\n\n let dropWrapper: HTMLElement | null = null\n let dropPlan: DropPlan | null = null\n const clearIndicator = () => {\n dropWrapper?.removeAttribute('data-ec-drop')\n dropWrapper?.removeAttribute('data-ec-drop-axis')\n dropWrapper = null\n dropPlan = null\n }\n const showIndicator = (wrapper: HTMLElement | null, state: string | null, axis: FlowAxis) => {\n if (dropWrapper && dropWrapper !== wrapper) {\n dropWrapper.removeAttribute('data-ec-drop')\n dropWrapper.removeAttribute('data-ec-drop-axis')\n }\n dropWrapper = wrapper\n if (!wrapper) return\n if (state) {\n wrapper.setAttribute('data-ec-drop', state)\n // Before/after bars render along the parent's flow axis (EC-162).\n wrapper.setAttribute('data-ec-drop-axis', axis === 'horizontal' ? 'x' : 'y')\n } else {\n wrapper.removeAttribute('data-ec-drop')\n wrapper.removeAttribute('data-ec-drop-axis')\n }\n }\n\n // Wrappers are armed draggable per-gesture on mousedown, never via render\n // state: the active inline editor (EC-158) must keep native caret clicks\n // and text selection, so a press inside the editing host disarms its\n // wrapper instead of starting a node drag.\n const onMouseDown = (e: Event) => {\n const wrapper = wrapperAt(e.target)\n if (!wrapper) return\n const root = currentRoot()\n const nodeId = wrapper.getAttribute('data-ec-node-id')\n const inEditor = editingRef.current?.host.contains(e.target as Node) ?? false\n if (root && nodeId && nodeId !== root.id && !inEditor) {\n wrapper.setAttribute('draggable', 'true')\n } else {\n wrapper.removeAttribute('draggable')\n }\n }\n\n const onDragStart = (e: DragEvent) => {\n // Drags inside the live inline editor are native text drags — leave them alone.\n if (editingRef.current?.host.contains(e.target as Node)) return\n const wrapper = wrapperAt(e.target)\n if (!wrapper) return\n const root = currentRoot()\n const nodeId = wrapper.getAttribute('data-ec-node-id')!\n const node = root ? getNode(root, nodeId) : undefined\n if (!root || !node || nodeId === root.id) {\n e.preventDefault()\n return\n }\n beginDrag(\n { subject: { kind: 'node', nodeId, componentId: node.componentId }, source: 'canvas', label: node.componentId },\n e.dataTransfer,\n )\n }\n\n const onDragOver = (e: DragEvent) => {\n const drag = activeDrag()\n if (!drag) return\n const root = currentRoot()\n if (!root) return\n const wrapper = wrapperAt(e.target)\n // Empty space around the page subtree targets the root slot (append).\n const refId = wrapper?.getAttribute('data-ec-node-id') ?? root.id\n const refWrapper =\n wrapper ?? (doc.querySelector(`[data-ec-node-id=\"${root.id}\"]`) as HTMLElement | null)\n const refNode = getNode(root, refId)\n if (!refNode) {\n clearIndicator()\n return\n }\n let position: DropPosition = 'into'\n let flow: FlowAxis = 'vertical'\n if (wrapper && refId !== root.id) {\n const rect = wrapper.getBoundingClientRect()\n // Hit-bands follow the parent's flow axis (EC-162); the cross-axis\n // side bands of vertically flowing targets are the wrap gesture.\n flow = containerFlowAxis(getParentInfo(root, refId)?.parent)\n position = dropPositionForPoint({\n x: rect.width > 0 ? (e.clientX - rect.left) / rect.width : 0.5,\n y: rect.height > 0 ? (e.clientY - rect.top) / rect.height : 0.5,\n flow,\n nestable: canNestInto(getManifest, refNode.componentId),\n edges: true,\n wrappable: true,\n })\n }\n const resolution = resolveDrop({\n root,\n getManifest,\n subject: drag.subject,\n location: { nodeId: refId, position },\n })\n showIndicator(refWrapper, dropIndicatorFor(resolution, position), flow)\n if (resolution.eligible) {\n dropPlan = resolution\n e.preventDefault()\n if (e.dataTransfer) {\n e.dataTransfer.dropEffect = drag.subject.kind === 'node' ? 'move' : 'copy'\n }\n } else {\n dropPlan = null\n }\n }\n\n const onDrop = (e: DragEvent) => {\n const drag = activeDrag()\n if (!drag) return\n const plan = dropPlan\n clearIndicator()\n if (!plan) return\n e.preventDefault()\n // Exactly one dispatch per completed drop (one undo step, one command).\n if (plan.kind === 'move') {\n dispatch({ type: 'move', id: plan.nodeId, target: plan.target })\n } else if (plan.kind === 'wrap') {\n // EC-162 gesture: reference + subject become a two-column Grid.\n const subject =\n plan.nodeId !== undefined\n ? { kind: 'node' as const, nodeId: plan.nodeId }\n : drag.buildNode\n ? { kind: 'component' as const, node: drag.buildNode() }\n : null\n if (!subject) {\n endDrag('cancelled')\n return\n }\n dispatch({ type: 'wrap', referenceId: plan.referenceId, side: plan.side, subject })\n } else if (drag.buildNode) {\n dispatch({ type: 'insert', target: plan.target, node: drag.buildNode() })\n } else {\n endDrag('cancelled')\n return\n }\n endDrag('dropped')\n }\n\n const onDragEnd = () => {\n clearIndicator()\n endDrag('cancelled')\n }\n\n const onDragLeave = (e: DragEvent) => {\n // Leaving the iframe document entirely (relatedTarget is null).\n if (!e.relatedTarget) clearIndicator()\n }\n\n // Cleanup when the drag ends anywhere (e.g. dropped on the page tree).\n const unsubscribe = onDragSessionEnd(() => clearIndicator())\n\n doc.addEventListener('mousedown', onMouseDown, true)\n doc.addEventListener('dragstart', onDragStart)\n doc.addEventListener('dragover', onDragOver)\n doc.addEventListener('drop', onDrop)\n doc.addEventListener('dragend', onDragEnd)\n doc.addEventListener('dragleave', onDragLeave)\n return () => {\n unsubscribe()\n clearIndicator()\n doc.removeEventListener('mousedown', onMouseDown, true)\n doc.removeEventListener('dragstart', onDragStart)\n doc.removeEventListener('dragover', onDragOver)\n doc.removeEventListener('drop', onDrop)\n doc.removeEventListener('dragend', onDragEnd)\n doc.removeEventListener('dragleave', onDragLeave)\n }\n }, [doc, dispatch, studio])\n\n // Inline editing (EC-158 plain text, EC-159 rich text): double-click on a\n // node whose manifest flags an inline-editable prop turns the *rendered\n // element itself* contenteditable — no overlay, no input swap, so typography\n // and layout are identical in edit and render states. A session finishes\n // exactly once, and a commit dispatches exactly one `setProp` action — one\n // undo step, mirrored as one `graph.setNodeProp` operations command by the\n // builder-store persister. The graph stays the single source of truth.\n //\n // Commit/cancel gestures: plain text commits on Enter or blur; rich text is\n // multiline, so plain Enter inserts a paragraph and the commit gesture is\n // Cmd/Ctrl+Enter or blur (Enter-to-commit would make multi-paragraph content\n // unauthorable in place). Esc always cancels, dispatching nothing.\n useEffect(() => {\n if (!doc) return\n\n // ---- plain text (EC-158) ------------------------------------------------\n const beginText = (wrapper: HTMLElement, host: HTMLElement, session: InlineEditSession) => {\n let done = false\n const finish = (mode: 'commit' | 'cancel') => {\n if (done) return\n done = true\n editingRef.current = null\n const text = host.textContent ?? ''\n host.removeEventListener('keydown', onKeyDown)\n host.removeEventListener('blur', onBlur)\n host.removeAttribute('contenteditable')\n host.removeAttribute('data-ec-edit')\n wrapper.removeAttribute('data-ec-editing')\n if (mode === 'commit') {\n const outcome = session.commit(text)\n if (outcome) {\n dispatch({\n type: 'setProp',\n id: outcome.nodeId,\n name: outcome.propName,\n value: staticProp(outcome.value),\n })\n }\n } else {\n const outcome = session.cancel()\n if (outcome) host.textContent = outcome.value\n }\n doc.getSelection()?.removeAllRanges()\n }\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Enter') {\n e.preventDefault()\n e.stopPropagation()\n finish('commit')\n } else if (e.key === 'Escape') {\n e.preventDefault()\n e.stopPropagation()\n finish('cancel')\n }\n }\n const onBlur = () => finish('commit')\n // `plaintext-only` keeps paste/typing plain; fall back where unsupported.\n host.setAttribute('contenteditable', 'plaintext-only')\n if (!host.isContentEditable) host.setAttribute('contenteditable', 'true')\n host.setAttribute('data-ec-edit', 'text')\n wrapper.setAttribute('data-ec-editing', '')\n host.addEventListener('keydown', onKeyDown)\n host.addEventListener('blur', onBlur)\n editingRef.current = { nodeId: session.nodeId, host, finish }\n host.focus({ preventScroll: true })\n // Keep the gesture's native caret/word selection when it landed inside\n // the host; otherwise select the whole value.\n const sel = doc.getSelection()\n if (!sel || sel.rangeCount === 0 || !host.contains(sel.anchorNode)) {\n const range = doc.createRange()\n range.selectNodeContents(host)\n sel?.removeAllRanges()\n sel?.addRange(range)\n }\n }\n\n // ---- rich text (EC-159) -------------------------------------------------\n const beginRichText = (wrapper: HTMLElement, nodeId: string, target: InlineRichTextProp) => {\n const host = findRichTextHost(wrapper)\n if (!host) return\n // Pair the storage tree against the rendered DOM and stamp every\n // non-invertible node (custom blocks, unknown nodes, images, embeds)\n // with its exact JSON: they stay rendered *inside* the live editor as\n // non-editable atoms and survive the session byte-exact. A pairing\n // mismatch means the DOM diverged from the deterministic render —\n // refuse to start rather than risk lossy parsing (\"polished or absent\").\n const pairs = collectPreservedPairs(target.value.doc, host as unknown as RtDomElement)\n if (!pairs) return\n const session = createInlineRichTextSession({\n nodeId,\n propName: target.propName,\n original: target.value,\n })\n for (const pair of pairs) {\n const el = pair.element as unknown as HTMLElement\n el.setAttribute(PRESERVE_NODE_ATTR, JSON.stringify(pair.node))\n el.setAttribute('contenteditable', 'false')\n }\n let done = false\n const finish = (mode: 'commit' | 'cancel') => {\n if (done) return\n done = true\n editingRef.current = null\n setRichEditHost(null)\n // Parse the edited DOM, detach the session, then bump the node's\n // remount epoch: React discards the editor-mutated DOM and renders\n // fresh from the graph — committed content or, on cancel, the\n // untouched original. Never patch a fiber tree against DOM the\n // editing commands rewrote.\n const parsed = mode === 'commit' ? richTextDocFromDom(host as unknown as RtDomElement) : null\n host.removeEventListener('keydown', onKeyDown)\n host.removeEventListener('blur', onBlur)\n host.removeAttribute('contenteditable')\n host.removeAttribute('data-ec-edit')\n wrapper.removeAttribute('data-ec-editing')\n setRichEditEpochs((prev) => ({ ...prev, [nodeId]: (prev[nodeId] ?? 0) + 1 }))\n if (parsed) {\n // An unchanged document commits nothing: the stored value is never\n // rewritten, so untouched content round-trips byte-identical.\n const outcome = session.commit(parsed.doc)\n if (outcome) {\n dispatch({\n type: 'setProp',\n id: outcome.nodeId,\n name: outcome.propName,\n value: staticProp(outcome.value as unknown as JsonValue),\n })\n }\n } else {\n session.cancel()\n }\n doc.getSelection()?.removeAllRanges()\n }\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n e.preventDefault()\n e.stopPropagation()\n finish('cancel')\n } else if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {\n e.preventDefault()\n e.stopPropagation()\n finish('commit')\n }\n // Plain Enter falls through: native paragraph insertion (multiline).\n }\n const onBlur = (e: FocusEvent) => {\n // Interacting with the floating toolbar is part of the session.\n const next = e.relatedTarget as HTMLElement | null\n if (next?.closest?.('[data-ec-rt-toolbar]')) return\n finish('commit')\n }\n host.setAttribute('contenteditable', 'true')\n host.setAttribute('data-ec-edit', 'rich')\n wrapper.setAttribute('data-ec-editing', '')\n host.addEventListener('keydown', onKeyDown)\n host.addEventListener('blur', onBlur)\n editingRef.current = { nodeId, host, finish }\n setRichEditHost(host)\n host.focus({ preventScroll: true })\n }\n\n const onDblClick = (e: Event) => {\n const wrapper = wrapperAt(e.target)\n if (!wrapper) return\n const nodeId = wrapper.getAttribute('data-ec-node-id')!\n if (editingRef.current) {\n // Double-click inside the live editor is native word selection.\n if (editingRef.current.nodeId === nodeId) return\n editingRef.current.finish('commit')\n }\n const node = getNode(presentRef.current, nodeId)\n if (!node) return\n const manifest = studio.registry.getManifest(node.componentId)\n const target = resolveInlineEditableProp(manifest, node)\n if (target) {\n const host = findInlineTextHost(wrapper, target.value) as HTMLElement | null\n if (!host || typeof host.focus !== 'function') return\n dispatch({ type: 'select', id: nodeId })\n beginText(\n wrapper,\n host,\n createInlineEditSession({ nodeId, propName: target.propName, original: target.value }),\n )\n return\n }\n const richTarget = resolveInlineRichTextProp(manifest, node)\n if (!richTarget) return\n dispatch({ type: 'select', id: nodeId })\n beginRichText(wrapper, nodeId, richTarget)\n }\n\n doc.addEventListener('dblclick', onDblClick, true)\n return () => {\n doc.removeEventListener('dblclick', onDblClick, true)\n // Teardown mid-session (doc swap/unmount): cancel — never dispatch from cleanup.\n editingRef.current?.finish('cancel')\n }\n }, [doc, dispatch, studio])\n\n // Copy/cut/paste on the canvas (EC-175): the same studio clipboard the page\n // tree and blocks panel use, wired to the *iframe* document (events never\n // cross frames). Shortcuts stay inert while an inline editor is live and in\n // form fields; the iframe's own navigator.clipboard does the async I/O\n // (Chromium scopes clipboard access to the focused document).\n const clipboard = useStudioClipboard()\n useEffect(() => {\n if (!doc) return\n return attachClipboardListeners(doc, {\n clipboard,\n getSelectedId: () => selectedIdRef.current,\n isBlocked: () => editingRef.current !== null,\n asyncClipboard: doc.defaultView?.navigator.clipboard,\n getSelection: () => doc.getSelection(),\n })\n }, [doc, clipboard])\n\n // Only page nodes are editable; layout chrome (rendered around the page) is\n // read-only context and must not be selectable (it isn't in the page tree).\n const pageNodeIds = useMemo(() => collectIds(builder.present), [builder.present])\n\n // Bridge the renderer's untyped ItemContext (item: unknown) to the binding\n // layer's RepeaterItemContext (item: JsonValue). The renderer feeds back values\n // it resolved from JSON payloads, so the item is a JsonValue.\n const resolveBinding: ResolveBinding = useCallback(\n (ref, item) =>\n data.resolveValue(ref, item ? { item: item.item as JsonValue, index: item.index } : undefined),\n [data],\n )\n\n // EC-195: resolve an `asset`-typed Image prop (an asset id) to its url +\n // intrinsic dimensions from the live asset library, so the canvas previews\n // exactly what the host renders. A record without a resolved url (e.g. a\n // never-served seed) → null → the implementation's visible empty state.\n const assetsStore = useOptionalAssets()\n const resolveAsset: ResolveAsset = useCallback(\n (assetId) => {\n const record = assetsStore?.assets.find((a) => a.id === assetId)\n if (!record?.url) return null\n return {\n url: record.url,\n width: record.width ?? null,\n height: record.height ?? null,\n alt: record.alt ?? null,\n focalPoint: record.focalPoint ?? null,\n }\n },\n [assetsStore],\n )\n\n // Locale-fallback chips (EC-176): a node whose bound content currently\n // previews via EC-018 locale fallback wears a corner chip naming the locale\n // the value actually came from. Same decision function as the inspector's\n // Content tab (`cmsBindingFallback`), so chip and label can never disagree.\n const { locale: activeLocale } = useStudioLocale()\n\n // EC-254: populate a block's `relation` prop (the editor's document pick — a\n // ProductGrid's products, a Reusable's element) to delivery shape from the\n // active content session, in the canvas's active locale, so the canvas previews\n // picked documents exactly as the host renders them. The studio analog of the\n // host's baked relations map. A missing/invisible target → null → the block's\n // own empty state.\n const resolveRelation: ResolveRelation = useCallback(\n (ref) => resolveCanvasRelation(ref, activeLocale),\n [activeLocale],\n )\n\n // EC-255: resolve a listing block's query (a ProductGrid keyed to a category) to\n // its documents from the active content session, in the canvas's active locale, so\n // the canvas previews a dynamic grid exactly as the host renders it. The studio\n // analog of the host's baked listings map; an empty/invalid query → [] → the\n // block's own empty state.\n const resolveListing: ResolveListing = useCallback(\n (query) => resolveCanvasListing(query, activeLocale),\n [activeLocale],\n )\n\n const localeFallbackOf = useCallback(\n (node: ComponentNode): string | undefined => {\n for (const value of Object.values(node.props ?? {})) {\n if (value.kind !== 'binding') continue\n const fallback = cmsBindingFallback(\n value.binding,\n data.getSource(value.binding.sourceId),\n activeLocale,\n )\n if (fallback) return fallback.sourceLocale\n }\n return undefined\n },\n [data, activeLocale],\n )\n\n const ctx: RenderContext = useMemo(\n () => ({\n registry: studio.registry,\n implementations: studio.implementations,\n resolveBinding,\n resolveAsset,\n resolveRelation,\n resolveListing,\n decorateNode: (node, rendered) =>\n pageNodeIds.has(node.id) ? (\n <div\n // The epoch suffix forces a remount after a rich-text editing\n // session mutated this node's rendered DOM (see richEditEpochs).\n key={`${node.id}#${richEditEpochs[node.id] ?? 0}`}\n data-ec-node-id={node.id}\n className=\"ec-editable\"\n data-selected={node.id === builder.selectedId || undefined}\n data-hovered={node.id === builder.hoveredId || undefined}\n data-ec-locale-fallback={localeFallbackOf(node)}\n >\n {rendered}\n </div>\n ) : (\n rendered\n ),\n }),\n [studio, resolveBinding, resolveAsset, resolveRelation, resolveListing, pageNodeIds, builder.selectedId, builder.hoveredId, localeFallbackOf, richEditEpochs],\n )\n\n // Tokenized spacing handles (EC-163): a selected Stack/Grid with 2+ children\n // gets draggable gap handles between them, portaled into the iframe.\n const spacingTarget =\n doc && hasSpacingHandles(builder.selectedNode) && pageNodeIds.has(builder.selectedNode.id)\n ? builder.selectedNode\n : null\n\n return (\n <div className=\"canvas-grid relative flex min-h-0 flex-1 items-stretch justify-center overflow-auto p-6\">\n {/* Copy/cut/paste outcomes (EC-175): non-blocking, with issue deep links. */}\n <ClipboardNoticeOverlay />\n <div style={{ width: VIEWPORT_WIDTH[builder.viewport], maxWidth: '100%' }} className=\"flex\">\n <iframe\n ref={iframeRef}\n onLoad={wireUp}\n title=\"Page preview\"\n className=\"min-h-[480px] w-full rounded-lg border border-border bg-background shadow-md\"\n srcDoc=\"<!doctype html><html><head></head><body></body></html>\"\n />\n {doc\n ? createPortal(\n // Render the composition root directly (EC-287: no layout\n // indirection — a page renders its blocks).\n renderPageComposition(activeRoot, ctx),\n doc.body,\n )\n : null}\n {spacingTarget && doc ? (\n <SpacingHandles key={spacingTarget.id} doc={doc} node={spacingTarget} />\n ) : null}\n {/* Floating mark toolbar for the live rich-text session (EC-159). */}\n {richEditHost && doc ? <RichTextToolbar doc={doc} host={richEditHost} /> : null}\n </div>\n </div>\n )\n}\n"]}
|
|
1
|
+
{"version":3,"file":"canvas.js","sourceRoot":"","sources":["../../../src/components/builder/canvas.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport { createPortal } from 'react-dom'\nimport { renderPageComposition } from '@elytracms/core/runtime-renderer'\nimport type {\n RenderContext,\n ResolveAsset,\n ResolveBinding,\n ResolveLink,\n ResolveListing,\n ResolveRelation,\n} from '@elytracms/core/runtime-renderer'\nimport type { ComponentNode, JsonValue } from '@elytracms/core/project-graph'\nimport { useParams } from '@elytracms/studio/router'\nimport { getNode, getParentInfo, staticProp } from '@elytracms/core/project-graph'\nimport {\n canNestInto,\n containerFlowAxis,\n dropIndicatorFor,\n dropPositionForPoint,\n resolveDrop,\n} from '@elytracms/studio/lib/dnd/drop-rules'\nimport type { DropPlan, DropPosition, FlowAxis } from '@elytracms/studio/lib/dnd/drop-rules'\nimport { activeDrag, beginDrag, endDrag, onDragSessionEnd } from './drag-session'\nimport {\n PRESERVE_NODE_ATTR,\n collectPreservedPairs,\n richTextDocFromDom,\n} from '@elytracms/core/rich-text'\nimport type { RtDomElement } from '@elytracms/core/rich-text'\nimport { getStudioRegistry } from '@elytracms/studio/lib/registry'\nimport { useOptionalAssets } from '../assets/assets-store'\nimport {\n resolveCanvasLink,\n resolveCanvasListing,\n resolveCanvasRelation,\n useData,\n} from '@elytracms/studio/lib/data'\nimport { cmsBindingFallback, useStudioLocale } from '@elytracms/studio/lib/locale'\nimport { getInstantiatedKit } from '@elytracms/studio/lib/starters/kit-session'\nimport { getCanvasStylesheetUrl } from './canvas-stylesheet'\nimport { useBuilder } from './builder-store'\nimport { useCanvasViewports, viewportWidth } from './canvas-breakpoints'\nimport {\n ClipboardNoticeOverlay,\n attachClipboardListeners,\n useStudioClipboard,\n} from './clipboard-actions'\nimport {\n createInlineEditSession,\n createInlineRichTextSession,\n findInlineTextHost,\n resolveInlineEditableProp,\n resolveInlineRichTextProp,\n} from './inline-edit'\nimport type { InlineEditSession, InlineRichTextProp } from './inline-edit'\nimport { RichTextToolbar } from './rich-text-toolbar'\nimport { SpacingHandles, hasSpacingHandles } from './spacing-handles'\n\n\n/** Selection/hover outlines, scoped to the per-node editable wrappers. */\nconst OUTLINE_CSS = `\n html { color-scheme: dark; }\n body { margin: 0; padding: 16px; }\n .ec-editable { outline-offset: -1px; cursor: default; }\n .ec-editable[data-hovered]:not([data-selected]) { outline: 1px dashed rgba(130,150,190,0.75); }\n .ec-editable[data-selected] { outline: 2px solid #5b8cff; }\n /* Inline editing (EC-158 text, EC-159 rich text): the rendered element\n itself becomes the editor — no overlay, no replacement input — so\n typography, alignment, and layout are pixel-identical between edit and\n render states. The wrapper keeps the selection outline; the host only\n gets a caret. pre-wrap applies to PLAIN text hosts only — rich-text\n markup must keep its rendered whitespace handling (no layout shift). */\n .ec-editable[data-ec-editing] { outline: 2px solid #5b8cff; }\n .ec-editable [data-ec-edit] { outline: none; cursor: text; }\n .ec-editable [data-ec-edit='text'] { white-space: pre-wrap; }\n /* Preserved atoms during a rich-text session (custom blocks, unknown nodes,\n images, embeds): visible and rendered, but not text-editable. */\n .ec-editable [data-ec-edit='rich'] [${PRESERVE_NODE_ATTR}] { user-select: none; }\n /* Floating mark toolbar (EC-159), portaled into the iframe. */\n .ec-rt-toolbar { color-scheme: dark; }\n .ec-rt-toolbar-row { display: flex; align-items: center; gap: 2px; padding: 3px;\n border: 1px solid rgba(130,150,190,0.4); border-radius: 8px;\n background: rgb(24,26,32); box-shadow: 0 4px 16px rgba(0,0,0,0.4); }\n .ec-rt-toolbar-btn { min-width: 26px; height: 24px; padding: 0 7px; border: 0; border-radius: 5px;\n background: transparent; color: rgb(225,228,236); font: 600 12px/1 ui-sans-serif, system-ui, sans-serif;\n cursor: pointer; }\n .ec-rt-toolbar-btn:hover { background: rgba(130,150,190,0.25); }\n .ec-rt-toolbar-btn[data-active] { background: #5b8cff; color: white; }\n .ec-rt-toolbar-input { width: 200px; height: 24px; padding: 0 7px; border-radius: 5px;\n border: 1px solid rgba(130,150,190,0.4); background: rgb(16,18,22); color: rgb(225,228,236);\n font: 12px/1 ui-sans-serif, system-ui, sans-serif; outline: none; }\n /* Locale-fallback chips (EC-176): a node whose bound content is currently\n served via locale fallback wears a small corner chip naming the locale it\n actually came from — the canvas never silently mixes locales. */\n .ec-editable[data-ec-locale-fallback] { position: relative; }\n .ec-editable[data-ec-locale-fallback]::after {\n content: attr(data-ec-locale-fallback);\n position: absolute; top: 2px; right: 2px; z-index: 5; pointer-events: none;\n padding: 0 4px; border-radius: 4px;\n font: 600 9px/1.5 ui-sans-serif, system-ui, sans-serif;\n letter-spacing: 0.05em; text-transform: uppercase;\n color: #fbbf24; background: rgba(180,83,9,0.25); border: 1px solid rgba(245,158,11,0.45);\n }\n /* Drag & drop states (EC-160/EC-162): eligibility comes from the shared pure\n rules (lib/dnd/drop-rules); these classes only *show* the resolution.\n Rejection is explicit and visible during the drag — never a silent failed\n drop. Before/after bars follow the parent's flow axis (data-ec-drop-axis);\n left/right are the wrap-into-columns gesture (violet, paired highlight). */\n .ec-editable[data-ec-drop='into'] { outline: 2px solid #34d399; outline-offset: -2px; background-color: rgba(52, 211, 153, 0.08); }\n .ec-editable[data-ec-drop='before'] { box-shadow: 0 -3px 0 0 #38bdf8; }\n .ec-editable[data-ec-drop='after'] { box-shadow: 0 3px 0 0 #38bdf8; }\n .ec-editable[data-ec-drop='before'][data-ec-drop-axis='x'] { box-shadow: -3px 0 0 0 #38bdf8; }\n .ec-editable[data-ec-drop='after'][data-ec-drop-axis='x'] { box-shadow: 3px 0 0 0 #38bdf8; }\n .ec-editable[data-ec-drop='left'] { box-shadow: -3px 0 0 0 #a78bfa; background-color: rgba(167, 139, 250, 0.1); }\n .ec-editable[data-ec-drop='right'] { box-shadow: 3px 0 0 0 #a78bfa; background-color: rgba(167, 139, 250, 0.1); }\n .ec-editable[data-ec-drop='blocked'] { outline: 2px solid #ef4444; outline-offset: -2px; background-color: rgba(239, 68, 68, 0.08); cursor: not-allowed; }\n`\n\nfunction nodeIdAt(target: EventTarget | null): string | null {\n return wrapperAt(target)?.getAttribute('data-ec-node-id') ?? null\n}\n\nfunction wrapperAt(target: EventTarget | null): HTMLElement | null {\n const el = target as HTMLElement | null\n return (el?.closest?.('[data-ec-node-id]') as HTMLElement | null) ?? null\n}\n\n/** A live inline-editing session bound to its DOM host (EC-158/EC-159). */\ninterface ActiveInlineEdit {\n nodeId: string\n host: HTMLElement\n /** Finish the session exactly once (later calls are no-ops). */\n finish: (mode: 'commit' | 'cancel') => void\n}\n\n/**\n * The rendered `.rich-text` element inside a node's wrapper — the rich-text\n * editing host (EC-159). Never crosses into a nested node's wrapper.\n */\nfunction findRichTextHost(wrapper: HTMLElement): HTMLElement | null {\n const host = wrapper.querySelector('.rich-text') as HTMLElement | null\n return host && host.closest('[data-ec-node-id]') === wrapper ? host : null\n}\n\n/** Collect every node id under a root (the page subtree). */\nfunction collectIds(node: ComponentNode, out: Set<string> = new Set()): Set<string> {\n out.add(node.id)\n for (const children of Object.values(node.slots ?? {})) {\n for (const child of children) collectIds(child, out)\n }\n return out\n}\n\n/**\n * Iframe-isolated page preview (brief §8.2, EC-061, EC-064). The graph renders\n * into a same-origin iframe via a React portal — true style isolation + true\n * responsive widths. Each node is wrapped with an editable marker so clicks\n * select and hover/selection draw outlines. Native listeners bridge the iframe\n * boundary (React's synthetic events don't cross frames).\n */\nexport function BuilderCanvas() {\n const builder = useBuilder()\n const { dispatch } = builder\n const studio = getStudioRegistry()\n const data = useData()\n\n // The edited composition root, composed for preview. EC-287 retired the project\n // graph and its published-graph preview; the canvas always renders the live,\n // editable draft (content publishing is per document).\n const activeRoot: ComponentNode = builder.present\n const iframeRef = useRef<HTMLIFrameElement>(null)\n const [doc, setDoc] = useState<Document | null>(null)\n\n // Inline editing (EC-158/EC-159): the active session lives in a ref — it is\n // imperative DOM state inside the iframe, not render state. Graph/page refs\n // let the native dblclick handler read the *current* graph without\n // re-subscribing listeners on every edit. The rich-text host additionally\n // mirrors into render state so the floating mark toolbar can mount.\n const editingRef = useRef<ActiveInlineEdit | null>(null)\n const [richEditHost, setRichEditHost] = useState<HTMLElement | null>(null)\n // A rich-text session mutates the rendered DOM behind React's back\n // (contenteditable + mark commands), so finishing one must REMOUNT the\n // node's subtree — bumping its epoch changes the decorate key, React\n // discards the edited DOM wholesale and re-renders from the graph (the\n // committed value, or the untouched original on cancel). Patching the old\n // fiber tree against editor-mutated DOM would leave stale artifacts.\n const [richEditEpochs, setRichEditEpochs] = useState<Record<string, number>>({})\n const presentRef = useRef(builder.present)\n presentRef.current = builder.present\n const selectedIdRef = useRef(builder.selectedId)\n selectedIdRef.current = builder.selectedId\n\n // When the project was created from a starter kit, its brand token palette drives the canvas UI\n // (EC-117) — injected after app.css so the kit's overrides win in the cascade.\n const params = useParams({ strict: false }) as { projectId?: string }\n const kitTokensCss = params.projectId ? getInstantiatedKit(params.projectId)?.tokensCss : undefined\n\n const wireUp = useCallback(() => {\n const d = iframeRef.current?.contentDocument\n if (!d || !d.body) return\n d.documentElement.classList.add('dark')\n const appCss = getCanvasStylesheetUrl()\n if (appCss && !d.getElementById('ec-app-css')) {\n const link = d.createElement('link')\n link.id = 'ec-app-css'\n link.rel = 'stylesheet'\n link.href = appCss\n d.head.appendChild(link)\n }\n if (!d.getElementById('ec-outline-css')) {\n const style = d.createElement('style')\n style.id = 'ec-outline-css'\n style.textContent = OUTLINE_CSS\n d.head.appendChild(style)\n }\n if (kitTokensCss) {\n const existing = d.getElementById('ec-kit-tokens')\n if (existing) existing.remove()\n const style = d.createElement('style')\n style.id = 'ec-kit-tokens'\n style.textContent = kitTokensCss\n d.head.appendChild(style)\n }\n d.body.className = 'bg-background text-foreground font-sans'\n setDoc(d)\n }, [kitTokensCss])\n\n // Handle the already-loaded case (srcDoc may load before onLoad attaches).\n useEffect(() => {\n if (iframeRef.current?.contentDocument?.readyState === 'complete') wireUp()\n }, [wireUp])\n\n // Native selection/hover listeners inside the iframe document.\n useEffect(() => {\n if (!doc) return\n const onClick = (e: Event) => {\n // Clicks inside the active inline editor are caret moves, not selection.\n if (editingRef.current?.host.contains(e.target as Node)) return\n const id = nodeIdAt(e.target)\n if (id) {\n e.preventDefault()\n dispatch({ type: 'select', id })\n }\n }\n const onOver = (e: Event) => dispatch({ type: 'hover', id: nodeIdAt(e.target) })\n const onLeave = () => dispatch({ type: 'hover', id: null })\n doc.addEventListener('click', onClick, true)\n doc.addEventListener('mouseover', onOver)\n doc.addEventListener('mouseleave', onLeave, true)\n return () => {\n doc.removeEventListener('click', onClick, true)\n doc.removeEventListener('mouseover', onOver)\n doc.removeEventListener('mouseleave', onLeave, true)\n }\n }, [doc, dispatch])\n\n // Drag & drop on the canvas (EC-160). Eligibility is the pure shared rule\n // set (`lib/dnd/drop-rules`) — the same functions the page tree consumes —\n // evaluated live during `dragover`; the wrapper under the pointer shows the\n // resolution (`data-ec-drop`: into/before/after, or `blocked` for a visible\n // rejection). A completed drop dispatches exactly one builder action (move\n // or insert) → one operations command → one undo step. Ineligible targets\n // never `preventDefault`, so releasing over one is a native no-op.\n useEffect(() => {\n if (!doc) return\n const getManifest = (id: string) => studio.registry.getManifest(id)\n const currentRoot = () => presentRef.current\n\n let dropWrapper: HTMLElement | null = null\n let dropPlan: DropPlan | null = null\n const clearIndicator = () => {\n dropWrapper?.removeAttribute('data-ec-drop')\n dropWrapper?.removeAttribute('data-ec-drop-axis')\n dropWrapper = null\n dropPlan = null\n }\n const showIndicator = (wrapper: HTMLElement | null, state: string | null, axis: FlowAxis) => {\n if (dropWrapper && dropWrapper !== wrapper) {\n dropWrapper.removeAttribute('data-ec-drop')\n dropWrapper.removeAttribute('data-ec-drop-axis')\n }\n dropWrapper = wrapper\n if (!wrapper) return\n if (state) {\n wrapper.setAttribute('data-ec-drop', state)\n // Before/after bars render along the parent's flow axis (EC-162).\n wrapper.setAttribute('data-ec-drop-axis', axis === 'horizontal' ? 'x' : 'y')\n } else {\n wrapper.removeAttribute('data-ec-drop')\n wrapper.removeAttribute('data-ec-drop-axis')\n }\n }\n\n // Wrappers are armed draggable per-gesture on mousedown, never via render\n // state: the active inline editor (EC-158) must keep native caret clicks\n // and text selection, so a press inside the editing host disarms its\n // wrapper instead of starting a node drag.\n const onMouseDown = (e: Event) => {\n const wrapper = wrapperAt(e.target)\n if (!wrapper) return\n const root = currentRoot()\n const nodeId = wrapper.getAttribute('data-ec-node-id')\n const inEditor = editingRef.current?.host.contains(e.target as Node) ?? false\n if (root && nodeId && nodeId !== root.id && !inEditor) {\n wrapper.setAttribute('draggable', 'true')\n } else {\n wrapper.removeAttribute('draggable')\n }\n }\n\n const onDragStart = (e: DragEvent) => {\n // Drags inside the live inline editor are native text drags — leave them alone.\n if (editingRef.current?.host.contains(e.target as Node)) return\n const wrapper = wrapperAt(e.target)\n if (!wrapper) return\n const root = currentRoot()\n const nodeId = wrapper.getAttribute('data-ec-node-id')!\n const node = root ? getNode(root, nodeId) : undefined\n if (!root || !node || nodeId === root.id) {\n e.preventDefault()\n return\n }\n beginDrag(\n { subject: { kind: 'node', nodeId, componentId: node.componentId }, source: 'canvas', label: node.componentId },\n e.dataTransfer,\n )\n }\n\n const onDragOver = (e: DragEvent) => {\n const drag = activeDrag()\n if (!drag) return\n const root = currentRoot()\n if (!root) return\n const wrapper = wrapperAt(e.target)\n // Empty space around the page subtree targets the root slot (append).\n const refId = wrapper?.getAttribute('data-ec-node-id') ?? root.id\n const refWrapper =\n wrapper ?? (doc.querySelector(`[data-ec-node-id=\"${root.id}\"]`) as HTMLElement | null)\n const refNode = getNode(root, refId)\n if (!refNode) {\n clearIndicator()\n return\n }\n let position: DropPosition = 'into'\n let flow: FlowAxis = 'vertical'\n if (wrapper && refId !== root.id) {\n const rect = wrapper.getBoundingClientRect()\n // Hit-bands follow the parent's flow axis (EC-162); the cross-axis\n // side bands of vertically flowing targets are the wrap gesture.\n flow = containerFlowAxis(getParentInfo(root, refId)?.parent)\n position = dropPositionForPoint({\n x: rect.width > 0 ? (e.clientX - rect.left) / rect.width : 0.5,\n y: rect.height > 0 ? (e.clientY - rect.top) / rect.height : 0.5,\n flow,\n nestable: canNestInto(getManifest, refNode.componentId),\n edges: true,\n wrappable: true,\n })\n }\n const resolution = resolveDrop({\n root,\n getManifest,\n subject: drag.subject,\n location: { nodeId: refId, position },\n })\n showIndicator(refWrapper, dropIndicatorFor(resolution, position), flow)\n if (resolution.eligible) {\n dropPlan = resolution\n e.preventDefault()\n if (e.dataTransfer) {\n e.dataTransfer.dropEffect = drag.subject.kind === 'node' ? 'move' : 'copy'\n }\n } else {\n dropPlan = null\n }\n }\n\n const onDrop = (e: DragEvent) => {\n const drag = activeDrag()\n if (!drag) return\n const plan = dropPlan\n clearIndicator()\n if (!plan) return\n e.preventDefault()\n // Exactly one dispatch per completed drop (one undo step, one command).\n if (plan.kind === 'move') {\n dispatch({ type: 'move', id: plan.nodeId, target: plan.target })\n } else if (plan.kind === 'wrap') {\n // EC-162 gesture: reference + subject become a two-column Grid.\n const subject =\n plan.nodeId !== undefined\n ? { kind: 'node' as const, nodeId: plan.nodeId }\n : drag.buildNode\n ? { kind: 'component' as const, node: drag.buildNode() }\n : null\n if (!subject) {\n endDrag('cancelled')\n return\n }\n dispatch({ type: 'wrap', referenceId: plan.referenceId, side: plan.side, subject })\n } else if (drag.buildNode) {\n dispatch({ type: 'insert', target: plan.target, node: drag.buildNode() })\n } else {\n endDrag('cancelled')\n return\n }\n endDrag('dropped')\n }\n\n const onDragEnd = () => {\n clearIndicator()\n endDrag('cancelled')\n }\n\n const onDragLeave = (e: DragEvent) => {\n // Leaving the iframe document entirely (relatedTarget is null).\n if (!e.relatedTarget) clearIndicator()\n }\n\n // Cleanup when the drag ends anywhere (e.g. dropped on the page tree).\n const unsubscribe = onDragSessionEnd(() => clearIndicator())\n\n doc.addEventListener('mousedown', onMouseDown, true)\n doc.addEventListener('dragstart', onDragStart)\n doc.addEventListener('dragover', onDragOver)\n doc.addEventListener('drop', onDrop)\n doc.addEventListener('dragend', onDragEnd)\n doc.addEventListener('dragleave', onDragLeave)\n return () => {\n unsubscribe()\n clearIndicator()\n doc.removeEventListener('mousedown', onMouseDown, true)\n doc.removeEventListener('dragstart', onDragStart)\n doc.removeEventListener('dragover', onDragOver)\n doc.removeEventListener('drop', onDrop)\n doc.removeEventListener('dragend', onDragEnd)\n doc.removeEventListener('dragleave', onDragLeave)\n }\n }, [doc, dispatch, studio])\n\n // Inline editing (EC-158 plain text, EC-159 rich text): double-click on a\n // node whose manifest flags an inline-editable prop turns the *rendered\n // element itself* contenteditable — no overlay, no input swap, so typography\n // and layout are identical in edit and render states. A session finishes\n // exactly once, and a commit dispatches exactly one `setProp` action — one\n // undo step, mirrored as one `graph.setNodeProp` operations command by the\n // builder-store persister. The graph stays the single source of truth.\n //\n // Commit/cancel gestures: plain text commits on Enter or blur; rich text is\n // multiline, so plain Enter inserts a paragraph and the commit gesture is\n // Cmd/Ctrl+Enter or blur (Enter-to-commit would make multi-paragraph content\n // unauthorable in place). Esc always cancels, dispatching nothing.\n useEffect(() => {\n if (!doc) return\n\n // ---- plain text (EC-158) ------------------------------------------------\n const beginText = (wrapper: HTMLElement, host: HTMLElement, session: InlineEditSession) => {\n let done = false\n const finish = (mode: 'commit' | 'cancel') => {\n if (done) return\n done = true\n editingRef.current = null\n const text = host.textContent ?? ''\n host.removeEventListener('keydown', onKeyDown)\n host.removeEventListener('blur', onBlur)\n host.removeAttribute('contenteditable')\n host.removeAttribute('data-ec-edit')\n wrapper.removeAttribute('data-ec-editing')\n if (mode === 'commit') {\n const outcome = session.commit(text)\n if (outcome) {\n dispatch({\n type: 'setProp',\n id: outcome.nodeId,\n name: outcome.propName,\n value: staticProp(outcome.value),\n })\n }\n } else {\n const outcome = session.cancel()\n if (outcome) host.textContent = outcome.value\n }\n doc.getSelection()?.removeAllRanges()\n }\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Enter') {\n e.preventDefault()\n e.stopPropagation()\n finish('commit')\n } else if (e.key === 'Escape') {\n e.preventDefault()\n e.stopPropagation()\n finish('cancel')\n }\n }\n const onBlur = () => finish('commit')\n // `plaintext-only` keeps paste/typing plain; fall back where unsupported.\n host.setAttribute('contenteditable', 'plaintext-only')\n if (!host.isContentEditable) host.setAttribute('contenteditable', 'true')\n host.setAttribute('data-ec-edit', 'text')\n wrapper.setAttribute('data-ec-editing', '')\n host.addEventListener('keydown', onKeyDown)\n host.addEventListener('blur', onBlur)\n editingRef.current = { nodeId: session.nodeId, host, finish }\n host.focus({ preventScroll: true })\n // Keep the gesture's native caret/word selection when it landed inside\n // the host; otherwise select the whole value.\n const sel = doc.getSelection()\n if (!sel || sel.rangeCount === 0 || !host.contains(sel.anchorNode)) {\n const range = doc.createRange()\n range.selectNodeContents(host)\n sel?.removeAllRanges()\n sel?.addRange(range)\n }\n }\n\n // ---- rich text (EC-159) -------------------------------------------------\n const beginRichText = (wrapper: HTMLElement, nodeId: string, target: InlineRichTextProp) => {\n const host = findRichTextHost(wrapper)\n if (!host) return\n // Pair the storage tree against the rendered DOM and stamp every\n // non-invertible node (custom blocks, unknown nodes, images, embeds)\n // with its exact JSON: they stay rendered *inside* the live editor as\n // non-editable atoms and survive the session byte-exact. A pairing\n // mismatch means the DOM diverged from the deterministic render —\n // refuse to start rather than risk lossy parsing (\"polished or absent\").\n const pairs = collectPreservedPairs(target.value.doc, host as unknown as RtDomElement)\n if (!pairs) return\n const session = createInlineRichTextSession({\n nodeId,\n propName: target.propName,\n original: target.value,\n })\n for (const pair of pairs) {\n const el = pair.element as unknown as HTMLElement\n el.setAttribute(PRESERVE_NODE_ATTR, JSON.stringify(pair.node))\n el.setAttribute('contenteditable', 'false')\n }\n let done = false\n const finish = (mode: 'commit' | 'cancel') => {\n if (done) return\n done = true\n editingRef.current = null\n setRichEditHost(null)\n // Parse the edited DOM, detach the session, then bump the node's\n // remount epoch: React discards the editor-mutated DOM and renders\n // fresh from the graph — committed content or, on cancel, the\n // untouched original. Never patch a fiber tree against DOM the\n // editing commands rewrote.\n const parsed = mode === 'commit' ? richTextDocFromDom(host as unknown as RtDomElement) : null\n host.removeEventListener('keydown', onKeyDown)\n host.removeEventListener('blur', onBlur)\n host.removeAttribute('contenteditable')\n host.removeAttribute('data-ec-edit')\n wrapper.removeAttribute('data-ec-editing')\n setRichEditEpochs((prev) => ({ ...prev, [nodeId]: (prev[nodeId] ?? 0) + 1 }))\n if (parsed) {\n // An unchanged document commits nothing: the stored value is never\n // rewritten, so untouched content round-trips byte-identical.\n const outcome = session.commit(parsed.doc)\n if (outcome) {\n dispatch({\n type: 'setProp',\n id: outcome.nodeId,\n name: outcome.propName,\n value: staticProp(outcome.value as unknown as JsonValue),\n })\n }\n } else {\n session.cancel()\n }\n doc.getSelection()?.removeAllRanges()\n }\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n e.preventDefault()\n e.stopPropagation()\n finish('cancel')\n } else if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {\n e.preventDefault()\n e.stopPropagation()\n finish('commit')\n }\n // Plain Enter falls through: native paragraph insertion (multiline).\n }\n const onBlur = (e: FocusEvent) => {\n // Interacting with the floating toolbar is part of the session.\n const next = e.relatedTarget as HTMLElement | null\n if (next?.closest?.('[data-ec-rt-toolbar]')) return\n finish('commit')\n }\n host.setAttribute('contenteditable', 'true')\n host.setAttribute('data-ec-edit', 'rich')\n wrapper.setAttribute('data-ec-editing', '')\n host.addEventListener('keydown', onKeyDown)\n host.addEventListener('blur', onBlur)\n editingRef.current = { nodeId, host, finish }\n setRichEditHost(host)\n host.focus({ preventScroll: true })\n }\n\n const onDblClick = (e: Event) => {\n const wrapper = wrapperAt(e.target)\n if (!wrapper) return\n const nodeId = wrapper.getAttribute('data-ec-node-id')!\n if (editingRef.current) {\n // Double-click inside the live editor is native word selection.\n if (editingRef.current.nodeId === nodeId) return\n editingRef.current.finish('commit')\n }\n const node = getNode(presentRef.current, nodeId)\n if (!node) return\n const manifest = studio.registry.getManifest(node.componentId)\n const target = resolveInlineEditableProp(manifest, node)\n if (target) {\n const host = findInlineTextHost(wrapper, target.value) as HTMLElement | null\n if (!host || typeof host.focus !== 'function') return\n dispatch({ type: 'select', id: nodeId })\n beginText(\n wrapper,\n host,\n createInlineEditSession({ nodeId, propName: target.propName, original: target.value }),\n )\n return\n }\n const richTarget = resolveInlineRichTextProp(manifest, node)\n if (!richTarget) return\n dispatch({ type: 'select', id: nodeId })\n beginRichText(wrapper, nodeId, richTarget)\n }\n\n doc.addEventListener('dblclick', onDblClick, true)\n return () => {\n doc.removeEventListener('dblclick', onDblClick, true)\n // Teardown mid-session (doc swap/unmount): cancel — never dispatch from cleanup.\n editingRef.current?.finish('cancel')\n }\n }, [doc, dispatch, studio])\n\n // Copy/cut/paste on the canvas (EC-175): the same studio clipboard the page\n // tree and blocks panel use, wired to the *iframe* document (events never\n // cross frames). Shortcuts stay inert while an inline editor is live and in\n // form fields; the iframe's own navigator.clipboard does the async I/O\n // (Chromium scopes clipboard access to the focused document).\n const clipboard = useStudioClipboard()\n useEffect(() => {\n if (!doc) return\n return attachClipboardListeners(doc, {\n clipboard,\n getSelectedId: () => selectedIdRef.current,\n isBlocked: () => editingRef.current !== null,\n asyncClipboard: doc.defaultView?.navigator.clipboard,\n getSelection: () => doc.getSelection(),\n })\n }, [doc, clipboard])\n\n // Only page nodes are editable; layout chrome (rendered around the page) is\n // read-only context and must not be selectable (it isn't in the page tree).\n const pageNodeIds = useMemo(() => collectIds(builder.present), [builder.present])\n\n // Bridge the renderer's untyped ItemContext (item: unknown) to the binding\n // layer's RepeaterItemContext (item: JsonValue). The renderer feeds back values\n // it resolved from JSON payloads, so the item is a JsonValue.\n const resolveBinding: ResolveBinding = useCallback(\n (ref, item) =>\n data.resolveValue(ref, item ? { item: item.item as JsonValue, index: item.index } : undefined),\n [data],\n )\n\n // EC-195: resolve an `asset`-typed Image prop (an asset id) to its url +\n // intrinsic dimensions from the live asset library, so the canvas previews\n // exactly what the host renders. A record without a resolved url (e.g. a\n // never-served seed) → null → the implementation's visible empty state.\n const assetsStore = useOptionalAssets()\n const resolveAsset: ResolveAsset = useCallback(\n (assetId) => {\n const record = assetsStore?.assets.find((a) => a.id === assetId)\n if (!record?.url) return null\n return {\n url: record.url,\n width: record.width ?? null,\n height: record.height ?? null,\n alt: record.alt ?? null,\n focalPoint: record.focalPoint ?? null,\n }\n },\n [assetsStore],\n )\n\n // Locale-fallback chips (EC-176): a node whose bound content currently\n // previews via EC-018 locale fallback wears a corner chip naming the locale\n // the value actually came from. Same decision function as the inspector's\n // Content tab (`cmsBindingFallback`), so chip and label can never disagree.\n const { locale: activeLocale } = useStudioLocale()\n\n // EC-254: populate a block's `relation` prop (the editor's document pick — a\n // ProductGrid's products, a Reusable's element) to delivery shape from the\n // active content session, in the canvas's active locale, so the canvas previews\n // picked documents exactly as the host renders them. The studio analog of the\n // host's baked relations map. A missing/invisible target → null → the block's\n // own empty state.\n const resolveRelation: ResolveRelation = useCallback(\n (ref) => resolveCanvasRelation(ref, activeLocale),\n [activeLocale],\n )\n\n // EC-255: resolve a listing block's query (a ProductGrid keyed to a category) to\n // its documents from the active content session, in the canvas's active locale, so\n // the canvas previews a dynamic grid exactly as the host renders it. The studio\n // analog of the host's baked listings map; an empty/invalid query → [] → the\n // block's own empty state.\n const resolveListing: ResolveListing = useCallback(\n (query) => resolveCanvasListing(query, activeLocale),\n [activeLocale],\n )\n\n // EC-291: resolve a rich-text internal link (`{type:'reference'}`) to its target's\n // URL path from the active content session, in the canvas's active locale — the\n // studio analog of the host's baked links map. A missing/unpublished/unroutable\n // target → null → the rich-text renderer shows the visible `unresolved-ref` issue.\n const resolveLink: ResolveLink = useCallback(\n (ref) => resolveCanvasLink(ref, activeLocale),\n [activeLocale],\n )\n\n const localeFallbackOf = useCallback(\n (node: ComponentNode): string | undefined => {\n for (const value of Object.values(node.props ?? {})) {\n if (value.kind !== 'binding') continue\n const fallback = cmsBindingFallback(\n value.binding,\n data.getSource(value.binding.sourceId),\n activeLocale,\n )\n if (fallback) return fallback.sourceLocale\n }\n return undefined\n },\n [data, activeLocale],\n )\n\n const ctx: RenderContext = useMemo(\n () => ({\n registry: studio.registry,\n implementations: studio.implementations,\n resolveBinding,\n resolveAsset,\n resolveRelation,\n resolveListing,\n resolveLink,\n decorateNode: (node, rendered) =>\n pageNodeIds.has(node.id) ? (\n <div\n // The epoch suffix forces a remount after a rich-text editing\n // session mutated this node's rendered DOM (see richEditEpochs).\n key={`${node.id}#${richEditEpochs[node.id] ?? 0}`}\n data-ec-node-id={node.id}\n className=\"ec-editable\"\n data-selected={node.id === builder.selectedId || undefined}\n data-hovered={node.id === builder.hoveredId || undefined}\n data-ec-locale-fallback={localeFallbackOf(node)}\n >\n {rendered}\n </div>\n ) : (\n rendered\n ),\n }),\n [studio, resolveBinding, resolveAsset, resolveRelation, resolveListing, resolveLink, pageNodeIds, builder.selectedId, builder.hoveredId, localeFallbackOf, richEditEpochs],\n )\n\n // Tokenized spacing handles (EC-163): a selected Stack/Grid with 2+ children\n // gets draggable gap handles between them, portaled into the iframe.\n const spacingTarget =\n doc && hasSpacingHandles(builder.selectedNode) && pageNodeIds.has(builder.selectedNode.id)\n ? builder.selectedNode\n : null\n\n // EC-250: the preview frame width derives from the config-sourced breakpoints\n // (Tailwind defaults when unconfigured), the same resolved set the toolbar\n // switcher offers.\n const { viewports } = useCanvasViewports()\n\n return (\n <div className=\"canvas-grid relative flex min-h-0 flex-1 items-stretch justify-center overflow-auto p-6\">\n {/* Copy/cut/paste outcomes (EC-175): non-blocking, with issue deep links. */}\n <ClipboardNoticeOverlay />\n <div style={{ width: viewportWidth(viewports, builder.viewport), maxWidth: '100%' }} className=\"flex\">\n <iframe\n ref={iframeRef}\n onLoad={wireUp}\n title=\"Page preview\"\n className=\"min-h-[480px] w-full rounded-lg border border-border bg-background shadow-md\"\n srcDoc=\"<!doctype html><html><head></head><body></body></html>\"\n />\n {doc\n ? createPortal(\n // Render the composition root directly (EC-287: no layout\n // indirection — a page renders its blocks).\n renderPageComposition(activeRoot, ctx),\n doc.body,\n )\n : null}\n {spacingTarget && doc ? (\n <SpacingHandles key={spacingTarget.id} doc={doc} node={spacingTarget} />\n ) : null}\n {/* Floating mark toolbar for the live rich-text session (EC-159). */}\n {richEditHost && doc ? <RichTextToolbar doc={doc} host={richEditHost} /> : null}\n </div>\n </div>\n )\n}\n"]}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
3
|
-
import {
|
|
3
|
+
import { AlertTriangle, Plus, Redo2, SlidersHorizontal, Undo2 } from 'lucide-react';
|
|
4
4
|
import { DEFAULT_SLOT, blockRootsOf } from '@elytracms/core/project-graph';
|
|
5
|
-
import { Button, Panel, Spacer, Toolbar, ToolbarGroup, ToolbarSeparator } from '@elytracms/studio/ui';
|
|
5
|
+
import { Button, Panel, Row, Spacer, Text, Toolbar, ToolbarGroup, ToolbarSeparator } from '@elytracms/studio/ui';
|
|
6
6
|
import { cn } from '@elytracms/studio/lib/utils';
|
|
7
7
|
import { BuilderProvider, useBuilder } from './builder-store';
|
|
8
|
+
import { useCanvasViewports } from './canvas-breakpoints';
|
|
8
9
|
import { BuilderCanvas } from './canvas';
|
|
9
10
|
import { PageTree } from './page-tree';
|
|
10
11
|
import { PropertyInspector } from './property-inspector';
|
|
@@ -89,21 +90,21 @@ function CompositionLift({ cardinality, onChange, readOnly, }) {
|
|
|
89
90
|
return null;
|
|
90
91
|
}
|
|
91
92
|
const EMPTY_BLOCKS = [];
|
|
92
|
-
/**
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
{ value: 'tablet', label: 'Tablet', icon: _jsx(Tablet, {}) },
|
|
99
|
-
{ value: 'mobile', label: 'Phone', icon: _jsx(Smartphone, {}) },
|
|
100
|
-
];
|
|
101
|
-
/** A breakpoint preview-width toggle (EC-211). Re-homed from the retired builder
|
|
102
|
-
* shell: switching the viewport resizes only the preview frame (it is not an
|
|
103
|
-
* undoable edit and writes no graph command). */
|
|
104
|
-
function ViewportButton({ value, label, icon, }) {
|
|
93
|
+
/** A breakpoint preview-width toggle (EC-211/EC-250). Switching the viewport
|
|
94
|
+
* resizes only the preview frame (not an undoable edit, writes no graph command).
|
|
95
|
+
* The label is the breakpoint name (`Full`/`sm`/`md`/…) with the real px width in
|
|
96
|
+
* the tooltip — so the switcher reflects the site's actual screens, not a fixed
|
|
97
|
+
* desktop/tablet/phone triple. */
|
|
98
|
+
function ViewportButton({ option }) {
|
|
105
99
|
const builder = useBuilder();
|
|
106
|
-
|
|
100
|
+
const active = builder.viewport === option.key;
|
|
101
|
+
return (_jsx(Button, { size: "sm", variant: "ghost", "aria-label": `${option.label} preview`, "aria-pressed": active, title: option.px !== undefined ? `${option.label} · ${option.px}px` : `${option.label} width`, onClick: () => builder.dispatch({ type: 'viewport', viewport: option.key }), className: cn('px-2 text-xs', active && 'bg-accent text-accent-foreground'), children: option.label }));
|
|
102
|
+
}
|
|
103
|
+
/** The centered breakpoint switcher (EC-250): the resolved viewport set, plus a
|
|
104
|
+
* visible validation issue when a project declared breakpoints but none are usable. */
|
|
105
|
+
function BreakpointSwitcher() {
|
|
106
|
+
const { viewports, issue } = useCanvasViewports();
|
|
107
|
+
return (_jsxs(Row, { gap: "1", align: "center", children: [_jsx(ToolbarGroup, { children: viewports.map((v) => (_jsx(ViewportButton, { option: v }, v.key))) }), issue ? (_jsxs(Row, { gap: "1", align: "center", className: "text-amber-600 dark:text-amber-400", title: issue, children: [_jsx(AlertTriangle, { "aria-hidden": true, className: "size-3.5" }), _jsx(Text, { size: "xs", children: "breakpoints" })] })) : null] }));
|
|
107
108
|
}
|
|
108
109
|
/**
|
|
109
110
|
* The slim editor surface — the canvas + layers + inspector, plus Insert and
|
|
@@ -135,6 +136,6 @@ function CompositionEditorShell({ readOnly, fieldAllow, trailingPanes, }) {
|
|
|
135
136
|
setInsertOpen(true);
|
|
136
137
|
}
|
|
137
138
|
}
|
|
138
|
-
return (_jsxs("div", { className: "flex h-full min-h-0 flex-col", children: [_jsx("div", { className: "shrink-0 border-b border-border", children: _jsxs(Toolbar, { children: [_jsxs(ToolbarGroup, { children: [_jsx(Button, { size: "icon-sm", variant: "ghost", "aria-label": "Undo", title: "Undo", disabled: readOnly || !builder.canUndo, onClick: () => dispatch({ type: 'undo' }), children: _jsx(Undo2, {}) }), _jsx(Button, { size: "icon-sm", variant: "ghost", "aria-label": "Redo", title: "Redo", disabled: readOnly || !builder.canRedo, onClick: () => dispatch({ type: 'redo' }), children: _jsx(Redo2, {}) })] }), _jsx(ToolbarSeparator, {}), _jsxs(Button, { size: "sm", variant: "ghost", disabled: readOnly, onClick: () => openInsert(builder.selectedId ?? rootId), children: [_jsx(Plus, {}), " Insert"] }), _jsx(Spacer, {}), _jsx(
|
|
139
|
+
return (_jsxs("div", { className: "flex h-full min-h-0 flex-col", children: [_jsx("div", { className: "shrink-0 border-b border-border", children: _jsxs(Toolbar, { children: [_jsxs(ToolbarGroup, { children: [_jsx(Button, { size: "icon-sm", variant: "ghost", "aria-label": "Undo", title: "Undo", disabled: readOnly || !builder.canUndo, onClick: () => dispatch({ type: 'undo' }), children: _jsx(Undo2, {}) }), _jsx(Button, { size: "icon-sm", variant: "ghost", "aria-label": "Redo", title: "Redo", disabled: readOnly || !builder.canRedo, onClick: () => dispatch({ type: 'redo' }), children: _jsx(Redo2, {}) })] }), _jsx(ToolbarSeparator, {}), _jsxs(Button, { size: "sm", variant: "ghost", disabled: readOnly, onClick: () => openInsert(builder.selectedId ?? rootId), children: [_jsx(Plus, {}), " Insert"] }), _jsx(Spacer, {}), _jsx(BreakpointSwitcher, {}), _jsx(Spacer, {})] }) }), _jsxs("div", { className: "grid min-h-0 flex-1 grid-cols-[minmax(200px,240px)_1fr_minmax(280px,320px)] gap-px bg-border", children: [_jsx(Panel, { className: cn('rounded-none border-0', readOnly && 'pointer-events-none opacity-60'), children: _jsx(PageTree, { onAddChild: openInsert }) }), _jsx("div", { className: cn('flex min-h-0 flex-col bg-background', readOnly && 'pointer-events-none opacity-60'), children: _jsx(BuilderCanvas, {}) }), _jsx(InspectorSidebar, { panes: panes })] }), _jsx(InsertMenu, { context: insertContext, open: insertOpen, onOpenChange: setInsertOpen })] }));
|
|
139
140
|
}
|
|
140
141
|
//# sourceMappingURL=composition-field-editor.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"composition-field-editor.js","sourceRoot":"","sources":["../../../src/components/builder/composition-field-editor.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useMemo, useRef, useState } from 'react'\nimport { Monitor, Plus, Redo2, Smartphone, SlidersHorizontal, Tablet, Undo2 } from 'lucide-react'\nimport { DEFAULT_SLOT, blockRootsOf } from '@elytracms/core/project-graph'\nimport type { ComponentNode } from '@elytracms/core/project-graph'\nimport type { BlocksFieldDef, Cardinality } from '@elytracms/core/cms-core'\nimport { Button, Panel, Spacer, Toolbar, ToolbarGroup, ToolbarSeparator } from '@elytracms/studio/ui'\nimport { cn } from '@elytracms/studio/lib/utils'\nimport { BuilderProvider, useBuilder } from './builder-store'\nimport type { Viewport } from './builder-store'\nimport { BuilderCanvas } from './canvas'\nimport { PageTree } from './page-tree'\nimport { PropertyInspector } from './property-inspector'\nimport { InspectorSidebar } from '../inspector/inspector-sidebar'\nimport type { InspectorPane } from '../inspector/inspector-sidebar'\nimport { InsertMenu } from './insert-menu'\nimport type { InsertContext } from './insert-menu'\nimport { resolveInsertContext } from './insert-context'\nimport { CompositionScopeProvider } from './composition-scope'\n\n/**\n * EC-188 — the block-field surface: one canvas engine mounted on a composition\n * **field value** (AD-12), not the project graph.\n *\n * The composition tree is wrapped in a synthetic single-page graph and edited by\n * a SCOPED `BuilderProvider` with **no `projectId`** (so the graph persister is a\n * no-op — builder-store.tsx). Every committed edit is lifted out via `onChange`\n * into the CMS document draft; the document's own Save persists it (the tree is\n * stored as document data, `v.any()`). This reuses the real canvas + inspector +\n * blocks panel unchanged — the same EC-158–164 mechanics — without touching the\n * project-wide builder store. Surfaced on a dedicated \"Canvas\" fieldset tab in\n * the CMS detail editor so the page builder gets full screen real estate rather\n * than being crammed into the field list.\n */\n\n/** Stable scope value (a fresh object each render would re-render consumers). */\nconst COMPOSITION_SCOPE = { active: true } as const\n\nconst SYNTH_ROOT_ID = 'composition.root'\n/** The implicit root container the field's blocks compose inside (like a page root). */\nconst SYNTH_ROOT_COMPONENT = 'base.primitives.Stack'\n\n/**\n * Wrap the field value's blocks into a synthetic root container (EC-287). The\n * builder store edits this single root-bearing composition directly; its\n * children are the field's blocks, lifted back out via `onChange`.\n */\nfunction buildSyntheticRoot(value: unknown): ComponentNode {\n return {\n id: SYNTH_ROOT_ID,\n componentId: SYNTH_ROOT_COMPONENT,\n slots: { [DEFAULT_SLOT]: blockRootsOf(value) },\n }\n}\n\n/** Unwrap the synthetic root's children back to a field value per cardinality. */\nfunction unwrapValue(blocks: readonly ComponentNode[], cardinality: Cardinality): unknown {\n return cardinality === 'one' ? blocks[0] : [...blocks]\n}\n\nexport function CompositionFieldEditor({\n field,\n value,\n onChange,\n readOnly,\n trailingPanes,\n}: {\n field: BlocksFieldDef\n value: unknown\n onChange: (value: unknown) => void\n readOnly: boolean\n /** Inspector panes the document editor appends after the canvas-supplied\n * `Properties` pane (EC-224 — the frame-owned `Versions` pane). */\n trailingPanes?: readonly InspectorPane[]\n}) {\n // Build the synthetic root ONCE from the value at mount. The editor is\n // uncontrolled thereafter — it is the source of truth while open and lifts\n // changes out via onChange; re-opening the tab remounts from the current draft.\n // (BuilderProvider reads `composition` only in its lazy initial state, so later\n // prop changes are ignored regardless.)\n const initialRoot = useMemo(() => buildSyntheticRoot(value), [])\n\n return (\n <BuilderProvider composition={initialRoot}>\n <CompositionLift cardinality={field.cardinality} onChange={onChange} readOnly={readOnly} />\n <CompositionScopeProvider value={COMPOSITION_SCOPE}>\n <CompositionEditorShell\n readOnly={readOnly}\n fieldAllow={field.allow}\n trailingPanes={trailingPanes}\n />\n </CompositionScopeProvider>\n </BuilderProvider>\n )\n}\n\n/**\n * Lifts committed edits out of the scoped store into the CMS draft. The first\n * render establishes the (container-normalized) baseline so simply opening the\n * tab never marks the document dirty; every subsequent change to the block list\n * emits the unwrapped value.\n */\nfunction CompositionLift({\n cardinality,\n onChange,\n readOnly,\n}: {\n cardinality: Cardinality\n onChange: (value: unknown) => void\n readOnly: boolean\n}) {\n const builder = useBuilder()\n const blocks = builder.present.slots?.[DEFAULT_SLOT] ?? EMPTY_BLOCKS\n const onChangeRef = useRef(onChange)\n onChangeRef.current = onChange\n const baselineRef = useRef<readonly ComponentNode[] | null>(null)\n\n useEffect(() => {\n // EC-226: never lift edits out of a READ-ONLY canvas (the Published view).\n // The canvas's keyboard listeners (clipboard paste/cut) aren't blocked by the\n // visual `pointer-events-none`, so without this a Cmd+V could mutate the\n // graph; refusing to lift keeps the document draft intact regardless. The\n // editor remounts per perspective, so any transient in-canvas change is\n // discarded on switch.\n if (readOnly) return\n if (baselineRef.current === null) {\n baselineRef.current = blocks\n return\n }\n if (blocks !== baselineRef.current) {\n baselineRef.current = blocks\n onChangeRef.current(unwrapValue(blocks, cardinality))\n }\n }, [blocks, cardinality, readOnly])\n\n return null\n}\n\nconst EMPTY_BLOCKS: readonly ComponentNode[] = []\n\n/** Canvas breakpoint preview widths (EC-211) — the values `canvas.tsx` maps via\n * `VIEWPORT_WIDTH`. Static-props (v1): this is a preview-width control, not\n * per-breakpoint authoring — it lets the author verify components' own responsive\n * behavior, nothing more. */\nconst VIEWPORTS: readonly { value: Viewport; label: string; icon: React.ReactNode }[] = [\n { value: 'desktop', label: 'Desktop', icon: <Monitor /> },\n { value: 'tablet', label: 'Tablet', icon: <Tablet /> },\n { value: 'mobile', label: 'Phone', icon: <Smartphone /> },\n]\n\n/** A breakpoint preview-width toggle (EC-211). Re-homed from the retired builder\n * shell: switching the viewport resizes only the preview frame (it is not an\n * undoable edit and writes no graph command). */\nfunction ViewportButton({\n value,\n label,\n icon,\n}: {\n value: Viewport\n label: string\n icon: React.ReactNode\n}) {\n const builder = useBuilder()\n return (\n <Button\n size=\"icon-sm\"\n variant=\"ghost\"\n aria-label={`${label} preview`}\n aria-pressed={builder.viewport === value}\n title={`${label} preview`}\n onClick={() => builder.dispatch({ type: 'viewport', viewport: value })}\n className={cn(builder.viewport === value && 'bg-accent text-accent-foreground')}\n >\n {icon}\n </Button>\n )\n}\n\n/**\n * The slim editor surface — the canvas + layers + inspector, plus Insert and\n * undo/redo. It deliberately drops the project-builder *lifecycle* chrome\n * (publish, versions, AI, page SEO): the document owns the lifecycle, not the\n * field. It keeps spatial/preview controls — including the breakpoint\n * preview-width switcher (EC-211), a canvas concern, not a lifecycle one.\n */\nfunction CompositionEditorShell({\n readOnly,\n fieldAllow,\n trailingPanes,\n}: {\n readOnly: boolean\n /** The field's top-level component vocabulary (EC-188) — the leash applied to\n * inserts at the composition root so forbidden components aren't even offered. */\n fieldAllow?: readonly string[]\n /** Document-level panes appended after `Properties` (EC-224). */\n trailingPanes?: readonly InspectorPane[]\n}) {\n const builder = useBuilder()\n const dispatch = builder.dispatch\n const [insertContext, setInsertContext] = useState<InsertContext | null>(null)\n const [insertOpen, setInsertOpen] = useState(false)\n const rootId = builder.present.id\n\n // The canvas SUPPLIES the `Properties` pane (EC-224); the document editor\n // appends its own (`Versions`). Properties dims under read-only just as the\n // whole inspector column used to — but the appended panes stay interactive.\n const propertiesPane = useMemo<InspectorPane>(\n () => ({\n id: 'properties',\n label: 'Properties',\n icon: <SlidersHorizontal className=\"size-3.5\" />,\n render: () => (\n <div\n className={cn(\n 'h-full min-h-0 overflow-hidden',\n readOnly && 'pointer-events-none opacity-60',\n )}\n >\n <PropertyInspector />\n </div>\n ),\n }),\n [readOnly],\n )\n const panes = useMemo<InspectorPane[]>(\n () => [propertiesPane, ...(trailingPanes ?? [])],\n [propertiesPane, trailingPanes],\n )\n\n function openInsert(nodeId: string) {\n const ctx = resolveInsertContext(builder, nodeId, fieldAllow)\n if (ctx) {\n setInsertContext(ctx)\n setInsertOpen(true)\n }\n }\n\n return (\n <div className=\"flex h-full min-h-0 flex-col\">\n <div className=\"shrink-0 border-b border-border\">\n <Toolbar>\n <ToolbarGroup>\n <Button\n size=\"icon-sm\"\n variant=\"ghost\"\n aria-label=\"Undo\"\n title=\"Undo\"\n disabled={readOnly || !builder.canUndo}\n onClick={() => dispatch({ type: 'undo' })}\n >\n <Undo2 />\n </Button>\n <Button\n size=\"icon-sm\"\n variant=\"ghost\"\n aria-label=\"Redo\"\n title=\"Redo\"\n disabled={readOnly || !builder.canRedo}\n onClick={() => dispatch({ type: 'redo' })}\n >\n <Redo2 />\n </Button>\n </ToolbarGroup>\n <ToolbarSeparator />\n <Button\n size=\"sm\"\n variant=\"ghost\"\n disabled={readOnly}\n onClick={() => openInsert(builder.selectedId ?? rootId)}\n >\n <Plus /> Insert\n </Button>\n <Spacer />\n {/* Breakpoint preview-width switcher (EC-211). A preview control, not an\n edit — stays active even in read-only published preview. */}\n <ToolbarGroup>\n {VIEWPORTS.map((v) => (\n <ViewportButton key={v.value} value={v.value} label={v.label} icon={v.icon} />\n ))}\n </ToolbarGroup>\n </Toolbar>\n </div>\n\n <div className=\"grid min-h-0 flex-1 grid-cols-[minmax(200px,240px)_1fr_minmax(280px,320px)] gap-px bg-border\">\n <Panel\n className={cn('rounded-none border-0', readOnly && 'pointer-events-none opacity-60')}\n >\n <PageTree onAddChild={openInsert} />\n </Panel>\n <div\n className={cn(\n 'flex min-h-0 flex-col bg-background',\n readOnly && 'pointer-events-none opacity-60',\n )}\n >\n <BuilderCanvas />\n </div>\n <InspectorSidebar panes={panes} />\n </div>\n\n <InsertMenu context={insertContext} open={insertOpen} onOpenChange={setInsertOpen} />\n </div>\n )\n}\n"]}
|
|
1
|
+
{"version":3,"file":"composition-field-editor.js","sourceRoot":"","sources":["../../../src/components/builder/composition-field-editor.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useMemo, useRef, useState } from 'react'\nimport { AlertTriangle, Plus, Redo2, SlidersHorizontal, Undo2 } from 'lucide-react'\nimport { DEFAULT_SLOT, blockRootsOf } from '@elytracms/core/project-graph'\nimport type { ComponentNode } from '@elytracms/core/project-graph'\nimport type { BlocksFieldDef, Cardinality } from '@elytracms/core/cms-core'\nimport { Button, Panel, Row, Spacer, Text, Toolbar, ToolbarGroup, ToolbarSeparator } from '@elytracms/studio/ui'\nimport { cn } from '@elytracms/studio/lib/utils'\nimport { BuilderProvider, useBuilder } from './builder-store'\nimport { useCanvasViewports } from './canvas-breakpoints'\nimport type { ViewportOption } from './canvas-breakpoints'\nimport { BuilderCanvas } from './canvas'\nimport { PageTree } from './page-tree'\nimport { PropertyInspector } from './property-inspector'\nimport { InspectorSidebar } from '../inspector/inspector-sidebar'\nimport type { InspectorPane } from '../inspector/inspector-sidebar'\nimport { InsertMenu } from './insert-menu'\nimport type { InsertContext } from './insert-menu'\nimport { resolveInsertContext } from './insert-context'\nimport { CompositionScopeProvider } from './composition-scope'\n\n/**\n * EC-188 — the block-field surface: one canvas engine mounted on a composition\n * **field value** (AD-12), not the project graph.\n *\n * The composition tree is wrapped in a synthetic single-page graph and edited by\n * a SCOPED `BuilderProvider` with **no `projectId`** (so the graph persister is a\n * no-op — builder-store.tsx). Every committed edit is lifted out via `onChange`\n * into the CMS document draft; the document's own Save persists it (the tree is\n * stored as document data, `v.any()`). This reuses the real canvas + inspector +\n * blocks panel unchanged — the same EC-158–164 mechanics — without touching the\n * project-wide builder store. Surfaced on a dedicated \"Canvas\" fieldset tab in\n * the CMS detail editor so the page builder gets full screen real estate rather\n * than being crammed into the field list.\n */\n\n/** Stable scope value (a fresh object each render would re-render consumers). */\nconst COMPOSITION_SCOPE = { active: true } as const\n\nconst SYNTH_ROOT_ID = 'composition.root'\n/** The implicit root container the field's blocks compose inside (like a page root). */\nconst SYNTH_ROOT_COMPONENT = 'base.primitives.Stack'\n\n/**\n * Wrap the field value's blocks into a synthetic root container (EC-287). The\n * builder store edits this single root-bearing composition directly; its\n * children are the field's blocks, lifted back out via `onChange`.\n */\nfunction buildSyntheticRoot(value: unknown): ComponentNode {\n return {\n id: SYNTH_ROOT_ID,\n componentId: SYNTH_ROOT_COMPONENT,\n slots: { [DEFAULT_SLOT]: blockRootsOf(value) },\n }\n}\n\n/** Unwrap the synthetic root's children back to a field value per cardinality. */\nfunction unwrapValue(blocks: readonly ComponentNode[], cardinality: Cardinality): unknown {\n return cardinality === 'one' ? blocks[0] : [...blocks]\n}\n\nexport function CompositionFieldEditor({\n field,\n value,\n onChange,\n readOnly,\n trailingPanes,\n}: {\n field: BlocksFieldDef\n value: unknown\n onChange: (value: unknown) => void\n readOnly: boolean\n /** Inspector panes the document editor appends after the canvas-supplied\n * `Properties` pane (EC-224 — the frame-owned `Versions` pane). */\n trailingPanes?: readonly InspectorPane[]\n}) {\n // Build the synthetic root ONCE from the value at mount. The editor is\n // uncontrolled thereafter — it is the source of truth while open and lifts\n // changes out via onChange; re-opening the tab remounts from the current draft.\n // (BuilderProvider reads `composition` only in its lazy initial state, so later\n // prop changes are ignored regardless.)\n const initialRoot = useMemo(() => buildSyntheticRoot(value), [])\n\n return (\n <BuilderProvider composition={initialRoot}>\n <CompositionLift cardinality={field.cardinality} onChange={onChange} readOnly={readOnly} />\n <CompositionScopeProvider value={COMPOSITION_SCOPE}>\n <CompositionEditorShell\n readOnly={readOnly}\n fieldAllow={field.allow}\n trailingPanes={trailingPanes}\n />\n </CompositionScopeProvider>\n </BuilderProvider>\n )\n}\n\n/**\n * Lifts committed edits out of the scoped store into the CMS draft. The first\n * render establishes the (container-normalized) baseline so simply opening the\n * tab never marks the document dirty; every subsequent change to the block list\n * emits the unwrapped value.\n */\nfunction CompositionLift({\n cardinality,\n onChange,\n readOnly,\n}: {\n cardinality: Cardinality\n onChange: (value: unknown) => void\n readOnly: boolean\n}) {\n const builder = useBuilder()\n const blocks = builder.present.slots?.[DEFAULT_SLOT] ?? EMPTY_BLOCKS\n const onChangeRef = useRef(onChange)\n onChangeRef.current = onChange\n const baselineRef = useRef<readonly ComponentNode[] | null>(null)\n\n useEffect(() => {\n // EC-226: never lift edits out of a READ-ONLY canvas (the Published view).\n // The canvas's keyboard listeners (clipboard paste/cut) aren't blocked by the\n // visual `pointer-events-none`, so without this a Cmd+V could mutate the\n // graph; refusing to lift keeps the document draft intact regardless. The\n // editor remounts per perspective, so any transient in-canvas change is\n // discarded on switch.\n if (readOnly) return\n if (baselineRef.current === null) {\n baselineRef.current = blocks\n return\n }\n if (blocks !== baselineRef.current) {\n baselineRef.current = blocks\n onChangeRef.current(unwrapValue(blocks, cardinality))\n }\n }, [blocks, cardinality, readOnly])\n\n return null\n}\n\nconst EMPTY_BLOCKS: readonly ComponentNode[] = []\n\n/** A breakpoint preview-width toggle (EC-211/EC-250). Switching the viewport\n * resizes only the preview frame (not an undoable edit, writes no graph command).\n * The label is the breakpoint name (`Full`/`sm`/`md`/…) with the real px width in\n * the tooltip — so the switcher reflects the site's actual screens, not a fixed\n * desktop/tablet/phone triple. */\nfunction ViewportButton({ option }: { option: ViewportOption }) {\n const builder = useBuilder()\n const active = builder.viewport === option.key\n return (\n <Button\n size=\"sm\"\n variant=\"ghost\"\n aria-label={`${option.label} preview`}\n aria-pressed={active}\n title={option.px !== undefined ? `${option.label} · ${option.px}px` : `${option.label} width`}\n onClick={() => builder.dispatch({ type: 'viewport', viewport: option.key })}\n className={cn('px-2 text-xs', active && 'bg-accent text-accent-foreground')}\n >\n {option.label}\n </Button>\n )\n}\n\n/** The centered breakpoint switcher (EC-250): the resolved viewport set, plus a\n * visible validation issue when a project declared breakpoints but none are usable. */\nfunction BreakpointSwitcher() {\n const { viewports, issue } = useCanvasViewports()\n return (\n <Row gap=\"1\" align=\"center\">\n <ToolbarGroup>\n {viewports.map((v) => (\n <ViewportButton key={v.key} option={v} />\n ))}\n </ToolbarGroup>\n {issue ? (\n <Row gap=\"1\" align=\"center\" className=\"text-amber-600 dark:text-amber-400\" title={issue}>\n <AlertTriangle aria-hidden className=\"size-3.5\" />\n <Text size=\"xs\">breakpoints</Text>\n </Row>\n ) : null}\n </Row>\n )\n}\n\n/**\n * The slim editor surface — the canvas + layers + inspector, plus Insert and\n * undo/redo. It deliberately drops the project-builder *lifecycle* chrome\n * (publish, versions, AI, page SEO): the document owns the lifecycle, not the\n * field. It keeps spatial/preview controls — including the breakpoint\n * preview-width switcher (EC-211), a canvas concern, not a lifecycle one.\n */\nfunction CompositionEditorShell({\n readOnly,\n fieldAllow,\n trailingPanes,\n}: {\n readOnly: boolean\n /** The field's top-level component vocabulary (EC-188) — the leash applied to\n * inserts at the composition root so forbidden components aren't even offered. */\n fieldAllow?: readonly string[]\n /** Document-level panes appended after `Properties` (EC-224). */\n trailingPanes?: readonly InspectorPane[]\n}) {\n const builder = useBuilder()\n const dispatch = builder.dispatch\n const [insertContext, setInsertContext] = useState<InsertContext | null>(null)\n const [insertOpen, setInsertOpen] = useState(false)\n const rootId = builder.present.id\n\n // The canvas SUPPLIES the `Properties` pane (EC-224); the document editor\n // appends its own (`Versions`). Properties dims under read-only just as the\n // whole inspector column used to — but the appended panes stay interactive.\n const propertiesPane = useMemo<InspectorPane>(\n () => ({\n id: 'properties',\n label: 'Properties',\n icon: <SlidersHorizontal className=\"size-3.5\" />,\n render: () => (\n <div\n className={cn(\n 'h-full min-h-0 overflow-hidden',\n readOnly && 'pointer-events-none opacity-60',\n )}\n >\n <PropertyInspector />\n </div>\n ),\n }),\n [readOnly],\n )\n const panes = useMemo<InspectorPane[]>(\n () => [propertiesPane, ...(trailingPanes ?? [])],\n [propertiesPane, trailingPanes],\n )\n\n function openInsert(nodeId: string) {\n const ctx = resolveInsertContext(builder, nodeId, fieldAllow)\n if (ctx) {\n setInsertContext(ctx)\n setInsertOpen(true)\n }\n }\n\n return (\n <div className=\"flex h-full min-h-0 flex-col\">\n <div className=\"shrink-0 border-b border-border\">\n <Toolbar>\n <ToolbarGroup>\n <Button\n size=\"icon-sm\"\n variant=\"ghost\"\n aria-label=\"Undo\"\n title=\"Undo\"\n disabled={readOnly || !builder.canUndo}\n onClick={() => dispatch({ type: 'undo' })}\n >\n <Undo2 />\n </Button>\n <Button\n size=\"icon-sm\"\n variant=\"ghost\"\n aria-label=\"Redo\"\n title=\"Redo\"\n disabled={readOnly || !builder.canRedo}\n onClick={() => dispatch({ type: 'redo' })}\n >\n <Redo2 />\n </Button>\n </ToolbarGroup>\n <ToolbarSeparator />\n <Button\n size=\"sm\"\n variant=\"ghost\"\n disabled={readOnly}\n onClick={() => openInsert(builder.selectedId ?? rootId)}\n >\n <Plus /> Insert\n </Button>\n {/* Breakpoint preview-width switcher (EC-211/EC-250), centered over the\n canvas via flanking spacers. A preview control, not an edit — stays\n active even in read-only published preview. */}\n <Spacer />\n <BreakpointSwitcher />\n <Spacer />\n </Toolbar>\n </div>\n\n <div className=\"grid min-h-0 flex-1 grid-cols-[minmax(200px,240px)_1fr_minmax(280px,320px)] gap-px bg-border\">\n <Panel\n className={cn('rounded-none border-0', readOnly && 'pointer-events-none opacity-60')}\n >\n <PageTree onAddChild={openInsert} />\n </Panel>\n <div\n className={cn(\n 'flex min-h-0 flex-col bg-background',\n readOnly && 'pointer-events-none opacity-60',\n )}\n >\n <BuilderCanvas />\n </div>\n <InspectorSidebar panes={panes} />\n </div>\n\n <InsertMenu context={insertContext} open={insertOpen} onOpenChange={setInsertOpen} />\n </div>\n )\n}\n"]}
|
|
@@ -1,17 +1,28 @@
|
|
|
1
1
|
import type { CmsDocument, CmsValidationIssue, DocumentRef, PublicationState } from '@elytracms/core/cms-core';
|
|
2
|
+
import type { FilterScalar } from '@elytracms/core/content';
|
|
2
3
|
import type { OperationError, OperationResult } from '@elytracms/studio/lib/operations';
|
|
3
4
|
/**
|
|
4
5
|
* Bulk actions over a table selection (EC-169) — the pure half.
|
|
5
6
|
*
|
|
6
7
|
* Each selected document gets its OWN operations command
|
|
7
|
-
* (`documents.publish` / `documents.unpublish` / `documents.delete`, EC-224
|
|
8
|
-
* awaited in selection order, and
|
|
8
|
+
* (`documents.publish` / `documents.unpublish` / `documents.delete`, EC-224;
|
|
9
|
+
* `documents.upsert` for a set-field, EC-247), awaited in selection order, and
|
|
9
10
|
* its own per-item outcome. The report enumerates every failure with its
|
|
10
11
|
* reason (validation gate, authorization denial, not-found, …) — a partial
|
|
11
12
|
* failure is never silent and never collapsed into one toast. Session state is
|
|
12
13
|
* only updated for items whose command succeeded.
|
|
13
14
|
*/
|
|
14
|
-
export type BulkAction = 'publish' | 'unpublish' | 'delete';
|
|
15
|
+
export type BulkAction = 'publish' | 'unpublish' | 'delete' | 'set-field';
|
|
16
|
+
/**
|
|
17
|
+
* The payload of a `set-field` bulk action (EC-247): assign one filterable
|
|
18
|
+
* scalar field the same value across every selected document. The value is
|
|
19
|
+
* folded into each document the locale-aware way (`applyCellValue`) by the
|
|
20
|
+
* caller's `applySetField`/`dispatch`; this module only carries it.
|
|
21
|
+
*/
|
|
22
|
+
export interface BulkSetField {
|
|
23
|
+
field: string;
|
|
24
|
+
value: FilterScalar;
|
|
25
|
+
}
|
|
15
26
|
/** The outcome of one document's command within a bulk run. */
|
|
16
27
|
export interface BulkItemOutcome {
|
|
17
28
|
ref: DocumentRef;
|
|
@@ -25,6 +36,8 @@ export interface BulkItemOutcome {
|
|
|
25
36
|
}
|
|
26
37
|
export interface BulkReport {
|
|
27
38
|
action: BulkAction;
|
|
39
|
+
/** Present for `set-field`: the field + value that was applied (report label). */
|
|
40
|
+
setField?: BulkSetField;
|
|
28
41
|
results: BulkItemOutcome[];
|
|
29
42
|
}
|
|
30
43
|
export declare function bulkSummary(report: BulkReport): {
|
|
@@ -43,6 +56,11 @@ export interface BulkContext {
|
|
|
43
56
|
applyState(ref: DocumentRef, state: PublicationState): void;
|
|
44
57
|
/** Apply one succeeded delete to the session. */
|
|
45
58
|
applyDelete(ref: DocumentRef): void;
|
|
59
|
+
/**
|
|
60
|
+
* Apply one succeeded set-field to the session (locale-aware fold). Required
|
|
61
|
+
* when the action is `set-field`; unused otherwise.
|
|
62
|
+
*/
|
|
63
|
+
applySetField?(ref: DocumentRef, set: BulkSetField): void;
|
|
46
64
|
/**
|
|
47
65
|
* Dispatch the ONE operations command for an item. Absent in fixture-only
|
|
48
66
|
* sessions (no project scope) — items then mutate the session directly.
|
|
@@ -52,9 +70,9 @@ export interface BulkContext {
|
|
|
52
70
|
/**
|
|
53
71
|
* Run one bulk action over a selection, item by item:
|
|
54
72
|
* 1. publish gates on validation — an invalid document fails with its issues
|
|
55
|
-
* and dispatches NOTHING,
|
|
73
|
+
* and dispatches NOTHING (a set-field is a draft edit, EC-225 — never gated),
|
|
56
74
|
* 2. each remaining item dispatches exactly one operations command, awaited,
|
|
57
75
|
* 3. only succeeded items mutate the session,
|
|
58
76
|
* 4. the report carries one honest outcome per item, in selection order.
|
|
59
77
|
*/
|
|
60
|
-
export declare function runBulkAction(context: BulkContext, collectionId: string, ids: readonly string[], action: BulkAction): Promise<BulkReport>;
|
|
78
|
+
export declare function runBulkAction(context: BulkContext, collectionId: string, ids: readonly string[], action: BulkAction, setField?: BulkSetField): Promise<BulkReport>;
|
|
@@ -19,12 +19,12 @@ const ACTION_STATE = {
|
|
|
19
19
|
/**
|
|
20
20
|
* Run one bulk action over a selection, item by item:
|
|
21
21
|
* 1. publish gates on validation — an invalid document fails with its issues
|
|
22
|
-
* and dispatches NOTHING,
|
|
22
|
+
* and dispatches NOTHING (a set-field is a draft edit, EC-225 — never gated),
|
|
23
23
|
* 2. each remaining item dispatches exactly one operations command, awaited,
|
|
24
24
|
* 3. only succeeded items mutate the session,
|
|
25
25
|
* 4. the report carries one honest outcome per item, in selection order.
|
|
26
26
|
*/
|
|
27
|
-
export async function runBulkAction(context, collectionId, ids, action) {
|
|
27
|
+
export async function runBulkAction(context, collectionId, ids, action, setField) {
|
|
28
28
|
const results = [];
|
|
29
29
|
for (const id of ids) {
|
|
30
30
|
const ref = { collection: collectionId, id };
|
|
@@ -86,10 +86,14 @@ export async function runBulkAction(context, collectionId, ids, action) {
|
|
|
86
86
|
}
|
|
87
87
|
if (action === 'delete')
|
|
88
88
|
context.applyDelete(ref);
|
|
89
|
+
else if (action === 'set-field') {
|
|
90
|
+
if (setField)
|
|
91
|
+
context.applySetField?.(ref, setField);
|
|
92
|
+
}
|
|
89
93
|
else
|
|
90
94
|
context.applyState(ref, ACTION_STATE[action]);
|
|
91
95
|
results.push({ ref, title, ok: true });
|
|
92
96
|
}
|
|
93
|
-
return { action, results };
|
|
97
|
+
return { action, ...(setField ? { setField } : {}), results };
|
|
94
98
|
}
|
|
95
99
|
//# sourceMappingURL=bulk-actions.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bulk-actions.js","sourceRoot":"","sources":["../../../src/components/cms/bulk-actions.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n CmsDocument,\n CmsValidationIssue,\n DocumentRef,\n PublicationState,\n} from '@elytracms/core/cms-core'\nimport type { OperationError, OperationResult } from '@elytracms/studio/lib/operations'\n\n/**\n * Bulk actions over a table selection (EC-169) — the pure half.\n *\n * Each selected document gets its OWN operations command\n * (`documents.publish` / `documents.unpublish` / `documents.delete`, EC-224),\n * awaited in selection order, and\n * its own per-item outcome. The report enumerates every failure with its\n * reason (validation gate, authorization denial, not-found, …) — a partial\n * failure is never silent and never collapsed into one toast. Session state is\n * only updated for items whose command succeeded.\n */\n\nexport type BulkAction = 'publish' | 'unpublish' | 'delete'\n\n/** The outcome of one document's command within a bulk run. */\nexport interface BulkItemOutcome {\n ref: DocumentRef\n /** Display title at dispatch time (deleted docs still get named in the report). */\n title: string\n ok: boolean\n /** Human-readable failure reason — present exactly when `ok` is false. */\n reason?: string\n /** The structured error behind the reason (operation result / validation gate). */\n error?: OperationError\n}\n\nexport interface BulkReport {\n action: BulkAction\n results: BulkItemOutcome[]\n}\n\nexport function bulkSummary(report: BulkReport): { succeeded: number; failed: number } {\n let succeeded = 0\n for (const result of report.results) if (result.ok) succeeded += 1\n return { succeeded, failed: report.results.length - succeeded }\n}\n\n/** The failure reason a report row shows: validation issues beat the envelope. */\nexport function bulkErrorReason(error: OperationError): string {\n if (error.code === 'validation' && error.issues.length > 0) {\n return error.issues.map((issue) => issue.message).join(' · ')\n }\n return error.message\n}\n\nexport interface BulkContext {\n /** Current session documents (lookup + validation input). */\n documents: readonly CmsDocument[]\n titleOf(ref: DocumentRef): string\n /** Full document validation — gates publish: invalid documents never dispatch. */\n validate(doc: CmsDocument): CmsValidationIssue[]\n /** Apply one succeeded state transition to the session. */\n applyState(ref: DocumentRef, state: PublicationState): void\n /** Apply one succeeded delete to the session. */\n applyDelete(ref: DocumentRef): void\n /**\n * Dispatch the ONE operations command for an item. Absent in fixture-only\n * sessions (no project scope) — items then mutate the session directly.\n */\n dispatch?: (action: BulkAction, ref: DocumentRef) => Promise<OperationResult<unknown>>\n}\n\nconst ACTION_STATE: Record<Exclude<BulkAction, 'delete'>, PublicationState> = {\n publish: 'published',\n unpublish: 'draft',\n}\n\n/**\n * Run one bulk action over a selection, item by item:\n * 1. publish gates on validation — an invalid document fails with its issues\n * and dispatches NOTHING,\n * 2. each remaining item dispatches exactly one operations command, awaited,\n * 3. only succeeded items mutate the session,\n * 4. the report carries one honest outcome per item, in selection order.\n */\nexport async function runBulkAction(\n context: BulkContext,\n collectionId: string,\n ids: readonly string[],\n action: BulkAction,\n): Promise<BulkReport> {\n const results: BulkItemOutcome[] = []\n\n for (const id of ids) {\n const ref: DocumentRef = { collection: collectionId, id }\n const title = context.titleOf(ref)\n const fail = (error: OperationError) =>\n results.push({ ref, title, ok: false, reason: bulkErrorReason(error), error })\n\n const doc = context.documents.find((d) => d.collection === collectionId && d.id === id)\n if (!doc) {\n fail({\n code: 'not-found',\n message: 'Document is no longer in the session.',\n entity: 'cms-document',\n key: `${collectionId}:${id}`,\n })\n continue\n }\n\n if (action === 'publish') {\n const errors = context.validate(doc).filter((issue) => issue.severity === 'error')\n if (errors.length > 0) {\n fail({\n code: 'validation',\n message: 'Document is not valid to publish.',\n issues: errors.map((issue) => ({\n code: issue.code,\n message: issue.message,\n path: [...issue.path],\n domain: 'cms',\n })),\n })\n continue\n }\n }\n\n if (context.dispatch) {\n let result: OperationResult<unknown>\n try {\n result = await context.dispatch(action, ref)\n } catch (cause) {\n // Operations never throw by contract; a broken transport still becomes\n // a visible per-item failure instead of an aborted bulk run.\n result = {\n ok: false,\n error: {\n code: 'unknown',\n message: cause instanceof Error ? cause.message : String(cause),\n },\n }\n }\n if (!result.ok) {\n // Idempotent delete: the document was never persisted (e.g. a draft\n // whose baseline upsert was rejected) or another editor already\n // deleted it. The desired end state — gone — holds, so removing it\n // from the session is the honest outcome, not a failure.\n const idempotentDelete = action === 'delete' && result.error.code === 'not-found'\n if (!idempotentDelete) {\n fail(result.error)\n continue\n }\n }\n }\n\n if (action === 'delete') context.applyDelete(ref)\n else context.applyState(ref, ACTION_STATE[action])\n results.push({ ref, title, ok: true })\n }\n\n return { action, results }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"bulk-actions.js","sourceRoot":"","sources":["../../../src/components/cms/bulk-actions.ts"],"names":[],"mappings":"AAqDA,MAAM,UAAU,WAAW,CAAC,MAAkB;IAC5C,IAAI,SAAS,GAAG,CAAC,CAAA;IACjB,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,OAAO;QAAE,IAAI,MAAM,CAAC,EAAE;YAAE,SAAS,IAAI,CAAC,CAAA;IAClE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,SAAS,EAAE,CAAA;AACjE,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,eAAe,CAAC,KAAqB;IACnD,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3D,OAAO,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IAC/D,CAAC;IACD,OAAO,KAAK,CAAC,OAAO,CAAA;AACtB,CAAC;AAwBD,MAAM,YAAY,GAAsD;IACtE,OAAO,EAAE,WAAW;IACpB,SAAS,EAAE,OAAO;CACnB,CAAA;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,OAAoB,EACpB,YAAoB,EACpB,GAAsB,EACtB,MAAkB,EAClB,QAAuB;IAEvB,MAAM,OAAO,GAAsB,EAAE,CAAA;IAErC,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC;QACrB,MAAM,GAAG,GAAgB,EAAE,UAAU,EAAE,YAAY,EAAE,EAAE,EAAE,CAAA;QACzD,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QAClC,MAAM,IAAI,GAAG,CAAC,KAAqB,EAAE,EAAE,CACrC,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,CAAC,CAAA;QAEhF,MAAM,GAAG,GAAG,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,YAAY,IAAI,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAA;QACvF,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,IAAI,CAAC;gBACH,IAAI,EAAE,WAAW;gBACjB,OAAO,EAAE,uCAAuC;gBAChD,MAAM,EAAE,cAAc;gBACtB,GAAG,EAAE,GAAG,YAAY,IAAI,EAAE,EAAE;aAC7B,CAAC,CAAA;YACF,SAAQ;QACV,CAAC;QAED,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAA;YAClF,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACtB,IAAI,CAAC;oBACH,IAAI,EAAE,YAAY;oBAClB,OAAO,EAAE,mCAAmC;oBAC5C,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;wBAC7B,IAAI,EAAE,KAAK,CAAC,IAAI;wBAChB,OAAO,EAAE,KAAK,CAAC,OAAO;wBACtB,IAAI,EAAE,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC;wBACrB,MAAM,EAAE,KAAK;qBACd,CAAC,CAAC;iBACJ,CAAC,CAAA;gBACF,SAAQ;YACV,CAAC;QACH,CAAC;QAED,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;YACrB,IAAI,MAAgC,CAAA;YACpC,IAAI,CAAC;gBACH,MAAM,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;YAC9C,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,uEAAuE;gBACvE,6DAA6D;gBAC7D,MAAM,GAAG;oBACP,EAAE,EAAE,KAAK;oBACT,KAAK,EAAE;wBACL,IAAI,EAAE,SAAS;wBACf,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;qBAChE;iBACF,CAAA;YACH,CAAC;YACD,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;gBACf,oEAAoE;gBACpE,gEAAgE;gBAChE,mEAAmE;gBACnE,yDAAyD;gBACzD,MAAM,gBAAgB,GAAG,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,KAAK,WAAW,CAAA;gBACjF,IAAI,CAAC,gBAAgB,EAAE,CAAC;oBACtB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;oBAClB,SAAQ;gBACV,CAAC;YACH,CAAC;QACH,CAAC;QAED,IAAI,MAAM,KAAK,QAAQ;YAAE,OAAO,CAAC,WAAW,CAAC,GAAG,CAAC,CAAA;aAC5C,IAAI,MAAM,KAAK,WAAW,EAAE,CAAC;YAChC,IAAI,QAAQ;gBAAE,OAAO,CAAC,aAAa,EAAE,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAA;QACtD,CAAC;;YAAM,OAAO,CAAC,UAAU,CAAC,GAAG,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC,CAAA;QACpD,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAA;IACxC,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,OAAO,EAAE,CAAA;AAC/D,CAAC","sourcesContent":["import type {\n CmsDocument,\n CmsValidationIssue,\n DocumentRef,\n PublicationState,\n} from '@elytracms/core/cms-core'\nimport type { FilterScalar } from '@elytracms/core/content'\nimport type { OperationError, OperationResult } from '@elytracms/studio/lib/operations'\n\n/**\n * Bulk actions over a table selection (EC-169) — the pure half.\n *\n * Each selected document gets its OWN operations command\n * (`documents.publish` / `documents.unpublish` / `documents.delete`, EC-224;\n * `documents.upsert` for a set-field, EC-247), awaited in selection order, and\n * its own per-item outcome. The report enumerates every failure with its\n * reason (validation gate, authorization denial, not-found, …) — a partial\n * failure is never silent and never collapsed into one toast. Session state is\n * only updated for items whose command succeeded.\n */\n\nexport type BulkAction = 'publish' | 'unpublish' | 'delete' | 'set-field'\n\n/**\n * The payload of a `set-field` bulk action (EC-247): assign one filterable\n * scalar field the same value across every selected document. The value is\n * folded into each document the locale-aware way (`applyCellValue`) by the\n * caller's `applySetField`/`dispatch`; this module only carries it.\n */\nexport interface BulkSetField {\n field: string\n value: FilterScalar\n}\n\n/** The outcome of one document's command within a bulk run. */\nexport interface BulkItemOutcome {\n ref: DocumentRef\n /** Display title at dispatch time (deleted docs still get named in the report). */\n title: string\n ok: boolean\n /** Human-readable failure reason — present exactly when `ok` is false. */\n reason?: string\n /** The structured error behind the reason (operation result / validation gate). */\n error?: OperationError\n}\n\nexport interface BulkReport {\n action: BulkAction\n /** Present for `set-field`: the field + value that was applied (report label). */\n setField?: BulkSetField\n results: BulkItemOutcome[]\n}\n\nexport function bulkSummary(report: BulkReport): { succeeded: number; failed: number } {\n let succeeded = 0\n for (const result of report.results) if (result.ok) succeeded += 1\n return { succeeded, failed: report.results.length - succeeded }\n}\n\n/** The failure reason a report row shows: validation issues beat the envelope. */\nexport function bulkErrorReason(error: OperationError): string {\n if (error.code === 'validation' && error.issues.length > 0) {\n return error.issues.map((issue) => issue.message).join(' · ')\n }\n return error.message\n}\n\nexport interface BulkContext {\n /** Current session documents (lookup + validation input). */\n documents: readonly CmsDocument[]\n titleOf(ref: DocumentRef): string\n /** Full document validation — gates publish: invalid documents never dispatch. */\n validate(doc: CmsDocument): CmsValidationIssue[]\n /** Apply one succeeded state transition to the session. */\n applyState(ref: DocumentRef, state: PublicationState): void\n /** Apply one succeeded delete to the session. */\n applyDelete(ref: DocumentRef): void\n /**\n * Apply one succeeded set-field to the session (locale-aware fold). Required\n * when the action is `set-field`; unused otherwise.\n */\n applySetField?(ref: DocumentRef, set: BulkSetField): void\n /**\n * Dispatch the ONE operations command for an item. Absent in fixture-only\n * sessions (no project scope) — items then mutate the session directly.\n */\n dispatch?: (action: BulkAction, ref: DocumentRef) => Promise<OperationResult<unknown>>\n}\n\nconst ACTION_STATE: Record<'publish' | 'unpublish', PublicationState> = {\n publish: 'published',\n unpublish: 'draft',\n}\n\n/**\n * Run one bulk action over a selection, item by item:\n * 1. publish gates on validation — an invalid document fails with its issues\n * and dispatches NOTHING (a set-field is a draft edit, EC-225 — never gated),\n * 2. each remaining item dispatches exactly one operations command, awaited,\n * 3. only succeeded items mutate the session,\n * 4. the report carries one honest outcome per item, in selection order.\n */\nexport async function runBulkAction(\n context: BulkContext,\n collectionId: string,\n ids: readonly string[],\n action: BulkAction,\n setField?: BulkSetField,\n): Promise<BulkReport> {\n const results: BulkItemOutcome[] = []\n\n for (const id of ids) {\n const ref: DocumentRef = { collection: collectionId, id }\n const title = context.titleOf(ref)\n const fail = (error: OperationError) =>\n results.push({ ref, title, ok: false, reason: bulkErrorReason(error), error })\n\n const doc = context.documents.find((d) => d.collection === collectionId && d.id === id)\n if (!doc) {\n fail({\n code: 'not-found',\n message: 'Document is no longer in the session.',\n entity: 'cms-document',\n key: `${collectionId}:${id}`,\n })\n continue\n }\n\n if (action === 'publish') {\n const errors = context.validate(doc).filter((issue) => issue.severity === 'error')\n if (errors.length > 0) {\n fail({\n code: 'validation',\n message: 'Document is not valid to publish.',\n issues: errors.map((issue) => ({\n code: issue.code,\n message: issue.message,\n path: [...issue.path],\n domain: 'cms',\n })),\n })\n continue\n }\n }\n\n if (context.dispatch) {\n let result: OperationResult<unknown>\n try {\n result = await context.dispatch(action, ref)\n } catch (cause) {\n // Operations never throw by contract; a broken transport still becomes\n // a visible per-item failure instead of an aborted bulk run.\n result = {\n ok: false,\n error: {\n code: 'unknown',\n message: cause instanceof Error ? cause.message : String(cause),\n },\n }\n }\n if (!result.ok) {\n // Idempotent delete: the document was never persisted (e.g. a draft\n // whose baseline upsert was rejected) or another editor already\n // deleted it. The desired end state — gone — holds, so removing it\n // from the session is the honest outcome, not a failure.\n const idempotentDelete = action === 'delete' && result.error.code === 'not-found'\n if (!idempotentDelete) {\n fail(result.error)\n continue\n }\n }\n }\n\n if (action === 'delete') context.applyDelete(ref)\n else if (action === 'set-field') {\n if (setField) context.applySetField?.(ref, setField)\n } else context.applyState(ref, ACTION_STATE[action])\n results.push({ ref, title, ok: true })\n }\n\n return { action, ...(setField ? { setField } : {}), results }\n}\n"]}
|