@elytracms/studio 0.0.31 → 0.0.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/dist/components/app-views/default-modules.js +63 -21
  2. package/dist/components/app-views/default-modules.js.map +1 -1
  3. package/dist/components/assets/asset-library.js +21 -13
  4. package/dist/components/assets/asset-library.js.map +1 -1
  5. package/dist/components/assets/asset-thumb.js +3 -2
  6. package/dist/components/assets/asset-thumb.js.map +1 -1
  7. package/dist/components/backend-gate.js +4 -3
  8. package/dist/components/backend-gate.js.map +1 -1
  9. package/dist/components/builder/blocks-panel.js +11 -2
  10. package/dist/components/builder/blocks-panel.js.map +1 -1
  11. package/dist/components/builder/canvas.js +6 -5
  12. package/dist/components/builder/canvas.js.map +1 -1
  13. package/dist/components/builder/clipboard-actions.js +12 -6
  14. package/dist/components/builder/clipboard-actions.js.map +1 -1
  15. package/dist/components/builder/composition-field-editor.js +7 -4
  16. package/dist/components/builder/composition-field-editor.js.map +1 -1
  17. package/dist/components/builder/fixture-thumbnail.js +2 -1
  18. package/dist/components/builder/fixture-thumbnail.js.map +1 -1
  19. package/dist/components/builder/layout-inspector.d.ts +5 -5
  20. package/dist/components/builder/layout-inspector.js +21 -20
  21. package/dist/components/builder/layout-inspector.js.map +1 -1
  22. package/dist/components/builder/page-tree.js +5 -4
  23. package/dist/components/builder/page-tree.js.map +1 -1
  24. package/dist/components/builder/property-inspector.js +10 -10
  25. package/dist/components/builder/property-inspector.js.map +1 -1
  26. package/dist/components/builder/reuse-context.js +9 -9
  27. package/dist/components/builder/reuse-context.js.map +1 -1
  28. package/dist/components/builder/rich-text-toolbar.js +4 -3
  29. package/dist/components/builder/rich-text-toolbar.js.map +1 -1
  30. package/dist/components/builder/spacing-handles.js +4 -3
  31. package/dist/components/builder/spacing-handles.js.map +1 -1
  32. package/dist/components/cms/asset-picker.js +8 -7
  33. package/dist/components/cms/asset-picker.js.map +1 -1
  34. package/dist/components/cms/bulk-bar.js +8 -5
  35. package/dist/components/cms/bulk-bar.js.map +1 -1
  36. package/dist/components/cms/cell.js +9 -5
  37. package/dist/components/cms/cell.js.map +1 -1
  38. package/dist/components/cms/cms-store.js +4 -3
  39. package/dist/components/cms/cms-store.js.map +1 -1
  40. package/dist/components/cms/collection-table.js +18 -7
  41. package/dist/components/cms/collection-table.js.map +1 -1
  42. package/dist/components/cms/detail-editor.js +38 -27
  43. package/dist/components/cms/detail-editor.js.map +1 -1
  44. package/dist/components/cms/editable-cell.js +4 -4
  45. package/dist/components/cms/editable-cell.js.map +1 -1
  46. package/dist/components/cms/field-control.js +29 -21
  47. package/dist/components/cms/field-control.js.map +1 -1
  48. package/dist/components/cms/filter-builder.js +9 -8
  49. package/dist/components/cms/filter-builder.js.map +1 -1
  50. package/dist/components/cms/reference-confirm.js +24 -10
  51. package/dist/components/cms/reference-confirm.js.map +1 -1
  52. package/dist/components/cms/relation-picker.js +5 -4
  53. package/dist/components/cms/relation-picker.js.map +1 -1
  54. package/dist/components/cms/scalar-value-editor.js +5 -5
  55. package/dist/components/cms/scalar-value-editor.js.map +1 -1
  56. package/dist/components/cms/version-compare.js +36 -21
  57. package/dist/components/cms/version-compare.js.map +1 -1
  58. package/dist/components/cms/versions-pane.js +8 -5
  59. package/dist/components/cms/versions-pane.js.map +1 -1
  60. package/dist/components/cms/view-bar.js +3 -2
  61. package/dist/components/cms/view-bar.js.map +1 -1
  62. package/dist/components/components/component-detail.js +3 -2
  63. package/dist/components/components/component-detail.js.map +1 -1
  64. package/dist/components/components/component-status.js +8 -13
  65. package/dist/components/components/component-status.js.map +1 -1
  66. package/dist/components/components/components-index.js +5 -1
  67. package/dist/components/components/components-index.js.map +1 -1
  68. package/dist/components/components/field-inspector.js +4 -4
  69. package/dist/components/components/field-inspector.js.map +1 -1
  70. package/dist/components/components/fixture-runner.js +4 -1
  71. package/dist/components/components/fixture-runner.js.map +1 -1
  72. package/dist/components/components/registry-browser.js +4 -3
  73. package/dist/components/components/registry-browser.js.map +1 -1
  74. package/dist/components/delivery-health-banner.js +2 -1
  75. package/dist/components/delivery-health-banner.js.map +1 -1
  76. package/dist/components/dev-login-badge.js +2 -1
  77. package/dist/components/dev-login-badge.js.map +1 -1
  78. package/dist/components/extensions/extensions-view.js +22 -21
  79. package/dist/components/extensions/extensions-view.js.map +1 -1
  80. package/dist/components/guards.js +3 -2
  81. package/dist/components/guards.js.map +1 -1
  82. package/dist/components/operation-error.js +3 -2
  83. package/dist/components/operation-error.js.map +1 -1
  84. package/dist/components/playground-badge.js +2 -1
  85. package/dist/components/playground-badge.js.map +1 -1
  86. package/dist/components/project-list.js +5 -4
  87. package/dist/components/project-list.js.map +1 -1
  88. package/dist/components/project-session.js +3 -2
  89. package/dist/components/project-session.js.map +1 -1
  90. package/dist/components/project-switcher.js +4 -3
  91. package/dist/components/project-switcher.js.map +1 -1
  92. package/dist/components/redirects/redirects-module.js +3 -2
  93. package/dist/components/redirects/redirects-module.js.map +1 -1
  94. package/dist/components/references/references-panel.js +7 -6
  95. package/dist/components/references/references-panel.js.map +1 -1
  96. package/dist/components/section-nav.js +8 -5
  97. package/dist/components/section-nav.js.map +1 -1
  98. package/dist/components/section-state.js +13 -32
  99. package/dist/components/section-state.js.map +1 -1
  100. package/dist/components/settings-form.js +2 -1
  101. package/dist/components/settings-form.js.map +1 -1
  102. package/dist/components/studio-project-screen.js +3 -2
  103. package/dist/components/studio-project-screen.js.map +1 -1
  104. package/dist/components/studio-shell.js +3 -2
  105. package/dist/components/studio-shell.js.map +1 -1
  106. package/dist/components/users/users-module.js +5 -4
  107. package/dist/components/users/users-module.js.map +1 -1
  108. package/dist/components/workspace-gate.js +2 -1
  109. package/dist/components/workspace-gate.js.map +1 -1
  110. package/dist/lib/i18n/i18n.d.ts +18 -4
  111. package/dist/lib/i18n/i18n.js +22 -16
  112. package/dist/lib/i18n/i18n.js.map +1 -1
  113. package/dist/lib/i18n/messages.en.d.ts +11 -4
  114. package/dist/lib/i18n/messages.en.js +636 -6
  115. package/dist/lib/i18n/messages.en.js.map +1 -1
  116. 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 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 type { CanvasPerspective } 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 { useReuse } from './reuse-context'\nimport { reuseEmbedRef } from './reuse'\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 perspective = 'draft',\n}: {\n /** Resolve references against this perspective (EC-300/AD-16). The editable Draft\n * view passes `'draft'` (a just-made reusable shows immediately); the read-only\n * Published view passes `'published'` (honest preview of what delivery serves). */\n perspective?: CanvasPerspective\n} = {}) {\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 // EC-300: the reuse capability identifies a transcluding embed + its relation prop,\n // so the canvas can detect a HARD reference whose target doesn't resolve and show a\n // placeholder instead of an empty section. Null-safe (no provider / no capability).\n const reuse = useReuse()\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, perspective),\n [activeLocale, perspective],\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, perspective),\n [activeLocale, perspective],\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, perspective),\n [activeLocale, perspective],\n )\n\n // EC-300/AD-16: a transcluding embed whose target doesn't resolve in the active\n // perspective renders a placeholder, never an empty section. `unpublished` = the\n // target exists as a draft but isn't live (Published view of a draft-only reusable);\n // `missing` = no resolvable target at all (deleted). `null` = resolves fine, or the\n // node isn't an embed / has no target picked yet (its own empty state owns that).\n const unresolvedReuse = useCallback(\n (node: ComponentNode): 'unpublished' | 'missing' | null => {\n const cap = reuse?.capability\n if (!cap) return null\n const ref = reuseEmbedRef(node, cap)\n if (!ref) return null\n if (resolveCanvasRelation(ref, activeLocale, perspective)) return null\n return resolveCanvasRelation(ref, activeLocale, 'draft') ? 'unpublished' : 'missing'\n },\n [reuse, activeLocale, perspective],\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 if (!pageNodeIds.has(node.id)) return rendered\n // An unresolved hard reference shows the placeholder in place of the empty\n // transclusion; the node stays selectable, so its inspector relation chip\n // (EC-296) deep-links to the reusable to edit/publish it.\n const unresolved = unresolvedReuse(node)\n return (\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 {unresolved ? <UnresolvedReusablePlaceholder status={unresolved} /> : rendered}\n </div>\n )\n },\n }),\n [studio, resolveBinding, resolveAsset, resolveRelation, resolveListing, resolveLink, unresolvedReuse, 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\n/**\n * In-canvas placeholder for a transcluding embed whose target doesn't resolve\n * (EC-300/AD-16) — shown in place of the empty section so a hard reference never\n * silently vanishes. Rendered INTO the iframe (portal), which carries only the\n * project's stylesheet, so it uses inline styles rather than studio UI classes.\n * The node stays selectable; the inspector's `element` relation chip (EC-296)\n * deep-links to the reusable to edit or publish it.\n */\nfunction UnresolvedReusablePlaceholder({ status }: { status: 'unpublished' | 'missing' }) {\n const title =\n status === 'unpublished'\n ? 'This reusable element isn’t published yet'\n : 'This reusable element is missing'\n const hint =\n status === 'unpublished'\n ? 'Publish it to show it here and on live pages — or open it from the Properties panel to keep editing.'\n : 'Open the embed’s “Element” in the Properties panel to fix the reference.'\n return (\n <div\n style={{\n boxSizing: 'border-box',\n border: '1px dashed rgba(120,120,120,0.5)',\n borderRadius: 8,\n padding: '24px 28px',\n margin: '12px 16px',\n background: 'rgba(127,127,127,0.06)',\n color: 'rgba(90,90,90,0.95)',\n font: '14px/1.5 system-ui, -apple-system, sans-serif',\n textAlign: 'center',\n }}\n >\n <div style={{ fontWeight: 600 }}>⚠ {title}</div>\n <div style={{ marginTop: 6, fontSize: 13, opacity: 0.85 }}>{hint}</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 { translate } from '@elytracms/studio/lib/i18n'\nimport { useOptionalAssets } from '../assets/assets-store'\nimport {\n resolveCanvasLink,\n resolveCanvasListing,\n resolveCanvasRelation,\n useData,\n} from '@elytracms/studio/lib/data'\nimport type { CanvasPerspective } 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 { useReuse } from './reuse-context'\nimport { reuseEmbedRef } from './reuse'\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 perspective = 'draft',\n}: {\n /** Resolve references against this perspective (EC-300/AD-16). The editable Draft\n * view passes `'draft'` (a just-made reusable shows immediately); the read-only\n * Published view passes `'published'` (honest preview of what delivery serves). */\n perspective?: CanvasPerspective\n} = {}) {\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 // EC-300: the reuse capability identifies a transcluding embed + its relation prop,\n // so the canvas can detect a HARD reference whose target doesn't resolve and show a\n // placeholder instead of an empty section. Null-safe (no provider / no capability).\n const reuse = useReuse()\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, perspective),\n [activeLocale, perspective],\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, perspective),\n [activeLocale, perspective],\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, perspective),\n [activeLocale, perspective],\n )\n\n // EC-300/AD-16: a transcluding embed whose target doesn't resolve in the active\n // perspective renders a placeholder, never an empty section. `unpublished` = the\n // target exists as a draft but isn't live (Published view of a draft-only reusable);\n // `missing` = no resolvable target at all (deleted). `null` = resolves fine, or the\n // node isn't an embed / has no target picked yet (its own empty state owns that).\n const unresolvedReuse = useCallback(\n (node: ComponentNode): 'unpublished' | 'missing' | null => {\n const cap = reuse?.capability\n if (!cap) return null\n const ref = reuseEmbedRef(node, cap)\n if (!ref) return null\n if (resolveCanvasRelation(ref, activeLocale, perspective)) return null\n return resolveCanvasRelation(ref, activeLocale, 'draft') ? 'unpublished' : 'missing'\n },\n [reuse, activeLocale, perspective],\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 if (!pageNodeIds.has(node.id)) return rendered\n // An unresolved hard reference shows the placeholder in place of the empty\n // transclusion; the node stays selectable, so its inspector relation chip\n // (EC-296) deep-links to the reusable to edit/publish it.\n const unresolved = unresolvedReuse(node)\n return (\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 {unresolved ? <UnresolvedReusablePlaceholder status={unresolved} /> : rendered}\n </div>\n )\n },\n }),\n [studio, resolveBinding, resolveAsset, resolveRelation, resolveListing, resolveLink, unresolvedReuse, 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={translate('builder.canvas.pagePreview')}\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\n/**\n * In-canvas placeholder for a transcluding embed whose target doesn't resolve\n * (EC-300/AD-16) — shown in place of the empty section so a hard reference never\n * silently vanishes. Rendered INTO the iframe (portal), which carries only the\n * project's stylesheet, so it uses inline styles rather than studio UI classes.\n * The node stays selectable; the inspector's `element` relation chip (EC-296)\n * deep-links to the reusable to edit or publish it.\n */\nfunction UnresolvedReusablePlaceholder({ status }: { status: 'unpublished' | 'missing' }) {\n const title =\n status === 'unpublished'\n ? translate('builder.canvas.reusableUnpublishedTitle')\n : translate('builder.canvas.reusableMissingTitle')\n const hint =\n status === 'unpublished'\n ? translate('builder.canvas.reusableUnpublishedHint')\n : translate('builder.canvas.reusableMissingHint')\n return (\n <div\n style={{\n boxSizing: 'border-box',\n border: '1px dashed rgba(120,120,120,0.5)',\n borderRadius: 8,\n padding: '24px 28px',\n margin: '12px 16px',\n background: 'rgba(127,127,127,0.06)',\n color: 'rgba(90,90,90,0.95)',\n font: '14px/1.5 system-ui, -apple-system, sans-serif',\n textAlign: 'center',\n }}\n >\n <div style={{ fontWeight: 600 }}>⚠ {title}</div>\n <div style={{ marginTop: 6, fontSize: 13, opacity: 0.85 }}>{hint}</div>\n </div>\n )\n}\n"]}
@@ -5,6 +5,7 @@ import { cloneWithNewIds } from '@elytracms/core/project-graph';
5
5
  import { buildFragmentFromComposition } from '@elytracms/core/clipboard';
6
6
  import { readFragmentFromClipboard, readFragmentFromDataTransfer, resolvePasteTarget, diffPastedSubtree, pasteSummaryMessage, writeFragmentToClipboard, writeFragmentToDataTransfer, } from '@elytracms/studio/lib/clipboard';
7
7
  import { getStudioRegistry } from '@elytracms/studio/lib/registry';
8
+ import { translate } from '@elytracms/studio/lib/i18n';
8
9
  import { useData } from '@elytracms/studio/lib/data';
9
10
  import { useCollab } from '../collab/collab-context';
10
11
  import { Badge, Row, Text } from '@elytracms/studio/ui';
@@ -84,7 +85,7 @@ export function useStudioClipboard() {
84
85
  .then(() => publishClipboardNotice({ kind, label }))
85
86
  .catch((cause) => {
86
87
  const detail = cause instanceof Error ? cause.message : String(cause);
87
- publishClipboardNotice({ kind: 'error', message: `Could not write to the clipboard: ${detail}` });
88
+ publishClipboardNotice({ kind: 'error', message: translate('builder.clipboard.writeError', { detail }) });
88
89
  });
89
90
  };
90
91
  const currentRoot = () => {
@@ -132,7 +133,7 @@ export function useStudioClipboard() {
132
133
  const { builder: b } = stateRef.current;
133
134
  const root = currentRoot();
134
135
  if (root && nodeId === root.id) {
135
- publishClipboardNotice({ kind: 'error', message: 'The page root cannot be cut.' });
136
+ publishClipboardNotice({ kind: 'error', message: translate('builder.clipboard.rootCannotBeCut') });
136
137
  return;
137
138
  }
138
139
  const built = fragmentFor(nodeId);
@@ -236,11 +237,16 @@ export function ClipboardNoticeOverlay() {
236
237
  return null;
237
238
  const tone = notice.kind === 'error' ? 'error' : notice.kind === 'pasted' && notice.issues.length > 0 ? 'warning' : 'success';
238
239
  return (_jsx("div", { "data-testid": "clipboard-notice", "data-notice-kind": notice.kind, className: "absolute bottom-4 left-1/2 z-20 w-max max-w-[min(420px,90%)] -translate-x-1/2", children: _jsxs("div", { className: "rounded-lg border border-border bg-popover p-2 text-popover-foreground shadow-lg", children: [_jsxs(Row, { gap: "2", align: "start", children: [tone === 'error' ? (_jsx(AlertTriangle, { className: "mt-0.5 size-3.5 shrink-0 text-destructive", "aria-hidden": true })) : tone === 'warning' ? (_jsx(AlertTriangle, { className: "mt-0.5 size-3.5 shrink-0 text-warning", "aria-hidden": true })) : (_jsx(CircleCheck, { className: "mt-0.5 size-3.5 shrink-0 text-success", "aria-hidden": true })), _jsx(Text, { size: "xs", className: "min-w-0 flex-1", children: notice.kind === 'copied'
239
- ? `Copied ${notice.label}`
240
+ ? translate('builder.clipboard.copied', { label: notice.label })
240
241
  : notice.kind === 'cut'
241
- ? `Cut ${notice.label}`
242
+ ? translate('builder.clipboard.cutNotice', { label: notice.label })
242
243
  : notice.kind === 'pasted'
243
- ? `${notice.summary} · into ${notice.destinationLabel}`
244
- : notice.message }), _jsx("button", { type: "button", "aria-label": "Dismiss", onClick: () => publishClipboardNotice(null), className: "shrink-0 rounded p-0.5 text-muted-foreground hover:bg-muted hover:text-foreground", children: _jsx(X, { className: "size-3.5" }) })] }), notice.kind === 'pasted' && notice.issues.length > 0 ? (_jsx("div", { className: "mt-1.5 flex flex-col gap-1", children: notice.issues.map((issue, i) => (_jsxs("button", { type: "button", "data-testid": "clipboard-notice-issue", onClick: () => builder.dispatch({ type: 'select', id: issue.nodeId }), className: "flex w-full items-center gap-2 rounded-md p-1 text-left transition-colors hover:bg-muted", children: [_jsx(Badge, { variant: "warning", className: "shrink-0", children: issue.kind === 'missing-component' ? 'component' : 'binding' }), _jsx(Text, { size: "xs", tone: "muted", className: "min-w-0 flex-1 truncate", children: issue.message }), _jsx(Crosshair, { className: "size-3.5 shrink-0 text-muted-foreground", "aria-hidden": true })] }, `${issue.nodeId}-${i}`))) })) : null] }) }));
244
+ ? translate('builder.clipboard.pastedInto', {
245
+ summary: notice.summary,
246
+ destination: notice.destinationLabel,
247
+ })
248
+ : notice.message }), _jsx("button", { type: "button", "aria-label": translate('common.dismiss'), onClick: () => publishClipboardNotice(null), className: "shrink-0 rounded p-0.5 text-muted-foreground hover:bg-muted hover:text-foreground", children: _jsx(X, { className: "size-3.5" }) })] }), notice.kind === 'pasted' && notice.issues.length > 0 ? (_jsx("div", { className: "mt-1.5 flex flex-col gap-1", children: notice.issues.map((issue, i) => (_jsxs("button", { type: "button", "data-testid": "clipboard-notice-issue", onClick: () => builder.dispatch({ type: 'select', id: issue.nodeId }), className: "flex w-full items-center gap-2 rounded-md p-1 text-left transition-colors hover:bg-muted", children: [_jsx(Badge, { variant: "warning", className: "shrink-0", children: issue.kind === 'missing-component'
249
+ ? translate('builder.clipboard.issueComponent')
250
+ : translate('builder.clipboard.issueBinding') }), _jsx(Text, { size: "xs", tone: "muted", className: "min-w-0 flex-1 truncate", children: issue.message }), _jsx(Crosshair, { className: "size-3.5 shrink-0 text-muted-foreground", "aria-hidden": true })] }, `${issue.nodeId}-${i}`))) })) : null] }) }));
245
251
  }
246
252
  //# sourceMappingURL=clipboard-actions.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"clipboard-actions.js","sourceRoot":"","sources":["../../../src/components/builder/clipboard-actions.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useMemo, useRef, useSyncExternalStore } from 'react'\nimport { AlertTriangle, CircleCheck, Crosshair, X } from 'lucide-react'\nimport { cloneWithNewIds } from '@elytracms/core/project-graph'\nimport type { ComponentNode } from '@elytracms/core/project-graph'\nimport { buildFragmentFromComposition } from '@elytracms/core/clipboard'\nimport type { ClipboardFragment, ParseFragmentResult } from '@elytracms/core/clipboard'\nimport {\n readFragmentFromClipboard,\n readFragmentFromDataTransfer,\n resolvePasteTarget,\n diffPastedSubtree,\n pasteSummaryMessage,\n writeFragmentToClipboard,\n writeFragmentToDataTransfer,\n} from '@elytracms/studio/lib/clipboard'\nimport type { AsyncClipboard, PasteIssue } from '@elytracms/studio/lib/clipboard'\nimport { getStudioRegistry } from '@elytracms/studio/lib/registry'\nimport { useData } from '@elytracms/studio/lib/data'\nimport { useCollab } from '../collab/collab-context'\nimport { Badge, Row, Text } from '@elytracms/studio/ui'\nimport { useBuilder } from './builder-store'\n\n/**\n * Copy/cut/paste everywhere (EC-175, vision 3.3/3.7).\n *\n * One hook ({@link useStudioClipboard}) backs every affordance — canvas\n * keyboard shortcuts (wired inside the iframe by the canvas), page-tree\n * shortcuts and row buttons, and the blocks panel's \"copy exact\n * configuration\" — so they all produce/consume the same EC-174 fragment via\n * the OS clipboard (`lib/clipboard/os-clipboard`), never an app-internal\n * buffer. That is what makes paste work across documents, pages, projects,\n * and browser tabs.\n *\n * Dispatch contract: a paste is exactly ONE `insert` action (the fragment\n * root is a single subtree) → one history entry → one mirrored\n * `graph.insertNode` command — one undo step removes everything the paste\n * inserted. A cut is copy + exactly one `remove`; one undo restores it.\n *\n * Paste is PERMISSIVE: missing components and unresolvable bindings insert\n * anyway (the renderer's fallbacks keep them visible) and surface in a\n * notice — \"Pasted — 2 bindings unresolved, 1 component missing\" — whose\n * issues deep-link to the inserted nodes. Only structurally invalid clipboard\n * content is reported as a non-blocking error, never a crash or silent no-op.\n */\n\n// ---------------------------------------------------------------------------\n// Notice store — module-level pub/sub (the drag-session pattern) so any\n// surface can publish and the canvas overlay renders it.\n// ---------------------------------------------------------------------------\n\nexport type ClipboardNotice =\n | { kind: 'copied'; label: string }\n | { kind: 'cut'; label: string }\n | { kind: 'pasted'; summary: string; destinationLabel: string; issues: PasteIssue[] }\n | { kind: 'error'; message: string }\n\nlet currentNotice: ClipboardNotice | null = null\nconst noticeListeners = new Set<() => void>()\n\nexport function publishClipboardNotice(notice: ClipboardNotice | null): void {\n currentNotice = notice\n for (const listener of [...noticeListeners]) listener()\n}\n\nfunction subscribeNotice(listener: () => void): () => void {\n noticeListeners.add(listener)\n return () => noticeListeners.delete(listener)\n}\n\nfunction getNotice(): ClipboardNotice | null {\n return currentNotice\n}\n\nexport function useClipboardNotice(): ClipboardNotice | null {\n return useSyncExternalStore(subscribeNotice, getNotice, getNotice)\n}\n\n// ---------------------------------------------------------------------------\n// Shortcut scoping\n// ---------------------------------------------------------------------------\n\n/**\n * Whether a clipboard shortcut/event must stay inert for this target: form\n * fields and any contenteditable (which includes the EC-158/EC-159 inline\n * editors) own their own native clipboard behaviour.\n */\nexport function clipboardEventBlocked(target: EventTarget | null): boolean {\n const el = target as HTMLElement | null\n if (!el) return false\n const tag = el.tagName\n if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') return true\n if (typeof el.closest === 'function' && el.closest('[contenteditable]')) return true\n return el.isContentEditable === true\n}\n\n// ---------------------------------------------------------------------------\n// The clipboard surface\n// ---------------------------------------------------------------------------\n\nexport interface StudioClipboard {\n /**\n * Copy a canvas/tree node's subtree. Writes synchronously when a\n * ClipboardEvent's DataTransfer is given, else via the async Clipboard API\n * (`asyncClipboard` lets the canvas pass the iframe's own navigator —\n * Chromium scopes clipboard access to the *focused* document).\n */\n copyNode(nodeId: string, dt?: DataTransfer | null, asyncClipboard?: AsyncClipboard): void\n /** Copy, then remove the source in exactly one dispatch (one undo restores). */\n cutNode(nodeId: string, dt?: DataTransfer | null, asyncClipboard?: AsyncClipboard): void\n /** Copy a prebuilt fragment (blocks-panel \"copy exact configuration\"). */\n copyFragment(fragment: ClipboardFragment, label: string): void\n /** Paste from a paste event's DataTransfer (no permissions needed). */\n pasteFromDataTransfer(dt: DataTransfer, anchorId?: string | null): void\n /** Paste via the async Clipboard API (explicit affordances + shortcuts). */\n pasteFromClipboard(anchorId?: string | null, asyncClipboard?: AsyncClipboard): Promise<void>\n}\n\nfunction shortId(componentId: string): string {\n return componentId.replace('base.primitives.', '').replace('project.', '')\n}\n\n/** Mirrors the builder reducer's fresh-node-id counter (`node-<n>`). */\nfunction counterFrom(start: number): () => string {\n let n = start\n return () => {\n n += 1\n return `node-${n}`\n }\n}\n\nexport function useStudioClipboard(): StudioClipboard {\n const builder = useBuilder()\n const data = useData()\n const collab = useCollab()\n const studio = getStudioRegistry()\n\n // Event listeners outlive renders; refs keep them on the current state.\n const stateRef = useRef({ builder, data, projectId: collab.projectId })\n stateRef.current = { builder, data, projectId: collab.projectId }\n\n return useMemo<StudioClipboard>(() => {\n const fragmentFor = (nodeId: string): { fragment: ClipboardFragment; label: string } | null => {\n const { builder: b, projectId } = stateRef.current\n const built = buildFragmentFromComposition(b.present, nodeId, { source: { projectId } })\n if (!built.ok) {\n publishClipboardNotice({ kind: 'error', message: built.error.message })\n return null\n }\n return { fragment: built.fragment, label: shortId(built.fragment.payload.root.componentId) }\n }\n\n const write = (\n fragment: ClipboardFragment,\n label: string,\n kind: 'copied' | 'cut',\n dt?: DataTransfer | null,\n asyncClipboard?: AsyncClipboard,\n ): void => {\n if (dt) {\n writeFragmentToDataTransfer(dt, fragment)\n publishClipboardNotice({ kind, label })\n return\n }\n writeFragmentToClipboard(asyncClipboard ?? navigator.clipboard, fragment)\n .then(() => publishClipboardNotice({ kind, label }))\n .catch((cause: unknown) => {\n const detail = cause instanceof Error ? cause.message : String(cause)\n publishClipboardNotice({ kind: 'error', message: `Could not write to the clipboard: ${detail}` })\n })\n }\n\n const currentRoot = (): ComponentNode | undefined => {\n const { builder: b } = stateRef.current\n return b.present\n }\n\n const applyPaste = (result: ParseFragmentResult, anchorId?: string | null): void => {\n const { builder: b, data: d } = stateRef.current\n if (!result.ok) {\n publishClipboardNotice({ kind: 'error', message: result.error.message })\n return\n }\n const root = currentRoot()\n if (!root) return\n const { target, destinationLabel } = resolvePasteTarget({\n root,\n anchorId: anchorId !== undefined ? anchorId : b.selectedId,\n getManifest: (id) => studio.registry.getManifest(id),\n })\n // Mirror the reducer's deterministic re-id so the issue deep links point\n // at exactly the node ids the inserted subtree will carry.\n const inserted = cloneWithNewIds(result.fragment.payload.root, counterFrom(b.nodeSeq))\n // ONE dispatch: one history entry, one graph.insertNode command, one undo.\n b.dispatch({ type: 'insert', target, node: result.fragment.payload.root })\n const diff = diffPastedSubtree({\n node: inserted,\n hasComponent: (id) => studio.registry.getManifest(id) !== undefined,\n knownSourceIds: d.knownSourceIds,\n })\n publishClipboardNotice({\n kind: 'pasted',\n summary: pasteSummaryMessage(diff),\n destinationLabel,\n issues: diff.issues,\n })\n }\n\n return {\n copyNode(nodeId, dt, asyncClipboard) {\n const built = fragmentFor(nodeId)\n if (built) write(built.fragment, built.label, 'copied', dt, asyncClipboard)\n },\n cutNode(nodeId, dt, asyncClipboard) {\n const { builder: b } = stateRef.current\n const root = currentRoot()\n if (root && nodeId === root.id) {\n publishClipboardNotice({ kind: 'error', message: 'The page root cannot be cut.' })\n return\n }\n const built = fragmentFor(nodeId)\n if (!built) return\n write(built.fragment, built.label, 'cut', dt, asyncClipboard)\n // Exactly one structural dispatch: a single undo restores the cut node.\n b.dispatch({ type: 'remove', id: nodeId })\n },\n copyFragment(fragment, label) {\n write(fragment, label, 'copied')\n },\n pasteFromDataTransfer(dt, anchorId) {\n applyPaste(readFragmentFromDataTransfer(dt), anchorId)\n },\n async pasteFromClipboard(anchorId, asyncClipboard) {\n applyPaste(await readFragmentFromClipboard(asyncClipboard ?? navigator.clipboard), anchorId)\n },\n }\n }, [studio])\n}\n\n// ---------------------------------------------------------------------------\n// Listener wiring — one implementation for the page-tree window and the\n// canvas iframe document (events never cross the frame boundary).\n// ---------------------------------------------------------------------------\n\nexport interface ClipboardListenerOptions {\n clipboard: StudioClipboard\n /** Current selection: the copy/cut subject and the paste anchor. */\n getSelectedId: () => string | null\n /** Extra guard (e.g. the canvas's live inline-edit session). */\n isBlocked?: (target: EventTarget | null) => boolean\n /** The async Clipboard API of the listening document's own window. */\n asyncClipboard?: AsyncClipboard\n /** The listening document's selection — real text selections stay native. */\n getSelection?: () => Selection | null\n}\n\ninterface ListenerTarget {\n addEventListener(type: string, listener: (e: Event) => void): void\n removeEventListener(type: string, listener: (e: Event) => void): void\n}\n\n/**\n * Wire Cmd/Ctrl+C/X/V plus native copy/cut/paste ClipboardEvents.\n *\n * Shortcuts are handled on `keydown` (a selected node has no text selection,\n * so the browser would not fire native clipboard events for it); the\n * `preventDefault` there suppresses the native event, so the ClipboardEvent\n * listeners only ever see *other* triggers — context-menu copy/paste and\n * synthetic events — and the two paths can never double-handle one gesture.\n * Everything stays inert in form fields and inside the inline editors.\n */\nexport function attachClipboardListeners(target: ListenerTarget, opts: ClipboardListenerOptions): () => void {\n const blocked = (t: EventTarget | null): boolean =>\n clipboardEventBlocked(t) || (opts.isBlocked?.(t) ?? false)\n const hasTextSelection = (): boolean => {\n const selection = opts.getSelection?.()\n return selection !== null && selection !== undefined && !selection.isCollapsed\n }\n\n const onKeyDown = (e: Event) => {\n const event = e as KeyboardEvent\n if (!(event.metaKey || event.ctrlKey) || event.altKey || event.shiftKey) return\n const key = event.key.toLowerCase()\n if (key !== 'c' && key !== 'x' && key !== 'v') return\n if (blocked(event.target)) return\n if ((key === 'c' || key === 'x') && hasTextSelection()) return\n const selectedId = opts.getSelectedId()\n if (key === 'v') {\n event.preventDefault()\n void opts.clipboard.pasteFromClipboard(selectedId, opts.asyncClipboard)\n return\n }\n if (!selectedId) return\n event.preventDefault()\n if (key === 'c') opts.clipboard.copyNode(selectedId, null, opts.asyncClipboard)\n else opts.clipboard.cutNode(selectedId, null, opts.asyncClipboard)\n }\n\n const onCopyOrCut = (e: Event) => {\n const event = e as ClipboardEvent\n if (blocked(event.target) || hasTextSelection()) return\n const selectedId = opts.getSelectedId()\n if (!selectedId || !event.clipboardData) return\n event.preventDefault()\n if (event.type === 'copy') opts.clipboard.copyNode(selectedId, event.clipboardData)\n else opts.clipboard.cutNode(selectedId, event.clipboardData)\n }\n\n const onPaste = (e: Event) => {\n const event = e as ClipboardEvent\n if (blocked(event.target) || !event.clipboardData) return\n event.preventDefault()\n opts.clipboard.pasteFromDataTransfer(event.clipboardData, opts.getSelectedId())\n }\n\n target.addEventListener('keydown', onKeyDown)\n target.addEventListener('copy', onCopyOrCut)\n target.addEventListener('cut', onCopyOrCut)\n target.addEventListener('paste', onPaste)\n return () => {\n target.removeEventListener('keydown', onKeyDown)\n target.removeEventListener('copy', onCopyOrCut)\n target.removeEventListener('cut', onCopyOrCut)\n target.removeEventListener('paste', onPaste)\n }\n}\n\n// ---------------------------------------------------------------------------\n// Notice overlay — rendered by the canvas; issues deep-link to their nodes.\n// ---------------------------------------------------------------------------\n\nexport function ClipboardNoticeOverlay() {\n const notice = useClipboardNotice()\n const builder = useBuilder()\n if (!notice) return null\n\n const tone =\n notice.kind === 'error' ? 'error' : notice.kind === 'pasted' && notice.issues.length > 0 ? 'warning' : 'success'\n\n return (\n <div\n data-testid=\"clipboard-notice\"\n data-notice-kind={notice.kind}\n className=\"absolute bottom-4 left-1/2 z-20 w-max max-w-[min(420px,90%)] -translate-x-1/2\"\n >\n <div className=\"rounded-lg border border-border bg-popover p-2 text-popover-foreground shadow-lg\">\n <Row gap=\"2\" align=\"start\">\n {tone === 'error' ? (\n <AlertTriangle className=\"mt-0.5 size-3.5 shrink-0 text-destructive\" aria-hidden />\n ) : tone === 'warning' ? (\n <AlertTriangle className=\"mt-0.5 size-3.5 shrink-0 text-warning\" aria-hidden />\n ) : (\n <CircleCheck className=\"mt-0.5 size-3.5 shrink-0 text-success\" aria-hidden />\n )}\n <Text size=\"xs\" className=\"min-w-0 flex-1\">\n {notice.kind === 'copied'\n ? `Copied ${notice.label}`\n : notice.kind === 'cut'\n ? `Cut ${notice.label}`\n : notice.kind === 'pasted'\n ? `${notice.summary} · into ${notice.destinationLabel}`\n : notice.message}\n </Text>\n <button\n type=\"button\"\n aria-label=\"Dismiss\"\n onClick={() => publishClipboardNotice(null)}\n className=\"shrink-0 rounded p-0.5 text-muted-foreground hover:bg-muted hover:text-foreground\"\n >\n <X className=\"size-3.5\" />\n </button>\n </Row>\n {notice.kind === 'pasted' && notice.issues.length > 0 ? (\n <div className=\"mt-1.5 flex flex-col gap-1\">\n {notice.issues.map((issue, i) => (\n <button\n key={`${issue.nodeId}-${i}`}\n type=\"button\"\n data-testid=\"clipboard-notice-issue\"\n onClick={() => builder.dispatch({ type: 'select', id: issue.nodeId })}\n className=\"flex w-full items-center gap-2 rounded-md p-1 text-left transition-colors hover:bg-muted\"\n >\n <Badge variant=\"warning\" className=\"shrink-0\">\n {issue.kind === 'missing-component' ? 'component' : 'binding'}\n </Badge>\n <Text size=\"xs\" tone=\"muted\" className=\"min-w-0 flex-1 truncate\">\n {issue.message}\n </Text>\n <Crosshair className=\"size-3.5 shrink-0 text-muted-foreground\" aria-hidden />\n </button>\n ))}\n </div>\n ) : null}\n </div>\n </div>\n )\n}\n"]}
1
+ {"version":3,"file":"clipboard-actions.js","sourceRoot":"","sources":["../../../src/components/builder/clipboard-actions.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useMemo, useRef, useSyncExternalStore } from 'react'\nimport { AlertTriangle, CircleCheck, Crosshair, X } from 'lucide-react'\nimport { cloneWithNewIds } from '@elytracms/core/project-graph'\nimport type { ComponentNode } from '@elytracms/core/project-graph'\nimport { buildFragmentFromComposition } from '@elytracms/core/clipboard'\nimport type { ClipboardFragment, ParseFragmentResult } from '@elytracms/core/clipboard'\nimport {\n readFragmentFromClipboard,\n readFragmentFromDataTransfer,\n resolvePasteTarget,\n diffPastedSubtree,\n pasteSummaryMessage,\n writeFragmentToClipboard,\n writeFragmentToDataTransfer,\n} from '@elytracms/studio/lib/clipboard'\nimport type { AsyncClipboard, PasteIssue } from '@elytracms/studio/lib/clipboard'\nimport { getStudioRegistry } from '@elytracms/studio/lib/registry'\nimport { translate } from '@elytracms/studio/lib/i18n'\nimport { useData } from '@elytracms/studio/lib/data'\nimport { useCollab } from '../collab/collab-context'\nimport { Badge, Row, Text } from '@elytracms/studio/ui'\nimport { useBuilder } from './builder-store'\n\n/**\n * Copy/cut/paste everywhere (EC-175, vision 3.3/3.7).\n *\n * One hook ({@link useStudioClipboard}) backs every affordance — canvas\n * keyboard shortcuts (wired inside the iframe by the canvas), page-tree\n * shortcuts and row buttons, and the blocks panel's \"copy exact\n * configuration\" — so they all produce/consume the same EC-174 fragment via\n * the OS clipboard (`lib/clipboard/os-clipboard`), never an app-internal\n * buffer. That is what makes paste work across documents, pages, projects,\n * and browser tabs.\n *\n * Dispatch contract: a paste is exactly ONE `insert` action (the fragment\n * root is a single subtree) → one history entry → one mirrored\n * `graph.insertNode` command — one undo step removes everything the paste\n * inserted. A cut is copy + exactly one `remove`; one undo restores it.\n *\n * Paste is PERMISSIVE: missing components and unresolvable bindings insert\n * anyway (the renderer's fallbacks keep them visible) and surface in a\n * notice — \"Pasted — 2 bindings unresolved, 1 component missing\" — whose\n * issues deep-link to the inserted nodes. Only structurally invalid clipboard\n * content is reported as a non-blocking error, never a crash or silent no-op.\n */\n\n// ---------------------------------------------------------------------------\n// Notice store — module-level pub/sub (the drag-session pattern) so any\n// surface can publish and the canvas overlay renders it.\n// ---------------------------------------------------------------------------\n\nexport type ClipboardNotice =\n | { kind: 'copied'; label: string }\n | { kind: 'cut'; label: string }\n | { kind: 'pasted'; summary: string; destinationLabel: string; issues: PasteIssue[] }\n | { kind: 'error'; message: string }\n\nlet currentNotice: ClipboardNotice | null = null\nconst noticeListeners = new Set<() => void>()\n\nexport function publishClipboardNotice(notice: ClipboardNotice | null): void {\n currentNotice = notice\n for (const listener of [...noticeListeners]) listener()\n}\n\nfunction subscribeNotice(listener: () => void): () => void {\n noticeListeners.add(listener)\n return () => noticeListeners.delete(listener)\n}\n\nfunction getNotice(): ClipboardNotice | null {\n return currentNotice\n}\n\nexport function useClipboardNotice(): ClipboardNotice | null {\n return useSyncExternalStore(subscribeNotice, getNotice, getNotice)\n}\n\n// ---------------------------------------------------------------------------\n// Shortcut scoping\n// ---------------------------------------------------------------------------\n\n/**\n * Whether a clipboard shortcut/event must stay inert for this target: form\n * fields and any contenteditable (which includes the EC-158/EC-159 inline\n * editors) own their own native clipboard behaviour.\n */\nexport function clipboardEventBlocked(target: EventTarget | null): boolean {\n const el = target as HTMLElement | null\n if (!el) return false\n const tag = el.tagName\n if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') return true\n if (typeof el.closest === 'function' && el.closest('[contenteditable]')) return true\n return el.isContentEditable === true\n}\n\n// ---------------------------------------------------------------------------\n// The clipboard surface\n// ---------------------------------------------------------------------------\n\nexport interface StudioClipboard {\n /**\n * Copy a canvas/tree node's subtree. Writes synchronously when a\n * ClipboardEvent's DataTransfer is given, else via the async Clipboard API\n * (`asyncClipboard` lets the canvas pass the iframe's own navigator —\n * Chromium scopes clipboard access to the *focused* document).\n */\n copyNode(nodeId: string, dt?: DataTransfer | null, asyncClipboard?: AsyncClipboard): void\n /** Copy, then remove the source in exactly one dispatch (one undo restores). */\n cutNode(nodeId: string, dt?: DataTransfer | null, asyncClipboard?: AsyncClipboard): void\n /** Copy a prebuilt fragment (blocks-panel \"copy exact configuration\"). */\n copyFragment(fragment: ClipboardFragment, label: string): void\n /** Paste from a paste event's DataTransfer (no permissions needed). */\n pasteFromDataTransfer(dt: DataTransfer, anchorId?: string | null): void\n /** Paste via the async Clipboard API (explicit affordances + shortcuts). */\n pasteFromClipboard(anchorId?: string | null, asyncClipboard?: AsyncClipboard): Promise<void>\n}\n\nfunction shortId(componentId: string): string {\n return componentId.replace('base.primitives.', '').replace('project.', '')\n}\n\n/** Mirrors the builder reducer's fresh-node-id counter (`node-<n>`). */\nfunction counterFrom(start: number): () => string {\n let n = start\n return () => {\n n += 1\n return `node-${n}`\n }\n}\n\nexport function useStudioClipboard(): StudioClipboard {\n const builder = useBuilder()\n const data = useData()\n const collab = useCollab()\n const studio = getStudioRegistry()\n\n // Event listeners outlive renders; refs keep them on the current state.\n const stateRef = useRef({ builder, data, projectId: collab.projectId })\n stateRef.current = { builder, data, projectId: collab.projectId }\n\n return useMemo<StudioClipboard>(() => {\n const fragmentFor = (nodeId: string): { fragment: ClipboardFragment; label: string } | null => {\n const { builder: b, projectId } = stateRef.current\n const built = buildFragmentFromComposition(b.present, nodeId, { source: { projectId } })\n if (!built.ok) {\n publishClipboardNotice({ kind: 'error', message: built.error.message })\n return null\n }\n return { fragment: built.fragment, label: shortId(built.fragment.payload.root.componentId) }\n }\n\n const write = (\n fragment: ClipboardFragment,\n label: string,\n kind: 'copied' | 'cut',\n dt?: DataTransfer | null,\n asyncClipboard?: AsyncClipboard,\n ): void => {\n if (dt) {\n writeFragmentToDataTransfer(dt, fragment)\n publishClipboardNotice({ kind, label })\n return\n }\n writeFragmentToClipboard(asyncClipboard ?? navigator.clipboard, fragment)\n .then(() => publishClipboardNotice({ kind, label }))\n .catch((cause: unknown) => {\n const detail = cause instanceof Error ? cause.message : String(cause)\n publishClipboardNotice({ kind: 'error', message: translate('builder.clipboard.writeError', { detail }) })\n })\n }\n\n const currentRoot = (): ComponentNode | undefined => {\n const { builder: b } = stateRef.current\n return b.present\n }\n\n const applyPaste = (result: ParseFragmentResult, anchorId?: string | null): void => {\n const { builder: b, data: d } = stateRef.current\n if (!result.ok) {\n publishClipboardNotice({ kind: 'error', message: result.error.message })\n return\n }\n const root = currentRoot()\n if (!root) return\n const { target, destinationLabel } = resolvePasteTarget({\n root,\n anchorId: anchorId !== undefined ? anchorId : b.selectedId,\n getManifest: (id) => studio.registry.getManifest(id),\n })\n // Mirror the reducer's deterministic re-id so the issue deep links point\n // at exactly the node ids the inserted subtree will carry.\n const inserted = cloneWithNewIds(result.fragment.payload.root, counterFrom(b.nodeSeq))\n // ONE dispatch: one history entry, one graph.insertNode command, one undo.\n b.dispatch({ type: 'insert', target, node: result.fragment.payload.root })\n const diff = diffPastedSubtree({\n node: inserted,\n hasComponent: (id) => studio.registry.getManifest(id) !== undefined,\n knownSourceIds: d.knownSourceIds,\n })\n publishClipboardNotice({\n kind: 'pasted',\n summary: pasteSummaryMessage(diff),\n destinationLabel,\n issues: diff.issues,\n })\n }\n\n return {\n copyNode(nodeId, dt, asyncClipboard) {\n const built = fragmentFor(nodeId)\n if (built) write(built.fragment, built.label, 'copied', dt, asyncClipboard)\n },\n cutNode(nodeId, dt, asyncClipboard) {\n const { builder: b } = stateRef.current\n const root = currentRoot()\n if (root && nodeId === root.id) {\n publishClipboardNotice({ kind: 'error', message: translate('builder.clipboard.rootCannotBeCut') })\n return\n }\n const built = fragmentFor(nodeId)\n if (!built) return\n write(built.fragment, built.label, 'cut', dt, asyncClipboard)\n // Exactly one structural dispatch: a single undo restores the cut node.\n b.dispatch({ type: 'remove', id: nodeId })\n },\n copyFragment(fragment, label) {\n write(fragment, label, 'copied')\n },\n pasteFromDataTransfer(dt, anchorId) {\n applyPaste(readFragmentFromDataTransfer(dt), anchorId)\n },\n async pasteFromClipboard(anchorId, asyncClipboard) {\n applyPaste(await readFragmentFromClipboard(asyncClipboard ?? navigator.clipboard), anchorId)\n },\n }\n }, [studio])\n}\n\n// ---------------------------------------------------------------------------\n// Listener wiring — one implementation for the page-tree window and the\n// canvas iframe document (events never cross the frame boundary).\n// ---------------------------------------------------------------------------\n\nexport interface ClipboardListenerOptions {\n clipboard: StudioClipboard\n /** Current selection: the copy/cut subject and the paste anchor. */\n getSelectedId: () => string | null\n /** Extra guard (e.g. the canvas's live inline-edit session). */\n isBlocked?: (target: EventTarget | null) => boolean\n /** The async Clipboard API of the listening document's own window. */\n asyncClipboard?: AsyncClipboard\n /** The listening document's selection — real text selections stay native. */\n getSelection?: () => Selection | null\n}\n\ninterface ListenerTarget {\n addEventListener(type: string, listener: (e: Event) => void): void\n removeEventListener(type: string, listener: (e: Event) => void): void\n}\n\n/**\n * Wire Cmd/Ctrl+C/X/V plus native copy/cut/paste ClipboardEvents.\n *\n * Shortcuts are handled on `keydown` (a selected node has no text selection,\n * so the browser would not fire native clipboard events for it); the\n * `preventDefault` there suppresses the native event, so the ClipboardEvent\n * listeners only ever see *other* triggers — context-menu copy/paste and\n * synthetic events — and the two paths can never double-handle one gesture.\n * Everything stays inert in form fields and inside the inline editors.\n */\nexport function attachClipboardListeners(target: ListenerTarget, opts: ClipboardListenerOptions): () => void {\n const blocked = (t: EventTarget | null): boolean =>\n clipboardEventBlocked(t) || (opts.isBlocked?.(t) ?? false)\n const hasTextSelection = (): boolean => {\n const selection = opts.getSelection?.()\n return selection !== null && selection !== undefined && !selection.isCollapsed\n }\n\n const onKeyDown = (e: Event) => {\n const event = e as KeyboardEvent\n if (!(event.metaKey || event.ctrlKey) || event.altKey || event.shiftKey) return\n const key = event.key.toLowerCase()\n if (key !== 'c' && key !== 'x' && key !== 'v') return\n if (blocked(event.target)) return\n if ((key === 'c' || key === 'x') && hasTextSelection()) return\n const selectedId = opts.getSelectedId()\n if (key === 'v') {\n event.preventDefault()\n void opts.clipboard.pasteFromClipboard(selectedId, opts.asyncClipboard)\n return\n }\n if (!selectedId) return\n event.preventDefault()\n if (key === 'c') opts.clipboard.copyNode(selectedId, null, opts.asyncClipboard)\n else opts.clipboard.cutNode(selectedId, null, opts.asyncClipboard)\n }\n\n const onCopyOrCut = (e: Event) => {\n const event = e as ClipboardEvent\n if (blocked(event.target) || hasTextSelection()) return\n const selectedId = opts.getSelectedId()\n if (!selectedId || !event.clipboardData) return\n event.preventDefault()\n if (event.type === 'copy') opts.clipboard.copyNode(selectedId, event.clipboardData)\n else opts.clipboard.cutNode(selectedId, event.clipboardData)\n }\n\n const onPaste = (e: Event) => {\n const event = e as ClipboardEvent\n if (blocked(event.target) || !event.clipboardData) return\n event.preventDefault()\n opts.clipboard.pasteFromDataTransfer(event.clipboardData, opts.getSelectedId())\n }\n\n target.addEventListener('keydown', onKeyDown)\n target.addEventListener('copy', onCopyOrCut)\n target.addEventListener('cut', onCopyOrCut)\n target.addEventListener('paste', onPaste)\n return () => {\n target.removeEventListener('keydown', onKeyDown)\n target.removeEventListener('copy', onCopyOrCut)\n target.removeEventListener('cut', onCopyOrCut)\n target.removeEventListener('paste', onPaste)\n }\n}\n\n// ---------------------------------------------------------------------------\n// Notice overlay — rendered by the canvas; issues deep-link to their nodes.\n// ---------------------------------------------------------------------------\n\nexport function ClipboardNoticeOverlay() {\n const notice = useClipboardNotice()\n const builder = useBuilder()\n if (!notice) return null\n\n const tone =\n notice.kind === 'error' ? 'error' : notice.kind === 'pasted' && notice.issues.length > 0 ? 'warning' : 'success'\n\n return (\n <div\n data-testid=\"clipboard-notice\"\n data-notice-kind={notice.kind}\n className=\"absolute bottom-4 left-1/2 z-20 w-max max-w-[min(420px,90%)] -translate-x-1/2\"\n >\n <div className=\"rounded-lg border border-border bg-popover p-2 text-popover-foreground shadow-lg\">\n <Row gap=\"2\" align=\"start\">\n {tone === 'error' ? (\n <AlertTriangle className=\"mt-0.5 size-3.5 shrink-0 text-destructive\" aria-hidden />\n ) : tone === 'warning' ? (\n <AlertTriangle className=\"mt-0.5 size-3.5 shrink-0 text-warning\" aria-hidden />\n ) : (\n <CircleCheck className=\"mt-0.5 size-3.5 shrink-0 text-success\" aria-hidden />\n )}\n <Text size=\"xs\" className=\"min-w-0 flex-1\">\n {notice.kind === 'copied'\n ? translate('builder.clipboard.copied', { label: notice.label })\n : notice.kind === 'cut'\n ? translate('builder.clipboard.cutNotice', { label: notice.label })\n : notice.kind === 'pasted'\n ? translate('builder.clipboard.pastedInto', {\n summary: notice.summary,\n destination: notice.destinationLabel,\n })\n : notice.message}\n </Text>\n <button\n type=\"button\"\n aria-label={translate('common.dismiss')}\n onClick={() => publishClipboardNotice(null)}\n className=\"shrink-0 rounded p-0.5 text-muted-foreground hover:bg-muted hover:text-foreground\"\n >\n <X className=\"size-3.5\" />\n </button>\n </Row>\n {notice.kind === 'pasted' && notice.issues.length > 0 ? (\n <div className=\"mt-1.5 flex flex-col gap-1\">\n {notice.issues.map((issue, i) => (\n <button\n key={`${issue.nodeId}-${i}`}\n type=\"button\"\n data-testid=\"clipboard-notice-issue\"\n onClick={() => builder.dispatch({ type: 'select', id: issue.nodeId })}\n className=\"flex w-full items-center gap-2 rounded-md p-1 text-left transition-colors hover:bg-muted\"\n >\n <Badge variant=\"warning\" className=\"shrink-0\">\n {issue.kind === 'missing-component'\n ? translate('builder.clipboard.issueComponent')\n : translate('builder.clipboard.issueBinding')}\n </Badge>\n <Text size=\"xs\" tone=\"muted\" className=\"min-w-0 flex-1 truncate\">\n {issue.message}\n </Text>\n <Crosshair className=\"size-3.5 shrink-0 text-muted-foreground\" aria-hidden />\n </button>\n ))}\n </div>\n ) : null}\n </div>\n </div>\n )\n}\n"]}
@@ -4,6 +4,7 @@ import { AlertTriangle, Plus, Redo2, SlidersHorizontal, Undo2 } from 'lucide-rea
4
4
  import { DEFAULT_SLOT, blockRootsOf } from '@elytracms/core/project-graph';
5
5
  import { Button, Panel, Row, Spacer, Text, Toolbar, ToolbarGroup, ToolbarSeparator } from '@elytracms/studio/ui';
6
6
  import { cn } from '@elytracms/studio/lib/utils';
7
+ import { translate } from '@elytracms/studio/lib/i18n';
7
8
  import { BuilderProvider, useBuilder } from './builder-store';
8
9
  import { useCanvasViewports } from './canvas-breakpoints';
9
10
  import { BuilderCanvas } from './canvas';
@@ -99,13 +100,15 @@ const EMPTY_BLOCKS = [];
99
100
  function ViewportButton({ option }) {
100
101
  const builder = useBuilder();
101
102
  const active = builder.viewport === option.key;
102
- 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 }));
103
+ return (_jsx(Button, { size: "sm", variant: "ghost", "aria-label": translate('builder.canvas.viewportPreviewAria', { label: option.label }), "aria-pressed": active, title: option.px !== undefined
104
+ ? `${option.label} · ${option.px}px`
105
+ : translate('builder.canvas.viewportWidthTitle', { label: option.label }), onClick: () => builder.dispatch({ type: 'viewport', viewport: option.key }), className: cn('px-2 text-xs', active && 'bg-accent text-accent-foreground'), children: option.label }));
103
106
  }
104
107
  /** The centered breakpoint switcher (EC-250): the resolved viewport set, plus a
105
108
  * visible validation issue when a project declared breakpoints but none are usable. */
106
109
  function BreakpointSwitcher() {
107
110
  const { viewports, issue } = useCanvasViewports();
108
- 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] }));
111
+ 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: translate('builder.canvas.breakpointsIssueLabel') })] })) : null] }));
109
112
  }
110
113
  /**
111
114
  * The slim editor surface — the canvas + layers + inspector, plus Insert and
@@ -125,7 +128,7 @@ function CompositionEditorShell({ readOnly, fieldAllow, trailingPanes, }) {
125
128
  // whole inspector column used to — but the appended panes stay interactive.
126
129
  const propertiesPane = useMemo(() => ({
127
130
  id: 'properties',
128
- label: 'Properties',
131
+ label: translate('builder.inspector.propertiesPane'),
129
132
  icon: _jsx(SlidersHorizontal, { className: "size-3.5" }),
130
133
  render: () => (_jsx("div", { className: cn('h-full min-h-0 overflow-hidden', readOnly && 'pointer-events-none opacity-60'), children: _jsx(PropertyInspector, {}) })),
131
134
  }), [readOnly]);
@@ -137,6 +140,6 @@ function CompositionEditorShell({ readOnly, fieldAllow, trailingPanes, }) {
137
140
  setInsertOpen(true);
138
141
  }
139
142
  }
140
- 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, { perspective: readOnly ? 'published' : 'draft' }) }), _jsx(InspectorSidebar, { panes: panes })] }), _jsx(InsertMenu, { context: insertContext, open: insertOpen, onOpenChange: setInsertOpen })] }));
143
+ 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": translate('builder.canvas.undo'), title: translate('builder.canvas.undo'), disabled: readOnly || !builder.canUndo, onClick: () => dispatch({ type: 'undo' }), children: _jsx(Undo2, {}) }), _jsx(Button, { size: "icon-sm", variant: "ghost", "aria-label": translate('builder.canvas.redo'), title: translate('builder.canvas.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, {}), " ", translate('builder.canvas.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, { perspective: readOnly ? 'published' : 'draft' }) }), _jsx(InspectorSidebar, { panes: panes })] }), _jsx(InsertMenu, { context: insertContext, open: insertOpen, onOpenChange: setInsertOpen })] }));
141
144
  }
142
145
  //# sourceMappingURL=composition-field-editor.js.map