@jbpark/live-editor 4.4.0 → 4.5.0
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/README.md +2 -2
- package/dist/{boundary-B17cepvm.js → boundary-DnjGgsK6.js} +8 -5
- package/dist/boundary-DnjGgsK6.js.map +1 -0
- package/dist/{context-DB5kW3qe.js → context-DsEXJl4h.js} +10 -4
- package/dist/context-DsEXJl4h.js.map +1 -0
- package/dist/dnd/index.d.ts +1 -1
- package/dist/dnd/index.js +1 -1
- package/dist/{dnd-BE5ZizqX.js → dnd-DGNp20Nk.js} +370 -305
- package/dist/dnd-DGNp20Nk.js.map +1 -0
- package/dist/{document-BDn_f08_.js → document-DGHTfrda.js} +7738 -7735
- package/dist/document-DGHTfrda.js.map +1 -0
- package/dist/error/index.d.ts +1 -1
- package/dist/error/index.js +2 -2
- package/dist/{error-DWhxQEom.js → error-DwqwjJ2C.js} +13 -6
- package/dist/error-DwqwjJ2C.js.map +1 -0
- package/dist/{index-f7ijuNTa.d.ts → index-CHknA6k1.d.ts} +108 -154
- package/dist/{index-Dgh1JTX3.d.ts → index-P9Aifaui.d.ts} +3 -2
- package/dist/index-WQgFy510.d.ts +11 -0
- package/dist/{index-C4SGQ3Ci.d.ts → index-vtyggWy0.d.ts} +4 -1
- package/dist/index.d.ts +18 -18
- package/dist/index.js +11 -7
- package/dist/index.js.map +1 -1
- package/dist/{ast-dYb78955.js → items-DdrwGxJw.js} +324 -373
- package/dist/items-DdrwGxJw.js.map +1 -0
- package/dist/messages-Cp3g1mn1.d.ts +129 -0
- package/dist/messages-CzUPVnzH.js +132 -0
- package/dist/messages-CzUPVnzH.js.map +1 -0
- package/dist/preview/index.js +2 -2
- package/dist/{preview-BnaCEFsF.js → preview-BKFgq_5u.js} +6 -4
- package/dist/preview-BKFgq_5u.js.map +1 -0
- package/dist/provider/index.d.ts +3 -8
- package/dist/provider/index.js +3 -2
- package/dist/{sections-CBVMO8Jl.js → sections-BRH3KHYc.js} +8 -3
- package/dist/sections-BRH3KHYc.js.map +1 -0
- package/dist/style.css +505 -21
- package/dist/{use-dynamic-tailwind-Bcipc1hL.js → use-dynamic-tailwind-T9r9ExbR.js} +9 -3
- package/dist/{use-dynamic-tailwind-Bcipc1hL.js.map → use-dynamic-tailwind-T9r9ExbR.js.map} +1 -1
- package/dist/utils/ast/index.d.ts +3 -2
- package/dist/utils/ast/index.js +66 -3
- package/dist/utils/ast/index.js.map +1 -0
- package/dist/utils/index.d.ts +30 -13
- package/dist/utils/index.js +26 -6
- package/dist/utils/index.js.map +1 -0
- package/dist/validate-DB59Z_Nx.d.ts +100 -0
- package/dist/validate-iLo8IONY.js +64 -0
- package/dist/validate-iLo8IONY.js.map +1 -0
- package/package.json +3 -3
- package/dist/ast-dYb78955.js.map +0 -1
- package/dist/boundary-B17cepvm.js.map +0 -1
- package/dist/context-DB5kW3qe.js.map +0 -1
- package/dist/dnd-BE5ZizqX.js.map +0 -1
- package/dist/document-BDn_f08_.js.map +0 -1
- package/dist/error-DWhxQEom.js.map +0 -1
- package/dist/preview-BnaCEFsF.js.map +0 -1
- package/dist/sections-CBVMO8Jl.js.map +0 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { i as useLiveMessages } from "./messages-CzUPVnzH.js";
|
|
1
2
|
import { n as compile, s as withScriptBlobs } from "./compile-DDfk5P25.js";
|
|
2
3
|
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
3
4
|
import { jsx } from "react/jsx-runtime";
|
|
@@ -522,6 +523,7 @@ const baseModules = {
|
|
|
522
523
|
//#region src/components/preview/use-compiled-module.ts
|
|
523
524
|
const useCompiledModule = (code, _modules) => {
|
|
524
525
|
const modules = useStableModules(_modules);
|
|
526
|
+
const { unknown } = useLiveMessages().errors;
|
|
525
527
|
const mergedModules = useMemo(() => ({
|
|
526
528
|
...baseModules,
|
|
527
529
|
...modules
|
|
@@ -533,10 +535,14 @@ const useCompiledModule = (code, _modules) => {
|
|
|
533
535
|
} catch (e) {
|
|
534
536
|
return {
|
|
535
537
|
exports: {},
|
|
536
|
-
error: e instanceof Error ? e.message :
|
|
538
|
+
error: e instanceof Error ? e.message : unknown
|
|
537
539
|
};
|
|
538
540
|
}
|
|
539
|
-
}, [
|
|
541
|
+
}, [
|
|
542
|
+
code,
|
|
543
|
+
mergedModules,
|
|
544
|
+
unknown
|
|
545
|
+
]);
|
|
540
546
|
};
|
|
541
547
|
//#endregion
|
|
542
548
|
//#region src/components/preview/use-dynamic-tailwind.ts
|
|
@@ -568,4 +574,4 @@ const useDynamicTailwind = (code, enabled) => {
|
|
|
568
574
|
//#endregion
|
|
569
575
|
export { Frame as a, useStableModules as i, useCompiledModule as n, baseModules as r, useDynamicTailwind as t };
|
|
570
576
|
|
|
571
|
-
//# sourceMappingURL=use-dynamic-tailwind-
|
|
577
|
+
//# sourceMappingURL=use-dynamic-tailwind-T9r9ExbR.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-dynamic-tailwind-Bcipc1hL.js","names":[],"sources":["../src/components/frame/measure.ts","../src/components/frame/style-sync.ts","../src/components/frame/viewport-units.ts","../src/components/frame/iframe.tsx","../src/components/frame/shadow.tsx","../src/components/frame/frame.tsx","../src/components/preview/use-stable-modules.ts","../src/components/preview/base-modules.ts","../src/components/preview/use-compiled-module.ts","../src/components/preview/use-dynamic-tailwind.ts"],"sourcesContent":["// Pure helpers behind iframe.tsx's autoHeight measurement (#132 stages\n// 2-3) — kept DOM-free so they're testable under this repo's node-\n// environment vitest setup; the DOM walking itself (getComputedStyle,\n// querySelectorAll, offsetHeight reads) has no real layout engine to run\n// against outside a real browser and stays in iframe.tsx, verified\n// separately with a real Chromium instance instead of a unit test.\n\n// A fallback used only when there's no `[data-frame-container]` scroll\n// container to measure against at all — e.g. `Frame` used directly by a\n// library consumer, without `Dnd`. There's no better reference height to\n// wait for in that case (unlike the \"container exists but hasn't laid out\n// yet\" case below, which defers instead), so this just needs to be *some*\n// reasonable default. Matches the source fork's own constant — a common\n// mobile viewport height, chosen there for the same reason.\nexport const FALLBACK_PROBE_HEIGHT = 812;\n\n// The reference height for the preview's CSS containment context (see\n// ensureContainerStyle in iframe.tsx) — everything sized in `vh`-family\n// units (converted to `cqh` by convertViewportUnits) resolves against\n// this instead of the iframe's own height, which is what breaks the old\n// approach's fold-to-0px-then-measure circularity (#132 problem 1).\n//\n// `clientHeight` excludes the scroll container's own border, but not its\n// padding, nor anything between the iframe and that container. The caller\n// adds all of that up into `wrapperInsets` (see `verticalInsets`) while\n// walking from the iframe to the scroll container. This codebase's own\n// Sortable/Renderer/Frame add little (the Droppable's border and padding),\n// but a consumer's CSS, `provider` or `Canvas` className can add more.\n//\n// Returns `null` (not a guessed fallback) when the container hasn't been\n// laid out yet (`clientHeight` still 0, e.g. mid-transition) — the\n// caller should skip this measurement pass rather than settle on a\n// number that has nothing to do with the actual available space and\n// that no future event would ever correct (see the issue's own\n// reasoning for why a `window.innerHeight` fallback here was wrong).\n// The vertical space one element takes around the box inside it: margin,\n// border and padding, top and bottom. Margin counts too (#440): a 100vh\n// section in a wrapper with a vertical margin would otherwise be as tall as\n// the container and overflow it by that margin.\n//\n// Summed per element, so a margin that collapses through a parent with no\n// border or padding is counted twice. That errs on the side of a slightly\n// smaller probe, which fits, rather than a larger one, which scrolls.\nexport const verticalInsets = (\n style: Pick<\n CSSStyleDeclaration,\n | 'marginTop'\n | 'marginBottom'\n | 'borderTopWidth'\n | 'borderBottomWidth'\n | 'paddingTop'\n | 'paddingBottom'\n >,\n): number =>\n [\n style.marginTop,\n style.marginBottom,\n style.borderTopWidth,\n style.borderBottomWidth,\n style.paddingTop,\n style.paddingBottom,\n ].reduce((sum, value) => sum + (parseFloat(value) || 0), 0);\n\nexport const computeProbeHeight = (\n scrollContainerClientHeight: number,\n wrapperInsets: number,\n): number | null => {\n const usable = scrollContainerClientHeight - wrapperInsets;\n\n return usable > 0 ? usable : null;\n};\n\n// `visibility:hidden` and `opacity:0` elements keep a non-zero\n// offsetHeight/scrollHeight (unlike `display:none`, which zeroes them\n// out on its own) — without this check, a closed bottom sheet or a\n// not-yet-faded-in overlay sitting in the DOM inflates the measured\n// height by however tall it would be if shown.\n//\n// `opacity:0` on its own can't separate that closed overlay from the\n// *first frames of a fade-in*, which is on its way to being visible and\n// belongs in the height (#374). `isAnimating` is how the caller resolves\n// it: an element part-way through a keyframe animation is measured even\n// while fully transparent. It deliberately doesn't rescue\n// `visibility:hidden` — an element animating some unrelated property\n// while hidden is still hidden.\nexport const isVisuallyHidden = (\n computed: {\n visibility: string;\n opacity: string;\n },\n isAnimating = false,\n): boolean =>\n computed.visibility === 'hidden' ||\n (computed.opacity === '0' && !isAnimating);\n\n// Which `Animation.playState`s mean \"this element's appearance is still\n// in flight, don't trust the current frame as its resting state\".\n//\n// `playState !== 'idle'` would be the tempting shorthand and is wrong:\n// a *finished* fade-out with `animation-fill-mode: forwards` sits in\n// `finished` while holding `opacity: 0`, and that element really is\n// hidden for good — counting it as animating would put the full height\n// of a dismissed overlay back into the estimate. `paused` counts because\n// a paused fade-in is stopped part-way through, not dismissed.\nexport const isAnimationActive = (playState: string): boolean =>\n playState === 'running' || playState === 'paused';\n\n// `translate(Xpx, Ypx)` / `translateY(Ypx)` / `matrix(a,b,c,d,tx,ty)`'s Y\n// component — a positioned popup/overlay is commonly offset this way\n// (Radix/floating-ui do), so its *effective* bottom edge is `offsetY +\n// offsetHeight` from its positioned ancestor, not just `offsetHeight`\n// alone. Returns 0 for anything else (no transform, or an X-only/\n// unrecognized one) rather than throwing — an unparsed offset is safer\n// treated as \"no extra offset\" than as a measurement failure.\n//\n// iframe.tsx's only caller passes `getComputedStyle(el).transform`, which\n// every real browser normalizes to `matrix(...)` regardless of what\n// syntax (translate/translateY/none of the above) the original CSS used\n// — confirmed against a real Chromium instance, not assumed. The\n// translate()/translateY() branches mainly document intent and cover any\n// future caller that passes an *inline* style's transform instead (which\n// does preserve the author's original syntax).\nexport const parseTranslateY = (transform: string): number => {\n if (!transform || transform === 'none') {\n return 0;\n }\n\n // translateY(y) is single-argument — tried first and separately from\n // translate(x, y), since a naive \"match the arg after a comma\" pattern\n // has no comma to find here at all and would silently fall through to 0.\n const translateYMatch = transform.match(/translateY\\(\\s*([+-]?\\d*\\.?\\d+)/);\n\n if (translateYMatch?.[1]) {\n return parseFloat(translateYMatch[1]);\n }\n\n const translateMatch = transform.match(\n /translate\\([^,]+,\\s*([+-]?\\d*\\.?\\d+)/,\n );\n\n if (translateMatch?.[1]) {\n return parseFloat(translateMatch[1]);\n }\n\n const matrixMatch = transform.match(\n /matrix\\(\\s*[^,]+,\\s*[^,]+,\\s*[^,]+,\\s*[^,]+,\\s*[^,]+,\\s*([+-]?\\d*\\.?\\d+)/,\n );\n\n return matrixMatch?.[1] ? parseFloat(matrixMatch[1]) : 0;\n};\n\n// A `position:fixed`/`absolute` element is placed relative to the\n// viewport (or the nearest positioned ancestor, which for this preview\n// content is effectively the same scale) — `offsetY + offsetHeight` can\n// legitimately exceed the probe height (e.g. an element deliberately\n// positioned to bleed off-screen), but letting an unbounded value drive\n// the *whole document's* measured height would make one runaway overlay\n// balloon everything below it. Capping at `probeHeight` treats \"this\n// element's bottom edge is somewhere past the viewport\" the same as \"at\n// the viewport edge\" for sizing purposes, without needing to know how far\n// past.\nexport const estimatePositionedElementHeight = (\n offsetHeight: number,\n transform: string,\n probeHeight: number,\n): number => {\n const offsetY = parseTranslateY(transform);\n\n return Math.min(offsetY + offsetHeight, probeHeight);\n};\n","const SYNCED_STYLE_ATTR = 'data-live-editor-synced-style';\n\ntype SourceStyle = HTMLLinkElement | HTMLStyleElement;\ntype SyncedStyle = HTMLLinkElement | HTMLStyleElement;\n\nexport interface StyleSyncManager {\n clones: Map<SourceStyle, SyncedStyle>;\n}\n\nexport const createStyleSyncManager = (): StyleSyncManager => ({\n clones: new Map(),\n});\n\nconst isDocument = (target: Document | ShadowRoot): target is Document =>\n target.nodeType === Node.DOCUMENT_NODE;\n\nconst sourceStyles = (document: Document): SourceStyle[] =>\n Array.from(\n document.head.querySelectorAll<HTMLLinkElement | HTMLStyleElement>(\n 'link[rel=\"stylesheet\"], style',\n ),\n );\n\nconst copyAttributes = (\n source: SourceStyle,\n clone: SyncedStyle,\n transformStyle: (content: string) => string,\n) => {\n Array.from(clone.attributes).forEach(attribute => {\n if (\n attribute.name !== SYNCED_STYLE_ATTR &&\n !source.hasAttribute(attribute.name)\n ) {\n clone.removeAttribute(attribute.name);\n }\n });\n\n Array.from(source.attributes).forEach(attribute => {\n clone.setAttribute(attribute.name, attribute.value);\n });\n\n if (source.tagName === 'LINK' && clone.tagName === 'LINK') {\n (clone as HTMLLinkElement).disabled = (source as HTMLLinkElement).disabled;\n }\n\n clone.textContent =\n source.tagName === 'STYLE'\n ? transformStyle(source.textContent || '')\n : source.textContent;\n};\n\nconst createClone = (\n source: SourceStyle,\n target: Document | ShadowRoot,\n transformStyle: (content: string) => string,\n): SyncedStyle => {\n const ownerDocument = isDocument(target) ? target : target.ownerDocument;\n const clone = ownerDocument.createElement(\n source.tagName.toLowerCase(),\n ) as SyncedStyle;\n\n clone.setAttribute(SYNCED_STYLE_ATTR, '');\n copyAttributes(source, clone, transformStyle);\n\n return clone;\n};\n\nconst targetContainer = (target: Document | ShadowRoot) =>\n isDocument(target) ? target.head : target;\n\nconst isSyncedStyle = (node: Node): node is SyncedStyle => {\n const element = node as Element;\n\n return (\n (element.tagName === 'LINK' || element.tagName === 'STYLE') &&\n element.hasAttribute(SYNCED_STYLE_ATTR)\n );\n};\n\nconst removeClone = (clone: SyncedStyle) => {\n clone.remove();\n};\n\n// `anchor`, when given, is an element the synced block has to stay in front\n// of. Without it, a first sync appends the block after whatever the target\n// already holds, so a rule the target added for itself could end up before\n// the host's copies and lose to them on order (#441).\nexport const reconcileStyles = (\n sourceDocument: Document,\n target: Document | ShadowRoot,\n manager: StyleSyncManager,\n enabled: boolean,\n transformStyle: (content: string) => string = content => content,\n anchor: Node | null = null,\n) => {\n if (!enabled) {\n manager.clones.forEach(removeClone);\n manager.clones.clear();\n\n return;\n }\n\n const sources = sourceStyles(sourceDocument);\n const sourceSet = new Set(sources);\n const container = targetContainer(target);\n\n manager.clones.forEach((clone, source) => {\n if (!sourceSet.has(source)) {\n removeClone(clone);\n manager.clones.delete(source);\n }\n });\n\n let previous: SyncedStyle | null = null;\n\n sources.forEach(source => {\n let clone = manager.clones.get(source);\n\n if (!clone) {\n clone = createClone(source, target, transformStyle);\n manager.clones.set(source, clone);\n } else {\n copyAttributes(source, clone, transformStyle);\n }\n\n const nextSibling = previous\n ? previous.nextSibling\n : (Array.from(container.childNodes).find(isSyncedStyle) ??\n (anchor?.parentNode === container ? anchor : null));\n\n if (clone !== nextSibling) {\n container.insertBefore(clone, nextSibling);\n }\n\n previous = clone;\n });\n};\n\n// What a root-attribute sync has written onto the target, so a later pass\n// removes exactly that and never what the target set for itself.\nexport interface RootAttributeSync {\n attributes: Set<string>;\n classes: Set<string>;\n}\n\nexport const createRootAttributeSync = (): RootAttributeSync => ({\n attributes: new Set(),\n classes: new Set(),\n});\n\nconst isMirroredAttribute = (name: string) => name.startsWith('data-');\n\n// Mirrors the host `<html>`'s classes and `data-*` attributes onto an iframe's\n// `<html>`. A theme is usually switched there (`.dark`, `data-theme=\"dark\"`),\n// and copying the host's stylesheets alone doesn't help an iframe: its\n// selectors look for those on an ancestor, and the iframe document has a root\n// of its own. A shadow root needs none of this, since it sits under the host's\n// `<html>` already (#497).\n//\n// Classes are synced token by token, so a class the preview put on its own\n// root survives. Other attributes (`style`, `lang`, `dir`, ...) are left\n// alone: the frame relies on its own root for sizing and layout.\nexport const reconcileRootAttributes = (\n source: Element,\n target: Element,\n sync: RootAttributeSync,\n enabled: boolean,\n) => {\n const attributes = enabled\n ? Array.from(source.attributes).filter(({ name }) =>\n isMirroredAttribute(name),\n )\n : [];\n const classes = enabled ? Array.from(source.classList) : [];\n const attributeNames = new Set(attributes.map(({ name }) => name));\n const classNames = new Set(classes);\n\n sync.attributes.forEach(name => {\n if (!attributeNames.has(name)) {\n target.removeAttribute(name);\n }\n });\n attributes.forEach(({ name, value }) => {\n if (target.getAttribute(name) !== value) {\n target.setAttribute(name, value);\n }\n });\n\n sync.classes.forEach(name => {\n if (!classNames.has(name)) {\n target.classList.remove(name);\n }\n });\n classes.forEach(name => target.classList.add(name));\n\n if (!target.classList.length) {\n target.removeAttribute('class');\n }\n\n sync.attributes = attributeNames;\n sync.classes = classNames;\n};\n","// Part of #132 stage 1 — porting the internal GitLab fork's autoHeight\n// height-measurement redesign into this repo, one verifiable stage at a\n// time (see the issue for the full plan and why the current `updateHeight`\n// implementation needs replacing: folding the iframe to 0px before\n// measuring permanently collapses `vh`-sized content to 0, since `vh`\n// units resolve against the iframe's own height).\n//\n// The fork's fix replaces the iframe height as `vh`'s reference point with\n// a fixed-size CSS containment context (`html { container-type: size;\n// height: <probe>px }`) — see stage 2. That only works if the document's\n// own `vh`/`svh`/`lvh`/`dvh`/`vmin`/`vmax` usages are first rewritten to\n// the matching container-query unit (`cqh`/`cqmin`/`cqmax`), since a size\n// container doesn't retroactively change what `vh` itself resolves\n// against. `vw`/`vi` are deliberately left alone: they resolve against\n// width, which this measurement never touches, so rewriting them would\n// only add risk with no corresponding bug to fix.\nconst UNIT_MAP: Record<string, string> = {\n vh: 'cqh',\n svh: 'cqh',\n lvh: 'cqh',\n dvh: 'cqh',\n vmin: 'cqmin',\n vmax: 'cqmax',\n};\n\n// Longest-unit-first so `svh`/`lvh`/`dvh` aren't shadowed by a shorter\n// alternative matching a prefix of them first.\nconst UNITS_PATTERN = Object.keys(UNIT_MAP)\n .sort((a, b) => b.length - a.length)\n .join('|');\n\n// A CSS dimension token: optional sign, then digits with an optional\n// fractional part on either side of the decimal point (`100`, `50.5`,\n// `-10`, `.5` all match; a bare `-` or `.` alone does not).\nconst NUMBER_PATTERN = '-?(?:\\\\d+\\\\.?\\\\d*|\\\\.\\\\d+)';\n\n// Requires the matched number not to be immediately preceded by a letter,\n// digit, underscore, `.`, or `-` — this is what keeps `url(a5vh.png)`,\n// `.a5vh{}`, and `.hero-100vh{}` untouched (in all three, the digits\n// before `vh` are part of a larger filename/class-name token, not a\n// standalone CSS number) without needing any special-casing for\n// `url(...)`/selectors specifically. `-` has to be excluded too, not just\n// `\\w`/`.`, since a kebab-case identifier like `hero-100vh` uses it as a\n// word separator the same way `a5vh` uses no separator at all — otherwise\n// the number pattern's own optional leading `-?` would happily treat that\n// hyphen as a negative sign instead. This doesn't break real negative\n// values (`margin-top: -10vh`): there the character *before* the `-` is\n// whatever precedes the whole declaration (a space, in practice), so the\n// lookbehind still passes and the leading `-` is captured as part of the\n// number, same as before.\n//\n// Separately, `--my-vh: 10px` was never a candidate to begin with: there's\n// no digit immediately before `vh` there at all (the `y` in `-vh` is a\n// letter, not a number), so the pattern doesn't even try to match it.\nconst VIEWPORT_UNIT_RE = new RegExp(\n `(?<![\\\\w.-])(${NUMBER_PATTERN})(${UNITS_PATTERN})(?![a-zA-Z0-9_-])`,\n 'gi',\n);\n\n// Rewrites vh/svh/lvh/dvh/vmin/vmax to their cqh/cqmin/cqmax equivalent\n// wherever they appear as an actual CSS dimension — including nested\n// inside calc()/var() fallbacks, since this is a plain text substitution\n// rather than a CSS-aware parse. `vw` is left as-is (see UNIT_MAP above).\n//\n// Known limitation, inherited from the source fork and not fixed here:\n// this can't distinguish a real dimension from the same text sitting\n// inside a CSS string literal, e.g. `content: \"100vh\"` — the regex has no\n// concept of quoting, so that string's contents get rewritten too. In\n// practice this is rare (a `content` value that's coincidentally shaped\n// like a viewport dimension) and doesn't affect layout, since `content`\n// strings aren't parsed as CSS values.\nexport const convertViewportUnits = (css: string): string =>\n css.replace(\n VIEWPORT_UNIT_RE,\n (_match, number: string, unit: string) =>\n number + UNIT_MAP[unit.toLowerCase()],\n );\n\n// `convertViewportUnits` only ever runs over CSS *text* that this component\n// controls the injection of — the parent-document `<style>` copies (syncStyle)\n// and the `styles` prop (see iframe.tsx). Two paths carry a viewport unit into\n// the preview document without passing through either, so the container-context\n// fix (#132) never reaches them and their `vh` still resolves against the\n// iframe's own height — re-creating the exact circularity #132 removed:\n//\n// 1. inline `style` attributes — the visual editor's panel edits produce\n// these, so they're a common path here, not an edge case; and\n// 2. author `<style>` elements rendered *inside* the previewed component by\n// React, which never pass through `applyStyle`/`styles`.\n//\n// This rewrites both in place, walking only nodes that could carry a match so\n// the pass stays cheap. It's meant to be called from `updateHeight` right after\n// the container style is (re)applied and before any height is read.\n//\n// Idempotent, and specifically non-perturbing once converted: a rewritten value\n// contains `cqh`/`cqmin`/`cqmax`, none of which match the `vh`/`vmin`/`vmax`\n// attribute selectors, so a second pass re-selects nothing; and each node is\n// written back only when its text actually changed, so an already-converted\n// document isn't mutated — which matters because the write itself would\n// otherwise re-trip the MutationObserver in iframe.tsx and loop.\nexport const rewriteInlineViewportUnits = (root: HTMLElement): void => {\n // `vh` as a substring already covers `svh`/`lvh`/`dvh`; `vmin`/`vmax` need\n // their own terms. Case-insensitive so `100VH` inline is caught too.\n const inlineTargets = root.querySelectorAll<HTMLElement>(\n '[style*=\"vh\" i], [style*=\"vmin\" i], [style*=\"vmax\" i]',\n );\n\n inlineTargets.forEach(el => {\n const current = el.getAttribute('style');\n\n if (current == null) {\n return;\n }\n\n const converted = convertViewportUnits(current);\n\n if (converted !== current) {\n el.setAttribute('style', converted);\n }\n });\n\n root.querySelectorAll<HTMLStyleElement>('style').forEach(styleEl => {\n const current = styleEl.textContent;\n\n if (!current) {\n return;\n }\n\n const converted = convertViewportUnits(current);\n\n if (converted !== current) {\n styleEl.textContent = converted;\n }\n });\n};\n","import { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport type { ReactNode } from 'react';\nimport { createPortal } from 'react-dom';\n\nimport {\n useEventListener,\n useMutationObserver,\n useResizeObserver,\n} from '@jbpark/use-hooks';\n\nimport { withScriptBlobs } from '~/utils/scripts';\n\nimport {\n FALLBACK_PROBE_HEIGHT,\n computeProbeHeight,\n estimatePositionedElementHeight,\n isAnimationActive,\n isVisuallyHidden,\n verticalInsets,\n} from './measure';\nimport {\n createRootAttributeSync,\n createStyleSyncManager,\n reconcileRootAttributes,\n reconcileStyles,\n} from './style-sync';\nimport {\n convertViewportUnits,\n rewriteInlineViewportUnits,\n} from './viewport-units';\n\n// The <html> element's own container-context style — id'd so it can be\n// found/updated/removed across calls without holding a ref to it. Scoped\n// to `html` (not `:root`, which is equivalent but the fork's own\n// convention) so this only ever affects cq*-unit resolution and nothing\n// else about the document.\nconst CONTAINER_STYLE_ID = 'autoheight-container';\n\nexport interface Props {\n title?: string;\n /** Forwarded to the iframe's `sandbox` attribute for DOM/CSS isolation only — not a security boundary, since preview code executes in the host window's realm (see `compileModule` in `~/utils`). */\n sandbox?: string;\n style?: React.CSSProperties;\n scripts?: string[];\n styles?: string[];\n stylesheets?: string[];\n autoHeight?: boolean;\n syncStyle?: boolean;\n children: (container: HTMLElement) => ReactNode;\n onLoaded?: () => void;\n}\n\nconst EMPTY_STRING_ARRAY: string[] = [];\n\n// `getAnimations()` returns three kinds of animation mixed together:\n// CSSAnimation (a @keyframes rule), CSSTransition (a `transition`) and\n// plain Animation (`element.animate()`). They need telling apart below\n// because only the first two announce their own completion through a\n// bubbling DOM event. Both constructors are feature-detected rather than\n// assumed: jsdom exposes neither.\nconst isCssAnimation = (\n win: Window & typeof globalThis,\n animation: Animation,\n): boolean =>\n typeof win.CSSAnimation === 'function' &&\n animation instanceof win.CSSAnimation;\n\nconst isCssTransition = (\n win: Window & typeof globalThis,\n animation: Animation,\n): boolean =>\n typeof win.CSSTransition === 'function' &&\n animation instanceof win.CSSTransition;\n\n// Whether this element's look is still in flight, which is what tells a\n// transient `opacity: 0` (the first frames of a fade-in — measure it)\n// from a permanent one (a closed overlay — skip it).\n//\n// Transitions count here, unlike keyframe animations they are not\n// distinguished: now that a measurement pass only freezes transitions when\n// it moves the probe height (see withMeasurementOverrides), an element at\n// the very start of a fade-*in* transition genuinely reads `opacity: 0`\n// with a running transition and has to be measured. An element fading\n// *out* needs no special case — part-way through it reads a fractional\n// opacity, so it is measured for as long as it is still visible, and once\n// the transition is over it reads `0` with nothing running and drops out.\n//\n// getAnimations() is feature-detected: jsdom and pre-2020 browsers don't\n// implement it, and without it this returns false, which is the safe\n// direction (under-measuring a fading-in overlay rather than inflating a\n// section by the height of a dismissed one).\nconst hasActiveAnimation = (el: HTMLElement): boolean =>\n typeof el.getAnimations === 'function' &&\n el.getAnimations().some(animation => isAnimationActive(animation.playState));\n\n// `finished` never resolves for an animation that repeats forever, so a\n// handler attached to one would only pin its closure for as long as the\n// element lives. No signal is lost by skipping it: such an element is\n// perpetually \"animating\", so hasActiveAnimation keeps it in the estimate\n// on every pass anyway.\nconst neverFinishes = (animation: Animation): boolean => {\n const timing = animation.effect?.getComputedTiming();\n\n return timing?.iterations === Infinity || timing?.duration === Infinity;\n};\n\n// A second, separate style — inert (`media=\"not all\"`) except for the\n// brief window updateHeight actually measures in, toggled on right\n// before and off right after (#132 stage 4). Two things it guards\n// against:\n//\n// - transitions: if any rule in the preview (or a browser default)\n// gives `html`/an ancestor a `transition` on a property this\n// measurement touches, changing ensureContainerStyle's `height` would\n// animate instead of applying instantly, and a read taken right after\n// would catch a mid-transition value instead of the settled one. Only\n// applied on a pass that actually changes the probe height — see\n// `freezeTransitions`.\n// - scrollbar chrome: applying a new probe height can make a scrollbar\n// appear/disappear for exactly this measurement pass; on platforms\n// where it takes up layout width (Windows, unlike macOS's overlay\n// scrollbars), that narrows content and skews the height reading.\n// `scrollbar-width: none`/`::-webkit-scrollbar { display: none }`\n// only hides the *chrome* — unlike `overflow: hidden`, scrolling\n// itself still works, so content that ends up taller than its probe\n// height is still reachable rather than silently clipped.\n//\n// A single style element (not two, and never added/removed) so\n// toggling it can't itself trip the MutationObserver watching for\n// *content* changes.\nconst MEASUREMENT_OVERRIDE_STYLE_ID = 'autoheight-measurement-overrides';\n\nconst SCROLLBAR_OVERRIDE_RULES = [\n 'html, body { scrollbar-width: none !important; }',\n 'html::-webkit-scrollbar, body::-webkit-scrollbar { display: none !important; }',\n];\n\nconst FREEZE_TRANSITIONS_RULE =\n '*, *::before, *::after { transition: none !important; }';\n\n// `freezeTransitions` is deliberately not always on. `transition: none`\n// does not pause a transition for the duration of the measurement — it\n// *cancels* it, and lifting the override afterwards does not resume it.\n// Measured in Chromium: 0.5s into a 4s fade the element read `0.974`\n// with one running animation; inside the override window it read `0`\n// with none, and it was still `0` with none 0.8s after the override came\n// back off. The element had snapped to its end state for good.\n//\n// Freezing on every pass therefore killed every transition in the\n// preview that happened to overlap a measurement — and because a DOM\n// mutation is what both triggers a measurement and typically starts a\n// transition (a class toggle opening an overlay), that was most of them.\n//\n// The guard is only needed for what the measurement *itself* changes:\n// the probe height, and with it any `cq*`-unit descendant sized against\n// it. When the probe height is unchanged from the last pass, the pass\n// changes nothing, so there is nothing to freeze and the preview's own\n// transitions are left alone.\nconst withMeasurementOverrides = (\n doc: Document,\n freezeTransitions: boolean,\n measure: () => void,\n) => {\n let styleEl = doc.getElementById(\n MEASUREMENT_OVERRIDE_STYLE_ID,\n ) as HTMLStyleElement | null;\n\n if (!styleEl) {\n styleEl = doc.createElement('style');\n styleEl.id = MEASUREMENT_OVERRIDE_STYLE_ID;\n styleEl.media = 'not all';\n doc.head?.appendChild(styleEl);\n }\n\n const text = (\n freezeTransitions\n ? [FREEZE_TRANSITIONS_RULE, ...SCROLLBAR_OVERRIDE_RULES]\n : SCROLLBAR_OVERRIDE_RULES\n ).join('\\n');\n\n if (styleEl.textContent !== text) {\n styleEl.textContent = text;\n }\n\n styleEl.media = 'all';\n measure();\n styleEl.media = 'not all';\n};\n\nconst IFrame = ({\n title = 'Live Preview',\n sandbox,\n style = {},\n scripts = EMPTY_STRING_ARRAY,\n styles = EMPTY_STRING_ARRAY,\n stylesheets = EMPTY_STRING_ARRAY,\n autoHeight = false,\n syncStyle = false,\n children,\n onLoaded,\n ...props\n}: Props) => {\n const iframeRef = useRef<HTMLIFrameElement>(null);\n const [mountNode, setMountNode] = useState<HTMLElement | null>(null);\n // Tracks which script srcs have already been injected into this iframe's\n // document, keyed by src rather than a single loaded/not-loaded boolean —\n // a boolean latched to `true` forever meant a later change to `scripts`\n // (new entries) never got loaded once the first batch had.\n const loadedScriptsRef = useRef<Set<string>>(new Set());\n // The document those scripts went into. Moving the iframe in the DOM, as\n // reordering canvas sections does, reloads it with a fresh document that\n // has none of them (#507).\n const loadedScriptsDocRef = useRef<Document | null>(null);\n const prevStyleCountRef = useRef(0);\n const prevStylesheetCountRef = useRef(0);\n // Animations already given a \"re-measure once you settle\" handler, so a\n // long-running one still in flight across many measurement passes only\n // ever gets one (see trackScriptAnimations). Weak so it never keeps a\n // finished animation — or the element owning it — alive.\n const trackedAnimationsRef = useRef(new WeakSet<Animation>());\n // Lets those handlers call back into the *current* updateHeight without\n // updateHeight having to list itself as its own dependency.\n const updateHeightRef = useRef<(() => void) | null>(null);\n // The probe height the container style was last built for. A pass that\n // leaves it unchanged changes nothing about the document, which is what\n // lets that pass measure without freezing the preview's transitions.\n const lastProbeHeightRef = useRef<number | undefined>(undefined);\n const shouldAutoHeight = autoHeight && style.height == null;\n\n const styleManagerRef = useRef(createStyleSyncManager());\n const rootAttributeSyncRef = useRef(createRootAttributeSync());\n\n const applyStyle = useCallback(() => {\n const doc = iframeRef.current?.contentDocument;\n\n if (!doc) {\n return;\n }\n\n // The container style has to come after every host copy: both sides mark\n // their rules `!important`, so a host `html { height: 100% !important }`\n // synced in after it wins, and the frame folds to a pixel. That happened\n // whenever the first sync ran after the container style existed, as when\n // `syncStyle` is switched on after mount (#441).\n reconcileStyles(\n document,\n doc,\n styleManagerRef.current,\n syncStyle,\n convertViewportUnits,\n doc.getElementById(CONTAINER_STYLE_ID),\n );\n // The host's theme class or attribute, which those copied styles select\n // on (#497).\n reconcileRootAttributes(\n document.documentElement,\n doc.documentElement,\n rootAttributeSyncRef.current,\n syncStyle,\n );\n }, [syncStyle]);\n\n const applyStyleTimeoutRef = useRef<number>(undefined);\n\n // Debounced so a burst of head mutations (a stylesheet swap can fire\n // several in quick succession) only re-runs applyStyle once, matching the\n // original raw-MutationObserver setup's 50ms debounce.\n const debouncedApplyStyle = useCallback(() => {\n clearTimeout(applyStyleTimeoutRef.current);\n applyStyleTimeoutRef.current = window.setTimeout(applyStyle, 50);\n }, [applyStyle]);\n\n useEffect(() => {\n return () => clearTimeout(applyStyleTimeoutRef.current);\n }, []);\n\n useMutationObserver(document.head, debouncedApplyStyle, {\n enabled: syncStyle,\n childList: true,\n subtree: true,\n attributes: true,\n });\n\n // A theme switch only touches the host's `<html>` attributes, which the\n // head observer above doesn't see.\n useMutationObserver(document.documentElement, debouncedApplyStyle, {\n enabled: syncStyle,\n attributes: true,\n });\n\n useEffect(() => {\n const $iframe = iframeRef.current;\n\n if (!$iframe) {\n return;\n }\n\n const onLoad = () => {\n const doc = $iframe.contentDocument;\n\n if (!doc) {\n return;\n }\n\n if (loadedScriptsDocRef.current !== doc) {\n loadedScriptsDocRef.current = doc;\n loadedScriptsRef.current = new Set();\n }\n\n doc.body.style.overflowX = 'hidden';\n doc.body.style.margin = '0';\n\n let node = doc.getElementById('iframe-root');\n\n if (!node) {\n node = doc.createElement('div');\n node.id = 'iframe-root';\n doc.body.appendChild(node);\n }\n\n setMountNode(node);\n\n applyStyle();\n\n const pendingScripts = scripts.filter(\n src => !loadedScriptsRef.current.has(src),\n );\n\n if (pendingScripts.length) {\n pendingScripts.forEach(src => loadedScriptsRef.current.add(src));\n\n withScriptBlobs(pendingScripts, blobUrls => {\n if (!doc.head) {\n return;\n }\n\n const fragment = doc.createDocumentFragment();\n blobUrls.forEach(blobUrl => {\n const script = doc.createElement('script');\n script.src = blobUrl;\n fragment.appendChild(script);\n });\n doc.head.appendChild(fragment);\n });\n }\n\n onLoaded?.();\n };\n\n $iframe.addEventListener('load', onLoad);\n\n if ($iframe.contentDocument?.readyState === 'complete') {\n onLoad();\n }\n\n return () => {\n $iframe.removeEventListener('load', onLoad);\n };\n }, [scripts, onLoaded, applyStyle]);\n\n useEffect(() => {\n const doc = iframeRef.current?.contentDocument;\n\n if (!doc?.head) {\n return;\n }\n\n styles.forEach((css, index) => {\n const styleId = `injected-style-${index}`;\n let styleEl = doc.getElementById(styleId) as HTMLStyleElement | null;\n\n if (!styleEl) {\n styleEl = doc.createElement('style');\n styleEl.id = styleId;\n doc.head.appendChild(styleEl);\n }\n\n // The primary source of vh/svh/etc in a real preview — compiled\n // component CSS (e.g. Tailwind's `h-screen` -> `height: 100vh`).\n // See ensureContainerStyle below for why this needs converting.\n const convertedCss = convertViewportUnits(css);\n\n if (styleEl.textContent !== convertedCss) {\n styleEl.textContent = convertedCss;\n }\n });\n\n // Indices beyond the current array's length are stale from a previous,\n // longer `styles`/`stylesheets` — the loops above only add/update up to\n // the current length, so anything past it (from before an item was\n // removed, or the array shrank) would otherwise stay injected forever.\n for (\n let index = styles.length;\n index < prevStyleCountRef.current;\n index++\n ) {\n doc.getElementById(`injected-style-${index}`)?.remove();\n }\n prevStyleCountRef.current = styles.length;\n\n stylesheets.forEach((href, index) => {\n const linkId = `injected-stylesheet-${index}`;\n let linkEl = doc.getElementById(linkId) as HTMLLinkElement | null;\n\n if (!linkEl) {\n linkEl = doc.createElement('link');\n linkEl.id = linkId;\n linkEl.rel = 'stylesheet';\n doc.head.appendChild(linkEl);\n }\n\n if (linkEl.href !== href) {\n linkEl.href = href;\n }\n });\n\n for (\n let index = stylesheets.length;\n index < prevStylesheetCountRef.current;\n index++\n ) {\n doc.getElementById(`injected-stylesheet-${index}`)?.remove();\n }\n prevStylesheetCountRef.current = stylesheets.length;\n }, [styles, stylesheets]);\n\n // Permanently hides the iframe document's own scrollbar chrome while\n // autoHeight is sizing the iframe to its content. autoHeight sets the\n // iframe's height to `Math.ceil(contentHeight)`, so sub-pixel content or\n // a rounding remainder can leave the inner document a fraction taller\n // than its viewport — enough for the browser to draw a vertical\n // scrollbar inside every section's iframe (visual noise once several Dnd\n // sections stack). Unlike the measurement-only override above (toggled\n // off after each read), this one stays on: `scrollbar-width`/\n // `::-webkit-scrollbar` hide only the *chrome*, not scrolling itself, so\n // content that ever genuinely exceeds the measured height is still\n // reachable by wheel/keyboard rather than clipped.\n const HIDE_SCROLLBAR_STYLE_ID = 'autoheight-hide-scrollbar';\n\n // Ties `cqh`/`cqmin`/`cqmax` (what convertViewportUnits rewrote every\n // vh/svh/lvh/dvh/vmin/vmax to) to a *fixed* reference height instead of\n // the iframe's own height — this is what breaks the old approach's\n // circularity (#132 problem 1): folding the iframe to 0px before\n // measuring made vh-sized content resolve to 0 and stay there forever,\n // while measuring without folding never converges (vh content sized\n // against the iframe's own just-grown height keeps growing it further).\n // `container-type: size` requires an explicit height to size against,\n // which `probeHeight` (the *scroll container's* available height, not\n // the iframe's) provides — genuinely independent of whatever height this\n // function goes on to set on the iframe itself.\n const ensureContainerStyle = (doc: Document, probeHeight: number) => {\n let styleEl = doc.getElementById(\n CONTAINER_STYLE_ID,\n ) as HTMLStyleElement | null;\n\n if (!styleEl) {\n styleEl = doc.createElement('style');\n styleEl.id = CONTAINER_STYLE_ID;\n doc.head?.appendChild(styleEl);\n }\n\n const text = `html { container-type: size !important; height: ${probeHeight}px !important; }`;\n\n // Only written when it actually differs. Re-assigning identical\n // textContent would tear down and rebuild the same CSSOM rule on every\n // pass, and the whole point of the probe height being stable is that a\n // pass changes nothing about the document (see freezeTransitions).\n if (styleEl.textContent !== text) {\n styleEl.textContent = text;\n }\n };\n\n // Script-driven animations are the blind spot the animationend listeners\n // below can't cover: the Web Animations API fires no DOM event when one\n // ends, so nothing would re-run a measurement taken mid-fade. Their\n // `finished` promise is the equivalent signal, so each gets exactly one\n // re-measure scheduled for when it settles.\n //\n // CSS animations and transitions are both skipped: their\n // `animationend`/`transitionend` already bubble to the mount node, so\n // tracking them here too would only measure twice.\n //\n // A cancelled animation rejects `finished` with an AbortError and snaps\n // the element back to its un-animated style, which is as much a reason to\n // re-measure as a clean finish — hence one handler on both settle paths.\n const trackScriptAnimations = useCallback(\n (doc: Document, win: Window & typeof globalThis) => {\n if (typeof doc.getAnimations !== 'function') {\n return;\n }\n\n doc.getAnimations().forEach(animation => {\n if (\n trackedAnimationsRef.current.has(animation) ||\n !isAnimationActive(animation.playState) ||\n isCssAnimation(win, animation) ||\n isCssTransition(win, animation) ||\n neverFinishes(animation)\n ) {\n return;\n }\n\n trackedAnimationsRef.current.add(animation);\n\n animation.finished\n .catch(() => undefined)\n .then(() => updateHeightRef.current?.());\n });\n },\n [],\n );\n\n // Not ported from #132 stage 4: a \"settled scrollHeight + settled probe\n // height both unchanged -> skip\" guard, meant to avoid redundant re-runs\n // from updateHeight's own `iframe.style.height` write looping back\n // through the ResizeObserver below (a real path — the iframe's own box\n // size determines its *internal* viewport size, so this can genuinely\n // fire again). Left out deliberately: `scrollHeight` only reflects\n // normal document flow, but a position:fixed/absolute overlay opening or\n // closing (its whole reason for needing the full-subtree walk above)\n // often doesn't touch `scrollHeight` at all. A guard keyed on it would\n // silently skip exactly the kind of update stage 3 exists to catch —\n // reintroducing a narrower version of the bug this file just fixed\n // would be a worse trade than the redundant-recompute cost it'd save.\n const updateHeight = useCallback(() => {\n if (!shouldAutoHeight || !mountNode || !iframeRef.current) {\n return;\n }\n\n const iframe = iframeRef.current;\n const doc = iframe.contentDocument;\n const win = doc?.defaultView;\n\n if (!doc || !win) {\n return;\n }\n\n const scrollParent = iframe.closest<HTMLElement>('[data-frame-container]');\n\n let probeHeight: number;\n\n if (!scrollParent) {\n // No scroll container anywhere in the tree (Frame used directly,\n // without Dnd) — fall back to a fixed default; see\n // FALLBACK_PROBE_HEIGHT's own comment for why this differs from\n // the \"container exists but isn't laid out yet\" case below.\n probeHeight = FALLBACK_PROBE_HEIGHT;\n } else {\n // Starts at the iframe itself, whose own margin, border and padding\n // take space the same way a wrapper's do, and stops before the scroll\n // container, which only contributes its padding: `clientHeight`\n // already leaves out its border and margin (#440).\n const scrollParentStyle = win.getComputedStyle(scrollParent);\n let wrapperInsets =\n (parseFloat(scrollParentStyle.paddingTop) || 0) +\n (parseFloat(scrollParentStyle.paddingBottom) || 0);\n let node: HTMLElement | null = iframe;\n\n while (node && node !== scrollParent) {\n wrapperInsets += verticalInsets(win.getComputedStyle(node));\n node = node.parentElement;\n }\n\n const computed = computeProbeHeight(\n scrollParent.clientHeight,\n wrapperInsets,\n );\n\n if (computed === null) {\n // Layout not ready yet (mid-transition, just mounted, etc) —\n // skip this pass instead of guessing; the ResizeObserver/\n // MutationObserver below will call this again once something\n // actually changes, including the layout settling.\n return;\n }\n\n probeHeight = computed;\n }\n\n let contentHeight = 0;\n\n // First pass (`undefined`) and any pass that moves the probe height are\n // the only ones that change the container context, so they are the only\n // ones that need the preview's transitions out of the way.\n const freezeTransitions = probeHeight !== lastProbeHeightRef.current;\n\n lastProbeHeightRef.current = probeHeight;\n\n withMeasurementOverrides(doc, freezeTransitions, () => {\n ensureContainerStyle(doc, probeHeight);\n\n // Rewrite vh-family units the container context can't otherwise reach —\n // inline `style` attributes and in-preview `<style>` tags — so they too\n // resolve against the fixed probe height rather than the iframe's own\n // (see rewriteInlineViewportUnits). Must run after ensureContainerStyle\n // and before the reads below; it's idempotent, so the extra observer\n // pass its first-render rewrites trigger converges immediately.\n rewriteInlineViewportUnits(mountNode);\n\n // No 0px fold before measuring (that was the source of problem 1)\n // — with cq*-unit content now sized against the fixed probe height\n // instead of the iframe's own, a direct read is already stable.\n contentHeight = mountNode.scrollHeight;\n\n // Full subtree, not just direct children (a popup/overlay nested a\n // few components deep was previously invisible to this walk\n // entirely — problem 2's \"중첩된 오버레이는 아예 누락됩니다\").\n const descendants = mountNode.querySelectorAll<HTMLElement>('*');\n\n descendants.forEach(el => {\n const style = win.getComputedStyle(el);\n\n // visibility:hidden/opacity:0 elements (a closed bottom sheet,\n // a not-yet-faded-in overlay) keep a non-zero offsetHeight —\n // display:none doesn't need checking here since the browser\n // already zeroes *its* offsetHeight on its own.\n //\n // The animation lookup is confined to the one verdict it can\n // change — whether a fully transparent element is fading in\n // (measure it) or simply not shown (skip it) — so every other\n // element still costs nothing but the computed-style read.\n const isAnimating = style.opacity === '0' && hasActiveAnimation(el);\n\n if (isVisuallyHidden(style, isAnimating)) {\n return;\n }\n\n if (\n (style.position === 'fixed' || style.position === 'absolute') &&\n el.offsetHeight > 0\n ) {\n const estimatedHeight = estimatePositionedElementHeight(\n el.offsetHeight,\n style.transform,\n probeHeight,\n );\n\n contentHeight = Math.max(contentHeight, estimatedHeight);\n }\n });\n });\n\n if (contentHeight > 0) {\n iframe.style.height = `${Math.ceil(contentHeight)}px`;\n }\n\n // Outside the measurement window on purpose: the overrides above cancel\n // running transitions, and this read should see the document's real\n // animation set rather than one the act of measuring just altered.\n trackScriptAnimations(doc, win);\n }, [shouldAutoHeight, mountNode, trackScriptAnimations]);\n\n useEffect(() => {\n updateHeightRef.current = updateHeight;\n }, [updateHeight]);\n\n // updateHeight only ever adds/refreshes the container-context style —\n // if autoHeight is toggled off (or an explicit style.height is passed)\n // at runtime, nothing else would ever remove or update it again,\n // leaving cq*-unit content sized against a stale probe height instead\n // of correctly falling back to real viewport-relative sizing (which\n // cqh does on its own once nothing establishes a size container — see\n // ensureContainerStyle's own comment).\n useEffect(() => {\n if (shouldAutoHeight) {\n return;\n }\n\n iframeRef.current?.contentDocument\n ?.getElementById(CONTAINER_STYLE_ID)\n ?.remove();\n }, [shouldAutoHeight]);\n\n // A fixed-height iframe scrolls its own document. `syncStyle` copies the\n // host's stylesheets in, and an app shell often sets `body { overflow:\n // hidden }` to keep its own page from scrolling. Copied in, that rule left\n // the preview unable to scroll at all. An inline value wins over any copied\n // rule. With autoHeight the iframe is as tall as its content, so there's\n // nothing to scroll and the body is left as it was.\n useEffect(() => {\n const body = iframeRef.current?.contentDocument?.body;\n\n if (!body || !mountNode) {\n return;\n }\n\n if (shouldAutoHeight) {\n body.style.removeProperty('overflow-y');\n } else {\n body.style.setProperty('overflow-y', 'auto');\n }\n }, [shouldAutoHeight, mountNode]);\n\n // Keyed on mountNode (not just shouldAutoHeight) so it re-runs once the\n // iframe's document exists — before load there's no head to inject into.\n useEffect(() => {\n const doc = iframeRef.current?.contentDocument;\n\n if (!doc?.head) {\n return;\n }\n\n const existing = doc.getElementById(HIDE_SCROLLBAR_STYLE_ID);\n\n if (!shouldAutoHeight) {\n existing?.remove();\n return;\n }\n\n if (existing) {\n return;\n }\n\n const styleEl = doc.createElement('style');\n styleEl.id = HIDE_SCROLLBAR_STYLE_ID;\n styleEl.textContent = [\n 'html, body { scrollbar-width: none !important; }',\n 'html::-webkit-scrollbar, body::-webkit-scrollbar { display: none !important; }',\n ].join('\\n');\n doc.head.appendChild(styleEl);\n }, [shouldAutoHeight, mountNode]);\n\n useEffect(() => {\n updateHeight();\n }, [updateHeight]);\n\n const [resizeRef, resizeSize] = useResizeObserver<HTMLElement>();\n\n // useResizeObserver's ref callback isn't wired through this component's\n // own JSX (mountNode is the portal's imperatively-created container, not\n // something rendered here), so it's attached/detached imperatively\n // instead. Its reported size is intentionally unused - updateHeight's own\n // walk (every descendant, position:fixed/absolute ones capped and offset\n // by their transform) computes a more accurate height than mountNode's\n // own content-box size would, so a change in `resizeSize` is only used\n // as a trigger to recompute.\n useEffect(() => {\n if (!shouldAutoHeight || !mountNode) {\n return;\n }\n\n resizeRef(mountNode);\n return () => resizeRef(null);\n }, [shouldAutoHeight, mountNode, resizeRef]);\n\n useEffect(() => {\n updateHeight();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [resizeSize]);\n\n useMutationObserver(mountNode, updateHeight, {\n enabled: shouldAutoHeight,\n childList: true,\n subtree: true,\n attributes: true,\n characterData: true,\n });\n\n // An animation or transition finishing is neither a DOM mutation nor a\n // resize of the mount node, so neither observer above re-runs the\n // measurement — a fixed/absolute element read mid-fade would keep its\n // stale height on the iframe until something unrelated happened to the DOM\n // (#374). All four events bubble, so a single listener each on the mount\n // node covers every descendant.\n //\n // The `cancel` events count as much as the `end` ones: a cancelled\n // animation or transition snaps the element back to its un-animated style,\n // changing the height just the same.\n //\n // `transitionend`/`transitioncancel` used to be unnecessary, because every\n // measurement pass cancelled every transition outright. Now that a pass\n // only freezes transitions when it moves the probe height, a transition\n // can genuinely still be running when a pass reads it, so its completion\n // needs the same re-measure a keyframe animation's does.\n //\n // useEventListener resolves an element target through a ref, so mountNode\n // (portal-owned state, not something this component renders) is wrapped in\n // one that changes identity only when the node itself does. `enabled`\n // keeps it from polling for a node that isn't there yet.\n const mountNodeRef = useMemo(() => ({ current: mountNode }), [mountNode]);\n const settleListenerEnabled = shouldAutoHeight && mountNode !== null;\n\n useEventListener('animationend', updateHeight, {\n target: mountNodeRef,\n enabled: settleListenerEnabled,\n });\n\n useEventListener('animationcancel', updateHeight, {\n target: mountNodeRef,\n enabled: settleListenerEnabled,\n });\n\n useEventListener('transitionend', updateHeight, {\n target: mountNodeRef,\n enabled: settleListenerEnabled,\n });\n\n useEventListener('transitioncancel', updateHeight, {\n target: mountNodeRef,\n enabled: settleListenerEnabled,\n });\n\n const content = mountNode\n ? createPortal(children(mountNode), mountNode)\n : null;\n\n return (\n <iframe\n ref={iframeRef}\n style={{\n // An iframe is inline-level by default, so it sits on a text\n // baseline and leaves the descender gap (~4px) below it. With one\n // auto-height frame per canvas section, those gaps stacked into\n // height no content accounted for (#439).\n display: 'block',\n width: '100%',\n height: '100%',\n border: 'none',\n ...style,\n }}\n title={title}\n sandbox={sandbox}\n {...props}\n >\n {content}\n </iframe>\n );\n};\n\nexport default IFrame;\n","import {\n useCallback,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n} from 'react';\nimport type { ReactNode } from 'react';\nimport { createPortal } from 'react-dom';\n\nimport { useMutationObserver } from '@jbpark/use-hooks';\n\nimport { createStyleSyncManager, reconcileStyles } from './style-sync';\n\ninterface Props {\n // Clones the host document's <link rel=\"stylesheet\">/<style> tags into the\n // shadow root, mirroring `iframe.tsx`'s option of the same name. Unlike\n // `dynamicTailwind` (which recompiles only the classes it can find in the\n // rendered DOM, and only knows Tailwind's own default theme), this gets\n // the host's *actual* compiled CSS — including a consuming app's own\n // custom utilities/theme tokens — at the cost of only covering classes\n // that were already known at the host's own build time. The two are\n // complementary: this handles anything already in the host's stylesheets,\n // dynamicTailwind covers whatever's left (e.g. a class typed at runtime\n // that no build ever saw).\n syncStyle?: boolean;\n children: (hostContainer: HTMLElement | null) => ReactNode;\n}\n\nconst Shadow = ({ syncStyle = false, children }: Props) => {\n const hostRef = useRef<HTMLDivElement>(null);\n const shadowRootRef = useRef<ShadowRoot | null>(null);\n const renderTargetRef = useRef<HTMLDivElement | null>(null);\n const [renderTarget, setRenderTarget] = useState<HTMLDivElement | null>(null);\n const [hostContainer, setHostContainer] = useState<HTMLElement | null>(null);\n\n // Appended as siblings of the portal target (below), not inside it — that\n // subtree is React-owned via createPortal, and anything appended there\n // directly would get wiped on the next reconcile.\n const styleManagerRef = useRef(createStyleSyncManager());\n\n const applyStyle = useCallback(() => {\n const shadowRoot = shadowRootRef.current;\n\n if (!shadowRoot) {\n return;\n }\n\n reconcileStyles(document, shadowRoot, styleManagerRef.current, syncStyle);\n }, [syncStyle]);\n\n const applyStyleTimeoutRef = useRef<number>(undefined);\n\n // Debounced so a burst of head mutations (a stylesheet swap can fire\n // several in quick succession) only re-runs applyStyle once — same\n // rationale as iframe.tsx's identical setup.\n const debouncedApplyStyle = useCallback(() => {\n clearTimeout(applyStyleTimeoutRef.current);\n applyStyleTimeoutRef.current = window.setTimeout(applyStyle, 50);\n }, [applyStyle]);\n\n useEffect(() => {\n return () => clearTimeout(applyStyleTimeoutRef.current);\n }, []);\n\n useMutationObserver(document.head, debouncedApplyStyle, {\n enabled: syncStyle,\n childList: true,\n subtree: true,\n attributes: true,\n });\n\n useLayoutEffect(() => {\n if (!hostRef.current) {\n return;\n }\n\n const container = hostRef.current.closest(\n '[data-frame-container]',\n ) as HTMLElement | null;\n\n setHostContainer(container);\n\n let shadowRoot = shadowRootRef.current;\n\n if (!shadowRoot) {\n shadowRoot =\n hostRef.current.shadowRoot ||\n hostRef.current.attachShadow({ mode: 'open' });\n shadowRootRef.current = shadowRoot;\n }\n\n let target = renderTargetRef.current;\n\n if (!target) {\n target = document.createElement('div');\n shadowRoot.appendChild(target);\n renderTargetRef.current = target;\n setRenderTarget(target);\n }\n\n applyStyle();\n // click/pointerdown/pointerup are `composed: true` by spec, so they\n // already retarget across the shadow boundary and reach listeners\n // outside it (document, this host's ancestors, React's own root\n // listener) on their own - manually redispatching them here used to\n // make every one of those listeners see the interaction twice. Anyone\n // needing the real element inside the shadow tree (not the retargeted\n // host) can still read it via event.composedPath()[0], unaffected by\n // this removal. See #92.\n }, [applyStyle]);\n\n useLayoutEffect(() => {\n if (renderTargetRef.current && !renderTarget) {\n setRenderTarget(renderTargetRef.current);\n }\n }, [renderTarget]);\n\n return (\n <div ref={hostRef} style={{ display: 'contents' }}>\n {renderTarget && createPortal(children(hostContainer), renderTarget)}\n </div>\n );\n};\n\nexport default Shadow;\n","import { type ReactNode } from 'react';\n\nimport IFrame, { type Props as IframeProps } from './iframe';\nimport Shadow from './shadow';\n\nexport interface FrameProps extends Omit<IframeProps, 'children'> {\n mode?: 'iframe' | 'shadow';\n}\n\ninterface Props extends FrameProps {\n children: (container: HTMLElement) => ReactNode;\n}\n\nconst Frame = ({ mode, children, ...restProps }: Props) => {\n if (!mode) {\n return children(document.body);\n }\n\n if (mode === 'shadow') {\n return (\n <Shadow syncStyle={restProps.syncStyle}>\n {container => children(container || document.body)}\n </Shadow>\n );\n }\n\n return <IFrame {...restProps}>{children}</IFrame>;\n};\n\nexport default Frame;\n","import { useState } from 'react';\n\ntype Modules = Record<string, unknown> | undefined;\n\n// Same keys, each value the same object. A module is compared by identity,\n// never deeply: a module replaced with a changed object has to recompile\n// (#329). This is also how the compilation cache tells modules apart.\nconst sameModules = (a: Modules, b: Modules): boolean => {\n if (a === b) {\n return true;\n }\n\n if (!a || !b) {\n return false;\n }\n\n const keys = Object.keys(a);\n\n return (\n keys.length === Object.keys(b).length &&\n keys.every(key => Object.hasOwn(b, key) && Object.is(a[key], b[key]))\n );\n};\n\n// Hosts usually pass `modules` inline (`modules={{ Chart }}`), a new object\n// on every render. Compared by identity, that re-rendered and recompiled\n// every section on every edit, and past the compilation cache's limit sent\n// them back through Babel (#397). This keeps the previous object while the\n// entries are the same, so everything keyed on it sees no change.\nexport const useStableModules = <T extends Modules>(modules: T): T => {\n const [stable, setStable] = useState(modules);\n\n if (sameModules(stable, modules)) {\n return stable;\n }\n\n // Adjusted during render, so the changed modules are used in this render\n // rather than one render late.\n setStable(modules);\n\n return modules;\n};\n","import * as ui from '@jbpark/ui-kit';\nimport * as utils from '@jbpark/ui-kit/utils';\n\n// The specifiers every compiled sample can import without the host supplying\n// them (`import * as ui from 'ui-kit'`). Lives with the preview, its only\n// consumer, rather than in `~/utils`: evaluating ui-kit's namespace reaches\n// its stylesheet imports, so keeping it there made `@jbpark/live-editor/utils`\n// (and every entry built on it) unimportable outside a bundler — Node, SSR,\n// scripts. See #372.\nexport const baseModules = {\n 'ui-kit': ui,\n 'ui-kit/utils': utils,\n};\n","import { useMemo } from 'react';\n\nimport type { Module } from '~/types';\nimport { compile } from '~/utils/compile';\n\nimport { baseModules } from './base-modules';\nimport { useStableModules } from './use-stable-modules';\n\n// Shared by `preview/client.tsx` and `dnd/renderer.tsx`, which both turn a\n// code string into a renderable component in exactly the same way. They used\n// to implement this separately, which is how the two paths drifted apart on\n// error handling (see #246).\n//\n// Returns `null` for empty code so a caller can tell \"nothing to render\"\n// apart from \"compiled, but failed\" — the latter is a Module carrying\n// `error`, which callers should surface rather than render as blank.\nexport const useCompiledModule = (\n code: string,\n _modules?: Record<string, unknown>,\n): Module | null => {\n // An inline `modules` object from `Live.Preview`'s host is new each\n // render; only a changed entry should recompile (#397).\n const modules = useStableModules(_modules);\n\n const mergedModules = useMemo(\n () => ({ ...baseModules, ...modules }),\n [modules],\n );\n\n return useMemo(() => {\n if (!code) {\n return null;\n }\n\n try {\n return compile(code, mergedModules);\n } catch (e) {\n return {\n exports: {},\n error: e instanceof Error ? e.message : 'Module transformation error',\n };\n }\n }, [code, mergedModules]);\n};\n","import { useCallback, useEffect, useState } from 'react';\n\n// Compiles the Tailwind classes found in rendered output and returns them as\n// a CSS string for the caller to inject as a `<style>` tag. Shared by\n// `preview/client.tsx` and `dnd/renderer.tsx`, which previously carried\n// byte-identical copies of this logic and of the comments below (see #246).\n//\n// Scans the live DOM (whatever the returned `ref` is attached to) rather than\n// the source text it was rendered from, so classes contributed by an imported\n// component (e.g. ui-kit's `Button`) are picked up too — those never appear as\n// literal text in the previewed source, only in that component's own compiled\n// output.\n//\n// The scanned element is tracked as state via a callback ref rather than a\n// plain `useRef`, because in shadow mode it isn't mounted on the caller's\n// first commit at all — `Shadow` creates its portal target in its own effect\n// and only re-renders with it afterwards, one commit later. A plain ref read\n// in a `[code, enabled]`-keyed effect would see `null` on that first pass and\n// never retry; making the element itself a dependency re-runs the scan once\n// it actually exists.\n//\n// The Tailwind compiler and its theme are imported on first use rather than\n// with this module: they only matter with `dynamicTailwind` on, and were\n// otherwise ~300 kB of every consumer's initial bundle (#332).\nexport const useDynamicTailwind = (code: string, enabled: boolean) => {\n const [css, setCss] = useState('');\n const [element, setElement] = useState<HTMLDivElement | null>(null);\n\n const ref = useCallback((node: HTMLDivElement | null) => {\n setElement(node);\n }, []);\n\n useEffect(() => {\n if (!code || !enabled || !element) {\n return;\n }\n\n let cancelled = false;\n\n import('~/utils/tailwind')\n .then(({ generateTailwindCSSFromDOM }) =>\n generateTailwindCSSFromDOM(element),\n )\n .then(next => {\n if (!cancelled) {\n setCss(next);\n }\n });\n\n return () => {\n cancelled = true;\n };\n }, [code, enabled, element]);\n\n return { ref, css };\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"use-dynamic-tailwind-T9r9ExbR.js","names":[],"sources":["../src/components/frame/measure.ts","../src/components/frame/style-sync.ts","../src/components/frame/viewport-units.ts","../src/components/frame/iframe.tsx","../src/components/frame/shadow.tsx","../src/components/frame/frame.tsx","../src/components/preview/use-stable-modules.ts","../src/components/preview/base-modules.ts","../src/components/preview/use-compiled-module.ts","../src/components/preview/use-dynamic-tailwind.ts"],"sourcesContent":["// Pure helpers behind iframe.tsx's autoHeight measurement (#132 stages\n// 2-3) — kept DOM-free so they're testable under this repo's node-\n// environment vitest setup; the DOM walking itself (getComputedStyle,\n// querySelectorAll, offsetHeight reads) has no real layout engine to run\n// against outside a real browser and stays in iframe.tsx, verified\n// separately with a real Chromium instance instead of a unit test.\n\n// A fallback used only when there's no `[data-frame-container]` scroll\n// container to measure against at all — e.g. `Frame` used directly by a\n// library consumer, without `Dnd`. There's no better reference height to\n// wait for in that case (unlike the \"container exists but hasn't laid out\n// yet\" case below, which defers instead), so this just needs to be *some*\n// reasonable default. Matches the source fork's own constant — a common\n// mobile viewport height, chosen there for the same reason.\nexport const FALLBACK_PROBE_HEIGHT = 812;\n\n// The reference height for the preview's CSS containment context (see\n// ensureContainerStyle in iframe.tsx) — everything sized in `vh`-family\n// units (converted to `cqh` by convertViewportUnits) resolves against\n// this instead of the iframe's own height, which is what breaks the old\n// approach's fold-to-0px-then-measure circularity (#132 problem 1).\n//\n// `clientHeight` excludes the scroll container's own border, but not its\n// padding, nor anything between the iframe and that container. The caller\n// adds all of that up into `wrapperInsets` (see `verticalInsets`) while\n// walking from the iframe to the scroll container. This codebase's own\n// Sortable/Renderer/Frame add little (the Droppable's border and padding),\n// but a consumer's CSS, `provider` or `Canvas` className can add more.\n//\n// Returns `null` (not a guessed fallback) when the container hasn't been\n// laid out yet (`clientHeight` still 0, e.g. mid-transition) — the\n// caller should skip this measurement pass rather than settle on a\n// number that has nothing to do with the actual available space and\n// that no future event would ever correct (see the issue's own\n// reasoning for why a `window.innerHeight` fallback here was wrong).\n// The vertical space one element takes around the box inside it: margin,\n// border and padding, top and bottom. Margin counts too (#440): a 100vh\n// section in a wrapper with a vertical margin would otherwise be as tall as\n// the container and overflow it by that margin.\n//\n// Summed per element, so a margin that collapses through a parent with no\n// border or padding is counted twice. That errs on the side of a slightly\n// smaller probe, which fits, rather than a larger one, which scrolls.\nexport const verticalInsets = (\n style: Pick<\n CSSStyleDeclaration,\n | 'marginTop'\n | 'marginBottom'\n | 'borderTopWidth'\n | 'borderBottomWidth'\n | 'paddingTop'\n | 'paddingBottom'\n >,\n): number =>\n [\n style.marginTop,\n style.marginBottom,\n style.borderTopWidth,\n style.borderBottomWidth,\n style.paddingTop,\n style.paddingBottom,\n ].reduce((sum, value) => sum + (parseFloat(value) || 0), 0);\n\nexport const computeProbeHeight = (\n scrollContainerClientHeight: number,\n wrapperInsets: number,\n): number | null => {\n const usable = scrollContainerClientHeight - wrapperInsets;\n\n return usable > 0 ? usable : null;\n};\n\n// `visibility:hidden` and `opacity:0` elements keep a non-zero\n// offsetHeight/scrollHeight (unlike `display:none`, which zeroes them\n// out on its own) — without this check, a closed bottom sheet or a\n// not-yet-faded-in overlay sitting in the DOM inflates the measured\n// height by however tall it would be if shown.\n//\n// `opacity:0` on its own can't separate that closed overlay from the\n// *first frames of a fade-in*, which is on its way to being visible and\n// belongs in the height (#374). `isAnimating` is how the caller resolves\n// it: an element part-way through a keyframe animation is measured even\n// while fully transparent. It deliberately doesn't rescue\n// `visibility:hidden` — an element animating some unrelated property\n// while hidden is still hidden.\nexport const isVisuallyHidden = (\n computed: {\n visibility: string;\n opacity: string;\n },\n isAnimating = false,\n): boolean =>\n computed.visibility === 'hidden' ||\n (computed.opacity === '0' && !isAnimating);\n\n// Which `Animation.playState`s mean \"this element's appearance is still\n// in flight, don't trust the current frame as its resting state\".\n//\n// `playState !== 'idle'` would be the tempting shorthand and is wrong:\n// a *finished* fade-out with `animation-fill-mode: forwards` sits in\n// `finished` while holding `opacity: 0`, and that element really is\n// hidden for good — counting it as animating would put the full height\n// of a dismissed overlay back into the estimate. `paused` counts because\n// a paused fade-in is stopped part-way through, not dismissed.\nexport const isAnimationActive = (playState: string): boolean =>\n playState === 'running' || playState === 'paused';\n\n// `translate(Xpx, Ypx)` / `translateY(Ypx)` / `matrix(a,b,c,d,tx,ty)`'s Y\n// component — a positioned popup/overlay is commonly offset this way\n// (Radix/floating-ui do), so its *effective* bottom edge is `offsetY +\n// offsetHeight` from its positioned ancestor, not just `offsetHeight`\n// alone. Returns 0 for anything else (no transform, or an X-only/\n// unrecognized one) rather than throwing — an unparsed offset is safer\n// treated as \"no extra offset\" than as a measurement failure.\n//\n// iframe.tsx's only caller passes `getComputedStyle(el).transform`, which\n// every real browser normalizes to `matrix(...)` regardless of what\n// syntax (translate/translateY/none of the above) the original CSS used\n// — confirmed against a real Chromium instance, not assumed. The\n// translate()/translateY() branches mainly document intent and cover any\n// future caller that passes an *inline* style's transform instead (which\n// does preserve the author's original syntax).\nexport const parseTranslateY = (transform: string): number => {\n if (!transform || transform === 'none') {\n return 0;\n }\n\n // translateY(y) is single-argument — tried first and separately from\n // translate(x, y), since a naive \"match the arg after a comma\" pattern\n // has no comma to find here at all and would silently fall through to 0.\n const translateYMatch = transform.match(/translateY\\(\\s*([+-]?\\d*\\.?\\d+)/);\n\n if (translateYMatch?.[1]) {\n return parseFloat(translateYMatch[1]);\n }\n\n const translateMatch = transform.match(\n /translate\\([^,]+,\\s*([+-]?\\d*\\.?\\d+)/,\n );\n\n if (translateMatch?.[1]) {\n return parseFloat(translateMatch[1]);\n }\n\n const matrixMatch = transform.match(\n /matrix\\(\\s*[^,]+,\\s*[^,]+,\\s*[^,]+,\\s*[^,]+,\\s*[^,]+,\\s*([+-]?\\d*\\.?\\d+)/,\n );\n\n return matrixMatch?.[1] ? parseFloat(matrixMatch[1]) : 0;\n};\n\n// A `position:fixed`/`absolute` element is placed relative to the\n// viewport (or the nearest positioned ancestor, which for this preview\n// content is effectively the same scale) — `offsetY + offsetHeight` can\n// legitimately exceed the probe height (e.g. an element deliberately\n// positioned to bleed off-screen), but letting an unbounded value drive\n// the *whole document's* measured height would make one runaway overlay\n// balloon everything below it. Capping at `probeHeight` treats \"this\n// element's bottom edge is somewhere past the viewport\" the same as \"at\n// the viewport edge\" for sizing purposes, without needing to know how far\n// past.\nexport const estimatePositionedElementHeight = (\n offsetHeight: number,\n transform: string,\n probeHeight: number,\n): number => {\n const offsetY = parseTranslateY(transform);\n\n return Math.min(offsetY + offsetHeight, probeHeight);\n};\n","const SYNCED_STYLE_ATTR = 'data-live-editor-synced-style';\n\ntype SourceStyle = HTMLLinkElement | HTMLStyleElement;\ntype SyncedStyle = HTMLLinkElement | HTMLStyleElement;\n\nexport interface StyleSyncManager {\n clones: Map<SourceStyle, SyncedStyle>;\n}\n\nexport const createStyleSyncManager = (): StyleSyncManager => ({\n clones: new Map(),\n});\n\nconst isDocument = (target: Document | ShadowRoot): target is Document =>\n target.nodeType === Node.DOCUMENT_NODE;\n\nconst sourceStyles = (document: Document): SourceStyle[] =>\n Array.from(\n document.head.querySelectorAll<HTMLLinkElement | HTMLStyleElement>(\n 'link[rel=\"stylesheet\"], style',\n ),\n );\n\nconst copyAttributes = (\n source: SourceStyle,\n clone: SyncedStyle,\n transformStyle: (content: string) => string,\n) => {\n Array.from(clone.attributes).forEach(attribute => {\n if (\n attribute.name !== SYNCED_STYLE_ATTR &&\n !source.hasAttribute(attribute.name)\n ) {\n clone.removeAttribute(attribute.name);\n }\n });\n\n Array.from(source.attributes).forEach(attribute => {\n clone.setAttribute(attribute.name, attribute.value);\n });\n\n if (source.tagName === 'LINK' && clone.tagName === 'LINK') {\n (clone as HTMLLinkElement).disabled = (source as HTMLLinkElement).disabled;\n }\n\n clone.textContent =\n source.tagName === 'STYLE'\n ? transformStyle(source.textContent || '')\n : source.textContent;\n};\n\nconst createClone = (\n source: SourceStyle,\n target: Document | ShadowRoot,\n transformStyle: (content: string) => string,\n): SyncedStyle => {\n const ownerDocument = isDocument(target) ? target : target.ownerDocument;\n const clone = ownerDocument.createElement(\n source.tagName.toLowerCase(),\n ) as SyncedStyle;\n\n clone.setAttribute(SYNCED_STYLE_ATTR, '');\n copyAttributes(source, clone, transformStyle);\n\n return clone;\n};\n\nconst targetContainer = (target: Document | ShadowRoot) =>\n isDocument(target) ? target.head : target;\n\nconst isSyncedStyle = (node: Node): node is SyncedStyle => {\n const element = node as Element;\n\n return (\n (element.tagName === 'LINK' || element.tagName === 'STYLE') &&\n element.hasAttribute(SYNCED_STYLE_ATTR)\n );\n};\n\nconst removeClone = (clone: SyncedStyle) => {\n clone.remove();\n};\n\n// `anchor`, when given, is an element the synced block has to stay in front\n// of. Without it, a first sync appends the block after whatever the target\n// already holds, so a rule the target added for itself could end up before\n// the host's copies and lose to them on order (#441).\nexport const reconcileStyles = (\n sourceDocument: Document,\n target: Document | ShadowRoot,\n manager: StyleSyncManager,\n enabled: boolean,\n transformStyle: (content: string) => string = content => content,\n anchor: Node | null = null,\n) => {\n if (!enabled) {\n manager.clones.forEach(removeClone);\n manager.clones.clear();\n\n return;\n }\n\n const sources = sourceStyles(sourceDocument);\n const sourceSet = new Set(sources);\n const container = targetContainer(target);\n\n manager.clones.forEach((clone, source) => {\n if (!sourceSet.has(source)) {\n removeClone(clone);\n manager.clones.delete(source);\n }\n });\n\n let previous: SyncedStyle | null = null;\n\n sources.forEach(source => {\n let clone = manager.clones.get(source);\n\n if (!clone) {\n clone = createClone(source, target, transformStyle);\n manager.clones.set(source, clone);\n } else {\n copyAttributes(source, clone, transformStyle);\n }\n\n const nextSibling = previous\n ? previous.nextSibling\n : (Array.from(container.childNodes).find(isSyncedStyle) ??\n (anchor?.parentNode === container ? anchor : null));\n\n if (clone !== nextSibling) {\n container.insertBefore(clone, nextSibling);\n }\n\n previous = clone;\n });\n};\n\n// What a root-attribute sync has written onto the target, so a later pass\n// removes exactly that and never what the target set for itself.\nexport interface RootAttributeSync {\n attributes: Set<string>;\n classes: Set<string>;\n}\n\nexport const createRootAttributeSync = (): RootAttributeSync => ({\n attributes: new Set(),\n classes: new Set(),\n});\n\nconst isMirroredAttribute = (name: string) => name.startsWith('data-');\n\n// Mirrors the host `<html>`'s classes and `data-*` attributes onto an iframe's\n// `<html>`. A theme is usually switched there (`.dark`, `data-theme=\"dark\"`),\n// and copying the host's stylesheets alone doesn't help an iframe: its\n// selectors look for those on an ancestor, and the iframe document has a root\n// of its own. A shadow root needs none of this, since it sits under the host's\n// `<html>` already (#497).\n//\n// Classes are synced token by token, so a class the preview put on its own\n// root survives. Other attributes (`style`, `lang`, `dir`, ...) are left\n// alone: the frame relies on its own root for sizing and layout.\nexport const reconcileRootAttributes = (\n source: Element,\n target: Element,\n sync: RootAttributeSync,\n enabled: boolean,\n) => {\n const attributes = enabled\n ? Array.from(source.attributes).filter(({ name }) =>\n isMirroredAttribute(name),\n )\n : [];\n const classes = enabled ? Array.from(source.classList) : [];\n const attributeNames = new Set(attributes.map(({ name }) => name));\n const classNames = new Set(classes);\n\n sync.attributes.forEach(name => {\n if (!attributeNames.has(name)) {\n target.removeAttribute(name);\n }\n });\n attributes.forEach(({ name, value }) => {\n if (target.getAttribute(name) !== value) {\n target.setAttribute(name, value);\n }\n });\n\n sync.classes.forEach(name => {\n if (!classNames.has(name)) {\n target.classList.remove(name);\n }\n });\n classes.forEach(name => target.classList.add(name));\n\n if (!target.classList.length) {\n target.removeAttribute('class');\n }\n\n sync.attributes = attributeNames;\n sync.classes = classNames;\n};\n","// Part of #132 stage 1 — porting the internal GitLab fork's autoHeight\n// height-measurement redesign into this repo, one verifiable stage at a\n// time (see the issue for the full plan and why the current `updateHeight`\n// implementation needs replacing: folding the iframe to 0px before\n// measuring permanently collapses `vh`-sized content to 0, since `vh`\n// units resolve against the iframe's own height).\n//\n// The fork's fix replaces the iframe height as `vh`'s reference point with\n// a fixed-size CSS containment context (`html { container-type: size;\n// height: <probe>px }`) — see stage 2. That only works if the document's\n// own `vh`/`svh`/`lvh`/`dvh`/`vmin`/`vmax` usages are first rewritten to\n// the matching container-query unit (`cqh`/`cqmin`/`cqmax`), since a size\n// container doesn't retroactively change what `vh` itself resolves\n// against. `vw`/`vi` are deliberately left alone: they resolve against\n// width, which this measurement never touches, so rewriting them would\n// only add risk with no corresponding bug to fix.\nconst UNIT_MAP: Record<string, string> = {\n vh: 'cqh',\n svh: 'cqh',\n lvh: 'cqh',\n dvh: 'cqh',\n vmin: 'cqmin',\n vmax: 'cqmax',\n};\n\n// Longest-unit-first so `svh`/`lvh`/`dvh` aren't shadowed by a shorter\n// alternative matching a prefix of them first.\nconst UNITS_PATTERN = Object.keys(UNIT_MAP)\n .sort((a, b) => b.length - a.length)\n .join('|');\n\n// A CSS dimension token: optional sign, then digits with an optional\n// fractional part on either side of the decimal point (`100`, `50.5`,\n// `-10`, `.5` all match; a bare `-` or `.` alone does not).\nconst NUMBER_PATTERN = '-?(?:\\\\d+\\\\.?\\\\d*|\\\\.\\\\d+)';\n\n// Requires the matched number not to be immediately preceded by a letter,\n// digit, underscore, `.`, or `-` — this is what keeps `url(a5vh.png)`,\n// `.a5vh{}`, and `.hero-100vh{}` untouched (in all three, the digits\n// before `vh` are part of a larger filename/class-name token, not a\n// standalone CSS number) without needing any special-casing for\n// `url(...)`/selectors specifically. `-` has to be excluded too, not just\n// `\\w`/`.`, since a kebab-case identifier like `hero-100vh` uses it as a\n// word separator the same way `a5vh` uses no separator at all — otherwise\n// the number pattern's own optional leading `-?` would happily treat that\n// hyphen as a negative sign instead. This doesn't break real negative\n// values (`margin-top: -10vh`): there the character *before* the `-` is\n// whatever precedes the whole declaration (a space, in practice), so the\n// lookbehind still passes and the leading `-` is captured as part of the\n// number, same as before.\n//\n// Separately, `--my-vh: 10px` was never a candidate to begin with: there's\n// no digit immediately before `vh` there at all (the `y` in `-vh` is a\n// letter, not a number), so the pattern doesn't even try to match it.\nconst VIEWPORT_UNIT_RE = new RegExp(\n `(?<![\\\\w.-])(${NUMBER_PATTERN})(${UNITS_PATTERN})(?![a-zA-Z0-9_-])`,\n 'gi',\n);\n\n// Rewrites vh/svh/lvh/dvh/vmin/vmax to their cqh/cqmin/cqmax equivalent\n// wherever they appear as an actual CSS dimension — including nested\n// inside calc()/var() fallbacks, since this is a plain text substitution\n// rather than a CSS-aware parse. `vw` is left as-is (see UNIT_MAP above).\n//\n// Known limitation, inherited from the source fork and not fixed here:\n// this can't distinguish a real dimension from the same text sitting\n// inside a CSS string literal, e.g. `content: \"100vh\"` — the regex has no\n// concept of quoting, so that string's contents get rewritten too. In\n// practice this is rare (a `content` value that's coincidentally shaped\n// like a viewport dimension) and doesn't affect layout, since `content`\n// strings aren't parsed as CSS values.\nexport const convertViewportUnits = (css: string): string =>\n css.replace(\n VIEWPORT_UNIT_RE,\n (_match, number: string, unit: string) =>\n number + UNIT_MAP[unit.toLowerCase()],\n );\n\n// `convertViewportUnits` only ever runs over CSS *text* that this component\n// controls the injection of — the parent-document `<style>` copies (syncStyle)\n// and the `styles` prop (see iframe.tsx). Two paths carry a viewport unit into\n// the preview document without passing through either, so the container-context\n// fix (#132) never reaches them and their `vh` still resolves against the\n// iframe's own height — re-creating the exact circularity #132 removed:\n//\n// 1. inline `style` attributes — the visual editor's panel edits produce\n// these, so they're a common path here, not an edge case; and\n// 2. author `<style>` elements rendered *inside* the previewed component by\n// React, which never pass through `applyStyle`/`styles`.\n//\n// This rewrites both in place, walking only nodes that could carry a match so\n// the pass stays cheap. It's meant to be called from `updateHeight` right after\n// the container style is (re)applied and before any height is read.\n//\n// Idempotent, and specifically non-perturbing once converted: a rewritten value\n// contains `cqh`/`cqmin`/`cqmax`, none of which match the `vh`/`vmin`/`vmax`\n// attribute selectors, so a second pass re-selects nothing; and each node is\n// written back only when its text actually changed, so an already-converted\n// document isn't mutated — which matters because the write itself would\n// otherwise re-trip the MutationObserver in iframe.tsx and loop.\nexport const rewriteInlineViewportUnits = (root: HTMLElement): void => {\n // `vh` as a substring already covers `svh`/`lvh`/`dvh`; `vmin`/`vmax` need\n // their own terms. Case-insensitive so `100VH` inline is caught too.\n const inlineTargets = root.querySelectorAll<HTMLElement>(\n '[style*=\"vh\" i], [style*=\"vmin\" i], [style*=\"vmax\" i]',\n );\n\n inlineTargets.forEach(el => {\n const current = el.getAttribute('style');\n\n if (current == null) {\n return;\n }\n\n const converted = convertViewportUnits(current);\n\n if (converted !== current) {\n el.setAttribute('style', converted);\n }\n });\n\n root.querySelectorAll<HTMLStyleElement>('style').forEach(styleEl => {\n const current = styleEl.textContent;\n\n if (!current) {\n return;\n }\n\n const converted = convertViewportUnits(current);\n\n if (converted !== current) {\n styleEl.textContent = converted;\n }\n });\n};\n","import { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport type { ReactNode } from 'react';\nimport { createPortal } from 'react-dom';\n\nimport {\n useEventListener,\n useMutationObserver,\n useResizeObserver,\n} from '@jbpark/use-hooks';\n\nimport { withScriptBlobs } from '~/utils/scripts';\n\nimport {\n FALLBACK_PROBE_HEIGHT,\n computeProbeHeight,\n estimatePositionedElementHeight,\n isAnimationActive,\n isVisuallyHidden,\n verticalInsets,\n} from './measure';\nimport {\n createRootAttributeSync,\n createStyleSyncManager,\n reconcileRootAttributes,\n reconcileStyles,\n} from './style-sync';\nimport {\n convertViewportUnits,\n rewriteInlineViewportUnits,\n} from './viewport-units';\n\n// The <html> element's own container-context style — id'd so it can be\n// found/updated/removed across calls without holding a ref to it. Scoped\n// to `html` (not `:root`, which is equivalent but the fork's own\n// convention) so this only ever affects cq*-unit resolution and nothing\n// else about the document.\nconst CONTAINER_STYLE_ID = 'autoheight-container';\n\nexport interface Props {\n title?: string;\n /** Forwarded to the iframe's `sandbox` attribute for DOM/CSS isolation only — not a security boundary, since preview code executes in the host window's realm (see `compileModule` in `~/utils`). */\n sandbox?: string;\n style?: React.CSSProperties;\n scripts?: string[];\n styles?: string[];\n stylesheets?: string[];\n autoHeight?: boolean;\n syncStyle?: boolean;\n children: (container: HTMLElement) => ReactNode;\n onLoaded?: () => void;\n}\n\nconst EMPTY_STRING_ARRAY: string[] = [];\n\n// `getAnimations()` returns three kinds of animation mixed together:\n// CSSAnimation (a @keyframes rule), CSSTransition (a `transition`) and\n// plain Animation (`element.animate()`). They need telling apart below\n// because only the first two announce their own completion through a\n// bubbling DOM event. Both constructors are feature-detected rather than\n// assumed: jsdom exposes neither.\nconst isCssAnimation = (\n win: Window & typeof globalThis,\n animation: Animation,\n): boolean =>\n typeof win.CSSAnimation === 'function' &&\n animation instanceof win.CSSAnimation;\n\nconst isCssTransition = (\n win: Window & typeof globalThis,\n animation: Animation,\n): boolean =>\n typeof win.CSSTransition === 'function' &&\n animation instanceof win.CSSTransition;\n\n// Whether this element's look is still in flight, which is what tells a\n// transient `opacity: 0` (the first frames of a fade-in — measure it)\n// from a permanent one (a closed overlay — skip it).\n//\n// Transitions count here, unlike keyframe animations they are not\n// distinguished: now that a measurement pass only freezes transitions when\n// it moves the probe height (see withMeasurementOverrides), an element at\n// the very start of a fade-*in* transition genuinely reads `opacity: 0`\n// with a running transition and has to be measured. An element fading\n// *out* needs no special case — part-way through it reads a fractional\n// opacity, so it is measured for as long as it is still visible, and once\n// the transition is over it reads `0` with nothing running and drops out.\n//\n// getAnimations() is feature-detected: jsdom and pre-2020 browsers don't\n// implement it, and without it this returns false, which is the safe\n// direction (under-measuring a fading-in overlay rather than inflating a\n// section by the height of a dismissed one).\nconst hasActiveAnimation = (el: HTMLElement): boolean =>\n typeof el.getAnimations === 'function' &&\n el.getAnimations().some(animation => isAnimationActive(animation.playState));\n\n// `finished` never resolves for an animation that repeats forever, so a\n// handler attached to one would only pin its closure for as long as the\n// element lives. No signal is lost by skipping it: such an element is\n// perpetually \"animating\", so hasActiveAnimation keeps it in the estimate\n// on every pass anyway.\nconst neverFinishes = (animation: Animation): boolean => {\n const timing = animation.effect?.getComputedTiming();\n\n return timing?.iterations === Infinity || timing?.duration === Infinity;\n};\n\n// A second, separate style — inert (`media=\"not all\"`) except for the\n// brief window updateHeight actually measures in, toggled on right\n// before and off right after (#132 stage 4). Two things it guards\n// against:\n//\n// - transitions: if any rule in the preview (or a browser default)\n// gives `html`/an ancestor a `transition` on a property this\n// measurement touches, changing ensureContainerStyle's `height` would\n// animate instead of applying instantly, and a read taken right after\n// would catch a mid-transition value instead of the settled one. Only\n// applied on a pass that actually changes the probe height — see\n// `freezeTransitions`.\n// - scrollbar chrome: applying a new probe height can make a scrollbar\n// appear/disappear for exactly this measurement pass; on platforms\n// where it takes up layout width (Windows, unlike macOS's overlay\n// scrollbars), that narrows content and skews the height reading.\n// `scrollbar-width: none`/`::-webkit-scrollbar { display: none }`\n// only hides the *chrome* — unlike `overflow: hidden`, scrolling\n// itself still works, so content that ends up taller than its probe\n// height is still reachable rather than silently clipped.\n//\n// A single style element (not two, and never added/removed) so\n// toggling it can't itself trip the MutationObserver watching for\n// *content* changes.\nconst MEASUREMENT_OVERRIDE_STYLE_ID = 'autoheight-measurement-overrides';\n\nconst SCROLLBAR_OVERRIDE_RULES = [\n 'html, body { scrollbar-width: none !important; }',\n 'html::-webkit-scrollbar, body::-webkit-scrollbar { display: none !important; }',\n];\n\nconst FREEZE_TRANSITIONS_RULE =\n '*, *::before, *::after { transition: none !important; }';\n\n// `freezeTransitions` is deliberately not always on. `transition: none`\n// does not pause a transition for the duration of the measurement — it\n// *cancels* it, and lifting the override afterwards does not resume it.\n// Measured in Chromium: 0.5s into a 4s fade the element read `0.974`\n// with one running animation; inside the override window it read `0`\n// with none, and it was still `0` with none 0.8s after the override came\n// back off. The element had snapped to its end state for good.\n//\n// Freezing on every pass therefore killed every transition in the\n// preview that happened to overlap a measurement — and because a DOM\n// mutation is what both triggers a measurement and typically starts a\n// transition (a class toggle opening an overlay), that was most of them.\n//\n// The guard is only needed for what the measurement *itself* changes:\n// the probe height, and with it any `cq*`-unit descendant sized against\n// it. When the probe height is unchanged from the last pass, the pass\n// changes nothing, so there is nothing to freeze and the preview's own\n// transitions are left alone.\nconst withMeasurementOverrides = (\n doc: Document,\n freezeTransitions: boolean,\n measure: () => void,\n) => {\n let styleEl = doc.getElementById(\n MEASUREMENT_OVERRIDE_STYLE_ID,\n ) as HTMLStyleElement | null;\n\n if (!styleEl) {\n styleEl = doc.createElement('style');\n styleEl.id = MEASUREMENT_OVERRIDE_STYLE_ID;\n styleEl.media = 'not all';\n doc.head?.appendChild(styleEl);\n }\n\n const text = (\n freezeTransitions\n ? [FREEZE_TRANSITIONS_RULE, ...SCROLLBAR_OVERRIDE_RULES]\n : SCROLLBAR_OVERRIDE_RULES\n ).join('\\n');\n\n if (styleEl.textContent !== text) {\n styleEl.textContent = text;\n }\n\n styleEl.media = 'all';\n measure();\n styleEl.media = 'not all';\n};\n\nconst IFrame = ({\n title = 'Live Preview',\n sandbox,\n style = {},\n scripts = EMPTY_STRING_ARRAY,\n styles = EMPTY_STRING_ARRAY,\n stylesheets = EMPTY_STRING_ARRAY,\n autoHeight = false,\n syncStyle = false,\n children,\n onLoaded,\n ...props\n}: Props) => {\n const iframeRef = useRef<HTMLIFrameElement>(null);\n const [mountNode, setMountNode] = useState<HTMLElement | null>(null);\n // Tracks which script srcs have already been injected into this iframe's\n // document, keyed by src rather than a single loaded/not-loaded boolean —\n // a boolean latched to `true` forever meant a later change to `scripts`\n // (new entries) never got loaded once the first batch had.\n const loadedScriptsRef = useRef<Set<string>>(new Set());\n // The document those scripts went into. Moving the iframe in the DOM, as\n // reordering canvas sections does, reloads it with a fresh document that\n // has none of them (#507).\n const loadedScriptsDocRef = useRef<Document | null>(null);\n const prevStyleCountRef = useRef(0);\n const prevStylesheetCountRef = useRef(0);\n // Animations already given a \"re-measure once you settle\" handler, so a\n // long-running one still in flight across many measurement passes only\n // ever gets one (see trackScriptAnimations). Weak so it never keeps a\n // finished animation — or the element owning it — alive.\n const trackedAnimationsRef = useRef(new WeakSet<Animation>());\n // Lets those handlers call back into the *current* updateHeight without\n // updateHeight having to list itself as its own dependency.\n const updateHeightRef = useRef<(() => void) | null>(null);\n // The probe height the container style was last built for. A pass that\n // leaves it unchanged changes nothing about the document, which is what\n // lets that pass measure without freezing the preview's transitions.\n const lastProbeHeightRef = useRef<number | undefined>(undefined);\n const shouldAutoHeight = autoHeight && style.height == null;\n\n const styleManagerRef = useRef(createStyleSyncManager());\n const rootAttributeSyncRef = useRef(createRootAttributeSync());\n\n const applyStyle = useCallback(() => {\n const doc = iframeRef.current?.contentDocument;\n\n if (!doc) {\n return;\n }\n\n // The container style has to come after every host copy: both sides mark\n // their rules `!important`, so a host `html { height: 100% !important }`\n // synced in after it wins, and the frame folds to a pixel. That happened\n // whenever the first sync ran after the container style existed, as when\n // `syncStyle` is switched on after mount (#441).\n reconcileStyles(\n document,\n doc,\n styleManagerRef.current,\n syncStyle,\n convertViewportUnits,\n doc.getElementById(CONTAINER_STYLE_ID),\n );\n // The host's theme class or attribute, which those copied styles select\n // on (#497).\n reconcileRootAttributes(\n document.documentElement,\n doc.documentElement,\n rootAttributeSyncRef.current,\n syncStyle,\n );\n }, [syncStyle]);\n\n const applyStyleTimeoutRef = useRef<number>(undefined);\n\n // Debounced so a burst of head mutations (a stylesheet swap can fire\n // several in quick succession) only re-runs applyStyle once, matching the\n // original raw-MutationObserver setup's 50ms debounce.\n const debouncedApplyStyle = useCallback(() => {\n clearTimeout(applyStyleTimeoutRef.current);\n applyStyleTimeoutRef.current = window.setTimeout(applyStyle, 50);\n }, [applyStyle]);\n\n useEffect(() => {\n return () => clearTimeout(applyStyleTimeoutRef.current);\n }, []);\n\n useMutationObserver(document.head, debouncedApplyStyle, {\n enabled: syncStyle,\n childList: true,\n subtree: true,\n attributes: true,\n });\n\n // A theme switch only touches the host's `<html>` attributes, which the\n // head observer above doesn't see.\n useMutationObserver(document.documentElement, debouncedApplyStyle, {\n enabled: syncStyle,\n attributes: true,\n });\n\n useEffect(() => {\n const $iframe = iframeRef.current;\n\n if (!$iframe) {\n return;\n }\n\n const onLoad = () => {\n const doc = $iframe.contentDocument;\n\n if (!doc) {\n return;\n }\n\n if (loadedScriptsDocRef.current !== doc) {\n loadedScriptsDocRef.current = doc;\n loadedScriptsRef.current = new Set();\n }\n\n doc.body.style.overflowX = 'hidden';\n doc.body.style.margin = '0';\n\n let node = doc.getElementById('iframe-root');\n\n if (!node) {\n node = doc.createElement('div');\n node.id = 'iframe-root';\n doc.body.appendChild(node);\n }\n\n setMountNode(node);\n\n applyStyle();\n\n const pendingScripts = scripts.filter(\n src => !loadedScriptsRef.current.has(src),\n );\n\n if (pendingScripts.length) {\n pendingScripts.forEach(src => loadedScriptsRef.current.add(src));\n\n withScriptBlobs(pendingScripts, blobUrls => {\n if (!doc.head) {\n return;\n }\n\n const fragment = doc.createDocumentFragment();\n blobUrls.forEach(blobUrl => {\n const script = doc.createElement('script');\n script.src = blobUrl;\n fragment.appendChild(script);\n });\n doc.head.appendChild(fragment);\n });\n }\n\n onLoaded?.();\n };\n\n $iframe.addEventListener('load', onLoad);\n\n if ($iframe.contentDocument?.readyState === 'complete') {\n onLoad();\n }\n\n return () => {\n $iframe.removeEventListener('load', onLoad);\n };\n }, [scripts, onLoaded, applyStyle]);\n\n useEffect(() => {\n const doc = iframeRef.current?.contentDocument;\n\n if (!doc?.head) {\n return;\n }\n\n styles.forEach((css, index) => {\n const styleId = `injected-style-${index}`;\n let styleEl = doc.getElementById(styleId) as HTMLStyleElement | null;\n\n if (!styleEl) {\n styleEl = doc.createElement('style');\n styleEl.id = styleId;\n doc.head.appendChild(styleEl);\n }\n\n // The primary source of vh/svh/etc in a real preview — compiled\n // component CSS (e.g. Tailwind's `h-screen` -> `height: 100vh`).\n // See ensureContainerStyle below for why this needs converting.\n const convertedCss = convertViewportUnits(css);\n\n if (styleEl.textContent !== convertedCss) {\n styleEl.textContent = convertedCss;\n }\n });\n\n // Indices beyond the current array's length are stale from a previous,\n // longer `styles`/`stylesheets` — the loops above only add/update up to\n // the current length, so anything past it (from before an item was\n // removed, or the array shrank) would otherwise stay injected forever.\n for (\n let index = styles.length;\n index < prevStyleCountRef.current;\n index++\n ) {\n doc.getElementById(`injected-style-${index}`)?.remove();\n }\n prevStyleCountRef.current = styles.length;\n\n stylesheets.forEach((href, index) => {\n const linkId = `injected-stylesheet-${index}`;\n let linkEl = doc.getElementById(linkId) as HTMLLinkElement | null;\n\n if (!linkEl) {\n linkEl = doc.createElement('link');\n linkEl.id = linkId;\n linkEl.rel = 'stylesheet';\n doc.head.appendChild(linkEl);\n }\n\n if (linkEl.href !== href) {\n linkEl.href = href;\n }\n });\n\n for (\n let index = stylesheets.length;\n index < prevStylesheetCountRef.current;\n index++\n ) {\n doc.getElementById(`injected-stylesheet-${index}`)?.remove();\n }\n prevStylesheetCountRef.current = stylesheets.length;\n }, [styles, stylesheets]);\n\n // Permanently hides the iframe document's own scrollbar chrome while\n // autoHeight is sizing the iframe to its content. autoHeight sets the\n // iframe's height to `Math.ceil(contentHeight)`, so sub-pixel content or\n // a rounding remainder can leave the inner document a fraction taller\n // than its viewport — enough for the browser to draw a vertical\n // scrollbar inside every section's iframe (visual noise once several Dnd\n // sections stack). Unlike the measurement-only override above (toggled\n // off after each read), this one stays on: `scrollbar-width`/\n // `::-webkit-scrollbar` hide only the *chrome*, not scrolling itself, so\n // content that ever genuinely exceeds the measured height is still\n // reachable by wheel/keyboard rather than clipped.\n const HIDE_SCROLLBAR_STYLE_ID = 'autoheight-hide-scrollbar';\n\n // Ties `cqh`/`cqmin`/`cqmax` (what convertViewportUnits rewrote every\n // vh/svh/lvh/dvh/vmin/vmax to) to a *fixed* reference height instead of\n // the iframe's own height — this is what breaks the old approach's\n // circularity (#132 problem 1): folding the iframe to 0px before\n // measuring made vh-sized content resolve to 0 and stay there forever,\n // while measuring without folding never converges (vh content sized\n // against the iframe's own just-grown height keeps growing it further).\n // `container-type: size` requires an explicit height to size against,\n // which `probeHeight` (the *scroll container's* available height, not\n // the iframe's) provides — genuinely independent of whatever height this\n // function goes on to set on the iframe itself.\n const ensureContainerStyle = (doc: Document, probeHeight: number) => {\n let styleEl = doc.getElementById(\n CONTAINER_STYLE_ID,\n ) as HTMLStyleElement | null;\n\n if (!styleEl) {\n styleEl = doc.createElement('style');\n styleEl.id = CONTAINER_STYLE_ID;\n doc.head?.appendChild(styleEl);\n }\n\n const text = `html { container-type: size !important; height: ${probeHeight}px !important; }`;\n\n // Only written when it actually differs. Re-assigning identical\n // textContent would tear down and rebuild the same CSSOM rule on every\n // pass, and the whole point of the probe height being stable is that a\n // pass changes nothing about the document (see freezeTransitions).\n if (styleEl.textContent !== text) {\n styleEl.textContent = text;\n }\n };\n\n // Script-driven animations are the blind spot the animationend listeners\n // below can't cover: the Web Animations API fires no DOM event when one\n // ends, so nothing would re-run a measurement taken mid-fade. Their\n // `finished` promise is the equivalent signal, so each gets exactly one\n // re-measure scheduled for when it settles.\n //\n // CSS animations and transitions are both skipped: their\n // `animationend`/`transitionend` already bubble to the mount node, so\n // tracking them here too would only measure twice.\n //\n // A cancelled animation rejects `finished` with an AbortError and snaps\n // the element back to its un-animated style, which is as much a reason to\n // re-measure as a clean finish — hence one handler on both settle paths.\n const trackScriptAnimations = useCallback(\n (doc: Document, win: Window & typeof globalThis) => {\n if (typeof doc.getAnimations !== 'function') {\n return;\n }\n\n doc.getAnimations().forEach(animation => {\n if (\n trackedAnimationsRef.current.has(animation) ||\n !isAnimationActive(animation.playState) ||\n isCssAnimation(win, animation) ||\n isCssTransition(win, animation) ||\n neverFinishes(animation)\n ) {\n return;\n }\n\n trackedAnimationsRef.current.add(animation);\n\n animation.finished\n .catch(() => undefined)\n .then(() => updateHeightRef.current?.());\n });\n },\n [],\n );\n\n // Not ported from #132 stage 4: a \"settled scrollHeight + settled probe\n // height both unchanged -> skip\" guard, meant to avoid redundant re-runs\n // from updateHeight's own `iframe.style.height` write looping back\n // through the ResizeObserver below (a real path — the iframe's own box\n // size determines its *internal* viewport size, so this can genuinely\n // fire again). Left out deliberately: `scrollHeight` only reflects\n // normal document flow, but a position:fixed/absolute overlay opening or\n // closing (its whole reason for needing the full-subtree walk above)\n // often doesn't touch `scrollHeight` at all. A guard keyed on it would\n // silently skip exactly the kind of update stage 3 exists to catch —\n // reintroducing a narrower version of the bug this file just fixed\n // would be a worse trade than the redundant-recompute cost it'd save.\n const updateHeight = useCallback(() => {\n if (!shouldAutoHeight || !mountNode || !iframeRef.current) {\n return;\n }\n\n const iframe = iframeRef.current;\n const doc = iframe.contentDocument;\n const win = doc?.defaultView;\n\n if (!doc || !win) {\n return;\n }\n\n const scrollParent = iframe.closest<HTMLElement>('[data-frame-container]');\n\n let probeHeight: number;\n\n if (!scrollParent) {\n // No scroll container anywhere in the tree (Frame used directly,\n // without Dnd) — fall back to a fixed default; see\n // FALLBACK_PROBE_HEIGHT's own comment for why this differs from\n // the \"container exists but isn't laid out yet\" case below.\n probeHeight = FALLBACK_PROBE_HEIGHT;\n } else {\n // Starts at the iframe itself, whose own margin, border and padding\n // take space the same way a wrapper's do, and stops before the scroll\n // container, which only contributes its padding: `clientHeight`\n // already leaves out its border and margin (#440).\n const scrollParentStyle = win.getComputedStyle(scrollParent);\n let wrapperInsets =\n (parseFloat(scrollParentStyle.paddingTop) || 0) +\n (parseFloat(scrollParentStyle.paddingBottom) || 0);\n let node: HTMLElement | null = iframe;\n\n while (node && node !== scrollParent) {\n wrapperInsets += verticalInsets(win.getComputedStyle(node));\n node = node.parentElement;\n }\n\n const computed = computeProbeHeight(\n scrollParent.clientHeight,\n wrapperInsets,\n );\n\n if (computed === null) {\n // Layout not ready yet (mid-transition, just mounted, etc) —\n // skip this pass instead of guessing; the ResizeObserver/\n // MutationObserver below will call this again once something\n // actually changes, including the layout settling.\n return;\n }\n\n probeHeight = computed;\n }\n\n let contentHeight = 0;\n\n // First pass (`undefined`) and any pass that moves the probe height are\n // the only ones that change the container context, so they are the only\n // ones that need the preview's transitions out of the way.\n const freezeTransitions = probeHeight !== lastProbeHeightRef.current;\n\n lastProbeHeightRef.current = probeHeight;\n\n withMeasurementOverrides(doc, freezeTransitions, () => {\n ensureContainerStyle(doc, probeHeight);\n\n // Rewrite vh-family units the container context can't otherwise reach —\n // inline `style` attributes and in-preview `<style>` tags — so they too\n // resolve against the fixed probe height rather than the iframe's own\n // (see rewriteInlineViewportUnits). Must run after ensureContainerStyle\n // and before the reads below; it's idempotent, so the extra observer\n // pass its first-render rewrites trigger converges immediately.\n rewriteInlineViewportUnits(mountNode);\n\n // No 0px fold before measuring (that was the source of problem 1)\n // — with cq*-unit content now sized against the fixed probe height\n // instead of the iframe's own, a direct read is already stable.\n contentHeight = mountNode.scrollHeight;\n\n // Full subtree, not just direct children (a popup/overlay nested a\n // few components deep was previously invisible to this walk\n // entirely — problem 2's \"중첩된 오버레이는 아예 누락됩니다\").\n const descendants = mountNode.querySelectorAll<HTMLElement>('*');\n\n descendants.forEach(el => {\n const style = win.getComputedStyle(el);\n\n // visibility:hidden/opacity:0 elements (a closed bottom sheet,\n // a not-yet-faded-in overlay) keep a non-zero offsetHeight —\n // display:none doesn't need checking here since the browser\n // already zeroes *its* offsetHeight on its own.\n //\n // The animation lookup is confined to the one verdict it can\n // change — whether a fully transparent element is fading in\n // (measure it) or simply not shown (skip it) — so every other\n // element still costs nothing but the computed-style read.\n const isAnimating = style.opacity === '0' && hasActiveAnimation(el);\n\n if (isVisuallyHidden(style, isAnimating)) {\n return;\n }\n\n if (\n (style.position === 'fixed' || style.position === 'absolute') &&\n el.offsetHeight > 0\n ) {\n const estimatedHeight = estimatePositionedElementHeight(\n el.offsetHeight,\n style.transform,\n probeHeight,\n );\n\n contentHeight = Math.max(contentHeight, estimatedHeight);\n }\n });\n });\n\n if (contentHeight > 0) {\n iframe.style.height = `${Math.ceil(contentHeight)}px`;\n }\n\n // Outside the measurement window on purpose: the overrides above cancel\n // running transitions, and this read should see the document's real\n // animation set rather than one the act of measuring just altered.\n trackScriptAnimations(doc, win);\n }, [shouldAutoHeight, mountNode, trackScriptAnimations]);\n\n useEffect(() => {\n updateHeightRef.current = updateHeight;\n }, [updateHeight]);\n\n // updateHeight only ever adds/refreshes the container-context style —\n // if autoHeight is toggled off (or an explicit style.height is passed)\n // at runtime, nothing else would ever remove or update it again,\n // leaving cq*-unit content sized against a stale probe height instead\n // of correctly falling back to real viewport-relative sizing (which\n // cqh does on its own once nothing establishes a size container — see\n // ensureContainerStyle's own comment).\n useEffect(() => {\n if (shouldAutoHeight) {\n return;\n }\n\n iframeRef.current?.contentDocument\n ?.getElementById(CONTAINER_STYLE_ID)\n ?.remove();\n }, [shouldAutoHeight]);\n\n // A fixed-height iframe scrolls its own document. `syncStyle` copies the\n // host's stylesheets in, and an app shell often sets `body { overflow:\n // hidden }` to keep its own page from scrolling. Copied in, that rule left\n // the preview unable to scroll at all. An inline value wins over any copied\n // rule. With autoHeight the iframe is as tall as its content, so there's\n // nothing to scroll and the body is left as it was.\n useEffect(() => {\n const body = iframeRef.current?.contentDocument?.body;\n\n if (!body || !mountNode) {\n return;\n }\n\n if (shouldAutoHeight) {\n body.style.removeProperty('overflow-y');\n } else {\n body.style.setProperty('overflow-y', 'auto');\n }\n }, [shouldAutoHeight, mountNode]);\n\n // Keyed on mountNode (not just shouldAutoHeight) so it re-runs once the\n // iframe's document exists — before load there's no head to inject into.\n useEffect(() => {\n const doc = iframeRef.current?.contentDocument;\n\n if (!doc?.head) {\n return;\n }\n\n const existing = doc.getElementById(HIDE_SCROLLBAR_STYLE_ID);\n\n if (!shouldAutoHeight) {\n existing?.remove();\n return;\n }\n\n if (existing) {\n return;\n }\n\n const styleEl = doc.createElement('style');\n styleEl.id = HIDE_SCROLLBAR_STYLE_ID;\n styleEl.textContent = [\n 'html, body { scrollbar-width: none !important; }',\n 'html::-webkit-scrollbar, body::-webkit-scrollbar { display: none !important; }',\n ].join('\\n');\n doc.head.appendChild(styleEl);\n }, [shouldAutoHeight, mountNode]);\n\n useEffect(() => {\n updateHeight();\n }, [updateHeight]);\n\n const [resizeRef, resizeSize] = useResizeObserver<HTMLElement>();\n\n // useResizeObserver's ref callback isn't wired through this component's\n // own JSX (mountNode is the portal's imperatively-created container, not\n // something rendered here), so it's attached/detached imperatively\n // instead. Its reported size is intentionally unused - updateHeight's own\n // walk (every descendant, position:fixed/absolute ones capped and offset\n // by their transform) computes a more accurate height than mountNode's\n // own content-box size would, so a change in `resizeSize` is only used\n // as a trigger to recompute.\n useEffect(() => {\n if (!shouldAutoHeight || !mountNode) {\n return;\n }\n\n resizeRef(mountNode);\n return () => resizeRef(null);\n }, [shouldAutoHeight, mountNode, resizeRef]);\n\n useEffect(() => {\n updateHeight();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [resizeSize]);\n\n useMutationObserver(mountNode, updateHeight, {\n enabled: shouldAutoHeight,\n childList: true,\n subtree: true,\n attributes: true,\n characterData: true,\n });\n\n // An animation or transition finishing is neither a DOM mutation nor a\n // resize of the mount node, so neither observer above re-runs the\n // measurement — a fixed/absolute element read mid-fade would keep its\n // stale height on the iframe until something unrelated happened to the DOM\n // (#374). All four events bubble, so a single listener each on the mount\n // node covers every descendant.\n //\n // The `cancel` events count as much as the `end` ones: a cancelled\n // animation or transition snaps the element back to its un-animated style,\n // changing the height just the same.\n //\n // `transitionend`/`transitioncancel` used to be unnecessary, because every\n // measurement pass cancelled every transition outright. Now that a pass\n // only freezes transitions when it moves the probe height, a transition\n // can genuinely still be running when a pass reads it, so its completion\n // needs the same re-measure a keyframe animation's does.\n //\n // useEventListener resolves an element target through a ref, so mountNode\n // (portal-owned state, not something this component renders) is wrapped in\n // one that changes identity only when the node itself does. `enabled`\n // keeps it from polling for a node that isn't there yet.\n const mountNodeRef = useMemo(() => ({ current: mountNode }), [mountNode]);\n const settleListenerEnabled = shouldAutoHeight && mountNode !== null;\n\n useEventListener('animationend', updateHeight, {\n target: mountNodeRef,\n enabled: settleListenerEnabled,\n });\n\n useEventListener('animationcancel', updateHeight, {\n target: mountNodeRef,\n enabled: settleListenerEnabled,\n });\n\n useEventListener('transitionend', updateHeight, {\n target: mountNodeRef,\n enabled: settleListenerEnabled,\n });\n\n useEventListener('transitioncancel', updateHeight, {\n target: mountNodeRef,\n enabled: settleListenerEnabled,\n });\n\n const content = mountNode\n ? createPortal(children(mountNode), mountNode)\n : null;\n\n return (\n <iframe\n ref={iframeRef}\n style={{\n // An iframe is inline-level by default, so it sits on a text\n // baseline and leaves the descender gap (~4px) below it. With one\n // auto-height frame per canvas section, those gaps stacked into\n // height no content accounted for (#439).\n display: 'block',\n width: '100%',\n height: '100%',\n border: 'none',\n ...style,\n }}\n title={title}\n sandbox={sandbox}\n {...props}\n >\n {content}\n </iframe>\n );\n};\n\nexport default IFrame;\n","import {\n useCallback,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n} from 'react';\nimport type { ReactNode } from 'react';\nimport { createPortal } from 'react-dom';\n\nimport { useMutationObserver } from '@jbpark/use-hooks';\n\nimport { createStyleSyncManager, reconcileStyles } from './style-sync';\n\ninterface Props {\n // Clones the host document's <link rel=\"stylesheet\">/<style> tags into the\n // shadow root, mirroring `iframe.tsx`'s option of the same name. Unlike\n // `dynamicTailwind` (which recompiles only the classes it can find in the\n // rendered DOM, and only knows Tailwind's own default theme), this gets\n // the host's *actual* compiled CSS — including a consuming app's own\n // custom utilities/theme tokens — at the cost of only covering classes\n // that were already known at the host's own build time. The two are\n // complementary: this handles anything already in the host's stylesheets,\n // dynamicTailwind covers whatever's left (e.g. a class typed at runtime\n // that no build ever saw).\n syncStyle?: boolean;\n children: (hostContainer: HTMLElement | null) => ReactNode;\n}\n\nconst Shadow = ({ syncStyle = false, children }: Props) => {\n const hostRef = useRef<HTMLDivElement>(null);\n const shadowRootRef = useRef<ShadowRoot | null>(null);\n const renderTargetRef = useRef<HTMLDivElement | null>(null);\n const [renderTarget, setRenderTarget] = useState<HTMLDivElement | null>(null);\n const [hostContainer, setHostContainer] = useState<HTMLElement | null>(null);\n\n // Appended as siblings of the portal target (below), not inside it — that\n // subtree is React-owned via createPortal, and anything appended there\n // directly would get wiped on the next reconcile.\n const styleManagerRef = useRef(createStyleSyncManager());\n\n const applyStyle = useCallback(() => {\n const shadowRoot = shadowRootRef.current;\n\n if (!shadowRoot) {\n return;\n }\n\n reconcileStyles(document, shadowRoot, styleManagerRef.current, syncStyle);\n }, [syncStyle]);\n\n const applyStyleTimeoutRef = useRef<number>(undefined);\n\n // Debounced so a burst of head mutations (a stylesheet swap can fire\n // several in quick succession) only re-runs applyStyle once — same\n // rationale as iframe.tsx's identical setup.\n const debouncedApplyStyle = useCallback(() => {\n clearTimeout(applyStyleTimeoutRef.current);\n applyStyleTimeoutRef.current = window.setTimeout(applyStyle, 50);\n }, [applyStyle]);\n\n useEffect(() => {\n return () => clearTimeout(applyStyleTimeoutRef.current);\n }, []);\n\n useMutationObserver(document.head, debouncedApplyStyle, {\n enabled: syncStyle,\n childList: true,\n subtree: true,\n attributes: true,\n });\n\n useLayoutEffect(() => {\n if (!hostRef.current) {\n return;\n }\n\n const container = hostRef.current.closest(\n '[data-frame-container]',\n ) as HTMLElement | null;\n\n setHostContainer(container);\n\n let shadowRoot = shadowRootRef.current;\n\n if (!shadowRoot) {\n shadowRoot =\n hostRef.current.shadowRoot ||\n hostRef.current.attachShadow({ mode: 'open' });\n shadowRootRef.current = shadowRoot;\n }\n\n let target = renderTargetRef.current;\n\n if (!target) {\n target = document.createElement('div');\n shadowRoot.appendChild(target);\n renderTargetRef.current = target;\n setRenderTarget(target);\n }\n\n applyStyle();\n // click/pointerdown/pointerup are `composed: true` by spec, so they\n // already retarget across the shadow boundary and reach listeners\n // outside it (document, this host's ancestors, React's own root\n // listener) on their own - manually redispatching them here used to\n // make every one of those listeners see the interaction twice. Anyone\n // needing the real element inside the shadow tree (not the retargeted\n // host) can still read it via event.composedPath()[0], unaffected by\n // this removal. See #92.\n }, [applyStyle]);\n\n useLayoutEffect(() => {\n if (renderTargetRef.current && !renderTarget) {\n setRenderTarget(renderTargetRef.current);\n }\n }, [renderTarget]);\n\n return (\n <div ref={hostRef} style={{ display: 'contents' }}>\n {renderTarget && createPortal(children(hostContainer), renderTarget)}\n </div>\n );\n};\n\nexport default Shadow;\n","import { type ReactNode } from 'react';\n\nimport IFrame, { type Props as IframeProps } from './iframe';\nimport Shadow from './shadow';\n\nexport interface FrameProps extends Omit<IframeProps, 'children'> {\n mode?: 'iframe' | 'shadow';\n}\n\ninterface Props extends FrameProps {\n children: (container: HTMLElement) => ReactNode;\n}\n\nconst Frame = ({ mode, children, ...restProps }: Props) => {\n if (!mode) {\n return children(document.body);\n }\n\n if (mode === 'shadow') {\n return (\n <Shadow syncStyle={restProps.syncStyle}>\n {container => children(container || document.body)}\n </Shadow>\n );\n }\n\n return <IFrame {...restProps}>{children}</IFrame>;\n};\n\nexport default Frame;\n","import { useState } from 'react';\n\ntype Modules = Record<string, unknown> | undefined;\n\n// Same keys, each value the same object. A module is compared by identity,\n// never deeply: a module replaced with a changed object has to recompile\n// (#329). This is also how the compilation cache tells modules apart.\nconst sameModules = (a: Modules, b: Modules): boolean => {\n if (a === b) {\n return true;\n }\n\n if (!a || !b) {\n return false;\n }\n\n const keys = Object.keys(a);\n\n return (\n keys.length === Object.keys(b).length &&\n keys.every(key => Object.hasOwn(b, key) && Object.is(a[key], b[key]))\n );\n};\n\n// Hosts usually pass `modules` inline (`modules={{ Chart }}`), a new object\n// on every render. Compared by identity, that re-rendered and recompiled\n// every section on every edit, and past the compilation cache's limit sent\n// them back through Babel (#397). This keeps the previous object while the\n// entries are the same, so everything keyed on it sees no change.\nexport const useStableModules = <T extends Modules>(modules: T): T => {\n const [stable, setStable] = useState(modules);\n\n if (sameModules(stable, modules)) {\n return stable;\n }\n\n // Adjusted during render, so the changed modules are used in this render\n // rather than one render late.\n setStable(modules);\n\n return modules;\n};\n","import * as ui from '@jbpark/ui-kit';\nimport * as utils from '@jbpark/ui-kit/utils';\n\n// The specifiers every compiled sample can import without the host supplying\n// them (`import * as ui from 'ui-kit'`). Lives with the preview, its only\n// consumer, rather than in `~/utils`: evaluating ui-kit's namespace reaches\n// its stylesheet imports, so keeping it there made `@jbpark/live-editor/utils`\n// (and every entry built on it) unimportable outside a bundler — Node, SSR,\n// scripts. See #372.\nexport const baseModules = {\n 'ui-kit': ui,\n 'ui-kit/utils': utils,\n};\n","import { useMemo } from 'react';\n\nimport { useLiveMessages } from '~/components/context/messages';\nimport type { Module } from '~/types';\nimport { compile } from '~/utils/compile';\n\nimport { baseModules } from './base-modules';\nimport { useStableModules } from './use-stable-modules';\n\n// Shared by `preview/client.tsx` and `dnd/renderer.tsx`, which both turn a\n// code string into a renderable component in exactly the same way. They used\n// to implement this separately, which is how the two paths drifted apart on\n// error handling (see #246).\n//\n// Returns `null` for empty code so a caller can tell \"nothing to render\"\n// apart from \"compiled, but failed\" — the latter is a Module carrying\n// `error`, which callers should surface rather than render as blank.\nexport const useCompiledModule = (\n code: string,\n _modules?: Record<string, unknown>,\n): Module | null => {\n // An inline `modules` object from `Live.Preview`'s host is new each\n // render; only a changed entry should recompile (#397).\n const modules = useStableModules(_modules);\n const { unknown } = useLiveMessages().errors;\n\n const mergedModules = useMemo(\n () => ({ ...baseModules, ...modules }),\n [modules],\n );\n\n return useMemo(() => {\n if (!code) {\n return null;\n }\n\n try {\n return compile(code, mergedModules);\n } catch (e) {\n return {\n exports: {},\n error: e instanceof Error ? e.message : unknown,\n };\n }\n }, [code, mergedModules, unknown]);\n};\n","import { useCallback, useEffect, useState } from 'react';\n\n// Compiles the Tailwind classes found in rendered output and returns them as\n// a CSS string for the caller to inject as a `<style>` tag. Shared by\n// `preview/client.tsx` and `dnd/renderer.tsx`, which previously carried\n// byte-identical copies of this logic and of the comments below (see #246).\n//\n// Scans the live DOM (whatever the returned `ref` is attached to) rather than\n// the source text it was rendered from, so classes contributed by an imported\n// component (e.g. ui-kit's `Button`) are picked up too — those never appear as\n// literal text in the previewed source, only in that component's own compiled\n// output.\n//\n// The scanned element is tracked as state via a callback ref rather than a\n// plain `useRef`, because in shadow mode it isn't mounted on the caller's\n// first commit at all — `Shadow` creates its portal target in its own effect\n// and only re-renders with it afterwards, one commit later. A plain ref read\n// in a `[code, enabled]`-keyed effect would see `null` on that first pass and\n// never retry; making the element itself a dependency re-runs the scan once\n// it actually exists.\n//\n// The Tailwind compiler and its theme are imported on first use rather than\n// with this module: they only matter with `dynamicTailwind` on, and were\n// otherwise ~300 kB of every consumer's initial bundle (#332).\nexport const useDynamicTailwind = (code: string, enabled: boolean) => {\n const [css, setCss] = useState('');\n const [element, setElement] = useState<HTMLDivElement | null>(null);\n\n const ref = useCallback((node: HTMLDivElement | null) => {\n setElement(node);\n }, []);\n\n useEffect(() => {\n if (!code || !enabled || !element) {\n return;\n }\n\n let cancelled = false;\n\n import('~/utils/tailwind')\n .then(({ generateTailwindCSSFromDOM }) =>\n generateTailwindCSSFromDOM(element),\n )\n .then(next => {\n if (!cancelled) {\n setCss(next);\n }\n });\n\n return () => {\n cancelled = true;\n };\n }, [code, enabled, element]);\n\n return { ref, css };\n};\n"],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { _ as BindingWidget, a as validateBindingValue, c as BindingItem, d as BindingOptions, f as BindingRegistry, g as BindingType, h as BindingSchema, i as defaultValidationMessages, l as BindingKeyMap, m as BindingRenderMap, n as ValidationOptions, o as BindingComponentName, p as BindingRenderLeaf, r as ValidationResult, s as BindingFieldSpec, t as ValidationMessages, u as BindingOption, v as DataAttrNode } from "../../validate-DB59Z_Nx.js";
|
|
2
2
|
import { a as SectionOptions, c as createSectionPreviewCache, d as generateSectionPreview, f as generateSectionPreviews, g as replaceDocumentSections, h as parseDocument, i as DocumentTree, l as fillSectionIds, m as inspectDocument, n as DocumentOptions, o as SectionPreviewCache, p as getSections, r as DocumentProblem, s as clearDocumentParseCache, t as DocumentInspection, u as generateDocumentCode } from "../../document-Ct4jW78J.js";
|
|
3
|
-
|
|
3
|
+
import { A as removeArrayItems, B as UpdateFailure, C as moveArrayItem, D as parseBindingExpression, E as parseBinding, F as ChildrenAction, G as EditablePathSegment, H as bulkUpdate, I as ChildrenEdit, J as flattenEditableValue, K as EditablePrimitive, L as getChildrenSignatures, M as resolveBindings, N as updateArrayItemProperty, O as parseItems, P as updateArrayItemValue, Q as extract, R as UpdateAllResult, S as isComponentTagName, T as parseArrayExpression, U as update, V as UpdateResult, W as updateAll, X as setEditableValue, Y as parseValue, Z as findEditableChildren, _ as generateCode, a as NodeValueType, b as getRegistryBindings, c as canLosslesslyEvaluateSource, d as clone, f as createNodeFromValue, g as fillIds, h as extractObjectProperties, i as ItemKind, j as replaceIds, k as readNodeBindings, l as canStructurallyEditArray, m as extractNodeValue, n as EditableNodeValueType, o as appendArrayItem, p as duplicateArrayItems, q as EditableValueEntry, r as ExtractedNodeValue, s as arrayExpressionToCode, t as ArrayItem, u as clearExtractCache, v as getCurrentValue, w as moveArrayItems, x as getStructuredValue, y as getKeyBindings, z as UpdateEntry } from "../../index-CHknA6k1.js";
|
|
4
|
+
export { ArrayItem, type BindingComponentName, type BindingFieldSpec, type BindingItem, type BindingKeyMap, type BindingOption, type BindingOptions, type BindingRegistry, type BindingRenderLeaf, type BindingRenderMap, type BindingSchema, type BindingType, type BindingWidget, type ChildrenAction, type ChildrenEdit, type DataAttrNode, type DocumentInspection, type DocumentOptions, type DocumentProblem, type DocumentTree, EditableNodeValueType, type EditablePathSegment, type EditablePrimitive, type EditableValueEntry, ExtractedNodeValue, ItemKind, NodeValueType, type SectionOptions, type SectionPreviewCache, type UpdateAllResult, type UpdateEntry, type UpdateFailure, type UpdateResult, type ValidationMessages, type ValidationOptions, type ValidationResult, appendArrayItem, arrayExpressionToCode, bulkUpdate, canLosslesslyEvaluateSource, canStructurallyEditArray, clearDocumentParseCache, clearExtractCache, clone, createNodeFromValue, createSectionPreviewCache, defaultValidationMessages, duplicateArrayItems, extract, extractNodeValue, extractObjectProperties, fillIds, fillSectionIds, findEditableChildren, flattenEditableValue, generateCode, generateDocumentCode, generateSectionPreview, generateSectionPreviews, getChildrenSignatures, getCurrentValue, getKeyBindings, getRegistryBindings, getSections, getStructuredValue, inspectDocument, isComponentTagName, moveArrayItem, moveArrayItems, parseArrayExpression, parseBinding, parseBindingExpression, parseDocument, parseItems, parseValue, readNodeBindings, removeArrayItems, replaceDocumentSections, replaceIds, resolveBindings, setEditableValue, update, updateAll, updateArrayItemProperty, updateArrayItemValue, validateBindingValue };
|
package/dist/utils/ast/index.js
CHANGED
|
@@ -1,3 +1,66 @@
|
|
|
1
|
-
import { a as generateSectionPreview, c as inspectDocument, f as clone, h as replaceIds, i as generateDocumentCode, l as parseDocument, n as createSectionPreviewCache, o as generateSectionPreviews, p as fillIds, r as fillSectionIds, s as getSections, t as clearDocumentParseCache, u as replaceDocumentSections
|
|
2
|
-
import {
|
|
3
|
-
|
|
1
|
+
import { _ as generateCode$1, a as generateSectionPreview, c as inspectDocument, f as clone$1, h as replaceIds$1, i as generateDocumentCode, l as parseDocument, n as createSectionPreviewCache, o as generateSectionPreviews, p as fillIds$1, r as fillSectionIds, s as getSections, t as clearDocumentParseCache, u as replaceDocumentSections } from "../../document-DGHTfrda.js";
|
|
2
|
+
import { n as validateBindingValue, t as defaultValidationMessages } from "../../validate-iLo8IONY.js";
|
|
3
|
+
import { A as extractNodeValue$1, C as parseBinding$1, D as arrayExpressionToCode$1, E as resolveBindings$1, F as setEditableValue, M as flattenEditableValue, N as parseArrayExpression$1, O as canLosslesslyEvaluateSource$1, P as parseValue, S as isComponentTagName$1, T as readNodeBindings$1, _ as findEditableChildren, a as parseItems$1, b as getRegistryBindings$1, c as updateArrayItemValue$1, d as update, f as updateAll, g as extract, h as clearExtractCache$1, i as moveArrayItems$1, j as extractObjectProperties$1, k as createNodeFromValue$1, l as canStructurallyEditArray$1, n as duplicateArrayItems$1, o as removeArrayItems$1, p as getChildrenSignatures, r as moveArrayItem$1, s as updateArrayItemProperty$1, t as appendArrayItem$1, u as bulkUpdate, v as getCurrentValue$1, w as parseBindingExpression$1, x as getStructuredValue$1, y as getKeyBindings$1 } from "../../items-DdrwGxJw.js";
|
|
4
|
+
//#region src/utils/ast/index.ts
|
|
5
|
+
/** @deprecated Internal. Use `useDndItems` from `./dnd`. Removed in the next major. */
|
|
6
|
+
const appendArrayItem = appendArrayItem$1;
|
|
7
|
+
/** @deprecated Internal. Use `useDndItems` from `./dnd`. Removed in the next major. */
|
|
8
|
+
const duplicateArrayItems = duplicateArrayItems$1;
|
|
9
|
+
/** @deprecated Internal. Use `useDndItems` from `./dnd`. Removed in the next major. */
|
|
10
|
+
const moveArrayItem = moveArrayItem$1;
|
|
11
|
+
/** @deprecated Internal. Use `useDndItems` from `./dnd`. Removed in the next major. */
|
|
12
|
+
const moveArrayItems = moveArrayItems$1;
|
|
13
|
+
/** @deprecated Internal. Use `useDndItems` from `./dnd`. Removed in the next major. */
|
|
14
|
+
const parseItems = parseItems$1;
|
|
15
|
+
/** @deprecated Internal. Use `useDndItems` from `./dnd`. Removed in the next major. */
|
|
16
|
+
const removeArrayItems = removeArrayItems$1;
|
|
17
|
+
/** @deprecated Internal. Use `useDndItems` from `./dnd`. Removed in the next major. */
|
|
18
|
+
const updateArrayItemProperty = updateArrayItemProperty$1;
|
|
19
|
+
/** @deprecated Internal. Use `useDndItems` from `./dnd`. Removed in the next major. */
|
|
20
|
+
const updateArrayItemValue = updateArrayItemValue$1;
|
|
21
|
+
/** @deprecated Internal. Use `useDndItems` from `./dnd`. Removed in the next major. */
|
|
22
|
+
const parseArrayExpression = parseArrayExpression$1;
|
|
23
|
+
/** @deprecated Internal. Use `useDndItems` from `./dnd`. Removed in the next major. */
|
|
24
|
+
const arrayExpressionToCode = arrayExpressionToCode$1;
|
|
25
|
+
/** @deprecated Internal. Use `useDndItems` from `./dnd`. Removed in the next major. */
|
|
26
|
+
const canStructurallyEditArray = canStructurallyEditArray$1;
|
|
27
|
+
/** @deprecated Internal. Read values through `extract` or `PanelBinding.value`. Removed in the next major. */
|
|
28
|
+
const extractNodeValue = extractNodeValue$1;
|
|
29
|
+
/** @deprecated Internal. Read values through `extract` or `PanelBinding.value`. Removed in the next major. */
|
|
30
|
+
const extractObjectProperties = extractObjectProperties$1;
|
|
31
|
+
/** @deprecated Internal. Write values through `update` or `PanelBinding.onChange`. Removed in the next major. */
|
|
32
|
+
const createNodeFromValue = createNodeFromValue$1;
|
|
33
|
+
/** @deprecated Internal. Check `PanelBinding.canEditValue` instead. Removed in the next major. */
|
|
34
|
+
const canLosslesslyEvaluateSource = canLosslesslyEvaluateSource$1;
|
|
35
|
+
/** @deprecated Internal. Read values through `extract` or `PanelBinding.value`. Removed in the next major. */
|
|
36
|
+
const getCurrentValue = getCurrentValue$1;
|
|
37
|
+
/** @deprecated Internal. Read values through `extract` or `PanelBinding.value`. Removed in the next major. */
|
|
38
|
+
const getStructuredValue = getStructuredValue$1;
|
|
39
|
+
/** @deprecated Internal. Read bindings through `extract` or `useDndPanel().bindings`. Removed in the next major. */
|
|
40
|
+
const parseBinding = parseBinding$1;
|
|
41
|
+
/** @deprecated Internal. Read bindings through `extract` or `useDndPanel().bindings`. Removed in the next major. */
|
|
42
|
+
const parseBindingExpression = parseBindingExpression$1;
|
|
43
|
+
/** @deprecated Internal. Read bindings through `extract` or `useDndPanel().bindings`. Removed in the next major. */
|
|
44
|
+
const readNodeBindings = readNodeBindings$1;
|
|
45
|
+
/** @deprecated Internal. Read bindings through `extract` or `useDndPanel().bindings`. Removed in the next major. */
|
|
46
|
+
const resolveBindings = resolveBindings$1;
|
|
47
|
+
/** @deprecated Internal. Read bindings through `extract` or `useDndPanel().bindings`. Removed in the next major. */
|
|
48
|
+
const getKeyBindings = getKeyBindings$1;
|
|
49
|
+
/** @deprecated Internal. Read bindings through `extract` or `useDndPanel().bindings`. Removed in the next major. */
|
|
50
|
+
const getRegistryBindings = getRegistryBindings$1;
|
|
51
|
+
/** @deprecated Internal. Removed in the next major. */
|
|
52
|
+
const isComponentTagName = isComponentTagName$1;
|
|
53
|
+
/** @deprecated Internal. Removed in the next major. */
|
|
54
|
+
const fillIds = fillIds$1;
|
|
55
|
+
/** @deprecated Internal. Removed in the next major. */
|
|
56
|
+
const replaceIds = replaceIds$1;
|
|
57
|
+
/** @deprecated Internal. Removed in the next major. */
|
|
58
|
+
const clone = clone$1;
|
|
59
|
+
/** @deprecated Internal. Removed in the next major. */
|
|
60
|
+
const generateCode = generateCode$1;
|
|
61
|
+
/** @deprecated Internal. Use `clearEditorCaches` from `./utils`. Removed in the next major. */
|
|
62
|
+
const clearExtractCache = clearExtractCache$1;
|
|
63
|
+
//#endregion
|
|
64
|
+
export { appendArrayItem, arrayExpressionToCode, bulkUpdate, canLosslesslyEvaluateSource, canStructurallyEditArray, clearDocumentParseCache, clearExtractCache, clone, createNodeFromValue, createSectionPreviewCache, defaultValidationMessages, duplicateArrayItems, extract, extractNodeValue, extractObjectProperties, fillIds, fillSectionIds, findEditableChildren, flattenEditableValue, generateCode, generateDocumentCode, generateSectionPreview, generateSectionPreviews, getChildrenSignatures, getCurrentValue, getKeyBindings, getRegistryBindings, getSections, getStructuredValue, inspectDocument, isComponentTagName, moveArrayItem, moveArrayItems, parseArrayExpression, parseBinding, parseBindingExpression, parseDocument, parseItems, parseValue, readNodeBindings, removeArrayItems, replaceDocumentSections, replaceIds, resolveBindings, setEditableValue, update, updateAll, updateArrayItemProperty, updateArrayItemValue, validateBindingValue };
|
|
65
|
+
|
|
66
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["_appendArrayItem","_duplicateArrayItems","_moveArrayItem","_moveArrayItems","_parseItems","_removeArrayItems","_updateArrayItemProperty","_updateArrayItemValue","_parseArrayExpression","_arrayExpressionToCode","_canStructurallyEditArray","_extractNodeValue","_extractObjectProperties","_createNodeFromValue","_canLosslesslyEvaluateSource","_getCurrentValue","_getStructuredValue","_parseBinding","_parseBindingExpression","_readNodeBindings","_resolveBindings","_getKeyBindings","_getRegistryBindings","_isComponentTagName","_fillIds","_replaceIds","_clone","_generateCode","_clearExtractCache"],"sources":["../../../src/utils/ast/index.ts"],"sourcesContent":["// The public `./utils/ast` entry: reading a section's bindings (`extract`),\n// committing edits (`update`, `updateAll`), validating and flattening\n// values, and the types those take and return. Library code imports from\n// the module files, not from here, so the deprecated names below can leave\n// this barrel without touching their callers (#522).\nimport { canStructurallyEditArray as _canStructurallyEditArray } from './array-source';\nimport { getCurrentValue as _getCurrentValue } from './binding';\nimport { getStructuredValue as _getStructuredValue } from './binding';\nimport { parseBinding as _parseBinding } from './binding';\nimport { parseBindingExpression as _parseBindingExpression } from './binding';\nimport { readNodeBindings as _readNodeBindings } from './binding';\nimport { resolveBindings as _resolveBindings } from './binding';\nimport { getKeyBindings as _getKeyBindings } from './binding';\nimport { getRegistryBindings as _getRegistryBindings } from './binding';\nimport { isComponentTagName as _isComponentTagName } from './binding';\nimport { clearExtractCache as _clearExtractCache } from './extract';\nimport { generateCode as _generateCode } from './helpers';\nimport { appendArrayItem as _appendArrayItem } from './items';\nimport { duplicateArrayItems as _duplicateArrayItems } from './items';\nimport { moveArrayItem as _moveArrayItem } from './items';\nimport { moveArrayItems as _moveArrayItems } from './items';\nimport { parseItems as _parseItems } from './items';\nimport { removeArrayItems as _removeArrayItems } from './items';\nimport { updateArrayItemProperty as _updateArrayItemProperty } from './items';\nimport { updateArrayItemValue as _updateArrayItemValue } from './items';\nimport type { ArrayItem as _ArrayItem } from './items';\nimport type { ItemKind as _ItemKind } from './items';\nimport { fillIds as _fillIds } from './tree';\nimport { replaceIds as _replaceIds } from './tree';\nimport { clone as _clone } from './tree';\nimport type { ExtractedNodeValue as _ExtractedNodeValue } from './types';\nimport type { NodeValueType as _NodeValueType } from './types';\nimport type { EditableNodeValueType as _EditableNodeValueType } from './types';\nimport { parseArrayExpression as _parseArrayExpression } from './value';\nimport { arrayExpressionToCode as _arrayExpressionToCode } from './value';\nimport { extractNodeValue as _extractNodeValue } from './value';\nimport { extractObjectProperties as _extractObjectProperties } from './value';\nimport { createNodeFromValue as _createNodeFromValue } from './value';\nimport { canLosslesslyEvaluateSource as _canLosslesslyEvaluateSource } from './value';\n\nexport type {\n BindingFieldSpec,\n BindingItem,\n BindingOption,\n BindingRenderLeaf,\n BindingComponentName,\n BindingKeyMap,\n BindingOptions,\n BindingRegistry,\n BindingRenderMap,\n BindingSchema,\n BindingType,\n BindingWidget,\n DataAttrNode,\n} from './types';\nexport { findEditableChildren } from './binding';\nexport type {\n EditablePathSegment,\n EditablePrimitive,\n EditableValueEntry,\n} from './value';\nexport { flattenEditableValue, parseValue, setEditableValue } from './value';\nexport { extract } from './extract';\nexport type {\n DocumentOptions,\n DocumentProblem,\n SectionOptions,\n} from './document';\nexport { bulkUpdate, update, updateAll } from './update';\nexport type {\n UpdateAllResult,\n UpdateEntry,\n UpdateFailure,\n UpdateResult,\n} from './update';\nexport { getChildrenSignatures } from './children';\nexport type { ChildrenAction, ChildrenEdit } from './children';\nexport type {\n ValidationMessages,\n ValidationOptions,\n ValidationResult,\n} from './validate';\nexport { defaultValidationMessages, validateBindingValue } from './validate';\n\n// Internal helpers this entry used to export. Each still works, and leaves\n// the entry in the next major (#522). Most are `const` aliases, so their\n// `@deprecated` reaches the built declarations. The document layer stays a\n// plain re-export: as an alias here, its Babel AST types move this entry's\n// declarations into the chunk that also pulls in React, and\n// `check-package-types.mjs` fails. A re-export's JSDoc doesn't survive the\n// declaration bundle, so those are announced in the docs and changeset only.\n\n// Array-literal editing: the engine behind `useDndItems`.\n/** @deprecated Internal. Use `useDndItems` from `./dnd`. Removed in the next major. */\nexport const appendArrayItem = _appendArrayItem;\n/** @deprecated Internal. Use `useDndItems` from `./dnd`. Removed in the next major. */\nexport const duplicateArrayItems = _duplicateArrayItems;\n/** @deprecated Internal. Use `useDndItems` from `./dnd`. Removed in the next major. */\nexport const moveArrayItem = _moveArrayItem;\n/** @deprecated Internal. Use `useDndItems` from `./dnd`. Removed in the next major. */\nexport const moveArrayItems = _moveArrayItems;\n/** @deprecated Internal. Use `useDndItems` from `./dnd`. Removed in the next major. */\nexport const parseItems = _parseItems;\n/** @deprecated Internal. Use `useDndItems` from `./dnd`. Removed in the next major. */\nexport const removeArrayItems = _removeArrayItems;\n/** @deprecated Internal. Use `useDndItems` from `./dnd`. Removed in the next major. */\nexport const updateArrayItemProperty = _updateArrayItemProperty;\n/** @deprecated Internal. Use `useDndItems` from `./dnd`. Removed in the next major. */\nexport const updateArrayItemValue = _updateArrayItemValue;\n/** @deprecated Internal. Use `useDndItems` from `./dnd`. Removed in the next major. */\nexport const parseArrayExpression = _parseArrayExpression;\n/** @deprecated Internal. Use `useDndItems` from `./dnd`. Removed in the next major. */\nexport const arrayExpressionToCode = _arrayExpressionToCode;\n/** @deprecated Internal. Use `useDndItems` from `./dnd`. Removed in the next major. */\nexport const canStructurallyEditArray = _canStructurallyEditArray;\n/** @deprecated Internal. Use `useDndItems` from `./dnd`. Removed in the next major. */\nexport type ArrayItem = _ArrayItem;\n/** @deprecated Internal. Use `useDndItems` from `./dnd`. Removed in the next major. */\nexport type ItemKind = _ItemKind;\n\n// Value plumbing behind `extract`, `update` and `PanelBinding.value`.\n/** @deprecated Internal. Read values through `extract` or `PanelBinding.value`. Removed in the next major. */\nexport const extractNodeValue = _extractNodeValue;\n/** @deprecated Internal. Read values through `extract` or `PanelBinding.value`. Removed in the next major. */\nexport const extractObjectProperties = _extractObjectProperties;\n/** @deprecated Internal. Write values through `update` or `PanelBinding.onChange`. Removed in the next major. */\nexport const createNodeFromValue = _createNodeFromValue;\n/** @deprecated Internal. Check `PanelBinding.canEditValue` instead. Removed in the next major. */\nexport const canLosslesslyEvaluateSource = _canLosslesslyEvaluateSource;\n/** @deprecated Internal. Read values through `extract` or `PanelBinding.value`. Removed in the next major. */\nexport const getCurrentValue = _getCurrentValue;\n/** @deprecated Internal. Read values through `extract` or `PanelBinding.value`. Removed in the next major. */\nexport const getStructuredValue = _getStructuredValue;\n/** @deprecated Internal. Removed in the next major. */\nexport type ExtractedNodeValue = _ExtractedNodeValue;\n/** @deprecated Internal. Removed in the next major. */\nexport type NodeValueType = _NodeValueType;\n/** @deprecated Internal. Removed in the next major. */\nexport type EditableNodeValueType = _EditableNodeValueType;\n\n// Binding resolution behind `extract(source, { bindings, bindingKeys })`.\n/** @deprecated Internal. Read bindings through `extract` or `useDndPanel().bindings`. Removed in the next major. */\nexport const parseBinding = _parseBinding;\n/** @deprecated Internal. Read bindings through `extract` or `useDndPanel().bindings`. Removed in the next major. */\nexport const parseBindingExpression = _parseBindingExpression;\n/** @deprecated Internal. Read bindings through `extract` or `useDndPanel().bindings`. Removed in the next major. */\nexport const readNodeBindings = _readNodeBindings;\n/** @deprecated Internal. Read bindings through `extract` or `useDndPanel().bindings`. Removed in the next major. */\nexport const resolveBindings = _resolveBindings;\n/** @deprecated Internal. Read bindings through `extract` or `useDndPanel().bindings`. Removed in the next major. */\nexport const getKeyBindings = _getKeyBindings;\n/** @deprecated Internal. Read bindings through `extract` or `useDndPanel().bindings`. Removed in the next major. */\nexport const getRegistryBindings = _getRegistryBindings;\n/** @deprecated Internal. Removed in the next major. */\nexport const isComponentTagName = _isComponentTagName;\n\n// The document layer under `./utils` and `Live.Dnd`.\nexport {\n /** @deprecated Internal. Use `extractSections` from `./utils`. Removed in the next major. */\n parseDocument,\n /** @deprecated Internal. Use `extractSections` from `./utils`. Removed in the next major. */\n getSections,\n /** @deprecated Internal. Use `replaceSections` from `./utils`. Removed in the next major. */\n replaceDocumentSections,\n /** @deprecated Internal. Use `checkDocument` from `./utils`. Removed in the next major. */\n inspectDocument,\n /** @deprecated Internal. Removed in the next major. */\n generateDocumentCode,\n /** @deprecated Internal. Removed in the next major. */\n fillSectionIds,\n /** @deprecated Internal. Removed in the next major. */\n generateSectionPreview,\n /** @deprecated Internal. Removed in the next major. */\n generateSectionPreviews,\n /** @deprecated Internal. Removed in the next major. */\n createSectionPreviewCache,\n} from './document';\nexport type {\n /** @deprecated Internal. Use `checkDocument` from `./utils`. Removed in the next major. */\n DocumentInspection,\n /** @deprecated Internal. Removed in the next major. */\n DocumentTree,\n /** @deprecated Internal. Removed in the next major. */\n SectionPreviewCache,\n} from './document';\n\n// Ids, code generation and caches.\n/** @deprecated Internal. Removed in the next major. */\nexport const fillIds = _fillIds;\n/** @deprecated Internal. Removed in the next major. */\nexport const replaceIds = _replaceIds;\n/** @deprecated Internal. Removed in the next major. */\nexport const clone = _clone;\n/** @deprecated Internal. Removed in the next major. */\nexport const generateCode = _generateCode;\n/** @deprecated Internal. Use `clearEditorCaches` from `./utils`. Removed in the next major. */\nexport const clearExtractCache = _clearExtractCache;\nexport {\n /** @deprecated Internal. Use `clearEditorCaches` from `./utils`. Removed in the next major. */\n clearDocumentParseCache,\n} from './document';\n"],"mappings":";;;;;AA8FA,MAAa,kBAAkBA;;AAE/B,MAAa,sBAAsBC;;AAEnC,MAAa,gBAAgBC;;AAE7B,MAAa,iBAAiBC;;AAE9B,MAAa,aAAaC;;AAE1B,MAAa,mBAAmBC;;AAEhC,MAAa,0BAA0BC;;AAEvC,MAAa,uBAAuBC;;AAEpC,MAAa,uBAAuBC;;AAEpC,MAAa,wBAAwBC;;AAErC,MAAa,2BAA2BC;;AAQxC,MAAa,mBAAmBC;;AAEhC,MAAa,0BAA0BC;;AAEvC,MAAa,sBAAsBC;;AAEnC,MAAa,8BAA8BC;;AAE3C,MAAa,kBAAkBC;;AAE/B,MAAa,qBAAqBC;;AAUlC,MAAa,eAAeC;;AAE5B,MAAa,yBAAyBC;;AAEtC,MAAa,mBAAmBC;;AAEhC,MAAa,kBAAkBC;;AAE/B,MAAa,iBAAiBC;;AAE9B,MAAa,sBAAsBC;;AAEnC,MAAa,qBAAqBC;;AAkClC,MAAa,UAAUC;;AAEvB,MAAa,aAAaC;;AAE1B,MAAa,QAAQC;;AAErB,MAAa,eAAeC;;AAE5B,MAAa,oBAAoBC"}
|