@elytracms/studio 0.0.86 → 0.0.88
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/convex/asset-revalidate.test.ts +186 -0
- package/convex/assets.ts +53 -19
- package/convex/cms.ts +22 -0
- package/convex/delivery.ts +33 -116
- package/convex/functions.ts +3 -3
- package/convex/rail-write-parity.test.ts +180 -0
- package/convex/redirects.ts +33 -11
- package/convex/revalidate.ts +7 -5
- package/convex/triggers.ts +53 -3
- package/dist/components/ai/ai-chat-panel.js +8 -1
- package/dist/components/ai/ai-chat-panel.js.map +1 -1
- package/dist/components/app-views/default-modules.d.ts +20 -20
- package/dist/components/app-views/default-modules.js +27 -48
- package/dist/components/app-views/default-modules.js.map +1 -1
- package/dist/components/app-views/studio-config.d.ts +5 -6
- package/dist/components/app-views/studio-config.js.map +1 -1
- package/dist/components/assets/asset-metadata.d.ts +28 -19
- package/dist/components/assets/asset-metadata.js +24 -31
- package/dist/components/assets/asset-metadata.js.map +1 -1
- package/dist/components/assets/asset-selection.d.ts +24 -5
- package/dist/components/assets/asset-selection.js +39 -9
- package/dist/components/assets/asset-selection.js.map +1 -1
- package/dist/components/assets/assets-collection-view-source.d.ts +17 -14
- package/dist/components/assets/assets-collection-view-source.js +25 -31
- package/dist/components/assets/assets-collection-view-source.js.map +1 -1
- package/dist/components/assets/assets-store.d.ts +11 -1
- package/dist/components/assets/assets-store.js +10 -0
- package/dist/components/assets/assets-store.js.map +1 -1
- package/dist/components/assets/focal-point.d.ts +0 -7
- package/dist/components/assets/focal-point.js +0 -10
- package/dist/components/assets/focal-point.js.map +1 -1
- package/dist/components/builder/blocks-panel.js +3 -3
- package/dist/components/builder/blocks-panel.js.map +1 -1
- package/dist/components/builder/builder-store.d.ts +25 -1
- package/dist/components/builder/builder-store.js +3 -2
- package/dist/components/builder/builder-store.js.map +1 -1
- package/dist/components/builder/canvas-chrome-css.js +0 -17
- package/dist/components/builder/canvas-chrome-css.js.map +1 -1
- package/dist/components/builder/canvas-chrome.d.ts +10 -12
- package/dist/components/builder/canvas-chrome.js +12 -13
- package/dist/components/builder/canvas-chrome.js.map +1 -1
- package/dist/components/builder/canvas-dnd.d.ts +8 -0
- package/dist/components/builder/canvas-dnd.js +5 -3
- package/dist/components/builder/canvas-dnd.js.map +1 -1
- package/dist/components/builder/canvas-render-context.d.ts +23 -24
- package/dist/components/builder/canvas-render-context.js +35 -60
- package/dist/components/builder/canvas-render-context.js.map +1 -1
- package/dist/components/builder/canvas.js +5 -11
- package/dist/components/builder/canvas.js.map +1 -1
- package/dist/components/builder/clipboard-actions.d.ts +14 -6
- package/dist/components/builder/clipboard-actions.js +29 -20
- package/dist/components/builder/clipboard-actions.js.map +1 -1
- package/dist/components/builder/composition-field-editor.d.ts +19 -26
- package/dist/components/builder/composition-field-editor.js +30 -52
- package/dist/components/builder/composition-field-editor.js.map +1 -1
- package/dist/components/builder/insert-context.d.ts +3 -1
- package/dist/components/builder/insert-context.js +15 -10
- package/dist/components/builder/insert-context.js.map +1 -1
- package/dist/components/builder/page-tree.d.ts +29 -0
- package/dist/components/builder/page-tree.js +56 -58
- package/dist/components/builder/page-tree.js.map +1 -1
- package/dist/components/builder/reuse.js +5 -2
- package/dist/components/builder/reuse.js.map +1 -1
- package/dist/components/cms/asset-field.d.ts +2 -1
- package/dist/components/cms/asset-field.js +39 -83
- package/dist/components/cms/asset-field.js.map +1 -1
- package/dist/components/cms/asset-picker-core.d.ts +77 -40
- package/dist/components/cms/asset-picker-core.js +73 -46
- package/dist/components/cms/asset-picker-core.js.map +1 -1
- package/dist/components/cms/asset-picker.d.ts +48 -19
- package/dist/components/cms/asset-picker.js +69 -30
- package/dist/components/cms/asset-picker.js.map +1 -1
- package/dist/components/cms/cell-edit.d.ts +3 -1
- package/dist/components/cms/cell-edit.js +2 -2
- package/dist/components/cms/cell-edit.js.map +1 -1
- package/dist/components/cms/cms-composition-validators.js +11 -2
- package/dist/components/cms/cms-composition-validators.js.map +1 -1
- package/dist/components/cms/cms-store.js +15 -23
- package/dist/components/cms/cms-store.js.map +1 -1
- package/dist/components/cms/editable-cell.js +3 -3
- package/dist/components/cms/editable-cell.js.map +1 -1
- package/dist/components/cms/gate-validation.d.ts +27 -2
- package/dist/components/cms/gate-validation.js +19 -1
- package/dist/components/cms/gate-validation.js.map +1 -1
- package/dist/components/cms/rich-text/editor.js +2 -2
- package/dist/components/cms/rich-text/editor.js.map +1 -1
- package/dist/components/cms/rich-text/insert.d.ts +7 -0
- package/dist/components/cms/rich-text/insert.js +7 -0
- package/dist/components/cms/rich-text/insert.js.map +1 -1
- package/dist/components/cms/use-document-versions.js +1 -0
- package/dist/components/cms/use-document-versions.js.map +1 -1
- package/dist/components/cms/versions-model.d.ts +0 -12
- package/dist/components/cms/versions-model.js +3 -15
- package/dist/components/cms/versions-model.js.map +1 -1
- package/dist/components/components/component-detail.js +1 -1
- package/dist/components/components/component-detail.js.map +1 -1
- package/dist/components/components/transfer-dialog.js +2 -4
- package/dist/components/components/transfer-dialog.js.map +1 -1
- package/dist/components/components/transfer.d.ts +13 -10
- package/dist/components/components/transfer.js +21 -19
- package/dist/components/components/transfer.js.map +1 -1
- package/dist/components/nav-issues.d.ts +23 -3
- package/dist/components/nav-issues.js +38 -2
- package/dist/components/nav-issues.js.map +1 -1
- package/dist/components/project-session.d.ts +9 -0
- package/dist/components/project-session.js +1 -1
- package/dist/components/project-session.js.map +1 -1
- package/dist/components/redirects/redirects-collection-view-source.d.ts +17 -2
- package/dist/components/redirects/redirects-collection-view-source.js +20 -6
- package/dist/components/redirects/redirects-collection-view-source.js.map +1 -1
- package/dist/components/section-nav.js +26 -2
- package/dist/components/section-nav.js.map +1 -1
- package/dist/components/section-surface.js +3 -2
- package/dist/components/section-surface.js.map +1 -1
- package/dist/components/settings-form.js +1 -2
- package/dist/components/settings-form.js.map +1 -1
- package/dist/components/studio-context.js +1 -18
- package/dist/components/studio-context.js.map +1 -1
- package/dist/components/studio-project-screen.js +3 -5
- package/dist/components/studio-project-screen.js.map +1 -1
- package/dist/lib/ai/apply-content.js +73 -29
- package/dist/lib/ai/apply-content.js.map +1 -1
- package/dist/lib/ai/constraints.d.ts +35 -0
- package/dist/lib/ai/constraints.js +104 -4
- package/dist/lib/ai/constraints.js.map +1 -1
- package/dist/lib/ai/route.js +4 -1
- package/dist/lib/ai/route.js.map +1 -1
- package/dist/lib/ai/shape.d.ts +3 -2
- package/dist/lib/ai/shape.js +16 -24
- package/dist/lib/ai/shape.js.map +1 -1
- package/dist/lib/ai/tools.d.ts +10 -4
- package/dist/lib/ai/tools.js +12 -4
- package/dist/lib/ai/tools.js.map +1 -1
- package/dist/lib/ai/types.d.ts +8 -0
- package/dist/lib/ai/types.js.map +1 -1
- package/dist/lib/ai/update-fields.js +33 -12
- package/dist/lib/ai/update-fields.js.map +1 -1
- package/dist/lib/assets/asset-placements.d.ts +35 -0
- package/dist/lib/assets/asset-placements.js +74 -0
- package/dist/lib/assets/asset-placements.js.map +1 -0
- package/dist/lib/assets/backfill-thumbs.d.ts +9 -8
- package/dist/lib/assets/backfill-thumbs.js +13 -25
- package/dist/lib/assets/backfill-thumbs.js.map +1 -1
- package/dist/lib/clipboard/paste-issues.d.ts +17 -15
- package/dist/lib/clipboard/paste-issues.js +8 -45
- package/dist/lib/clipboard/paste-issues.js.map +1 -1
- package/dist/lib/clipboard/paste-target.d.ts +25 -1
- package/dist/lib/clipboard/paste-target.js +13 -6
- package/dist/lib/clipboard/paste-target.js.map +1 -1
- package/dist/lib/cms/published-documents.js +1 -0
- package/dist/lib/cms/published-documents.js.map +1 -1
- package/dist/lib/cms-views/view-model.d.ts +3 -3
- package/dist/lib/data/collection-content.d.ts +22 -1
- package/dist/lib/data/collection-content.js +15 -0
- package/dist/lib/data/collection-content.js.map +1 -1
- package/dist/lib/data/index.d.ts +0 -3
- package/dist/lib/data/index.js +4 -6
- package/dist/lib/data/index.js.map +1 -1
- package/dist/lib/dnd/drop-rules.d.ts +45 -2
- package/dist/lib/dnd/drop-rules.js +64 -9
- package/dist/lib/dnd/drop-rules.js.map +1 -1
- package/dist/lib/engine-version.d.ts +3 -3
- package/dist/lib/engine-version.js +3 -3
- package/dist/lib/engine-version.js.map +1 -1
- package/dist/lib/i18n/messages.de.js +9 -56
- package/dist/lib/i18n/messages.de.js.map +1 -1
- package/dist/lib/i18n/messages.en.d.ts +8 -53
- package/dist/lib/i18n/messages.en.js +14 -57
- package/dist/lib/i18n/messages.en.js.map +1 -1
- package/dist/lib/locale/fallback.js +8 -2
- package/dist/lib/locale/fallback.js.map +1 -1
- package/dist/lib/locale/index.d.ts +0 -1
- package/dist/lib/locale/index.js +0 -1
- package/dist/lib/locale/index.js.map +1 -1
- package/dist/lib/locale/locale-store.d.ts +10 -3
- package/dist/lib/locale/locale-store.js +12 -5
- package/dist/lib/locale/locale-store.js.map +1 -1
- package/dist/lib/operations/session-hydration.d.ts +24 -22
- package/dist/lib/operations/session-hydration.js +15 -53
- package/dist/lib/operations/session-hydration.js.map +1 -1
- package/dist/lib/persistence/convex-adapter.js +63 -75
- package/dist/lib/persistence/convex-adapter.js.map +1 -1
- package/dist/lib/persona/labels.d.ts +0 -2
- package/dist/lib/persona/labels.js +1 -7
- package/dist/lib/persona/labels.js.map +1 -1
- package/dist/lib/registry/index.d.ts +4 -2
- package/dist/lib/registry/index.js +26 -21
- package/dist/lib/registry/index.js.map +1 -1
- package/dist/lib/registry/project-components.js +0 -3
- package/dist/lib/registry/project-components.js.map +1 -1
- package/dist/lib/routes/route-issues.js +4 -5
- package/dist/lib/routes/route-issues.js.map +1 -1
- package/dist/lib/theme.js +3 -2
- package/dist/lib/theme.js.map +1 -1
- package/dist/lib/webhooks/tags.js +11 -14
- package/dist/lib/webhooks/tags.js.map +1 -1
- package/dist/lib/workspace/config-issues.d.ts +6 -0
- package/dist/lib/workspace/config-issues.js +8 -4
- package/dist/lib/workspace/config-issues.js.map +1 -1
- package/dist/lib/workspace/index.d.ts +2 -2
- package/dist/lib/workspace/index.js +2 -2
- package/dist/lib/workspace/index.js.map +1 -1
- package/dist/previews/enumerate.d.ts +27 -9
- package/dist/previews/enumerate.js +27 -6
- package/dist/previews/enumerate.js.map +1 -1
- package/dist/previews/manifest-format.d.ts +9 -0
- package/dist/previews/manifest-format.js.map +1 -1
- package/dist/router/match.d.ts +0 -2
- package/dist/router/match.js +0 -2
- package/dist/router/match.js.map +1 -1
- package/dist/router/navigate-once.d.ts +19 -4
- package/dist/router/navigate-once.js +22 -5
- package/dist/router/navigate-once.js.map +1 -1
- package/dist/ui/badge.d.ts +1 -1
- package/package.json +3 -3
- package/src/styles/studio.css +5 -4
- package/src/styles/tokens.css +72 -55
- package/convex/content.ts +0 -291
- package/dist/components/collab/collab-context.d.ts +0 -27
- package/dist/components/collab/collab-context.js +0 -39
- package/dist/components/collab/collab-context.js.map +0 -1
- package/dist/components/collab/collab-seed.d.ts +0 -15
- package/dist/components/collab/collab-seed.js +0 -20
- package/dist/components/collab/collab-seed.js.map +0 -1
- package/dist/components/extensions/extensions-view.d.ts +0 -7
- package/dist/components/extensions/extensions-view.js +0 -127
- package/dist/components/extensions/extensions-view.js.map +0 -1
- package/dist/lib/bindings/section-bindings.d.ts +0 -24
- package/dist/lib/bindings/section-bindings.js +0 -9
- package/dist/lib/bindings/section-bindings.js.map +0 -1
- package/dist/lib/data/data-store.d.ts +0 -98
- package/dist/lib/data/data-store.js +0 -211
- package/dist/lib/data/data-store.js.map +0 -1
- package/dist/lib/data/providers.d.ts +0 -143
- package/dist/lib/data/providers.js +0 -255
- package/dist/lib/data/providers.js.map +0 -1
- package/dist/lib/data/sample-capture.d.ts +0 -30
- package/dist/lib/data/sample-capture.js +0 -70
- package/dist/lib/data/sample-capture.js.map +0 -1
- package/dist/lib/data/sources-store.d.ts +0 -42
- package/dist/lib/data/sources-store.js +0 -120
- package/dist/lib/data/sources-store.js.map +0 -1
- package/dist/lib/locale/binding-fallback.d.ts +0 -35
- package/dist/lib/locale/binding-fallback.js +0 -73
- package/dist/lib/locale/binding-fallback.js.map +0 -1
- package/dist/lib/starters/kit-session.d.ts +0 -5
- package/dist/lib/starters/kit-session.js +0 -17
- package/dist/lib/starters/kit-session.js.map +0 -1
|
@@ -208,23 +208,6 @@ const CHROME_UI_CSS = `
|
|
|
208
208
|
color: rgb(225,228,236) !important;
|
|
209
209
|
font: 12px/1 ui-sans-serif, system-ui, sans-serif !important; outline: none !important; }
|
|
210
210
|
|
|
211
|
-
/* ── Locale-fallback chips (EC-176) ─────────────────────────────────────
|
|
212
|
-
A node whose bound content is currently served via locale fallback wears a
|
|
213
|
-
small corner chip naming the locale it actually came from — the canvas
|
|
214
|
-
never silently mixes locales. A pseudo-element cannot carry .ec-chrome, so
|
|
215
|
-
every declaration is !important (contract item 3). */
|
|
216
|
-
.ec-editable[data-ec-locale-fallback] { position: relative; }
|
|
217
|
-
.ec-editable[data-ec-locale-fallback]::after {
|
|
218
|
-
content: attr(data-ec-locale-fallback) !important;
|
|
219
|
-
position: absolute !important; top: 2px !important; right: 2px !important;
|
|
220
|
-
z-index: 5 !important; pointer-events: none !important;
|
|
221
|
-
padding: 0 4px !important; border-radius: 4px !important;
|
|
222
|
-
font: 600 9px/1.5 ui-sans-serif, system-ui, sans-serif !important;
|
|
223
|
-
letter-spacing: 0.05em !important; text-transform: uppercase !important;
|
|
224
|
-
color: #fbbf24 !important; background: rgba(180,83,9,0.25) !important;
|
|
225
|
-
border: 1px solid rgba(245,158,11,0.45) !important;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
211
|
/* ── Drag & drop states (EC-160/EC-162) ─────────────────────────────────
|
|
229
212
|
Eligibility comes from the shared pure rules (lib/dnd/drop-rules); these
|
|
230
213
|
classes only *show* the resolution. Rejection is explicit and visible
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"canvas-chrome-css.js","sourceRoot":"","sources":["../../../src/components/builder/canvas-chrome-css.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAA;AAyC9D;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAAG,4BAA4B,CAAA;AAE3E;;;;;;;;GAQG;AACH,MAAM,sBAAsB,GAAG;;;;;CAK9B,CAAA;AAED;;;;;;;;GAQG;AACH,MAAM,mBAAmB,GAAG;;CAE3B,CAAA;AAED;;;;;GAKG;AACH,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wCA4IkB,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwDzD,CAAA;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,KAAkB;IAChD,OAAO,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,sBAAsB,CAAC,GAAG,aAAa,CAAA;AAC5F,CAAC","sourcesContent":["import { PRESERVE_NODE_ATTR } from '@elytracms/core/rich-text'\n\n/**\n * The canvas chrome STYLESHEET (EC-414 / AD-B) — the studio's complete in-frame\n * styling, hand-written. This is deliberately NOT a second Tailwind world: the\n * chrome inside the canvas iframe styles itself with `ec-*` classes, `data-ec-*`\n * attributes, and inline styles only, so the consumer's theme is the ONLY design\n * system in the canvas document and the canvas resolves exactly what delivery\n * resolves (AD-A).\n *\n * The chrome contract (binding on ALL in-frame studio UI — EC-404/405/408 build\n * on this):\n *\n * 1. Only `ec-*` class names / `data-ec-*` attributes / inline styles — never\n * studio utility classes, never studio React UI primitives.\n * 2. Chrome roots carry the containment recipe (`.ec-chrome`): `all: initial`\n * plus explicit font/color/line-height/color-scheme re-establishment, so\n * consumer element rules and inherited typography cannot restyle chrome.\n * NOTE (verified, MDN): `all: initial` does NOT stop custom-property\n * inheritance — the real protection is that chrome never CONSUMES a var()\n * name a consumer could define (this file uses no var() at all; the\n * contract test enforces it).\n * 3. Where a consumer ELEMENT selector could still match chrome descendants\n * (`div`, `span`, `button`, `input` rules in a site theme), chrome rules\n * use `!important` deliberately — a small, documented surface: the\n * selection/DnD indicators, the rich-text toolbar, and the locale chip.\n * The `.ec-chrome` recipe itself stays normal-strength on purpose: inline\n * styles are the top styling layer for chrome-owned looks (placeholders,\n * spacing handles) and stylesheet-`!important` would beat them.\n * 4. Escape hatch (documented only, NOT built): if chrome ever outgrows\n * hand-written CSS, the verified prefixed-bundle recipe is the upgrade\n * path — granular Tailwind imports `theme.css layer(theme) prefix(ec)` +\n * `utilities.css layer(utilities) prefix(ec) important source(none)`\n * compiled into an engine-provided entry (compiles clean on 4.3.2).\n */\n\n/** The two canvas stylesheet worlds (AD-C): `legacy` = the pre-EC-414 merged\n * canvas.css (studio design system + consumer theme in one build); `new` = the\n * consumer's standalone full-Tailwind theme entry, studio-free. */\nexport type CanvasWorld = 'legacy' | 'new'\n\n/**\n * Injected FIRST into the canvas head (before the `ec-app-css` link): a bare\n * layer STATEMENT that pins `ec-canvas-backdrop` to the very bottom of the\n * document's layer order (layer order is first-occurrence order). The new-world\n * backdrop rules live in this layer, so they lose to EVERYTHING the consumer\n * ships — `@layer base` body rules, unlayered rules, later layers — and only\n * paint when the consumer paints nothing. (An unlayered backdrop would ALWAYS\n * beat the consumer's layered `@layer base body { background }` — the exact\n * NK-004 mechanism this task retires.) Inert in the legacy world: the legacy\n * chrome CSS puts nothing in the layer.\n */\nexport const CANVAS_BACKDROP_LAYER_STATEMENT = '@layer ec-canvas-backdrop;'\n\n/**\n * NEW-world backdrop (AD-B item 4): the studio-utility body-class default is\n * retired; these neutral hand-written rules give the canvas a light, site-like\n * page when the consumer theme paints nothing, in the weakest possible cascade\n * position (see {@link CANVAS_BACKDROP_LAYER_STATEMENT}). Background sits on\n * BODY (not html) so a consumer `body { background }` keeps its normal\n * canvas-propagation behavior — short pages paint edge-to-edge exactly as the\n * delivery build does.\n */\nconst NEW_WORLD_BACKDROP_CSS = `\n @layer ec-canvas-backdrop {\n html { color-scheme: light; }\n body { background-color: #ffffff; }\n }\n`\n\n/**\n * LEGACY-world backdrop — today's rule, verbatim and unlayered (EC-389-era\n * behavior applies EXACTLY in the legacy world, per AD-C): the canvas backdrop\n * is deliberately always light/site-like, so the native form-control/scrollbar\n * theme is forced to match and a page with real inputs never shows dark native\n * chrome floating on a light page. The light page itself comes from the studio\n * body-class default (`DEFAULT_CANVAS_BODY_CLASS`) resolving against the merged\n * stylesheet's studio tokens.\n */\nconst LEGACY_BACKDROP_CSS = `\n html { color-scheme: light; }\n`\n\n/**\n * The chrome UI rules — identical in both worlds. Sections:\n * page frame, containment recipe, editable wrappers, selection/hover overlay\n * (EC-404), inline editing, rich-text toolbar, locale-fallback chips, drag &\n * drop indicators.\n */\nconst CHROME_UI_CSS = `\n /* ── Page frame ─────────────────────────────────────────────────────────\n The canvas gutter — unlayered on purpose (identical in both worlds, and\n it must beat a consumer body margin: the 16px frame is editor ergonomics,\n not page styling). */\n body { margin: 0; padding: 16px; }\n\n /* ── Containment recipe (chrome contract item 2) ────────────────────────\n Every chrome ROOT (toolbar, spacing handles, in-canvas placeholders)\n carries .ec-chrome: reset everything, then re-establish the chrome's own\n typography and color-scheme explicitly. Inherited custom properties pierce\n 'all: initial' (and even shadow DOM) — that is why chrome never consumes\n var() names (contract-tested), not something this recipe could fix.\n The :where(div, span, p) guard re-links descendant text to the chrome\n root's typography with class-level specificity, so a consumer's bare\n 'div, span { color: … }' element rules cannot restyle chrome text; chrome\n elements styled inline stay inline-styled (inline always wins here). */\n .ec-chrome {\n all: initial;\n display: block;\n box-sizing: border-box;\n font-family: ui-sans-serif, system-ui, sans-serif;\n font-size: 13px;\n line-height: 1.5;\n color: rgb(30, 35, 45);\n color-scheme: light;\n }\n .ec-chrome *,\n .ec-chrome *::before,\n .ec-chrome *::after { box-sizing: border-box; }\n .ec-chrome :where(div, span, p) { color: inherit; font: inherit; }\n\n /* ── Editable wrappers ──────────────────────────────────────────────────\n Scoped to the per-node editable wrappers. The wrappers WRAP consumer\n content, so they are deliberately NOT contained (no .ec-chrome — they must\n stay layout-transparent). EC-404 RETIRED the in-document selection/hover\n outlines (data-selected / data-hovered): an outline paints in the\n element's own stacking order and any full-bleed background or higher\n z-index sibling paints over it — a structural occlusion, not a z-index bug.\n Selection + hover are now drawn by the topmost .ec-overlay layer below.\n The outline-offset stays for the editing outline (and DnD 'into' overrides\n it); its !important (contract item 3) keeps a site theme's element rules\n from hiding those states. */\n .ec-editable { outline-offset: -1px !important; cursor: default; }\n\n /* ── Selection/hover overlay (EC-404) ───────────────────────────────────\n The topmost editing layer, portaled into the iframe body as a sibling of\n the page composition and pinned to the maximum stacking level, so NOTHING\n the page paints can occlude the selection frame. The root carries the\n .ec-chrome containment recipe; geometry (left/top/width/height) is inline,\n everything visual lives here. Element-matching declarations carry\n !important (contract item 3): the frame boxes are <div>s and the controls\n are <button>s a site theme's element rules would otherwise restyle. Frames\n are click-through (pointer-events:none); only the bar re-enables events. */\n .ec-overlay {\n position: absolute !important; top: 0 !important; left: 0 !important;\n z-index: 2147483647 !important; pointer-events: none !important;\n width: 0 !important; height: 0 !important;\n }\n .ec-overlay-frame {\n position: absolute !important; box-sizing: border-box !important;\n pointer-events: none !important; border-radius: 2px !important;\n }\n /* Hover frame (EC-435 AD-3): accent-leaning and heavier than the old 1px\n grey-blue dash (easy to overlook over imagery; live-tuned on light AND\n busy sections — 1.5px collapses to 1px at 1x DPR, so 2px is the smallest\n deterministic bump). Still clearly secondary to the SELECTED frame: the\n same weight but dashed and translucent vs. solid opaque. */\n .ec-overlay-frame[data-ec-hover] { z-index: 1 !important; border: 2px dashed rgba(91,140,255,0.8) !important; }\n .ec-overlay-frame[data-ec-selected] { z-index: 2 !important; border: 2px solid #5b8cff !important; }\n /* ── Prop highlight (EC-408 reverse: field focus → sub-part) ─────────────\n A quiet FILLED tint (no hard border) on the focused prop's element,\n deliberately distinct from the selection frame (solid border) and the\n hover frame (dashed border). Sits below both frames so their borders stay\n crisp; click-through like the frames. */\n .ec-overlay-prop {\n position: absolute !important; z-index: 1 !important; box-sizing: border-box !important;\n pointer-events: none !important; border-radius: 3px !important;\n background: rgba(91,140,255,0.16) !important;\n box-shadow: inset 0 0 0 1px rgba(91,140,255,0.45) !important;\n }\n .ec-overlay-bar {\n position: absolute !important; z-index: 3 !important; pointer-events: auto !important;\n display: flex !important; align-items: center !important; gap: 1px !important;\n height: 22px !important; max-width: 320px !important; padding: 0 2px 0 6px !important;\n border-radius: 6px !important; background: #5b8cff !important;\n box-shadow: 0 2px 8px rgba(8,12,24,0.35) !important;\n font: 600 11px/1 ui-sans-serif, system-ui, sans-serif !important;\n color: #ffffff !important; white-space: nowrap !important;\n }\n .ec-overlay-label {\n overflow: hidden !important; text-overflow: ellipsis !important; white-space: nowrap !important;\n max-width: 180px !important; padding-right: 2px !important;\n font: inherit !important; color: #ffffff !important;\n }\n .ec-overlay-sep {\n width: 1px !important; height: 12px !important; margin: 0 3px !important;\n background: rgba(255,255,255,0.4) !important;\n }\n .ec-overlay-btn {\n display: inline-flex !important; align-items: center !important; justify-content: center !important;\n width: 20px !important; height: 18px !important; padding: 0 !important; margin: 0 !important;\n border: 0 !important; border-radius: 4px !important; background: transparent !important;\n color: #ffffff !important; cursor: pointer !important; -webkit-appearance: none !important; appearance: none !important;\n }\n .ec-overlay-btn:hover { background: rgba(255,255,255,0.22) !important; }\n .ec-overlay-btn:disabled { opacity: 0.4 !important; cursor: default !important; }\n\n /* ── Insertion affordances (EC-405) ─────────────────────────────────────\n A \"+\" button centered ON a block boundary (before/after edge, or the center\n of an empty container) — the geometry is inline (the anchor point), the\n translate here centers the button on it. Appears only on hover/selection\n (never during a drag or inline edit); pointer-events re-enabled so it is\n clickable while the overlay root stays click-through. Element-matching\n declarations !important (contract item 3): it is a <button> a site theme's\n element rules would otherwise restyle. */\n .ec-overlay-add {\n position: absolute !important; z-index: 4 !important; pointer-events: auto !important;\n transform: translate(-50%, -50%) !important;\n display: inline-flex !important; align-items: center !important; justify-content: center !important;\n width: 20px !important; height: 20px !important; padding: 0 !important; margin: 0 !important;\n border: 1.5px solid #ffffff !important; border-radius: 999px !important;\n background: #5b8cff !important; color: #ffffff !important; cursor: pointer !important;\n box-shadow: 0 1px 5px rgba(8,12,24,0.4) !important;\n -webkit-appearance: none !important; appearance: none !important;\n }\n .ec-overlay-add:hover { background: #3f74f5 !important; }\n\n /* ── Inline editing (EC-158 text, EC-159 rich text) ─────────────────────\n The rendered element itself becomes the editor — no overlay, no\n replacement input — so typography, alignment, and layout are\n pixel-identical between edit and render states. The wrapper keeps the\n selection outline; the host only gets a caret. pre-wrap applies to PLAIN\n text hosts only — rich-text markup must keep its rendered whitespace\n handling (no layout shift). */\n .ec-editable[data-ec-editing] { outline: 2px solid #5b8cff !important; }\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\n /* ── Floating mark toolbar (EC-159), portaled into the iframe ───────────\n All visual declarations !important (contract item 3): the toolbar is\n stylesheet-styled chrome whose button/input elements a site theme's\n element rules would otherwise match. */\n .ec-rt-toolbar { color-scheme: dark !important; }\n .ec-rt-toolbar-row { display: flex !important; align-items: center !important; gap: 2px !important;\n padding: 3px !important; margin: 0 !important;\n border: 1px solid rgba(130,150,190,0.4) !important; border-radius: 8px !important;\n background: rgb(24,26,32) !important; box-shadow: 0 4px 16px rgba(0,0,0,0.4) !important; }\n .ec-rt-toolbar-btn { min-width: 26px !important; height: 24px !important; padding: 0 7px !important;\n border: 0 !important; border-radius: 5px !important;\n background: transparent !important; color: rgb(225,228,236) !important;\n font: 600 12px/1 ui-sans-serif, system-ui, sans-serif !important;\n cursor: pointer !important; }\n .ec-rt-toolbar-btn:hover { background: rgba(130,150,190,0.25) !important; }\n .ec-rt-toolbar-btn[data-active] { background: #5b8cff !important; color: white !important; }\n .ec-rt-toolbar-input { width: 200px !important; height: 24px !important; padding: 0 7px !important;\n border-radius: 5px !important;\n border: 1px solid rgba(130,150,190,0.4) !important; background: rgb(16,18,22) !important;\n color: rgb(225,228,236) !important;\n font: 12px/1 ui-sans-serif, system-ui, sans-serif !important; outline: none !important; }\n\n /* ── Locale-fallback chips (EC-176) ─────────────────────────────────────\n A node whose bound content is currently served via locale fallback wears a\n small corner chip naming the locale it actually came from — the canvas\n never silently mixes locales. A pseudo-element cannot carry .ec-chrome, so\n every declaration is !important (contract item 3). */\n .ec-editable[data-ec-locale-fallback] { position: relative; }\n .ec-editable[data-ec-locale-fallback]::after {\n content: attr(data-ec-locale-fallback) !important;\n position: absolute !important; top: 2px !important; right: 2px !important;\n z-index: 5 !important; pointer-events: none !important;\n padding: 0 4px !important; border-radius: 4px !important;\n font: 600 9px/1.5 ui-sans-serif, system-ui, sans-serif !important;\n letter-spacing: 0.05em !important; text-transform: uppercase !important;\n color: #fbbf24 !important; background: rgba(180,83,9,0.25) !important;\n border: 1px solid rgba(245,158,11,0.45) !important;\n }\n\n /* ── Drag & drop states (EC-160/EC-162) ─────────────────────────────────\n Eligibility comes from the shared pure rules (lib/dnd/drop-rules); these\n classes only *show* the resolution. Rejection is explicit and visible\n during the drag — never a silent failed drop. Before/after bars follow the\n parent's flow axis (data-ec-drop-axis); left/right are the\n wrap-into-columns gesture (violet, paired highlight). Indicator\n declarations !important (contract item 3). */\n .ec-editable[data-ec-drop='into'] { outline: 2px solid #34d399 !important; outline-offset: -2px !important; background-color: rgba(52, 211, 153, 0.08) !important; }\n .ec-editable[data-ec-drop='before'] { box-shadow: 0 -3px 0 0 #38bdf8 !important; }\n .ec-editable[data-ec-drop='after'] { box-shadow: 0 3px 0 0 #38bdf8 !important; }\n .ec-editable[data-ec-drop='before'][data-ec-drop-axis='x'] { box-shadow: -3px 0 0 0 #38bdf8 !important; }\n .ec-editable[data-ec-drop='after'][data-ec-drop-axis='x'] { box-shadow: 3px 0 0 0 #38bdf8 !important; }\n .ec-editable[data-ec-drop='left'] { box-shadow: -3px 0 0 0 #a78bfa !important; background-color: rgba(167, 139, 250, 0.1) !important; }\n .ec-editable[data-ec-drop='right'] { box-shadow: 3px 0 0 0 #a78bfa !important; background-color: rgba(167, 139, 250, 0.1) !important; }\n .ec-editable[data-ec-drop='blocked'] { outline: 2px solid #ef4444 !important; outline-offset: -2px !important; background-color: rgba(239, 68, 68, 0.08) !important; cursor: not-allowed !important; }\n`\n\n/**\n * The complete chrome stylesheet for a world — the `ec-outline-css` element's\n * content (id kept from EC-389; the cascade position is pinned by test).\n */\nexport function canvasChromeCss(world: CanvasWorld): string {\n return (world === 'legacy' ? LEGACY_BACKDROP_CSS : NEW_WORLD_BACKDROP_CSS) + CHROME_UI_CSS\n}\n"]}
|
|
1
|
+
{"version":3,"file":"canvas-chrome-css.js","sourceRoot":"","sources":["../../../src/components/builder/canvas-chrome-css.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAA;AAyC9D;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAAG,4BAA4B,CAAA;AAE3E;;;;;;;;GAQG;AACH,MAAM,sBAAsB,GAAG;;;;;CAK9B,CAAA;AAED;;;;;;;;GAQG;AACH,MAAM,mBAAmB,GAAG;;CAE3B,CAAA;AAED;;;;;GAKG;AACH,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wCA4IkB,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuCzD,CAAA;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,KAAkB;IAChD,OAAO,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,sBAAsB,CAAC,GAAG,aAAa,CAAA;AAC5F,CAAC","sourcesContent":["import { PRESERVE_NODE_ATTR } from '@elytracms/core/rich-text'\n\n/**\n * The canvas chrome STYLESHEET (EC-414 / AD-B) — the studio's complete in-frame\n * styling, hand-written. This is deliberately NOT a second Tailwind world: the\n * chrome inside the canvas iframe styles itself with `ec-*` classes, `data-ec-*`\n * attributes, and inline styles only, so the consumer's theme is the ONLY design\n * system in the canvas document and the canvas resolves exactly what delivery\n * resolves (AD-A).\n *\n * The chrome contract (binding on ALL in-frame studio UI — EC-404/405/408 build\n * on this):\n *\n * 1. Only `ec-*` class names / `data-ec-*` attributes / inline styles — never\n * studio utility classes, never studio React UI primitives.\n * 2. Chrome roots carry the containment recipe (`.ec-chrome`): `all: initial`\n * plus explicit font/color/line-height/color-scheme re-establishment, so\n * consumer element rules and inherited typography cannot restyle chrome.\n * NOTE (verified, MDN): `all: initial` does NOT stop custom-property\n * inheritance — the real protection is that chrome never CONSUMES a var()\n * name a consumer could define (this file uses no var() at all; the\n * contract test enforces it).\n * 3. Where a consumer ELEMENT selector could still match chrome descendants\n * (`div`, `span`, `button`, `input` rules in a site theme), chrome rules\n * use `!important` deliberately — a small, documented surface: the\n * selection/DnD indicators, the rich-text toolbar, and the locale chip.\n * The `.ec-chrome` recipe itself stays normal-strength on purpose: inline\n * styles are the top styling layer for chrome-owned looks (placeholders,\n * spacing handles) and stylesheet-`!important` would beat them.\n * 4. Escape hatch (documented only, NOT built): if chrome ever outgrows\n * hand-written CSS, the verified prefixed-bundle recipe is the upgrade\n * path — granular Tailwind imports `theme.css layer(theme) prefix(ec)` +\n * `utilities.css layer(utilities) prefix(ec) important source(none)`\n * compiled into an engine-provided entry (compiles clean on 4.3.2).\n */\n\n/** The two canvas stylesheet worlds (AD-C): `legacy` = the pre-EC-414 merged\n * canvas.css (studio design system + consumer theme in one build); `new` = the\n * consumer's standalone full-Tailwind theme entry, studio-free. */\nexport type CanvasWorld = 'legacy' | 'new'\n\n/**\n * Injected FIRST into the canvas head (before the `ec-app-css` link): a bare\n * layer STATEMENT that pins `ec-canvas-backdrop` to the very bottom of the\n * document's layer order (layer order is first-occurrence order). The new-world\n * backdrop rules live in this layer, so they lose to EVERYTHING the consumer\n * ships — `@layer base` body rules, unlayered rules, later layers — and only\n * paint when the consumer paints nothing. (An unlayered backdrop would ALWAYS\n * beat the consumer's layered `@layer base body { background }` — the exact\n * NK-004 mechanism this task retires.) Inert in the legacy world: the legacy\n * chrome CSS puts nothing in the layer.\n */\nexport const CANVAS_BACKDROP_LAYER_STATEMENT = '@layer ec-canvas-backdrop;'\n\n/**\n * NEW-world backdrop (AD-B item 4): the studio-utility body-class default is\n * retired; these neutral hand-written rules give the canvas a light, site-like\n * page when the consumer theme paints nothing, in the weakest possible cascade\n * position (see {@link CANVAS_BACKDROP_LAYER_STATEMENT}). Background sits on\n * BODY (not html) so a consumer `body { background }` keeps its normal\n * canvas-propagation behavior — short pages paint edge-to-edge exactly as the\n * delivery build does.\n */\nconst NEW_WORLD_BACKDROP_CSS = `\n @layer ec-canvas-backdrop {\n html { color-scheme: light; }\n body { background-color: #ffffff; }\n }\n`\n\n/**\n * LEGACY-world backdrop — today's rule, verbatim and unlayered (EC-389-era\n * behavior applies EXACTLY in the legacy world, per AD-C): the canvas backdrop\n * is deliberately always light/site-like, so the native form-control/scrollbar\n * theme is forced to match and a page with real inputs never shows dark native\n * chrome floating on a light page. The light page itself comes from the studio\n * body-class default (`DEFAULT_CANVAS_BODY_CLASS`) resolving against the merged\n * stylesheet's studio tokens.\n */\nconst LEGACY_BACKDROP_CSS = `\n html { color-scheme: light; }\n`\n\n/**\n * The chrome UI rules — identical in both worlds. Sections:\n * page frame, containment recipe, editable wrappers, selection/hover overlay\n * (EC-404), inline editing, rich-text toolbar, locale-fallback chips, drag &\n * drop indicators.\n */\nconst CHROME_UI_CSS = `\n /* ── Page frame ─────────────────────────────────────────────────────────\n The canvas gutter — unlayered on purpose (identical in both worlds, and\n it must beat a consumer body margin: the 16px frame is editor ergonomics,\n not page styling). */\n body { margin: 0; padding: 16px; }\n\n /* ── Containment recipe (chrome contract item 2) ────────────────────────\n Every chrome ROOT (toolbar, spacing handles, in-canvas placeholders)\n carries .ec-chrome: reset everything, then re-establish the chrome's own\n typography and color-scheme explicitly. Inherited custom properties pierce\n 'all: initial' (and even shadow DOM) — that is why chrome never consumes\n var() names (contract-tested), not something this recipe could fix.\n The :where(div, span, p) guard re-links descendant text to the chrome\n root's typography with class-level specificity, so a consumer's bare\n 'div, span { color: … }' element rules cannot restyle chrome text; chrome\n elements styled inline stay inline-styled (inline always wins here). */\n .ec-chrome {\n all: initial;\n display: block;\n box-sizing: border-box;\n font-family: ui-sans-serif, system-ui, sans-serif;\n font-size: 13px;\n line-height: 1.5;\n color: rgb(30, 35, 45);\n color-scheme: light;\n }\n .ec-chrome *,\n .ec-chrome *::before,\n .ec-chrome *::after { box-sizing: border-box; }\n .ec-chrome :where(div, span, p) { color: inherit; font: inherit; }\n\n /* ── Editable wrappers ──────────────────────────────────────────────────\n Scoped to the per-node editable wrappers. The wrappers WRAP consumer\n content, so they are deliberately NOT contained (no .ec-chrome — they must\n stay layout-transparent). EC-404 RETIRED the in-document selection/hover\n outlines (data-selected / data-hovered): an outline paints in the\n element's own stacking order and any full-bleed background or higher\n z-index sibling paints over it — a structural occlusion, not a z-index bug.\n Selection + hover are now drawn by the topmost .ec-overlay layer below.\n The outline-offset stays for the editing outline (and DnD 'into' overrides\n it); its !important (contract item 3) keeps a site theme's element rules\n from hiding those states. */\n .ec-editable { outline-offset: -1px !important; cursor: default; }\n\n /* ── Selection/hover overlay (EC-404) ───────────────────────────────────\n The topmost editing layer, portaled into the iframe body as a sibling of\n the page composition and pinned to the maximum stacking level, so NOTHING\n the page paints can occlude the selection frame. The root carries the\n .ec-chrome containment recipe; geometry (left/top/width/height) is inline,\n everything visual lives here. Element-matching declarations carry\n !important (contract item 3): the frame boxes are <div>s and the controls\n are <button>s a site theme's element rules would otherwise restyle. Frames\n are click-through (pointer-events:none); only the bar re-enables events. */\n .ec-overlay {\n position: absolute !important; top: 0 !important; left: 0 !important;\n z-index: 2147483647 !important; pointer-events: none !important;\n width: 0 !important; height: 0 !important;\n }\n .ec-overlay-frame {\n position: absolute !important; box-sizing: border-box !important;\n pointer-events: none !important; border-radius: 2px !important;\n }\n /* Hover frame (EC-435 AD-3): accent-leaning and heavier than the old 1px\n grey-blue dash (easy to overlook over imagery; live-tuned on light AND\n busy sections — 1.5px collapses to 1px at 1x DPR, so 2px is the smallest\n deterministic bump). Still clearly secondary to the SELECTED frame: the\n same weight but dashed and translucent vs. solid opaque. */\n .ec-overlay-frame[data-ec-hover] { z-index: 1 !important; border: 2px dashed rgba(91,140,255,0.8) !important; }\n .ec-overlay-frame[data-ec-selected] { z-index: 2 !important; border: 2px solid #5b8cff !important; }\n /* ── Prop highlight (EC-408 reverse: field focus → sub-part) ─────────────\n A quiet FILLED tint (no hard border) on the focused prop's element,\n deliberately distinct from the selection frame (solid border) and the\n hover frame (dashed border). Sits below both frames so their borders stay\n crisp; click-through like the frames. */\n .ec-overlay-prop {\n position: absolute !important; z-index: 1 !important; box-sizing: border-box !important;\n pointer-events: none !important; border-radius: 3px !important;\n background: rgba(91,140,255,0.16) !important;\n box-shadow: inset 0 0 0 1px rgba(91,140,255,0.45) !important;\n }\n .ec-overlay-bar {\n position: absolute !important; z-index: 3 !important; pointer-events: auto !important;\n display: flex !important; align-items: center !important; gap: 1px !important;\n height: 22px !important; max-width: 320px !important; padding: 0 2px 0 6px !important;\n border-radius: 6px !important; background: #5b8cff !important;\n box-shadow: 0 2px 8px rgba(8,12,24,0.35) !important;\n font: 600 11px/1 ui-sans-serif, system-ui, sans-serif !important;\n color: #ffffff !important; white-space: nowrap !important;\n }\n .ec-overlay-label {\n overflow: hidden !important; text-overflow: ellipsis !important; white-space: nowrap !important;\n max-width: 180px !important; padding-right: 2px !important;\n font: inherit !important; color: #ffffff !important;\n }\n .ec-overlay-sep {\n width: 1px !important; height: 12px !important; margin: 0 3px !important;\n background: rgba(255,255,255,0.4) !important;\n }\n .ec-overlay-btn {\n display: inline-flex !important; align-items: center !important; justify-content: center !important;\n width: 20px !important; height: 18px !important; padding: 0 !important; margin: 0 !important;\n border: 0 !important; border-radius: 4px !important; background: transparent !important;\n color: #ffffff !important; cursor: pointer !important; -webkit-appearance: none !important; appearance: none !important;\n }\n .ec-overlay-btn:hover { background: rgba(255,255,255,0.22) !important; }\n .ec-overlay-btn:disabled { opacity: 0.4 !important; cursor: default !important; }\n\n /* ── Insertion affordances (EC-405) ─────────────────────────────────────\n A \"+\" button centered ON a block boundary (before/after edge, or the center\n of an empty container) — the geometry is inline (the anchor point), the\n translate here centers the button on it. Appears only on hover/selection\n (never during a drag or inline edit); pointer-events re-enabled so it is\n clickable while the overlay root stays click-through. Element-matching\n declarations !important (contract item 3): it is a <button> a site theme's\n element rules would otherwise restyle. */\n .ec-overlay-add {\n position: absolute !important; z-index: 4 !important; pointer-events: auto !important;\n transform: translate(-50%, -50%) !important;\n display: inline-flex !important; align-items: center !important; justify-content: center !important;\n width: 20px !important; height: 20px !important; padding: 0 !important; margin: 0 !important;\n border: 1.5px solid #ffffff !important; border-radius: 999px !important;\n background: #5b8cff !important; color: #ffffff !important; cursor: pointer !important;\n box-shadow: 0 1px 5px rgba(8,12,24,0.4) !important;\n -webkit-appearance: none !important; appearance: none !important;\n }\n .ec-overlay-add:hover { background: #3f74f5 !important; }\n\n /* ── Inline editing (EC-158 text, EC-159 rich text) ─────────────────────\n The rendered element itself becomes the editor — no overlay, no\n replacement input — so typography, alignment, and layout are\n pixel-identical between edit and render states. The wrapper keeps the\n selection outline; the host only gets a caret. pre-wrap applies to PLAIN\n text hosts only — rich-text markup must keep its rendered whitespace\n handling (no layout shift). */\n .ec-editable[data-ec-editing] { outline: 2px solid #5b8cff !important; }\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\n /* ── Floating mark toolbar (EC-159), portaled into the iframe ───────────\n All visual declarations !important (contract item 3): the toolbar is\n stylesheet-styled chrome whose button/input elements a site theme's\n element rules would otherwise match. */\n .ec-rt-toolbar { color-scheme: dark !important; }\n .ec-rt-toolbar-row { display: flex !important; align-items: center !important; gap: 2px !important;\n padding: 3px !important; margin: 0 !important;\n border: 1px solid rgba(130,150,190,0.4) !important; border-radius: 8px !important;\n background: rgb(24,26,32) !important; box-shadow: 0 4px 16px rgba(0,0,0,0.4) !important; }\n .ec-rt-toolbar-btn { min-width: 26px !important; height: 24px !important; padding: 0 7px !important;\n border: 0 !important; border-radius: 5px !important;\n background: transparent !important; color: rgb(225,228,236) !important;\n font: 600 12px/1 ui-sans-serif, system-ui, sans-serif !important;\n cursor: pointer !important; }\n .ec-rt-toolbar-btn:hover { background: rgba(130,150,190,0.25) !important; }\n .ec-rt-toolbar-btn[data-active] { background: #5b8cff !important; color: white !important; }\n .ec-rt-toolbar-input { width: 200px !important; height: 24px !important; padding: 0 7px !important;\n border-radius: 5px !important;\n border: 1px solid rgba(130,150,190,0.4) !important; background: rgb(16,18,22) !important;\n color: rgb(225,228,236) !important;\n font: 12px/1 ui-sans-serif, system-ui, sans-serif !important; outline: none !important; }\n\n /* ── Drag & drop states (EC-160/EC-162) ─────────────────────────────────\n Eligibility comes from the shared pure rules (lib/dnd/drop-rules); these\n classes only *show* the resolution. Rejection is explicit and visible\n during the drag — never a silent failed drop. Before/after bars follow the\n parent's flow axis (data-ec-drop-axis); left/right are the\n wrap-into-columns gesture (violet, paired highlight). Indicator\n declarations !important (contract item 3). */\n .ec-editable[data-ec-drop='into'] { outline: 2px solid #34d399 !important; outline-offset: -2px !important; background-color: rgba(52, 211, 153, 0.08) !important; }\n .ec-editable[data-ec-drop='before'] { box-shadow: 0 -3px 0 0 #38bdf8 !important; }\n .ec-editable[data-ec-drop='after'] { box-shadow: 0 3px 0 0 #38bdf8 !important; }\n .ec-editable[data-ec-drop='before'][data-ec-drop-axis='x'] { box-shadow: -3px 0 0 0 #38bdf8 !important; }\n .ec-editable[data-ec-drop='after'][data-ec-drop-axis='x'] { box-shadow: 3px 0 0 0 #38bdf8 !important; }\n .ec-editable[data-ec-drop='left'] { box-shadow: -3px 0 0 0 #a78bfa !important; background-color: rgba(167, 139, 250, 0.1) !important; }\n .ec-editable[data-ec-drop='right'] { box-shadow: 3px 0 0 0 #a78bfa !important; background-color: rgba(167, 139, 250, 0.1) !important; }\n .ec-editable[data-ec-drop='blocked'] { outline: 2px solid #ef4444 !important; outline-offset: -2px !important; background-color: rgba(239, 68, 68, 0.08) !important; cursor: not-allowed !important; }\n`\n\n/**\n * The complete chrome stylesheet for a world — the `ec-outline-css` element's\n * content (id kept from EC-389; the cascade position is pinned by test).\n */\nexport function canvasChromeCss(world: CanvasWorld): string {\n return (world === 'legacy' ? LEGACY_BACKDROP_CSS : NEW_WORLD_BACKDROP_CSS) + CHROME_UI_CSS\n}\n"]}
|
|
@@ -95,8 +95,6 @@ export interface CanvasChromeInputs {
|
|
|
95
95
|
stylesheetUrl: string | null;
|
|
96
96
|
/** Which stylesheet world the document is in (AD-C probe; see `wireCanvasChrome`). */
|
|
97
97
|
world: CanvasWorld;
|
|
98
|
-
/** An instantiated starter kit's brand token palette (EC-117), when the project has one. */
|
|
99
|
-
kitTokensCss?: string;
|
|
100
98
|
/** The consumer's canvas theme from `defineStudio({ canvas })` (EC-389). */
|
|
101
99
|
theme?: StudioCanvasTheme;
|
|
102
100
|
}
|
|
@@ -133,23 +131,23 @@ export interface CanvasChromeDocument {
|
|
|
133
131
|
* the legacy world also the studio design system);
|
|
134
132
|
* 3. `ec-outline-css` — the studio's editing chrome (world-composed:
|
|
135
133
|
* backdrop + selection/DnD/toolbar/containment styles);
|
|
136
|
-
* 4. `ec-
|
|
137
|
-
*
|
|
134
|
+
* 4. `ec-canvas-theme` — the consumer's `canvas.tokensCss` (consumer overrides).
|
|
135
|
+
* (An `ec-kit-tokens` layer used to sit between 3 and 4 for an instantiated
|
|
136
|
+
* starter kit's palette; the kits went in EC-574.)
|
|
138
137
|
*
|
|
139
138
|
* Later elements win the cascade at equal specificity, so studio defaults <
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
139
|
+
* consumer config — the customization-seam ordering. The body class is the
|
|
140
|
+
* consumer's `bodyClassName` when given (even `''`), else the legacy default in
|
|
141
|
+
* the legacy world and empty in the new world (AD-C). Safe to run repeatedly
|
|
142
|
+
* (iframe onLoad + the already-loaded fast path + the probe settling):
|
|
143
|
+
* links/styles are deduped by id, the chrome CSS is re-composed in place when
|
|
144
|
+
* the world flips, and the theme style is re-appended so a re-wire never
|
|
145
|
+
* changes who wins.
|
|
147
146
|
*/
|
|
148
147
|
export declare function applyCanvasChrome(d: CanvasChromeDocument, inputs: CanvasChromeInputs): void;
|
|
149
148
|
/** `applyCanvasChrome` inputs minus the probe-owned `world`, plus the settle callback. */
|
|
150
149
|
export interface WireCanvasChromeOptions {
|
|
151
150
|
stylesheetUrl: string | null;
|
|
152
|
-
kitTokensCss?: string;
|
|
153
151
|
theme?: StudioCanvasTheme;
|
|
154
152
|
/**
|
|
155
153
|
* Fired when the classification SETTLES — the stylesheet finished loading
|
|
@@ -89,17 +89,18 @@ export const DEFAULT_CANVAS_BODY_CLASS = 'bg-background text-foreground font-san
|
|
|
89
89
|
* the legacy world also the studio design system);
|
|
90
90
|
* 3. `ec-outline-css` — the studio's editing chrome (world-composed:
|
|
91
91
|
* backdrop + selection/DnD/toolbar/containment styles);
|
|
92
|
-
* 4. `ec-
|
|
93
|
-
*
|
|
92
|
+
* 4. `ec-canvas-theme` — the consumer's `canvas.tokensCss` (consumer overrides).
|
|
93
|
+
* (An `ec-kit-tokens` layer used to sit between 3 and 4 for an instantiated
|
|
94
|
+
* starter kit's palette; the kits went in EC-574.)
|
|
94
95
|
*
|
|
95
96
|
* Later elements win the cascade at equal specificity, so studio defaults <
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
97
|
+
* consumer config — the customization-seam ordering. The body class is the
|
|
98
|
+
* consumer's `bodyClassName` when given (even `''`), else the legacy default in
|
|
99
|
+
* the legacy world and empty in the new world (AD-C). Safe to run repeatedly
|
|
100
|
+
* (iframe onLoad + the already-loaded fast path + the probe settling):
|
|
101
|
+
* links/styles are deduped by id, the chrome CSS is re-composed in place when
|
|
102
|
+
* the world flips, and the theme style is re-appended so a re-wire never
|
|
103
|
+
* changes who wins.
|
|
103
104
|
*/
|
|
104
105
|
export function applyCanvasChrome(d, inputs) {
|
|
105
106
|
if (!d.getElementById('ec-layer-order')) {
|
|
@@ -128,9 +129,8 @@ export function applyCanvasChrome(d, inputs) {
|
|
|
128
129
|
style.textContent = chromeCss;
|
|
129
130
|
d.head.appendChild(style);
|
|
130
131
|
}
|
|
131
|
-
//
|
|
132
|
-
//
|
|
133
|
-
appendCssLayer(d, 'ec-kit-tokens', inputs.kitTokensCss);
|
|
132
|
+
// The theme layer re-appends (remove + add) on every wire, so it always lands
|
|
133
|
+
// last — after a re-composed chrome layer too.
|
|
134
134
|
appendCssLayer(d, 'ec-canvas-theme', inputs.theme?.tokensCss);
|
|
135
135
|
d.body.className =
|
|
136
136
|
inputs.theme?.bodyClassName ?? (inputs.world === 'legacy' ? DEFAULT_CANVAS_BODY_CLASS : '');
|
|
@@ -165,7 +165,6 @@ export function wireCanvasChrome(doc, options) {
|
|
|
165
165
|
applyCanvasChrome(doc, {
|
|
166
166
|
stylesheetUrl: options.stylesheetUrl,
|
|
167
167
|
world: canvasWorldOf(status),
|
|
168
|
-
...(options.kitTokensCss !== undefined ? { kitTokensCss: options.kitTokensCss } : {}),
|
|
169
168
|
...(options.theme !== undefined ? { theme: options.theme } : {}),
|
|
170
169
|
});
|
|
171
170
|
if (settled)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"canvas-chrome.js","sourceRoot":"","sources":["../../../src/components/builder/canvas-chrome.ts"],"names":[],"mappings":"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","sourcesContent":["import type { StudioCanvasTheme } from '../app-views/studio-config'\nimport { CANVAS_BACKDROP_LAYER_STATEMENT, canvasChromeCss } from './canvas-chrome-css'\nimport type { CanvasWorld } from './canvas-chrome-css'\n\nexport { canvasChromeCss, CANVAS_BACKDROP_LAYER_STATEMENT } from './canvas-chrome-css'\nexport type { CanvasWorld } from './canvas-chrome-css'\n\n/**\n * The canvas iframe's document chrome (EC-389, reshaped by EC-414) — the\n * framework-free half of `BuilderCanvas`'s `wireUp` (house pattern, EC-330):\n * everything injected into the preview document lives here, in ONE pinned\n * cascade order, unit-testable without jsdom.\n *\n * EC-414 (AD-A/AD-C): the studio design system is leaving the canvas document.\n * Which world a project lives in is decided by a RUNTIME PROBE of the loaded\n * canvas stylesheet — no flag, no config, zero consumer breakage:\n *\n * - `legacy` (marker tokens present): the merged pre-EC-414 canvas.css that\n * bundles `@elytracms/studio/styles.css` with the consumer theme. The\n * EC-389 body-class default and today's behavior apply EXACTLY.\n * - `new` (markers absent): the consumer's standalone full-Tailwind theme\n * entry. No studio utilities exist in the document, so the body-class\n * default retires and the neutral hand-written backdrop applies instead.\n */\n\n/**\n * The studio design system's marker tokens (AD-C probe). These are the plain\n * `:root` custom properties from the studio's generated `tokens.css`, which any\n * merged legacy build carries VERBATIM — unlike `@theme inline` variables\n * (e.g. `--color-panel`), which Tailwind v4 inlines into utilities and never\n * emits as custom properties (verified against the repo's Tailwind 4.3.2; the\n * task file's `--color-panel` example probe would classify every legacy build\n * as new). Two tokens ANDed so a consumer theme that happens to define one\n * generic name (`--panel`) cannot be mistaken for the studio design system.\n */\nexport const CANVAS_LEGACY_MARKER_TOKENS = ['--panel', '--grid-line'] as const\n\n/**\n * A Tailwind v4 build unconditionally emits `--default-font-family` on\n * `:root, :host` (preflight consumes it) — its absence after a successful\n * stylesheet load means the canvas stylesheet is not a Tailwind theme entry at\n * all (AD-D item 2's \"defines no @theme tokens\" invalid state).\n */\nexport const CANVAS_TAILWIND_MARKER_TOKEN = '--default-font-family'\n\n/** The settled classification of the canvas stylesheet (AD-C + AD-D item 2). */\nexport type CanvasThemeStatus =\n /** Pre-EC-414 merged canvas.css — studio design system + consumer theme. */\n | { kind: 'legacy' }\n /** The EC-414 shape: a standalone consumer Tailwind theme entry. */\n | { kind: 'new' }\n /** The app never provided a canvas stylesheet (`setCanvasStylesheetUrl`). */\n | { kind: 'no-stylesheet' }\n /** The stylesheet `<link>` failed to load. */\n | { kind: 'stylesheet-error' }\n /** Loaded, but defines no Tailwind theme tokens at all. */\n | { kind: 'no-theme-tokens' }\n\n/** The raw computed-style sample the pure classifier decides from. */\nexport interface CanvasThemeProbeSample {\n /** The `ec-app-css` link fired `error` (or never resolved). */\n loadFailed: boolean\n /** Computed values of {@link CANVAS_LEGACY_MARKER_TOKENS}, in order. */\n legacyMarkers: readonly string[]\n /** Computed value of {@link CANVAS_TAILWIND_MARKER_TOKEN}. */\n tailwindMarker: string\n}\n\nconst present = (value: string | null | undefined): boolean =>\n typeof value === 'string' && value.trim() !== ''\n\n/**\n * AD-C pure core: classify the canvas stylesheet world from computed-token\n * presence. Legacy needs EVERY legacy marker; a loaded stylesheet with no\n * Tailwind marker is the explicit `no-theme-tokens` invalid state.\n */\nexport function classifyCanvasTheme(sample: CanvasThemeProbeSample): CanvasThemeStatus {\n if (sample.loadFailed) return { kind: 'stylesheet-error' }\n if (sample.legacyMarkers.length > 0 && sample.legacyMarkers.every(present)) {\n return { kind: 'legacy' }\n }\n if (present(sample.tailwindMarker)) return { kind: 'new' }\n return { kind: 'no-theme-tokens' }\n}\n\n/** The world a status renders as: only the legacy merged stylesheet keeps the\n * EC-389 body-class default; every other state gets the neutral backdrop. */\nexport function canvasWorldOf(status: CanvasThemeStatus): CanvasWorld {\n return status.kind === 'legacy' ? 'legacy' : 'new'\n}\n\n/** Sample the live document's computed root styles for {@link classifyCanvasTheme}. */\nexport function probeCanvasTheme(doc: Document, loadFailed = false): CanvasThemeStatus {\n const root = doc.documentElement\n const win = doc.defaultView\n if (!root || !win) return { kind: 'new' }\n const computed = win.getComputedStyle(root)\n return classifyCanvasTheme({\n loadFailed,\n legacyMarkers: CANVAS_LEGACY_MARKER_TOKENS.map((token) => computed.getPropertyValue(token)),\n tailwindMarker: computed.getPropertyValue(CANVAS_TAILWIND_MARKER_TOKEN),\n })\n}\n\n/**\n * The studio's DEFAULT canvas body classes — LEGACY world only (EC-389 → EC-414):\n * a consumer's `defineStudio({ canvas: { bodyClassName } })` REPLACES it\n * wholesale (including `''` to drop it), because these utility classes\n * out-cascade a site theme's `@layer base body` rules — keeping them additive\n * would make the site's own page background unreachable (the NK-004 bug). In\n * the NEW world the default retires entirely (the utilities don't exist there);\n * the chrome stylesheet's neutral backdrop takes over.\n */\nexport const DEFAULT_CANVAS_BODY_CLASS = 'bg-background text-foreground font-sans'\n\n/** Everything injected into the canvas preview document. */\nexport interface CanvasChromeInputs {\n /** The consuming app's compiled canvas stylesheet URL; `null` → no link (tests/SSR). */\n stylesheetUrl: string | null\n /** Which stylesheet world the document is in (AD-C probe; see `wireCanvasChrome`). */\n world: CanvasWorld\n /** An instantiated starter kit's brand token palette (EC-117), when the project has one. */\n kitTokensCss?: string\n /** The consumer's canvas theme from `defineStudio({ canvas })` (EC-389). */\n theme?: StudioCanvasTheme\n}\n\n/** The head element surface the chrome creates (link/style). */\nexport interface CanvasChromeElement {\n id: string\n rel?: string\n href?: string\n textContent: string | null\n remove(): void\n}\n\n/**\n * The document surface `applyCanvasChrome` needs (the `canvas-dnd` house\n * pattern: a narrow structural type so the core is unit-testable with a\n * hand-rolled fake — no jsdom). A real iframe `Document` satisfies it.\n */\nexport interface CanvasChromeDocument {\n getElementById(id: string): CanvasChromeElement | null\n createElement(tag: string): CanvasChromeElement\n // `object` (not CanvasChromeElement): the real DOM `appendChild<T extends Node>`\n // is only bivariance-compatible with a parameter every Node satisfies.\n head: { appendChild(node: object): void }\n body: { className: string }\n}\n\n/**\n * Apply the canvas document chrome in the PINNED cascade order (EC-389/EC-414):\n *\n * 1. `ec-layer-order` — the backdrop layer STATEMENT, pinning the chrome's\n * `ec-canvas-backdrop` layer to the bottom of the layer order (it must\n * precede the consumer stylesheet in document order to lose to it);\n * 2. `ec-app-css` — the canvas stylesheet `<link>` (the consumer theme; in\n * the legacy world also the studio design system);\n * 3. `ec-outline-css` — the studio's editing chrome (world-composed:\n * backdrop + selection/DnD/toolbar/containment styles);\n * 4. `ec-kit-tokens` — an instantiated starter kit's `tokensCss` (kit provides…);\n * 5. `ec-canvas-theme` — the consumer's `canvas.tokensCss` (…consumer overrides).\n *\n * Later elements win the cascade at equal specificity, so studio defaults <\n * kit tokens < consumer config — the customization-seam ordering. The body\n * class is the consumer's `bodyClassName` when given (even `''`), else the\n * legacy default in the legacy world and empty in the new world (AD-C). Safe\n * to run repeatedly (iframe onLoad + the already-loaded fast path + the probe\n * settling): links/styles are deduped by id, the chrome CSS is re-composed in\n * place when the world flips, and the two token styles are re-appended IN\n * ORDER so a re-wire never flips who wins.\n */\nexport function applyCanvasChrome(d: CanvasChromeDocument, inputs: CanvasChromeInputs): void {\n if (!d.getElementById('ec-layer-order')) {\n const style = d.createElement('style')\n style.id = 'ec-layer-order'\n style.textContent = CANVAS_BACKDROP_LAYER_STATEMENT\n d.head.appendChild(style)\n }\n if (inputs.stylesheetUrl && !d.getElementById('ec-app-css')) {\n const link = d.createElement('link')\n link.id = 'ec-app-css'\n link.rel = 'stylesheet'\n link.href = inputs.stylesheetUrl\n d.head.appendChild(link)\n }\n const chromeCss = canvasChromeCss(inputs.world)\n const outline = d.getElementById('ec-outline-css')\n if (outline) {\n // Probe settled on the other world: re-compose in place (position stable).\n if (outline.textContent !== chromeCss) outline.textContent = chromeCss\n } else {\n const style = d.createElement('style')\n style.id = 'ec-outline-css'\n style.textContent = chromeCss\n d.head.appendChild(style)\n }\n // Both token layers re-append (remove + add) on every wire, in sequence, so\n // the consumer element always lands after the kit element.\n appendCssLayer(d, 'ec-kit-tokens', inputs.kitTokensCss)\n appendCssLayer(d, 'ec-canvas-theme', inputs.theme?.tokensCss)\n d.body.className =\n inputs.theme?.bodyClassName ?? (inputs.world === 'legacy' ? DEFAULT_CANVAS_BODY_CLASS : '')\n}\n\n/** Replace-append one identified `<style>` layer; absent CSS removes a stale element. */\nfunction appendCssLayer(d: CanvasChromeDocument, id: string, css: string | undefined): void {\n const existing = d.getElementById(id)\n if (existing) existing.remove()\n if (css === undefined || css === '') return\n const style = d.createElement('style')\n style.id = id\n style.textContent = css\n d.head.appendChild(style)\n}\n\n/** `applyCanvasChrome` inputs minus the probe-owned `world`, plus the settle callback. */\nexport interface WireCanvasChromeOptions {\n stylesheetUrl: string | null\n kitTokensCss?: string\n theme?: StudioCanvasTheme\n /**\n * Fired when the classification SETTLES — the stylesheet finished loading\n * (or failing), or there is no stylesheet to wait for. Feeds the AD-D dev\n * notice; may fire again on re-wires (callers dedupe by kind).\n */\n onStatus?: (status: CanvasThemeStatus) => void\n}\n\n/** Links whose settle listeners are already attached (re-wires must not stack them). */\nconst wiredLinks = new WeakSet<object>()\n\n/**\n * The DOM-level wiring around {@link applyCanvasChrome}: applies the chrome\n * immediately with the CURRENT classification, then re-applies once the\n * `ec-app-css` link settles (the AD-C probe reads computed custom properties,\n * which exist only after the stylesheet loads). Before the stylesheet arrives\n * neither world's styling is resolvable anyway (no consumer CSS yet), so the\n * pre-settle pass is visually neutral in both worlds.\n */\nexport function wireCanvasChrome(doc: Document, options: WireCanvasChromeOptions): void {\n const apply = (settled: boolean, loadFailed: boolean) => {\n const status: CanvasThemeStatus = options.stylesheetUrl\n ? probeCanvasTheme(doc, loadFailed)\n : { kind: 'no-stylesheet' }\n applyCanvasChrome(doc, {\n stylesheetUrl: options.stylesheetUrl,\n world: canvasWorldOf(status),\n ...(options.kitTokensCss !== undefined ? { kitTokensCss: options.kitTokensCss } : {}),\n ...(options.theme !== undefined ? { theme: options.theme } : {}),\n })\n if (settled) options.onStatus?.(status)\n }\n\n if (!options.stylesheetUrl) {\n apply(true, false)\n return\n }\n // First pass injects the link (and everything else) with the current sample.\n const already = doc.getElementById('ec-app-css') as HTMLLinkElement | null\n const loaded = already?.sheet != null\n apply(loaded, false)\n if (loaded) return\n const link = doc.getElementById('ec-app-css') as HTMLLinkElement | null\n if (!link || wiredLinks.has(link)) return\n wiredLinks.add(link)\n link.addEventListener('load', () => apply(true, false), { once: true })\n link.addEventListener('error', () => apply(true, true), { once: true })\n}\n"]}
|
|
1
|
+
{"version":3,"file":"canvas-chrome.js","sourceRoot":"","sources":["../../../src/components/builder/canvas-chrome.ts"],"names":[],"mappings":"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","sourcesContent":["import type { StudioCanvasTheme } from '../app-views/studio-config'\nimport { CANVAS_BACKDROP_LAYER_STATEMENT, canvasChromeCss } from './canvas-chrome-css'\nimport type { CanvasWorld } from './canvas-chrome-css'\n\nexport { canvasChromeCss, CANVAS_BACKDROP_LAYER_STATEMENT } from './canvas-chrome-css'\nexport type { CanvasWorld } from './canvas-chrome-css'\n\n/**\n * The canvas iframe's document chrome (EC-389, reshaped by EC-414) — the\n * framework-free half of `BuilderCanvas`'s `wireUp` (house pattern, EC-330):\n * everything injected into the preview document lives here, in ONE pinned\n * cascade order, unit-testable without jsdom.\n *\n * EC-414 (AD-A/AD-C): the studio design system is leaving the canvas document.\n * Which world a project lives in is decided by a RUNTIME PROBE of the loaded\n * canvas stylesheet — no flag, no config, zero consumer breakage:\n *\n * - `legacy` (marker tokens present): the merged pre-EC-414 canvas.css that\n * bundles `@elytracms/studio/styles.css` with the consumer theme. The\n * EC-389 body-class default and today's behavior apply EXACTLY.\n * - `new` (markers absent): the consumer's standalone full-Tailwind theme\n * entry. No studio utilities exist in the document, so the body-class\n * default retires and the neutral hand-written backdrop applies instead.\n */\n\n/**\n * The studio design system's marker tokens (AD-C probe). These are the plain\n * `:root` custom properties from the studio's generated `tokens.css`, which any\n * merged legacy build carries VERBATIM — unlike `@theme inline` variables\n * (e.g. `--color-panel`), which Tailwind v4 inlines into utilities and never\n * emits as custom properties (verified against the repo's Tailwind 4.3.2; the\n * task file's `--color-panel` example probe would classify every legacy build\n * as new). Two tokens ANDed so a consumer theme that happens to define one\n * generic name (`--panel`) cannot be mistaken for the studio design system.\n */\nexport const CANVAS_LEGACY_MARKER_TOKENS = ['--panel', '--grid-line'] as const\n\n/**\n * A Tailwind v4 build unconditionally emits `--default-font-family` on\n * `:root, :host` (preflight consumes it) — its absence after a successful\n * stylesheet load means the canvas stylesheet is not a Tailwind theme entry at\n * all (AD-D item 2's \"defines no @theme tokens\" invalid state).\n */\nexport const CANVAS_TAILWIND_MARKER_TOKEN = '--default-font-family'\n\n/** The settled classification of the canvas stylesheet (AD-C + AD-D item 2). */\nexport type CanvasThemeStatus =\n /** Pre-EC-414 merged canvas.css — studio design system + consumer theme. */\n | { kind: 'legacy' }\n /** The EC-414 shape: a standalone consumer Tailwind theme entry. */\n | { kind: 'new' }\n /** The app never provided a canvas stylesheet (`setCanvasStylesheetUrl`). */\n | { kind: 'no-stylesheet' }\n /** The stylesheet `<link>` failed to load. */\n | { kind: 'stylesheet-error' }\n /** Loaded, but defines no Tailwind theme tokens at all. */\n | { kind: 'no-theme-tokens' }\n\n/** The raw computed-style sample the pure classifier decides from. */\nexport interface CanvasThemeProbeSample {\n /** The `ec-app-css` link fired `error` (or never resolved). */\n loadFailed: boolean\n /** Computed values of {@link CANVAS_LEGACY_MARKER_TOKENS}, in order. */\n legacyMarkers: readonly string[]\n /** Computed value of {@link CANVAS_TAILWIND_MARKER_TOKEN}. */\n tailwindMarker: string\n}\n\nconst present = (value: string | null | undefined): boolean =>\n typeof value === 'string' && value.trim() !== ''\n\n/**\n * AD-C pure core: classify the canvas stylesheet world from computed-token\n * presence. Legacy needs EVERY legacy marker; a loaded stylesheet with no\n * Tailwind marker is the explicit `no-theme-tokens` invalid state.\n */\nexport function classifyCanvasTheme(sample: CanvasThemeProbeSample): CanvasThemeStatus {\n if (sample.loadFailed) return { kind: 'stylesheet-error' }\n if (sample.legacyMarkers.length > 0 && sample.legacyMarkers.every(present)) {\n return { kind: 'legacy' }\n }\n if (present(sample.tailwindMarker)) return { kind: 'new' }\n return { kind: 'no-theme-tokens' }\n}\n\n/** The world a status renders as: only the legacy merged stylesheet keeps the\n * EC-389 body-class default; every other state gets the neutral backdrop. */\nexport function canvasWorldOf(status: CanvasThemeStatus): CanvasWorld {\n return status.kind === 'legacy' ? 'legacy' : 'new'\n}\n\n/** Sample the live document's computed root styles for {@link classifyCanvasTheme}. */\nexport function probeCanvasTheme(doc: Document, loadFailed = false): CanvasThemeStatus {\n const root = doc.documentElement\n const win = doc.defaultView\n if (!root || !win) return { kind: 'new' }\n const computed = win.getComputedStyle(root)\n return classifyCanvasTheme({\n loadFailed,\n legacyMarkers: CANVAS_LEGACY_MARKER_TOKENS.map((token) => computed.getPropertyValue(token)),\n tailwindMarker: computed.getPropertyValue(CANVAS_TAILWIND_MARKER_TOKEN),\n })\n}\n\n/**\n * The studio's DEFAULT canvas body classes — LEGACY world only (EC-389 → EC-414):\n * a consumer's `defineStudio({ canvas: { bodyClassName } })` REPLACES it\n * wholesale (including `''` to drop it), because these utility classes\n * out-cascade a site theme's `@layer base body` rules — keeping them additive\n * would make the site's own page background unreachable (the NK-004 bug). In\n * the NEW world the default retires entirely (the utilities don't exist there);\n * the chrome stylesheet's neutral backdrop takes over.\n */\nexport const DEFAULT_CANVAS_BODY_CLASS = 'bg-background text-foreground font-sans'\n\n/** Everything injected into the canvas preview document. */\nexport interface CanvasChromeInputs {\n /** The consuming app's compiled canvas stylesheet URL; `null` → no link (tests/SSR). */\n stylesheetUrl: string | null\n /** Which stylesheet world the document is in (AD-C probe; see `wireCanvasChrome`). */\n world: CanvasWorld\n /** The consumer's canvas theme from `defineStudio({ canvas })` (EC-389). */\n theme?: StudioCanvasTheme\n}\n\n/** The head element surface the chrome creates (link/style). */\nexport interface CanvasChromeElement {\n id: string\n rel?: string\n href?: string\n textContent: string | null\n remove(): void\n}\n\n/**\n * The document surface `applyCanvasChrome` needs (the `canvas-dnd` house\n * pattern: a narrow structural type so the core is unit-testable with a\n * hand-rolled fake — no jsdom). A real iframe `Document` satisfies it.\n */\nexport interface CanvasChromeDocument {\n getElementById(id: string): CanvasChromeElement | null\n createElement(tag: string): CanvasChromeElement\n // `object` (not CanvasChromeElement): the real DOM `appendChild<T extends Node>`\n // is only bivariance-compatible with a parameter every Node satisfies.\n head: { appendChild(node: object): void }\n body: { className: string }\n}\n\n/**\n * Apply the canvas document chrome in the PINNED cascade order (EC-389/EC-414):\n *\n * 1. `ec-layer-order` — the backdrop layer STATEMENT, pinning the chrome's\n * `ec-canvas-backdrop` layer to the bottom of the layer order (it must\n * precede the consumer stylesheet in document order to lose to it);\n * 2. `ec-app-css` — the canvas stylesheet `<link>` (the consumer theme; in\n * the legacy world also the studio design system);\n * 3. `ec-outline-css` — the studio's editing chrome (world-composed:\n * backdrop + selection/DnD/toolbar/containment styles);\n * 4. `ec-canvas-theme` — the consumer's `canvas.tokensCss` (consumer overrides).\n * (An `ec-kit-tokens` layer used to sit between 3 and 4 for an instantiated\n * starter kit's palette; the kits went in EC-574.)\n *\n * Later elements win the cascade at equal specificity, so studio defaults <\n * consumer config — the customization-seam ordering. The body class is the\n * consumer's `bodyClassName` when given (even `''`), else the legacy default in\n * the legacy world and empty in the new world (AD-C). Safe to run repeatedly\n * (iframe onLoad + the already-loaded fast path + the probe settling):\n * links/styles are deduped by id, the chrome CSS is re-composed in place when\n * the world flips, and the theme style is re-appended so a re-wire never\n * changes who wins.\n */\nexport function applyCanvasChrome(d: CanvasChromeDocument, inputs: CanvasChromeInputs): void {\n if (!d.getElementById('ec-layer-order')) {\n const style = d.createElement('style')\n style.id = 'ec-layer-order'\n style.textContent = CANVAS_BACKDROP_LAYER_STATEMENT\n d.head.appendChild(style)\n }\n if (inputs.stylesheetUrl && !d.getElementById('ec-app-css')) {\n const link = d.createElement('link')\n link.id = 'ec-app-css'\n link.rel = 'stylesheet'\n link.href = inputs.stylesheetUrl\n d.head.appendChild(link)\n }\n const chromeCss = canvasChromeCss(inputs.world)\n const outline = d.getElementById('ec-outline-css')\n if (outline) {\n // Probe settled on the other world: re-compose in place (position stable).\n if (outline.textContent !== chromeCss) outline.textContent = chromeCss\n } else {\n const style = d.createElement('style')\n style.id = 'ec-outline-css'\n style.textContent = chromeCss\n d.head.appendChild(style)\n }\n // The theme layer re-appends (remove + add) on every wire, so it always lands\n // last — after a re-composed chrome layer too.\n appendCssLayer(d, 'ec-canvas-theme', inputs.theme?.tokensCss)\n d.body.className =\n inputs.theme?.bodyClassName ?? (inputs.world === 'legacy' ? DEFAULT_CANVAS_BODY_CLASS : '')\n}\n\n/** Replace-append one identified `<style>` layer; absent CSS removes a stale element. */\nfunction appendCssLayer(d: CanvasChromeDocument, id: string, css: string | undefined): void {\n const existing = d.getElementById(id)\n if (existing) existing.remove()\n if (css === undefined || css === '') return\n const style = d.createElement('style')\n style.id = id\n style.textContent = css\n d.head.appendChild(style)\n}\n\n/** `applyCanvasChrome` inputs minus the probe-owned `world`, plus the settle callback. */\nexport interface WireCanvasChromeOptions {\n stylesheetUrl: string | null\n theme?: StudioCanvasTheme\n /**\n * Fired when the classification SETTLES — the stylesheet finished loading\n * (or failing), or there is no stylesheet to wait for. Feeds the AD-D dev\n * notice; may fire again on re-wires (callers dedupe by kind).\n */\n onStatus?: (status: CanvasThemeStatus) => void\n}\n\n/** Links whose settle listeners are already attached (re-wires must not stack them). */\nconst wiredLinks = new WeakSet<object>()\n\n/**\n * The DOM-level wiring around {@link applyCanvasChrome}: applies the chrome\n * immediately with the CURRENT classification, then re-applies once the\n * `ec-app-css` link settles (the AD-C probe reads computed custom properties,\n * which exist only after the stylesheet loads). Before the stylesheet arrives\n * neither world's styling is resolvable anyway (no consumer CSS yet), so the\n * pre-settle pass is visually neutral in both worlds.\n */\nexport function wireCanvasChrome(doc: Document, options: WireCanvasChromeOptions): void {\n const apply = (settled: boolean, loadFailed: boolean) => {\n const status: CanvasThemeStatus = options.stylesheetUrl\n ? probeCanvasTheme(doc, loadFailed)\n : { kind: 'no-stylesheet' }\n applyCanvasChrome(doc, {\n stylesheetUrl: options.stylesheetUrl,\n world: canvasWorldOf(status),\n ...(options.theme !== undefined ? { theme: options.theme } : {}),\n })\n if (settled) options.onStatus?.(status)\n }\n\n if (!options.stylesheetUrl) {\n apply(true, false)\n return\n }\n // First pass injects the link (and everything else) with the current sample.\n const already = doc.getElementById('ec-app-css') as HTMLLinkElement | null\n const loaded = already?.sheet != null\n apply(loaded, false)\n if (loaded) return\n const link = doc.getElementById('ec-app-css') as HTMLLinkElement | null\n if (!link || wiredLinks.has(link)) return\n wiredLinks.add(link)\n link.addEventListener('load', () => apply(true, false), { once: true })\n link.addEventListener('error', () => apply(true, true), { once: true })\n}\n"]}
|
|
@@ -31,6 +31,12 @@ export interface CanvasDndDeps {
|
|
|
31
31
|
currentRoot(): ComponentNode;
|
|
32
32
|
/** Manifest lookup feeding the pure drop rules. */
|
|
33
33
|
getManifest(componentId: string): ManifestRules | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* The composition field's top-level `allow` (EC-554; `undefined` = the field
|
|
36
|
+
* declares none) — judged by `resolveDrop` for drops whose parent is the root.
|
|
37
|
+
* A required key: the wiring must state it.
|
|
38
|
+
*/
|
|
39
|
+
fieldAllow: readonly string[] | undefined;
|
|
34
40
|
dispatch(action: BuilderAction): void;
|
|
35
41
|
/**
|
|
36
42
|
* Whether `target` sits inside the live inline editor (EC-158): a press
|
|
@@ -56,4 +62,6 @@ export declare function useCanvasDnd(doc: Document | null, deps: {
|
|
|
56
62
|
studio: StudioRegistry;
|
|
57
63
|
/** The live inline-edit session (owned by `useCanvasInlineEditing`). */
|
|
58
64
|
editingRef: RefObject<ActiveInlineEdit | null>;
|
|
65
|
+
/** The composition field's top-level `allow` (EC-554) — `builder.field.allow`. */
|
|
66
|
+
fieldAllow: readonly string[] | undefined;
|
|
59
67
|
}): void;
|
|
@@ -8,7 +8,7 @@ import { wrapperAt } from './canvas-dom.js';
|
|
|
8
8
|
* React's synthetic events don't cross frames). Returns the detach function.
|
|
9
9
|
*/
|
|
10
10
|
export function attachCanvasDndListeners(doc, deps) {
|
|
11
|
-
const { currentRoot, getManifest, dispatch } = deps;
|
|
11
|
+
const { currentRoot, getManifest, fieldAllow, dispatch } = deps;
|
|
12
12
|
let dropWrapper = null;
|
|
13
13
|
let dropPlan = null;
|
|
14
14
|
const clearIndicator = () => {
|
|
@@ -106,6 +106,7 @@ export function attachCanvasDndListeners(doc, deps) {
|
|
|
106
106
|
const resolution = resolveDrop({
|
|
107
107
|
root,
|
|
108
108
|
getManifest,
|
|
109
|
+
fieldAllow,
|
|
109
110
|
subject: drag.subject,
|
|
110
111
|
location: { nodeId: refId, position },
|
|
111
112
|
});
|
|
@@ -192,7 +193,7 @@ export function attachCanvasDndListeners(doc, deps) {
|
|
|
192
193
|
* session without re-subscribing on every edit.
|
|
193
194
|
*/
|
|
194
195
|
export function useCanvasDnd(doc, deps) {
|
|
195
|
-
const { presentRef, dispatch, studio, editingRef } = deps;
|
|
196
|
+
const { presentRef, dispatch, studio, editingRef, fieldAllow } = deps;
|
|
196
197
|
useEffect(() => {
|
|
197
198
|
if (!doc)
|
|
198
199
|
return;
|
|
@@ -201,9 +202,10 @@ export function useCanvasDnd(doc, deps) {
|
|
|
201
202
|
// EC-386: adapt through `manifestRules` so blocks-prop regions (e.g. a
|
|
202
203
|
// Section's `body`) receive drops with their `allow` leash, like raw slots.
|
|
203
204
|
getManifest: (id) => manifestRules(studio.registry.getManifest(id)),
|
|
205
|
+
fieldAllow,
|
|
204
206
|
dispatch,
|
|
205
207
|
isEditingTarget: (target) => editingRef.current?.host.contains(target) ?? false,
|
|
206
208
|
});
|
|
207
|
-
}, [doc, dispatch, studio, presentRef, editingRef]);
|
|
209
|
+
}, [doc, dispatch, studio, presentRef, editingRef, fieldAllow]);
|
|
208
210
|
}
|
|
209
211
|
//# sourceMappingURL=canvas-dnd.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"canvas-dnd.js","sourceRoot":"","sources":["../../../src/components/builder/canvas-dnd.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEjC,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAA;AAEtE,OAAO,EACL,WAAW,EACX,iBAAiB,EACjB,gBAAgB,EAChB,oBAAoB,EACpB,aAAa,EACb,WAAW,GACZ,MAAM,sCAAsC,CAAA;AAI7C,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAEjF,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAuCxC;;;GAGG;AACH,MAAM,UAAU,wBAAwB,CAAC,GAAsB,EAAE,IAAmB;IAClF,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAA;IAEnD,IAAI,WAAW,GAAuB,IAAI,CAAA;IAC1C,IAAI,QAAQ,GAAoB,IAAI,CAAA;IACpC,MAAM,cAAc,GAAG,GAAG,EAAE;QAC1B,WAAW,EAAE,eAAe,CAAC,cAAc,CAAC,CAAA;QAC5C,WAAW,EAAE,eAAe,CAAC,mBAAmB,CAAC,CAAA;QACjD,WAAW,GAAG,IAAI,CAAA;QAClB,QAAQ,GAAG,IAAI,CAAA;IACjB,CAAC,CAAA;IACD,MAAM,aAAa,GAAG,CAAC,OAA2B,EAAE,KAAoB,EAAE,IAAc,EAAE,EAAE;QAC1F,IAAI,WAAW,IAAI,WAAW,KAAK,OAAO,EAAE,CAAC;YAC3C,WAAW,CAAC,eAAe,CAAC,cAAc,CAAC,CAAA;YAC3C,WAAW,CAAC,eAAe,CAAC,mBAAmB,CAAC,CAAA;QAClD,CAAC;QACD,WAAW,GAAG,OAAO,CAAA;QACrB,IAAI,CAAC,OAAO;YAAE,OAAM;QACpB,IAAI,KAAK,EAAE,CAAC;YACV,OAAO,CAAC,YAAY,CAAC,cAAc,EAAE,KAAK,CAAC,CAAA;YAC3C,kEAAkE;YAClE,OAAO,CAAC,YAAY,CAAC,mBAAmB,EAAE,IAAI,KAAK,YAAY,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;QAC9E,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,eAAe,CAAC,cAAc,CAAC,CAAA;YACvC,OAAO,CAAC,eAAe,CAAC,mBAAmB,CAAC,CAAA;QAC9C,CAAC;IACH,CAAC,CAAA;IAED,0EAA0E;IAC1E,yEAAyE;IACzE,qEAAqE;IACrE,2CAA2C;IAC3C,MAAM,WAAW,GAAG,CAAC,CAAQ,EAAE,EAAE;QAC/B,MAAM,OAAO,GAAG,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAA;QACnC,IAAI,CAAC,OAAO;YAAE,OAAM;QACpB,MAAM,IAAI,GAAG,WAAW,EAAE,CAAA;QAC1B,MAAM,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,iBAAiB,CAAC,CAAA;QACtD,MAAM,QAAQ,GAAG,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,MAAM,CAAC,CAAA;QAC/C,IAAI,IAAI,IAAI,MAAM,IAAI,MAAM,KAAK,IAAI,CAAC,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC;YACtD,OAAO,CAAC,YAAY,CAAC,WAAW,EAAE,MAAM,CAAC,CAAA;QAC3C,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,eAAe,CAAC,WAAW,CAAC,CAAA;QACtC,CAAC;IACH,CAAC,CAAA;IAED,MAAM,WAAW,GAAG,CAAC,CAAQ,EAAE,EAAE;QAC/B,MAAM,KAAK,GAAG,CAAc,CAAA;QAC5B,gFAAgF;QAChF,IAAI,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,MAAM,CAAC;YAAE,OAAM;QAC9C,MAAM,OAAO,GAAG,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;QACvC,IAAI,CAAC,OAAO;YAAE,OAAM;QACpB,MAAM,IAAI,GAAG,WAAW,EAAE,CAAA;QAC1B,MAAM,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,iBAAiB,CAAE,CAAA;QACvD,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;QACrD,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,IAAI,MAAM,KAAK,IAAI,CAAC,EAAE,EAAE,CAAC;YACzC,KAAK,CAAC,cAAc,EAAE,CAAA;YACtB,OAAM;QACR,CAAC;QACD,SAAS,CACP,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,CAAC,WAAW,EAAE,EAC/G,KAAK,CAAC,YAAY,CACnB,CAAA;IACH,CAAC,CAAA;IAED,MAAM,UAAU,GAAG,CAAC,CAAQ,EAAE,EAAE;QAC9B,MAAM,KAAK,GAAG,CAAc,CAAA;QAC5B,MAAM,IAAI,GAAG,UAAU,EAAE,CAAA;QACzB,IAAI,CAAC,IAAI;YAAE,OAAM;QACjB,MAAM,IAAI,GAAG,WAAW,EAAE,CAAA;QAC1B,IAAI,CAAC,IAAI;YAAE,OAAM;QACjB,MAAM,OAAO,GAAG,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;QACvC,sEAAsE;QACtE,MAAM,KAAK,GAAG,OAAO,EAAE,YAAY,CAAC,iBAAiB,CAAC,IAAI,IAAI,CAAC,EAAE,CAAA;QACjE,MAAM,UAAU,GACd,OAAO,IAAK,GAAG,CAAC,aAAa,CAAC,qBAAqB,IAAI,CAAC,EAAE,IAAI,CAAwB,CAAA;QACxF,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;QACpC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,cAAc,EAAE,CAAA;YAChB,OAAM;QACR,CAAC;QACD,IAAI,QAAQ,GAAiB,MAAM,CAAA;QACnC,IAAI,IAAI,GAAa,UAAU,CAAA;QAC/B,IAAI,OAAO,IAAI,KAAK,KAAK,IAAI,CAAC,EAAE,EAAE,CAAC;YACjC,MAAM,IAAI,GAAG,OAAO,CAAC,qBAAqB,EAAE,CAAA;YAC5C,mEAAmE;YACnE,iEAAiE;YACjE,IAAI,GAAG,iBAAiB,CAAC,aAAa,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,CAAA;YAC5D,QAAQ,GAAG,oBAAoB,CAAC;gBAC9B,CAAC,EAAE,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG;gBAClE,CAAC,EAAE,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG;gBACnE,IAAI;gBACJ,QAAQ,EAAE,WAAW,CAAC,WAAW,EAAE,OAAO,CAAC,WAAW,CAAC;gBACvD,KAAK,EAAE,IAAI;gBACX,SAAS,EAAE,IAAI;aAChB,CAAC,CAAA;QACJ,CAAC;QACD,MAAM,UAAU,GAAG,WAAW,CAAC;YAC7B,IAAI;YACJ,WAAW;YACX,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,QAAQ,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE;SACtC,CAAC,CAAA;QACF,aAAa,CAAC,UAAU,EAAE,gBAAgB,CAAC,UAAU,EAAE,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAA;QACvE,IAAI,UAAU,CAAC,QAAQ,EAAE,CAAC;YACxB,QAAQ,GAAG,UAAU,CAAA;YACrB,KAAK,CAAC,cAAc,EAAE,CAAA;YACtB,IAAI,KAAK,CAAC,YAAY,EAAE,CAAC;gBACvB,KAAK,CAAC,YAAY,CAAC,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAA;YAChF,CAAC;QACH,CAAC;aAAM,CAAC;YACN,QAAQ,GAAG,IAAI,CAAA;QACjB,CAAC;IACH,CAAC,CAAA;IAED,MAAM,MAAM,GAAG,CAAC,CAAQ,EAAE,EAAE;QAC1B,MAAM,KAAK,GAAG,CAAc,CAAA;QAC5B,MAAM,IAAI,GAAG,UAAU,EAAE,CAAA;QACzB,IAAI,CAAC,IAAI;YAAE,OAAM;QACjB,MAAM,IAAI,GAAG,QAAQ,CAAA;QACrB,cAAc,EAAE,CAAA;QAChB,IAAI,CAAC,IAAI;YAAE,OAAM;QACjB,KAAK,CAAC,cAAc,EAAE,CAAA;QACtB,wEAAwE;QACxE,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YACzB,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAA;QAClE,CAAC;aAAM,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAChC,gEAAgE;YAChE,MAAM,OAAO,GACX,IAAI,CAAC,MAAM,KAAK,SAAS;gBACvB,CAAC,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE;gBAChD,CAAC,CAAC,IAAI,CAAC,SAAS;oBACd,CAAC,CAAC,EAAE,IAAI,EAAE,WAAoB,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,EAAE,EAAE;oBACxD,CAAC,CAAC,IAAI,CAAA;YACZ,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,OAAO,CAAC,WAAW,CAAC,CAAA;gBACpB,OAAM;YACR,CAAC;YACD,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,CAAA;QACrF,CAAC;aAAM,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YAC1B,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC,CAAA;QAC3E,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,WAAW,CAAC,CAAA;YACpB,OAAM;QACR,CAAC;QACD,OAAO,CAAC,SAAS,CAAC,CAAA;IACpB,CAAC,CAAA;IAED,MAAM,SAAS,GAAG,GAAG,EAAE;QACrB,cAAc,EAAE,CAAA;QAChB,OAAO,CAAC,WAAW,CAAC,CAAA;IACtB,CAAC,CAAA;IAED,MAAM,WAAW,GAAG,CAAC,CAAQ,EAAE,EAAE;QAC/B,gEAAgE;QAChE,IAAI,CAAE,CAAe,CAAC,aAAa;YAAE,cAAc,EAAE,CAAA;IACvD,CAAC,CAAA;IAED,uEAAuE;IACvE,MAAM,WAAW,GAAG,gBAAgB,CAAC,GAAG,EAAE,CAAC,cAAc,EAAE,CAAC,CAAA;IAE5D,GAAG,CAAC,gBAAgB,CAAC,WAAW,EAAE,WAAW,EAAE,IAAI,CAAC,CAAA;IACpD,GAAG,CAAC,gBAAgB,CAAC,WAAW,EAAE,WAAW,CAAC,CAAA;IAC9C,GAAG,CAAC,gBAAgB,CAAC,UAAU,EAAE,UAAU,CAAC,CAAA;IAC5C,GAAG,CAAC,gBAAgB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACpC,GAAG,CAAC,gBAAgB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAA;IAC1C,GAAG,CAAC,gBAAgB,CAAC,WAAW,EAAE,WAAW,CAAC,CAAA;IAC9C,OAAO,GAAG,EAAE;QACV,WAAW,EAAE,CAAA;QACb,cAAc,EAAE,CAAA;QAChB,GAAG,CAAC,mBAAmB,CAAC,WAAW,EAAE,WAAW,EAAE,IAAI,CAAC,CAAA;QACvD,GAAG,CAAC,mBAAmB,CAAC,WAAW,EAAE,WAAW,CAAC,CAAA;QACjD,GAAG,CAAC,mBAAmB,CAAC,UAAU,EAAE,UAAU,CAAC,CAAA;QAC/C,GAAG,CAAC,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;QACvC,GAAG,CAAC,mBAAmB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAA;QAC7C,GAAG,CAAC,mBAAmB,CAAC,WAAW,EAAE,WAAW,CAAC,CAAA;IACnD,CAAC,CAAA;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAC1B,GAAoB,EACpB,IAOC;IAED,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,IAAI,CAAA;IACzD,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,GAAG;YAAE,OAAM;QAChB,OAAO,wBAAwB,CAAC,GAAG,EAAE;YACnC,WAAW,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,OAAO;YACrC,uEAAuE;YACvE,4EAA4E;YAC5E,WAAW,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,aAAa,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;YACnE,QAAQ;YACR,eAAe,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,MAAc,CAAC,IAAI,KAAK;SACxF,CAAC,CAAA;IACJ,CAAC,EAAE,CAAC,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC,CAAA;AACrD,CAAC","sourcesContent":["import { useEffect } from 'react'\nimport type { RefObject } from 'react'\nimport { getNode, getParentInfo } from '@elytracms/core/project-graph'\nimport type { ComponentNode } from '@elytracms/core/project-graph'\nimport {\n canNestInto,\n containerFlowAxis,\n dropIndicatorFor,\n dropPositionForPoint,\n manifestRules,\n resolveDrop,\n} from '@elytracms/studio/lib/dnd/drop-rules'\nimport type { DropPlan, DropPosition, FlowAxis, ManifestRules } from '@elytracms/studio/lib/dnd/drop-rules'\nimport type { StudioRegistry } from '@elytracms/studio/lib/registry'\nimport type { BuilderAction } from './builder-store'\nimport { activeDrag, beginDrag, endDrag, onDragSessionEnd } from './drag-session'\nimport type { ActiveInlineEdit } from './canvas-inline-editing'\nimport { wrapperAt } from './canvas-dom'\n\n/**\n * Drag & drop on the canvas (EC-160), extracted from `BuilderCanvas` (EC-330).\n * Eligibility is the pure shared rule set (`lib/dnd/drop-rules`) — the same\n * functions the page tree consumes — evaluated live during `dragover`; the\n * wrapper under the pointer shows the resolution (`data-ec-drop`:\n * into/before/after, or `blocked` for a visible rejection). A completed drop\n * dispatches exactly one builder action (move or insert) → one operations\n * command → one undo step. Ineligible targets never `preventDefault`, so\n * releasing over one is a native no-op.\n *\n * The listener wiring is framework-free ({@link attachCanvasDndListeners},\n * the `clipboard-actions` pattern) so the whole gesture state machine —\n * arm-on-mousedown, live indicator, plan resolution, one-dispatch drop — is\n * unit-testable without a DOM environment; {@link useCanvasDnd} only owns the\n * React effect lifecycle.\n */\n\n/** The structural slice of the iframe document the DnD wiring needs. */\nexport interface CanvasDndDocument {\n addEventListener(type: string, listener: (e: Event) => void, capture?: boolean): void\n removeEventListener(type: string, listener: (e: Event) => void, capture?: boolean): void\n querySelector(selectors: string): Element | null\n}\n\nexport interface CanvasDndDeps {\n /** The current composition root (read live — listeners outlive renders). */\n currentRoot(): ComponentNode\n /** Manifest lookup feeding the pure drop rules. */\n getManifest(componentId: string): ManifestRules | undefined\n dispatch(action: BuilderAction): void\n /**\n * Whether `target` sits inside the live inline editor (EC-158): a press\n * there must keep native caret clicks/text selection, never start a node drag.\n */\n isEditingTarget(target: EventTarget | null): boolean\n}\n\n/**\n * Wire the canvas drag-and-drop listeners onto the iframe document (native —\n * React's synthetic events don't cross frames). Returns the detach function.\n */\nexport function attachCanvasDndListeners(doc: CanvasDndDocument, deps: CanvasDndDeps): () => void {\n const { currentRoot, getManifest, dispatch } = deps\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 = deps.isEditingTarget(e.target)\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: Event) => {\n const event = e as DragEvent\n // Drags inside the live inline editor are native text drags — leave them alone.\n if (deps.isEditingTarget(event.target)) return\n const wrapper = wrapperAt(event.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 event.preventDefault()\n return\n }\n beginDrag(\n { subject: { kind: 'node', nodeId, componentId: node.componentId }, source: 'canvas', label: node.componentId },\n event.dataTransfer,\n )\n }\n\n const onDragOver = (e: Event) => {\n const event = e as DragEvent\n const drag = activeDrag()\n if (!drag) return\n const root = currentRoot()\n if (!root) return\n const wrapper = wrapperAt(event.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 ? (event.clientX - rect.left) / rect.width : 0.5,\n y: rect.height > 0 ? (event.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 event.preventDefault()\n if (event.dataTransfer) {\n event.dataTransfer.dropEffect = drag.subject.kind === 'node' ? 'move' : 'copy'\n }\n } else {\n dropPlan = null\n }\n }\n\n const onDrop = (e: Event) => {\n const event = e as DragEvent\n const drag = activeDrag()\n if (!drag) return\n const plan = dropPlan\n clearIndicator()\n if (!plan) return\n event.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: Event) => {\n // Leaving the iframe document entirely (relatedTarget is null).\n if (!(e as DragEvent).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}\n\n/**\n * The React face of the canvas DnD wiring: attach on the iframe document once\n * it exists, detach on doc swap/unmount. State lives in refs on purpose —\n * listeners outlive renders and must read the *current* graph and editing\n * session without re-subscribing on every edit.\n */\nexport function useCanvasDnd(\n doc: Document | null,\n deps: {\n /** The live composition root (`builder.present`, mirrored into a ref). */\n presentRef: RefObject<ComponentNode>\n dispatch: (action: BuilderAction) => void\n studio: StudioRegistry\n /** The live inline-edit session (owned by `useCanvasInlineEditing`). */\n editingRef: RefObject<ActiveInlineEdit | null>\n },\n): void {\n const { presentRef, dispatch, studio, editingRef } = deps\n useEffect(() => {\n if (!doc) return\n return attachCanvasDndListeners(doc, {\n currentRoot: () => presentRef.current,\n // EC-386: adapt through `manifestRules` so blocks-prop regions (e.g. a\n // Section's `body`) receive drops with their `allow` leash, like raw slots.\n getManifest: (id) => manifestRules(studio.registry.getManifest(id)),\n dispatch,\n isEditingTarget: (target) => editingRef.current?.host.contains(target as Node) ?? false,\n })\n }, [doc, dispatch, studio, presentRef, editingRef])\n}\n"]}
|
|
1
|
+
{"version":3,"file":"canvas-dnd.js","sourceRoot":"","sources":["../../../src/components/builder/canvas-dnd.ts"],"names":[],"mappings":"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","sourcesContent":["import { useEffect } from 'react'\nimport type { RefObject } from 'react'\nimport { getNode, getParentInfo } from '@elytracms/core/project-graph'\nimport type { ComponentNode } from '@elytracms/core/project-graph'\nimport {\n canNestInto,\n containerFlowAxis,\n dropIndicatorFor,\n dropPositionForPoint,\n manifestRules,\n resolveDrop,\n} from '@elytracms/studio/lib/dnd/drop-rules'\nimport type { DropPlan, DropPosition, FlowAxis, ManifestRules } from '@elytracms/studio/lib/dnd/drop-rules'\nimport type { StudioRegistry } from '@elytracms/studio/lib/registry'\nimport type { BuilderAction } from './builder-store'\nimport { activeDrag, beginDrag, endDrag, onDragSessionEnd } from './drag-session'\nimport type { ActiveInlineEdit } from './canvas-inline-editing'\nimport { wrapperAt } from './canvas-dom'\n\n/**\n * Drag & drop on the canvas (EC-160), extracted from `BuilderCanvas` (EC-330).\n * Eligibility is the pure shared rule set (`lib/dnd/drop-rules`) — the same\n * functions the page tree consumes — evaluated live during `dragover`; the\n * wrapper under the pointer shows the resolution (`data-ec-drop`:\n * into/before/after, or `blocked` for a visible rejection). A completed drop\n * dispatches exactly one builder action (move or insert) → one operations\n * command → one undo step. Ineligible targets never `preventDefault`, so\n * releasing over one is a native no-op.\n *\n * The listener wiring is framework-free ({@link attachCanvasDndListeners},\n * the `clipboard-actions` pattern) so the whole gesture state machine —\n * arm-on-mousedown, live indicator, plan resolution, one-dispatch drop — is\n * unit-testable without a DOM environment; {@link useCanvasDnd} only owns the\n * React effect lifecycle.\n */\n\n/** The structural slice of the iframe document the DnD wiring needs. */\nexport interface CanvasDndDocument {\n addEventListener(type: string, listener: (e: Event) => void, capture?: boolean): void\n removeEventListener(type: string, listener: (e: Event) => void, capture?: boolean): void\n querySelector(selectors: string): Element | null\n}\n\nexport interface CanvasDndDeps {\n /** The current composition root (read live — listeners outlive renders). */\n currentRoot(): ComponentNode\n /** Manifest lookup feeding the pure drop rules. */\n getManifest(componentId: string): ManifestRules | undefined\n /**\n * The composition field's top-level `allow` (EC-554; `undefined` = the field\n * declares none) — judged by `resolveDrop` for drops whose parent is the root.\n * A required key: the wiring must state it.\n */\n fieldAllow: readonly string[] | undefined\n dispatch(action: BuilderAction): void\n /**\n * Whether `target` sits inside the live inline editor (EC-158): a press\n * there must keep native caret clicks/text selection, never start a node drag.\n */\n isEditingTarget(target: EventTarget | null): boolean\n}\n\n/**\n * Wire the canvas drag-and-drop listeners onto the iframe document (native —\n * React's synthetic events don't cross frames). Returns the detach function.\n */\nexport function attachCanvasDndListeners(doc: CanvasDndDocument, deps: CanvasDndDeps): () => void {\n const { currentRoot, getManifest, fieldAllow, dispatch } = deps\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 = deps.isEditingTarget(e.target)\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: Event) => {\n const event = e as DragEvent\n // Drags inside the live inline editor are native text drags — leave them alone.\n if (deps.isEditingTarget(event.target)) return\n const wrapper = wrapperAt(event.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 event.preventDefault()\n return\n }\n beginDrag(\n { subject: { kind: 'node', nodeId, componentId: node.componentId }, source: 'canvas', label: node.componentId },\n event.dataTransfer,\n )\n }\n\n const onDragOver = (e: Event) => {\n const event = e as DragEvent\n const drag = activeDrag()\n if (!drag) return\n const root = currentRoot()\n if (!root) return\n const wrapper = wrapperAt(event.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 ? (event.clientX - rect.left) / rect.width : 0.5,\n y: rect.height > 0 ? (event.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 fieldAllow,\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 event.preventDefault()\n if (event.dataTransfer) {\n event.dataTransfer.dropEffect = drag.subject.kind === 'node' ? 'move' : 'copy'\n }\n } else {\n dropPlan = null\n }\n }\n\n const onDrop = (e: Event) => {\n const event = e as DragEvent\n const drag = activeDrag()\n if (!drag) return\n const plan = dropPlan\n clearIndicator()\n if (!plan) return\n event.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: Event) => {\n // Leaving the iframe document entirely (relatedTarget is null).\n if (!(e as DragEvent).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}\n\n/**\n * The React face of the canvas DnD wiring: attach on the iframe document once\n * it exists, detach on doc swap/unmount. State lives in refs on purpose —\n * listeners outlive renders and must read the *current* graph and editing\n * session without re-subscribing on every edit.\n */\nexport function useCanvasDnd(\n doc: Document | null,\n deps: {\n /** The live composition root (`builder.present`, mirrored into a ref). */\n presentRef: RefObject<ComponentNode>\n dispatch: (action: BuilderAction) => void\n studio: StudioRegistry\n /** The live inline-edit session (owned by `useCanvasInlineEditing`). */\n editingRef: RefObject<ActiveInlineEdit | null>\n /** The composition field's top-level `allow` (EC-554) — `builder.field.allow`. */\n fieldAllow: readonly string[] | undefined\n },\n): void {\n const { presentRef, dispatch, studio, editingRef, fieldAllow } = deps\n useEffect(() => {\n if (!doc) return\n return attachCanvasDndListeners(doc, {\n currentRoot: () => presentRef.current,\n // EC-386: adapt through `manifestRules` so blocks-prop regions (e.g. a\n // Section's `body`) receive drops with their `allow` leash, like raw slots.\n getManifest: (id) => manifestRules(studio.registry.getManifest(id)),\n fieldAllow,\n dispatch,\n isEditingTarget: (target) => editingRef.current?.host.contains(target as Node) ?? false,\n })\n }, [doc, dispatch, studio, presentRef, editingRef, fieldAllow])\n}\n"]}
|
|
@@ -1,21 +1,22 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
1
|
+
import type { AssetRecord } from '@elytracms/core/persistence';
|
|
2
|
+
import type { RenderAsset, RenderContext, ResolveAsset, ResolveLink, ResolveListing, ResolveRelation } from '@elytracms/core/runtime-renderer';
|
|
3
|
+
import type { ComponentNode } from '@elytracms/core/project-graph';
|
|
3
4
|
import type { StudioRegistry } from '@elytracms/studio/lib/registry';
|
|
4
5
|
import type { CanvasPerspective } from '@elytracms/studio/lib/data';
|
|
5
6
|
import type { ReuseCapability } from './reuse.js';
|
|
6
7
|
/**
|
|
7
8
|
* The canvas's `RenderContext` (EC-330, extracted from `BuilderCanvas`): the
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
9
|
+
* injected resolvers that make the iframe preview match delivery — assets
|
|
10
|
+
* (EC-195), relations (EC-254), listings (EC-255), rich-text links (EC-291) —
|
|
11
|
+
* plus the per-node decorator that stamps the editable wrappers (selection/hover
|
|
12
|
+
* state, rich-text remount epochs, and the EC-300 unresolved-reusable
|
|
13
|
+
* placeholder). The binding resolver and the EC-176 locale-fallback chips (which
|
|
14
|
+
* read bound props) went with the data-binding subsystem (EC-574).
|
|
13
15
|
*
|
|
14
16
|
* The context assembly is a pure factory ({@link createCanvasRenderContext})
|
|
15
|
-
* over pure decision
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* them to the live stores.
|
|
17
|
+
* over a pure decision helper ({@link unresolvedReuseStatus}), so the decoration
|
|
18
|
+
* and resolver wiring are unit-testable without providers;
|
|
19
|
+
* {@link useCanvasRenderContext} binds them to the live stores.
|
|
19
20
|
*/
|
|
20
21
|
/** Collect every node id under a root (the page subtree). */
|
|
21
22
|
export declare function collectIds(node: ComponentNode, out?: Set<string>): Set<string>;
|
|
@@ -30,21 +31,10 @@ export declare function unresolvedReuseStatus(node: ComponentNode, capability: R
|
|
|
30
31
|
collection: string;
|
|
31
32
|
id: string;
|
|
32
33
|
}, perspective: CanvasPerspective) => unknown, perspective: CanvasPerspective): 'unpublished' | 'missing' | null;
|
|
33
|
-
/**
|
|
34
|
-
* Locale-fallback chips (EC-176): the locale the node's first fallback-served
|
|
35
|
-
* bound prop actually came from, or undefined when every bound value is a real
|
|
36
|
-
* translation (or nothing is bound). Pure over an injected per-binding decision
|
|
37
|
-
* (the hook wires `cmsBindingFallback` — the same decision function as the
|
|
38
|
-
* inspector's Content tab, so chip and label can never disagree).
|
|
39
|
-
*/
|
|
40
|
-
export declare function firstBindingFallbackLocale(node: ComponentNode, fallbackOf: (binding: BindingReference) => {
|
|
41
|
-
sourceLocale: string;
|
|
42
|
-
} | null): string | undefined;
|
|
43
34
|
/** Everything the pure context factory needs — resolvers plus decoration state. */
|
|
44
35
|
export interface CanvasRenderContextDeps {
|
|
45
36
|
registry: RenderContext['registry'];
|
|
46
37
|
implementations: RenderContext['implementations'];
|
|
47
|
-
resolveBinding: ResolveBinding;
|
|
48
38
|
resolveAsset: ResolveAsset;
|
|
49
39
|
resolveRelation: ResolveRelation;
|
|
50
40
|
resolveListing: ResolveListing;
|
|
@@ -56,7 +46,6 @@ export interface CanvasRenderContextDeps {
|
|
|
56
46
|
/** Per-node remount epochs from the inline rich-text editing (EC-159). */
|
|
57
47
|
richEditEpochs: Record<string, number>;
|
|
58
48
|
unresolvedReuse(node: ComponentNode): 'unpublished' | 'missing' | null;
|
|
59
|
-
localeFallbackOf(node: ComponentNode): string | undefined;
|
|
60
49
|
/**
|
|
61
50
|
* EC-460 (never-silent): a human-readable message when the node declares a
|
|
62
51
|
* `listing` whose built query is STATICALLY invalid against the active session
|
|
@@ -73,10 +62,20 @@ export interface CanvasRenderContextDeps {
|
|
|
73
62
|
export declare function createCanvasRenderContext(deps: CanvasRenderContextDeps): RenderContext;
|
|
74
63
|
/**
|
|
75
64
|
* The live canvas render context: binds the pure factory to the studio's
|
|
76
|
-
* stores (
|
|
65
|
+
* stores (asset library, content session, active locale, reuse capability) for
|
|
77
66
|
* the given perspective. Returns the context plus the page-node id set the
|
|
78
67
|
* canvas also needs for spacing-handle gating.
|
|
79
68
|
*/
|
|
69
|
+
/**
|
|
70
|
+
* The canvas's asset reader IS delivery's (NK-049 finding, 2026-10-23). This used to
|
|
71
|
+
* hand-pick `url/width/height/alt/focalPoint`, read `alt` from the legacy column
|
|
72
|
+
* only and drop `meta` — so a component's AI badge (`meta.aiGenerated`) and an
|
|
73
|
+
* editor-set `meta.alt` rendered on the site but not on the canvas. One reader
|
|
74
|
+
* (`resolvedAsset`, `content/assets.ts`) means the canvas and the host cannot
|
|
75
|
+
* disagree about an asset's own facts; `RenderAsset` is a subset of
|
|
76
|
+
* `ResolvedAsset`, `lqip` included. Pure, exported for the node test.
|
|
77
|
+
*/
|
|
78
|
+
export declare function canvasRenderAsset(record: AssetRecord | undefined): RenderAsset | null;
|
|
80
79
|
export declare function useCanvasRenderContext(args: {
|
|
81
80
|
/** The edited composition root (`builder.present`). */
|
|
82
81
|
present: ComponentNode;
|