@jbpark/live-editor 4.2.0 → 4.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.ko.md +2 -7
  2. package/README.md +2 -7
  3. package/dist/{ast-CMD20ykx.js → ast-CehbCXwh.js} +198 -208
  4. package/dist/ast-CehbCXwh.js.map +1 -0
  5. package/dist/{boundary-D2IFja-3.js → boundary-B17cepvm.js} +12 -9
  6. package/dist/boundary-B17cepvm.js.map +1 -0
  7. package/dist/dnd/index.d.ts +2 -2
  8. package/dist/dnd/index.js +2 -2
  9. package/dist/{dnd-DBaH2L8N.js → dnd-x8Mvj--6.js} +462 -222
  10. package/dist/dnd-x8Mvj--6.js.map +1 -0
  11. package/dist/{document-C3eCRQlj.js → document-C-hGKMkk.js} +119 -59
  12. package/dist/document-C-hGKMkk.js.map +1 -0
  13. package/dist/error/index.d.ts +1 -1
  14. package/dist/error/index.js +2 -2
  15. package/dist/{error-KW8XCcjH.js → error-DWhxQEom.js} +2 -2
  16. package/dist/{error-KW8XCcjH.js.map → error-DWhxQEom.js.map} +1 -1
  17. package/dist/{index-DNNj_0ba.d.ts → index-C4SGQ3Ci.d.ts} +4 -3
  18. package/dist/{index-DVgwXnPa.d.ts → index-CtlUYnWq.d.ts} +25 -3
  19. package/dist/index.d.ts +4 -4
  20. package/dist/index.js +3 -3
  21. package/dist/index.js.map +1 -1
  22. package/dist/preview/index.js +2 -2
  23. package/dist/{preview-B9Jhv1Up.js → preview-BYck9mVK.js} +3 -3
  24. package/dist/{preview-B9Jhv1Up.js.map → preview-BYck9mVK.js.map} +1 -1
  25. package/dist/{sections-A4TAXUP8.js → sections-Ce63wbtG.js} +2 -2
  26. package/dist/{sections-A4TAXUP8.js.map → sections-Ce63wbtG.js.map} +1 -1
  27. package/dist/{use-dynamic-tailwind-BDfytfYC.js → use-dynamic-tailwind-QKcGvUJs.js} +35 -5
  28. package/dist/use-dynamic-tailwind-QKcGvUJs.js.map +1 -0
  29. package/dist/utils/ast/index.js +2 -2
  30. package/dist/utils/index.js +2 -2
  31. package/package.json +6 -6
  32. package/dist/ast-CMD20ykx.js.map +0 -1
  33. package/dist/boundary-D2IFja-3.js.map +0 -1
  34. package/dist/dnd-DBaH2L8N.js.map +0 -1
  35. package/dist/document-C3eCRQlj.js.map +0 -1
  36. package/dist/use-dynamic-tailwind-BDfytfYC.js.map +0 -1
@@ -1,2 +1,2 @@
1
- import { i as Guard, o as Props, r as Runtime, s as ErrorBoundary, t as Error } from "../index-DNNj_0ba.js";
1
+ import { i as Guard, o as Props, r as Runtime, s as ErrorBoundary, t as Error } from "../index-C4SGQ3Ci.js";
2
2
  export { ErrorBoundary, Guard as ErrorGuard, Runtime as ErrorRuntime, type Props, Error as default };
@@ -1,3 +1,3 @@
1
- import { t as ErrorBoundary } from "../boundary-D2IFja-3.js";
2
- import { n as Runtime, r as Guard, t as Error } from "../error-KW8XCcjH.js";
1
+ import { t as ErrorBoundary } from "../boundary-B17cepvm.js";
2
+ import { n as Runtime, r as Guard, t as Error } from "../error-DWhxQEom.js";
3
3
  export { ErrorBoundary, Guard as ErrorGuard, Runtime as ErrorRuntime, Error as default };
@@ -1,5 +1,5 @@
1
1
  import { r as useError } from "./states-Ci1AvoQ9.js";
2
- import { n as Error$1, t as ErrorBoundary } from "./boundary-D2IFja-3.js";
2
+ import { n as Error$1, t as ErrorBoundary } from "./boundary-B17cepvm.js";
3
3
  import { useEffect, useRef, useState } from "react";
4
4
  import { jsx } from "react/jsx-runtime";
5
5
  import { useEventListener } from "@jbpark/use-hooks";
@@ -79,4 +79,4 @@ Error.Guard = Guard;
79
79
  //#endregion
80
80
  export { Runtime as n, Guard as r, Error as t };
81
81
 
82
- //# sourceMappingURL=error-KW8XCcjH.js.map
82
+ //# sourceMappingURL=error-DWhxQEom.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"error-KW8XCcjH.js","names":["ErrorComponent","Error","ErrorImpl","ErrorRuntime","ErrorGuard"],"sources":["../src/components/error/guard.tsx","../src/components/error/runtime.tsx","../src/components/error/index.ts"],"sourcesContent":["import { useEffect, useRef, useState } from 'react';\n\nimport { useEventListener } from '@jbpark/use-hooks';\n\nimport ErrorComponent from './error';\n\nexport interface Props {\n children: React.ReactNode;\n onError?: (error: Error) => void;\n}\n\nconst Guard = ({ children, onError }: Props) => {\n const [error, setError] = useState<string | null>(null);\n\n const containerRef = useRef<HTMLDivElement>(null);\n const errorHandled = useRef(false);\n\n useEventListener(\n 'error',\n event => {\n if (errorHandled.current) {\n return;\n }\n\n errorHandled.current = true;\n\n const errorMessage =\n event.error?.message || event.message || 'Unknown error';\n\n console.error('⚡ [Guard] Event handler error:', errorMessage);\n\n event.preventDefault();\n\n setError(errorMessage);\n onError?.(event.error || new window.Error(errorMessage));\n\n setTimeout(() => {\n errorHandled.current = false;\n }, 100);\n },\n { capture: true },\n );\n\n useEffect(() => {\n const container = containerRef.current;\n\n if (!container) {\n return;\n }\n\n const handleUnhandledRejection = (event: PromiseRejectionEvent) => {\n if (errorHandled.current) {\n return;\n }\n\n errorHandled.current = true;\n\n const errorMessage = event.reason?.message || 'Promise rejection';\n\n event.preventDefault();\n setError(errorMessage);\n onError?.(event.reason || new window.Error(errorMessage));\n\n setTimeout(() => {\n errorHandled.current = false;\n }, 100);\n };\n\n window.addEventListener(\n 'unhandledrejection',\n handleUnhandledRejection,\n true,\n );\n\n return () => {\n window.removeEventListener(\n 'unhandledrejection',\n handleUnhandledRejection,\n true,\n );\n };\n }, [onError]);\n\n if (error) {\n return (\n <ErrorComponent\n title=\"Runtime Error\"\n message={error}\n className=\"m-4\"\n onReset={() => {\n setError(null);\n errorHandled.current = false;\n }}\n />\n );\n }\n\n return <div ref={containerRef}>{children}</div>;\n};\n\nexport default Guard;\n","import { useEffect } from 'react';\n\nimport { useEventListener } from '@jbpark/use-hooks';\n\nimport { useError } from '~/components/context/states';\n\nimport Error from './error';\n\nexport interface Props extends React.ComponentPropsWithRef<'div'> {\n open?: boolean;\n reset?: () => void;\n}\n\nconst Runtime = ({ open = true, reset }: Props) => {\n const { error: message, setError } = useError();\n\n useEventListener('error', e => {\n setError(e.message);\n e.preventDefault();\n });\n\n useEffect(() => {\n return () => setError(null);\n }, [setError]);\n\n if (!open) {\n return null;\n }\n\n return <Error message={message} onReset={reset} title=\"Runtime Error\" />;\n};\n\nexport default Runtime;\n","import ErrorBoundary from './boundary';\nimport ErrorImpl, { type Props } from './error';\nimport ErrorGuard from './guard';\nimport ErrorRuntime from './runtime';\n\ntype ErrorComponent = typeof ErrorImpl & {\n Boundary: typeof ErrorBoundary;\n Runtime: typeof ErrorRuntime;\n Guard: typeof ErrorGuard;\n};\n\nconst Error = ErrorImpl as ErrorComponent;\n\nError.Boundary = ErrorBoundary;\nError.Runtime = ErrorRuntime;\nError.Guard = ErrorGuard;\n\nexport { ErrorBoundary, ErrorRuntime, ErrorGuard };\nexport type { Props };\nexport default Error;\n"],"mappings":";;;;;;AAWA,MAAM,SAAS,EAAE,UAAU,cAAqB;CAC9C,MAAM,CAAC,OAAO,YAAY,SAAwB,IAAI;CAEtD,MAAM,eAAe,OAAuB,IAAI;CAChD,MAAM,eAAe,OAAO,KAAK;CAEjC,iBACE,UACA,UAAS;EACP,IAAI,aAAa,SACf;EAGF,aAAa,UAAU;EAEvB,MAAM,eACJ,MAAM,OAAO,WAAW,MAAM,WAAW;EAE3C,QAAQ,MAAM,kCAAkC,YAAY;EAE5D,MAAM,eAAe;EAErB,SAAS,YAAY;EACrB,UAAU,MAAM,SAAS,IAAI,OAAO,MAAM,YAAY,CAAC;EAEvD,iBAAiB;GACf,aAAa,UAAU;EACzB,GAAG,GAAG;CACR,GACA,EAAE,SAAS,KAAK,CAClB;CAEA,gBAAgB;EAGd,IAAI,CAFc,aAAa,SAG7B;EAGF,MAAM,4BAA4B,UAAiC;GACjE,IAAI,aAAa,SACf;GAGF,aAAa,UAAU;GAEvB,MAAM,eAAe,MAAM,QAAQ,WAAW;GAE9C,MAAM,eAAe;GACrB,SAAS,YAAY;GACrB,UAAU,MAAM,UAAU,IAAI,OAAO,MAAM,YAAY,CAAC;GAExD,iBAAiB;IACf,aAAa,UAAU;GACzB,GAAG,GAAG;EACR;EAEA,OAAO,iBACL,sBACA,0BACA,IACF;EAEA,aAAa;GACX,OAAO,oBACL,sBACA,0BACA,IACF;EACF;CACF,GAAG,CAAC,OAAO,CAAC;CAEZ,IAAI,OACF,OACE,oBAACA,SAAD;EACE,OAAM;EACN,SAAS;EACT,WAAU;EACV,eAAe;GACb,SAAS,IAAI;GACb,aAAa,UAAU;EACzB;CACD,CAAA;CAIL,OAAO,oBAAC,OAAD;EAAK,KAAK;EAAe;CAAc,CAAA;AAChD;;;ACrFA,MAAM,WAAW,EAAE,OAAO,MAAM,YAAmB;CACjD,MAAM,EAAE,OAAO,SAAS,aAAa,SAAS;CAE9C,iBAAiB,UAAS,MAAK;EAC7B,SAAS,EAAE,OAAO;EAClB,EAAE,eAAe;CACnB,CAAC;CAED,gBAAgB;EACd,aAAa,SAAS,IAAI;CAC5B,GAAG,CAAC,QAAQ,CAAC;CAEb,IAAI,CAAC,MACH,OAAO;CAGT,OAAO,oBAACC,SAAD;EAAgB;EAAS,SAAS;EAAO,OAAM;CAAiB,CAAA;AACzE;;;ACnBA,MAAM,QAAQC;AAEd,MAAM,WAAW;AACjB,MAAM,UAAUC;AAChB,MAAM,QAAQC"}
1
+ {"version":3,"file":"error-DWhxQEom.js","names":["ErrorComponent","Error","ErrorImpl","ErrorRuntime","ErrorGuard"],"sources":["../src/components/error/guard.tsx","../src/components/error/runtime.tsx","../src/components/error/index.ts"],"sourcesContent":["import { useEffect, useRef, useState } from 'react';\n\nimport { useEventListener } from '@jbpark/use-hooks';\n\nimport ErrorComponent from './error';\n\nexport interface Props {\n children: React.ReactNode;\n onError?: (error: Error) => void;\n}\n\nconst Guard = ({ children, onError }: Props) => {\n const [error, setError] = useState<string | null>(null);\n\n const containerRef = useRef<HTMLDivElement>(null);\n const errorHandled = useRef(false);\n\n useEventListener(\n 'error',\n event => {\n if (errorHandled.current) {\n return;\n }\n\n errorHandled.current = true;\n\n const errorMessage =\n event.error?.message || event.message || 'Unknown error';\n\n console.error('⚡ [Guard] Event handler error:', errorMessage);\n\n event.preventDefault();\n\n setError(errorMessage);\n onError?.(event.error || new window.Error(errorMessage));\n\n setTimeout(() => {\n errorHandled.current = false;\n }, 100);\n },\n { capture: true },\n );\n\n useEffect(() => {\n const container = containerRef.current;\n\n if (!container) {\n return;\n }\n\n const handleUnhandledRejection = (event: PromiseRejectionEvent) => {\n if (errorHandled.current) {\n return;\n }\n\n errorHandled.current = true;\n\n const errorMessage = event.reason?.message || 'Promise rejection';\n\n event.preventDefault();\n setError(errorMessage);\n onError?.(event.reason || new window.Error(errorMessage));\n\n setTimeout(() => {\n errorHandled.current = false;\n }, 100);\n };\n\n window.addEventListener(\n 'unhandledrejection',\n handleUnhandledRejection,\n true,\n );\n\n return () => {\n window.removeEventListener(\n 'unhandledrejection',\n handleUnhandledRejection,\n true,\n );\n };\n }, [onError]);\n\n if (error) {\n return (\n <ErrorComponent\n title=\"Runtime Error\"\n message={error}\n className=\"m-4\"\n onReset={() => {\n setError(null);\n errorHandled.current = false;\n }}\n />\n );\n }\n\n return <div ref={containerRef}>{children}</div>;\n};\n\nexport default Guard;\n","import { useEffect } from 'react';\n\nimport { useEventListener } from '@jbpark/use-hooks';\n\nimport { useError } from '~/components/context/states';\n\nimport Error from './error';\n\nexport interface Props extends React.ComponentPropsWithRef<'div'> {\n open?: boolean;\n reset?: () => void;\n}\n\nconst Runtime = ({ open = true, reset }: Props) => {\n const { error: message, setError } = useError();\n\n useEventListener('error', e => {\n setError(e.message);\n e.preventDefault();\n });\n\n useEffect(() => {\n return () => setError(null);\n }, [setError]);\n\n if (!open) {\n return null;\n }\n\n return <Error message={message} onReset={reset} title=\"Runtime Error\" />;\n};\n\nexport default Runtime;\n","import ErrorBoundary from './boundary';\nimport ErrorImpl, { type Props } from './error';\nimport ErrorGuard from './guard';\nimport ErrorRuntime from './runtime';\n\ntype ErrorComponent = typeof ErrorImpl & {\n Boundary: typeof ErrorBoundary;\n Runtime: typeof ErrorRuntime;\n Guard: typeof ErrorGuard;\n};\n\nconst Error = ErrorImpl as ErrorComponent;\n\nError.Boundary = ErrorBoundary;\nError.Runtime = ErrorRuntime;\nError.Guard = ErrorGuard;\n\nexport { ErrorBoundary, ErrorRuntime, ErrorGuard };\nexport type { Props };\nexport default Error;\n"],"mappings":";;;;;;AAWA,MAAM,SAAS,EAAE,UAAU,cAAqB;CAC9C,MAAM,CAAC,OAAO,YAAY,SAAwB,IAAI;CAEtD,MAAM,eAAe,OAAuB,IAAI;CAChD,MAAM,eAAe,OAAO,KAAK;CAEjC,iBACE,UACA,UAAS;EACP,IAAI,aAAa,SACf;EAGF,aAAa,UAAU;EAEvB,MAAM,eACJ,MAAM,OAAO,WAAW,MAAM,WAAW;EAE3C,QAAQ,MAAM,kCAAkC,YAAY;EAE5D,MAAM,eAAe;EAErB,SAAS,YAAY;EACrB,UAAU,MAAM,SAAS,IAAI,OAAO,MAAM,YAAY,CAAC;EAEvD,iBAAiB;GACf,aAAa,UAAU;EACzB,GAAG,GAAG;CACR,GACA,EAAE,SAAS,KAAK,CAClB;CAEA,gBAAgB;EAGd,IAAI,CAFc,aAAa,SAG7B;EAGF,MAAM,4BAA4B,UAAiC;GACjE,IAAI,aAAa,SACf;GAGF,aAAa,UAAU;GAEvB,MAAM,eAAe,MAAM,QAAQ,WAAW;GAE9C,MAAM,eAAe;GACrB,SAAS,YAAY;GACrB,UAAU,MAAM,UAAU,IAAI,OAAO,MAAM,YAAY,CAAC;GAExD,iBAAiB;IACf,aAAa,UAAU;GACzB,GAAG,GAAG;EACR;EAEA,OAAO,iBACL,sBACA,0BACA,IACF;EAEA,aAAa;GACX,OAAO,oBACL,sBACA,0BACA,IACF;EACF;CACF,GAAG,CAAC,OAAO,CAAC;CAEZ,IAAI,OACF,OACE,oBAACA,SAAD;EACE,OAAM;EACN,SAAS;EACT,WAAU;EACV,eAAe;GACb,SAAS,IAAI;GACb,aAAa,UAAU;EACzB;CACD,CAAA;CAIL,OAAO,oBAAC,OAAD;EAAK,KAAK;EAAe;CAAc,CAAA;AAChD;;;ACrFA,MAAM,WAAW,EAAE,OAAO,MAAM,YAAmB;CACjD,MAAM,EAAE,OAAO,SAAS,aAAa,SAAS;CAE9C,iBAAiB,UAAS,MAAK;EAC7B,SAAS,EAAE,OAAO;EAClB,EAAE,eAAe;CACnB,CAAC;CAED,gBAAgB;EACd,aAAa,SAAS,IAAI;CAC5B,GAAG,CAAC,QAAQ,CAAC;CAEb,IAAI,CAAC,MACH,OAAO;CAGT,OAAO,oBAACC,SAAD;EAAgB;EAAS,SAAS;EAAO,OAAM;CAAiB,CAAA;AACzE;;;ACnBA,MAAM,QAAQC;AAEd,MAAM,WAAW;AACjB,MAAM,UAAUC;AAChB,MAAM,QAAQC"}
@@ -9,12 +9,13 @@ interface Props$3 {
9
9
  interface State {
10
10
  hasError: boolean;
11
11
  error?: Error;
12
+ resetKeys?: readonly unknown[];
12
13
  }
13
14
  declare class ErrorBoundary extends React$1.Component<Props$3, State> {
14
15
  constructor(props: Props$3);
15
- static getDerivedStateFromError(error: Error): State;
16
+ static getDerivedStateFromError(error: Error): Partial<State>;
16
17
  componentDidCatch(error: Error, errorInfo: React$1.ErrorInfo): void;
17
- componentDidUpdate(prevProps: Props$3): void;
18
+ static getDerivedStateFromProps(props: Props$3, state: State): Partial<State> | null;
18
19
  reset: () => void;
19
20
  render(): string | number | bigint | boolean | Iterable<React$1.ReactNode> | Promise<string | number | bigint | boolean | React$1.ReactPortal | React$1.ReactElement<unknown, string | React$1.JSXElementConstructor<any>> | Iterable<React$1.ReactNode> | null | undefined> | React$1.JSX.Element | null | undefined;
20
21
  }
@@ -50,4 +51,4 @@ type ErrorComponent = typeof Error$2 & {
50
51
  declare const Error$1: ErrorComponent;
51
52
  //#endregion
52
53
  export { Props$1 as a, Guard as i, Props as n, Props$2 as o, Runtime as r, ErrorBoundary as s, Error$1 as t };
53
- //# sourceMappingURL=index-DNNj_0ba.d.ts.map
54
+ //# sourceMappingURL=index-C4SGQ3Ci.d.ts.map
@@ -45,6 +45,20 @@ type DndEditError = {
45
45
  };
46
46
  type DndRenderField = (props: FieldProps, builtin: React.ReactElement) => React.ReactNode | undefined;
47
47
  //#endregion
48
+ //#region src/components/dnd/inspector.d.ts
49
+ interface DndNodePick {
50
+ id: string;
51
+ sectionId: string;
52
+ }
53
+ interface DndInspector {
54
+ active: boolean;
55
+ activate: () => void;
56
+ deactivate: () => void;
57
+ toggle: () => void;
58
+ picked: DndNodePick | null;
59
+ }
60
+ declare const useDndInspector: () => DndInspector;
61
+ //#endregion
48
62
  //#region src/components/dnd/panel-binding.d.ts
49
63
  interface PanelNodeChange {
50
64
  (params: {
@@ -127,9 +141,10 @@ interface Props extends Omit<React.ComponentPropsWithRef<'div'>, 'onChange'> {
127
141
  onBeforeDelete?: (section: Section) => boolean | Promise<boolean>;
128
142
  sectionNameFallback?: (index: number) => string;
129
143
  containerId?: string;
144
+ onNodePick?: (pick: DndNodePick) => void;
130
145
  children?: React.ReactNode;
131
146
  }
132
- declare const Dnd$1: ({ value: _value, props, modules: _modules, onChange: _onChange, className, items, frame, dynamicTailwind, provider, renderField, onEditError, renderSectionFallback, shouldForceSectionFallback, onBeforeDelete, sectionNameFallback, containerId, children, ...restProps }: Props) => import("react").JSX.Element;
147
+ declare const Dnd$1: ({ value: _value, props, modules: _modules, onChange: _onChange, className, items, frame, dynamicTailwind, provider, renderField, onEditError, renderSectionFallback, shouldForceSectionFallback, onBeforeDelete, sectionNameFallback, containerId, onNodePick, children, ...restProps }: Props) => import("react").JSX.Element;
133
148
  //#endregion
134
149
  //#region src/components/dnd/draggable.d.ts
135
150
  interface DraggableItemDragState {
@@ -218,10 +233,17 @@ interface DndItemsActions {
218
233
  moveSelected: (direction: 'up' | 'down') => void;
219
234
  removeSelected: () => void;
220
235
  }
236
+ interface DndItemsExpansion {
237
+ expandedIds: string[];
238
+ isExpanded: (id: string) => boolean;
239
+ toggle: (id: string) => void;
240
+ setExpanded: (ids: string[]) => void;
241
+ }
221
242
  interface DndItems {
222
243
  kind: 'object' | 'primitive';
223
244
  items: DndItemsItem[];
224
245
  selection: ReturnType<typeof useMultiSelect>;
246
+ expansion: DndItemsExpansion;
225
247
  actions: DndItemsActions;
226
248
  canEditStructure: boolean;
227
249
  parseError: boolean;
@@ -287,5 +309,5 @@ type ChildrenEditor = DndChildren;
287
309
  /** @deprecated Renamed to `DndChildrenOptions`. */
288
310
  type ChildrenEditorOptions = DndChildrenOptions;
289
311
  //#endregion
290
- export { Field as $, Canvas as A, DndPanel as B, DndChildren as C, useDndLayout as D, DndLayout as E, Panel as F, PanelBindingData as G, DndRenderSectionFallback as H, DraggableItem as I, PanelNodesChange as J, PanelBindingSource as K, DraggableItemDragState as L, DndRegionProps as M, Layout as N, useDndPalette as O, Palette as P, DndRenderField as Q, DraggableItemProps as R, useDndItems as S, useDndChildren as T, DndSectionFallbackArgs as U, Props as V, PanelBinding as W, withPanelCommit as X, resolvePanelBindings as Y, DndEditError as Z, DndItemsActions as _, ItemsEditorActions as a, DndItemsNestedGroup as b, ItemsEditorNestedGroup as c, useItemsEditor as d, FieldProps as et, FieldKind as f, DndItems as g, isStructuralFieldKind as h, ItemsEditor as i, DndLayoutProps as j, useDndPanel as k, ItemsEditorOptions as l, getFieldKind as m, ChildrenEditorOptions as n, ItemsEditorItem as o, FieldKindBinding as p, PanelNodeChange as q, Dnd as r, ItemsEditorNestedElement as s, ChildrenEditor as t, useChildrenEditor as u, DndItemsItem as v, DndChildrenOptions as w, DndItemsOptions as x, DndItemsNestedElement as y, DndPalette as z };
291
- //# sourceMappingURL=index-DVgwXnPa.d.ts.map
312
+ export { DndNodePick as $, useDndPanel as A, DndPalette as B, useDndItems as C, DndLayout as D, useDndChildren as E, Palette as F, PanelBinding as G, Props as H, Panel as I, PanelNodeChange as J, PanelBindingData as K, DraggableItem as L, DndLayoutProps as M, DndRegionProps as N, useDndLayout as O, Layout as P, DndInspector as Q, DraggableItemDragState as R, DndItemsOptions as S, DndChildrenOptions as T, DndRenderSectionFallback as U, DndPanel as V, DndSectionFallbackArgs as W, resolvePanelBindings as X, PanelNodesChange as Y, withPanelCommit as Z, DndItemsActions as _, ItemsEditorActions as a, DndItemsNestedElement as b, ItemsEditorNestedGroup as c, useItemsEditor as d, useDndInspector as et, FieldKind as f, DndItems as g, isStructuralFieldKind as h, ItemsEditor as i, FieldProps as it, Canvas as j, useDndPalette as k, ItemsEditorOptions as l, getFieldKind as m, ChildrenEditorOptions as n, DndRenderField as nt, ItemsEditorItem as o, FieldKindBinding as p, PanelBindingSource as q, Dnd as r, Field as rt, ItemsEditorNestedElement as s, ChildrenEditor as t, DndEditError as tt, useChildrenEditor as u, DndItemsExpansion as v, DndChildren as w, DndItemsNestedGroup as x, DndItemsItem as y, DraggableItemProps as z };
313
+ //# sourceMappingURL=index-CtlUYnWq.d.ts.map
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { v as Section } from "./document-Ct4jW78J.js";
2
2
  import { t as FrameProps } from "./index-DpDU1IAw.js";
3
- import { A as Canvas, B as DndPanel, E as DndLayout, F as Panel, H as DndRenderSectionFallback, J as PanelNodesChange, N as Layout, P as Palette, Q as DndRenderField, R as DraggableItemProps, U as DndSectionFallbackArgs, V as Props, W as PanelBinding, Z as DndEditError, et as FieldProps, g as DndItems, i as ItemsEditor, l as ItemsEditorOptions, o as ItemsEditorItem, q as PanelNodeChange, r as Dnd, v as DndItemsItem, x as DndItemsOptions, z as DndPalette } from "./index-DVgwXnPa.js";
3
+ import { $ as DndNodePick, B as DndPalette, D as DndLayout, F as Palette, G as PanelBinding, H as Props, I as Panel, J as PanelNodeChange, P as Layout, Q as DndInspector, S as DndItemsOptions, U as DndRenderSectionFallback, V as DndPanel, W as DndSectionFallbackArgs, Y as PanelNodesChange, g as DndItems, i as ItemsEditor, it as FieldProps, j as Canvas, l as ItemsEditorOptions, nt as DndRenderField, o as ItemsEditorItem, r as Dnd, tt as DndEditError, y as DndItemsItem, z as DraggableItemProps } from "./index-CtlUYnWq.js";
4
4
  import { a as Props$2, n as EditorRenderData, r as Props$1, t as Editor } from "./index-13BDSrQc.js";
5
- import { a as Props$5, n as Props$4, o as Props$3, s as ErrorBoundary, t as Error } from "./index-DNNj_0ba.js";
5
+ import { a as Props$5, n as Props$4, o as Props$3, s as ErrorBoundary, t as Error } from "./index-C4SGQ3Ci.js";
6
6
  import { n as Preview, r as Props$6 } from "./index-DQkL37eL.js";
7
7
  //#region src/index.d.ts
8
8
  declare const App: {
@@ -19,7 +19,7 @@ declare const App: {
19
19
  Editor: (({ defaultValue, value: _value, debounce, onChange: _onChange, renderEditor, fragment, prettierOptions, ...props }: Props$1) => import("react").JSX.Element) & {
20
20
  Core: ({ value, theme, height, className, prettierOptions, fragment, raw, onChange, onSave, onError, ...props }: Props$2) => import("react").JSX.Element;
21
21
  };
22
- Dnd: (({ value: _value, props, modules: _modules, onChange: _onChange, className, items, frame, dynamicTailwind, provider, renderField, onEditError, renderSectionFallback, shouldForceSectionFallback, onBeforeDelete, sectionNameFallback, containerId, children, ...restProps }: Props) => import("react").JSX.Element) & {
22
+ Dnd: (({ value: _value, props, modules: _modules, onChange: _onChange, className, items, frame, dynamicTailwind, provider, renderField, onEditError, renderSectionFallback, shouldForceSectionFallback, onBeforeDelete, sectionNameFallback, containerId, onNodePick, children, ...restProps }: Props) => import("react").JSX.Element) & {
23
23
  DraggableItem: ({ item, children }: DraggableItemProps) => import("react").ReactNode;
24
24
  Field: (props: FieldProps) => import("react").JSX.Element;
25
25
  Palette: typeof Palette;
@@ -30,5 +30,5 @@ declare const App: {
30
30
  };
31
31
  declare const LiveRenderer: ({ code, props, modules, dynamicTailwind, provider, ...restProps }: Props$6) => import("react").JSX.Element;
32
32
  //#endregion
33
- export { type DndEditError, type DndItems, type DndItemsItem, type DndItemsOptions, type DndLayout, type DndPalette, type DndPanel, type DndRenderField, type DndRenderSectionFallback, type DndSectionFallbackArgs, type EditorRenderData, type FieldProps, type FrameProps, type ItemsEditor, type ItemsEditorItem, type ItemsEditorOptions, Dnd as LiveDnd, Editor as LiveEditor, Error as LiveError, Preview as LivePreview, App as LiveProvider, App as default, LiveRenderer, type PanelBinding, type PanelNodeChange, type PanelNodesChange, type Section };
33
+ export { type DndEditError, type DndInspector, type DndItems, type DndItemsItem, type DndItemsOptions, type DndLayout, type DndNodePick, type DndPalette, type DndPanel, type DndRenderField, type DndRenderSectionFallback, type DndSectionFallbackArgs, type EditorRenderData, type FieldProps, type FrameProps, type ItemsEditor, type ItemsEditorItem, type ItemsEditorOptions, Dnd as LiveDnd, Editor as LiveEditor, Error as LiveError, Preview as LivePreview, App as LiveProvider, App as default, LiveRenderer, type PanelBinding, type PanelNodeChange, type PanelNodesChange, type Section };
34
34
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { t as ContextProvider } from "./context-93X3gyfJ.js";
2
- import { t as Dnd } from "./dnd-DBaH2L8N.js";
2
+ import { t as Dnd } from "./dnd-x8Mvj--6.js";
3
3
  import { t as Editor } from "./editor-DvMruljw.js";
4
- import { t as Error } from "./error-KW8XCcjH.js";
5
- import { t as Preview } from "./preview-B9Jhv1Up.js";
4
+ import { t as Error } from "./error-DWhxQEom.js";
5
+ import { t as Preview } from "./preview-BYck9mVK.js";
6
6
  import { jsx } from "react/jsx-runtime";
7
7
  //#region src/index.tsx
8
8
  const App = ({ children }) => {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["Context","LivePreview","LiveError","LiveEditor","LiveDnd"],"sources":["../src/index.tsx"],"sourcesContent":["import './index.css';\n\nimport Context from './components/context';\nimport LiveDnd, {\n type DndEditError,\n type DndItems,\n type DndItemsItem,\n type DndItemsOptions,\n type DndLayout,\n type DndPalette,\n type DndPanel,\n type DndRenderField,\n type DndRenderSectionFallback,\n type DndSectionFallbackArgs,\n type FieldProps,\n type ItemsEditor,\n type ItemsEditorItem,\n type ItemsEditorOptions,\n type PanelBinding,\n type PanelNodeChange,\n type PanelNodesChange,\n} from './components/dnd';\nimport LiveEditor, { type EditorRenderData } from './components/editor';\nimport LiveError from './components/error';\nimport { type FrameProps } from './components/frame';\nimport LivePreview from './components/preview';\nimport type { Section } from './types';\n\nconst App = ({ children }: { children?: React.ReactNode }) => {\n return <Context>{children}</Context>;\n};\n\nconst LiveRenderer = LivePreview;\n\nexport {\n LivePreview,\n LiveError,\n LiveEditor,\n LiveDnd,\n LiveRenderer,\n App as LiveProvider,\n //\n};\n\nexport type {\n DndEditError,\n DndRenderField,\n DndRenderSectionFallback,\n DndSectionFallbackArgs,\n DndPalette,\n DndPanel,\n DndLayout,\n PanelBinding,\n PanelNodeChange,\n PanelNodesChange,\n FieldProps,\n DndItems,\n DndItemsItem,\n DndItemsOptions,\n // Deprecated aliases of the three above, kept until the next major.\n ItemsEditor,\n ItemsEditorItem,\n ItemsEditorOptions,\n EditorRenderData,\n FrameProps,\n Section,\n};\n\nApp.Preview = LivePreview;\nApp.Renderer = LiveRenderer;\nApp.Error = LiveError;\nApp.Editor = LiveEditor;\nApp.Dnd = LiveDnd;\n\nexport default App;\n"],"mappings":";;;;;;;AA4BA,MAAM,OAAO,EAAE,eAA+C;CAC5D,OAAO,oBAACA,iBAAD,EAAU,SAAkB,CAAA;AACrC;AAEA,MAAM,eAAeC;AAoCrB,IAAI,UAAUA;AACd,IAAI,WAAW;AACf,IAAI,QAAQC;AACZ,IAAI,SAASC;AACb,IAAI,MAAMC"}
1
+ {"version":3,"file":"index.js","names":["Context","LivePreview","LiveError","LiveEditor","LiveDnd"],"sources":["../src/index.tsx"],"sourcesContent":["import './index.css';\n\nimport Context from './components/context';\nimport LiveDnd, {\n type DndEditError,\n type DndInspector,\n type DndItems,\n type DndItemsItem,\n type DndItemsOptions,\n type DndLayout,\n type DndNodePick,\n type DndPalette,\n type DndPanel,\n type DndRenderField,\n type DndRenderSectionFallback,\n type DndSectionFallbackArgs,\n type FieldProps,\n type ItemsEditor,\n type ItemsEditorItem,\n type ItemsEditorOptions,\n type PanelBinding,\n type PanelNodeChange,\n type PanelNodesChange,\n} from './components/dnd';\nimport LiveEditor, { type EditorRenderData } from './components/editor';\nimport LiveError from './components/error';\nimport { type FrameProps } from './components/frame';\nimport LivePreview from './components/preview';\nimport type { Section } from './types';\n\nconst App = ({ children }: { children?: React.ReactNode }) => {\n return <Context>{children}</Context>;\n};\n\nconst LiveRenderer = LivePreview;\n\nexport {\n LivePreview,\n LiveError,\n LiveEditor,\n LiveDnd,\n LiveRenderer,\n App as LiveProvider,\n //\n};\n\nexport type {\n DndEditError,\n DndRenderField,\n DndRenderSectionFallback,\n DndSectionFallbackArgs,\n DndPalette,\n DndPanel,\n DndLayout,\n DndInspector,\n DndNodePick,\n PanelBinding,\n PanelNodeChange,\n PanelNodesChange,\n FieldProps,\n DndItems,\n DndItemsItem,\n DndItemsOptions,\n // Deprecated aliases of the three above, kept until the next major.\n ItemsEditor,\n ItemsEditorItem,\n ItemsEditorOptions,\n EditorRenderData,\n FrameProps,\n Section,\n};\n\nApp.Preview = LivePreview;\nApp.Renderer = LiveRenderer;\nApp.Error = LiveError;\nApp.Editor = LiveEditor;\nApp.Dnd = LiveDnd;\n\nexport default App;\n"],"mappings":";;;;;;;AA8BA,MAAM,OAAO,EAAE,eAA+C;CAC5D,OAAO,oBAACA,iBAAD,EAAU,SAAkB,CAAA;AACrC;AAEA,MAAM,eAAeC;AAsCrB,IAAI,UAAUA;AACd,IAAI,WAAW;AACf,IAAI,QAAQC;AACZ,IAAI,SAASC;AACb,IAAI,MAAMC"}
@@ -1,3 +1,3 @@
1
- import { r as baseModules } from "../use-dynamic-tailwind-BDfytfYC.js";
2
- import { t as Preview } from "../preview-B9Jhv1Up.js";
1
+ import { r as baseModules } from "../use-dynamic-tailwind-QKcGvUJs.js";
2
+ import { t as Preview } from "../preview-BYck9mVK.js";
3
3
  export { baseModules, Preview as default };
@@ -1,7 +1,7 @@
1
1
  import { i as usePreview, r as useError } from "./states-Ci1AvoQ9.js";
2
2
  import { t as cn } from "./cn-CAnad2vL.js";
3
- import { a as Frame, n as useCompiledModule, t as useDynamicTailwind } from "./use-dynamic-tailwind-BDfytfYC.js";
4
- import { t as Error } from "./error-KW8XCcjH.js";
3
+ import { a as Frame, n as useCompiledModule, t as useDynamicTailwind } from "./use-dynamic-tailwind-QKcGvUJs.js";
4
+ import { t as Error } from "./error-DWhxQEom.js";
5
5
  import { useMemo } from "react";
6
6
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
7
  //#region src/components/preview/defaults.ts
@@ -83,4 +83,4 @@ const Preview = ({ code, props = NO_PROPS, modules = NO_MODULES, dynamicTailwind
83
83
  //#endregion
84
84
  export { Preview as t };
85
85
 
86
- //# sourceMappingURL=preview-B9Jhv1Up.js.map
86
+ //# sourceMappingURL=preview-BYck9mVK.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"preview-B9Jhv1Up.js","names":["LiveError"],"sources":["../src/components/preview/defaults.ts","../src/components/preview/client.tsx","../src/components/preview/preview.tsx"],"sourcesContent":["// Stable defaults for optional object props. A `= {}` default is a new\n// object on every render, which re-runs every memo keyed on it — the merged\n// module map, and so `compile()` — even when the caller passed nothing (#348).\nexport const NO_PROPS: Record<string, unknown> = {};\nexport const NO_MODULES: Record<string, unknown> = {};\n","'use client';\n\nimport { useMemo } from 'react';\n\nimport { useError, usePreview } from '~/components/context/states';\nimport LiveError from '~/components/error';\nimport Frame, { type FrameProps } from '~/components/frame';\nimport { cn } from '~/utils/cn';\n\nimport { NO_MODULES, NO_PROPS } from './defaults';\nimport { type Props } from './preview';\nimport { useCompiledModule } from './use-compiled-module';\nimport { useDynamicTailwind } from './use-dynamic-tailwind';\n\nconst Client = ({\n code: _code,\n className,\n showError,\n props = NO_PROPS,\n modules = NO_MODULES,\n frame,\n dynamicTailwind = false,\n provider,\n}: Props) => {\n const { code } = usePreview();\n const { error, setError } = useError();\n const isError = !!showError && !!error;\n\n const classNames = cn(isError && 'hidden', className);\n\n const effectiveCode = _code === undefined ? code : _code;\n\n const module = useCompiledModule(effectiveCode, modules);\n\n // Attached to the wrapper below; scans the rendered DOM so classes coming\n // from an imported component are picked up too. See the hook for details.\n const { ref: contentRef, css: dynamicCSS } = useDynamicTailwind(\n effectiveCode,\n dynamicTailwind,\n );\n\n const componentProps = useMemo(\n () => ({\n ...props,\n }),\n [props],\n );\n\n const renderProvider = (component: React.ReactNode) => {\n return provider ? provider(component) : component;\n };\n\n if (module && module.error) {\n return (\n <>\n <div className={cn('relative h-full w-full', classNames)}>\n <LiveError message={module.error} title=\"Compile Error\" />\n </div>\n <LiveError.Runtime open={isError} />\n </>\n );\n }\n\n const Component = module?.exports?.default;\n\n if (!Component) {\n return null;\n }\n\n return (\n <>\n {frame ? (\n <div className={cn('h-full w-full', classNames)}>\n <Frame {...(frame as FrameProps)}>\n {container => (\n <div ref={contentRef} style={{ display: 'contents' }}>\n <LiveError.Boundary\n resetKeys={[effectiveCode]}\n onError={(e: Error) => setError(e.message)}\n >\n {renderProvider(\n <LiveError.Guard onError={e => setError(e.message)}>\n <Component {...componentProps} container={container} />\n {dynamicTailwind && dynamicCSS && (\n <style>{dynamicCSS}</style>\n )}\n </LiveError.Guard>,\n )}\n </LiveError.Boundary>\n </div>\n )}\n </Frame>\n </div>\n ) : (\n <div\n ref={contentRef}\n className={cn(\n 'relative h-full w-full overflow-x-hidden overflow-y-auto',\n classNames,\n )}\n style={{\n isolation: 'isolate',\n transform: 'translateZ(0)',\n containerType: 'inline-size',\n }}\n >\n <LiveError.Boundary\n resetKeys={[effectiveCode]}\n onError={e => setError(e.message)}\n >\n {renderProvider(\n <LiveError.Guard onError={e => setError(e.message)}>\n <Component {...componentProps} />\n {dynamicTailwind && dynamicCSS && <style>{dynamicCSS}</style>}\n </LiveError.Guard>,\n )}\n </LiveError.Boundary>\n </div>\n )}\n <LiveError.Runtime open={isError} />\n </>\n );\n};\n\nexport default Client;\n","import type React from 'react';\n\nimport type { FrameProps } from '../frame';\nimport Client from './client';\nimport { NO_MODULES, NO_PROPS } from './defaults';\n\nexport interface Props extends React.ComponentPropsWithRef<'div'> {\n code?: string;\n showError?: boolean;\n props?: Record<string, unknown>;\n container?: HTMLElement | null;\n frame?: boolean | FrameProps;\n modules?: Record<string, unknown>;\n dynamicTailwind?: boolean;\n provider?: (children: React.ReactNode) => React.ReactNode;\n}\n\n// A thin wrapper around Client, which does the actual compiling, error\n// handling, and frame wrapping. This used to have its own duplicate\n// compile-and-render branch for the `code` prop that never wrapped its\n// output in <Frame>, so `frame` was silently ignored whenever `code` was\n// passed — see #187. Client already handles `code` (falling back to\n// context when absent) and `frame`, so there is only one render path now.\nconst Preview = ({\n code,\n props = NO_PROPS,\n modules = NO_MODULES,\n dynamicTailwind = false,\n provider,\n ...restProps\n}: Props) => {\n return (\n <Client\n code={code}\n props={props}\n modules={modules}\n dynamicTailwind={dynamicTailwind}\n provider={provider}\n {...restProps}\n />\n );\n};\n\nexport default Preview;\n"],"mappings":";;;;;;;AAGA,MAAa,WAAoC,CAAC;AAClD,MAAa,aAAsC,CAAC;;;ACUpD,MAAM,UAAU,EACd,MAAM,OACN,WACA,WACA,QAAQ,UACR,UAAU,YACV,OACA,kBAAkB,OAClB,eACW;CACX,MAAM,EAAE,SAAS,WAAW;CAC5B,MAAM,EAAE,OAAO,aAAa,SAAS;CACrC,MAAM,UAAU,CAAC,CAAC,aAAa,CAAC,CAAC;CAEjC,MAAM,aAAa,GAAG,WAAW,UAAU,SAAS;CAEpD,MAAM,gBAAgB,UAAU,KAAA,IAAY,OAAO;CAEnD,MAAM,SAAS,kBAAkB,eAAe,OAAO;CAIvD,MAAM,EAAE,KAAK,YAAY,KAAK,eAAe,mBAC3C,eACA,eACF;CAEA,MAAM,iBAAiB,eACd,EACL,GAAG,MACL,IACA,CAAC,KAAK,CACR;CAEA,MAAM,kBAAkB,cAA+B;EACrD,OAAO,WAAW,SAAS,SAAS,IAAI;CAC1C;CAEA,IAAI,UAAU,OAAO,OACnB,OACE,qBAAA,UAAA,EAAA,UAAA,CACE,oBAAC,OAAD;EAAK,WAAW,GAAG,0BAA0B,UAAU;EACrD,UAAA,oBAACA,OAAD;GAAW,SAAS,OAAO;GAAO,OAAM;EAAiB,CAAA;CACtD,CAAA,GACL,oBAACA,MAAU,SAAX,EAAmB,MAAM,QAAU,CAAA,CACnC,EAAA,CAAA;CAIN,MAAM,YAAY,QAAQ,SAAS;CAEnC,IAAI,CAAC,WACH,OAAO;CAGT,OACE,qBAAA,UAAA,EAAA,UAAA,CACG,QACC,oBAAC,OAAD;EAAK,WAAW,GAAG,iBAAiB,UAAU;EAC5C,UAAA,oBAAC,OAAD;GAAO,GAAK;GACT,WAAA,cACC,oBAAC,OAAD;IAAK,KAAK;IAAY,OAAO,EAAE,SAAS,WAAW;IACjD,UAAA,oBAACA,MAAU,UAAX;KACE,WAAW,CAAC,aAAa;KACzB,UAAU,MAAa,SAAS,EAAE,OAAO;KAExC,UAAA,eACC,qBAACA,MAAU,OAAX;MAAiB,UAAS,MAAK,SAAS,EAAE,OAAO;MAAjD,UAAA,CACE,oBAAC,WAAD;OAAW,GAAI;OAA2B;MAAY,CAAA,GACrD,mBAAmB,cAClB,oBAAC,SAAD,EAAA,UAAQ,WAAkB,CAAA,CAEb;KACnB,CAAA,CAAA;IACkB,CAAA;GACjB,CAAA;EAEF,CAAA;CACJ,CAAA,IAEL,oBAAC,OAAD;EACE,KAAK;EACL,WAAW,GACT,4DACA,UACF;EACA,OAAO;GACL,WAAW;GACX,WAAW;GACX,eAAe;EACjB;EAEA,UAAA,oBAACA,MAAU,UAAX;GACE,WAAW,CAAC,aAAa;GACzB,UAAS,MAAK,SAAS,EAAE,OAAO;GAE/B,UAAA,eACC,qBAACA,MAAU,OAAX;IAAiB,UAAS,MAAK,SAAS,EAAE,OAAO;IAAjD,UAAA,CACE,oBAAC,WAAD,EAAW,GAAI,eAAiB,CAAA,GAC/B,mBAAmB,cAAc,oBAAC,SAAD,EAAA,UAAQ,WAAkB,CAAA,CAC7C;GACnB,CAAA,CAAA;EACkB,CAAA;CACjB,CAAA,GAEP,oBAACA,MAAU,SAAX,EAAmB,MAAM,QAAU,CAAA,CACnC,EAAA,CAAA;AAEN;;;ACnGA,MAAM,WAAW,EACf,MACA,QAAQ,UACR,UAAU,YACV,kBAAkB,OAClB,UACA,GAAG,gBACQ;CACX,OACE,oBAAC,QAAD;EACQ;EACC;EACE;EACQ;EACP;EACV,GAAI;CACL,CAAA;AAEL"}
1
+ {"version":3,"file":"preview-BYck9mVK.js","names":["LiveError"],"sources":["../src/components/preview/defaults.ts","../src/components/preview/client.tsx","../src/components/preview/preview.tsx"],"sourcesContent":["// Stable defaults for optional object props. A `= {}` default is a new\n// object on every render, which re-runs every memo keyed on it — the merged\n// module map, and so `compile()` — even when the caller passed nothing (#348).\nexport const NO_PROPS: Record<string, unknown> = {};\nexport const NO_MODULES: Record<string, unknown> = {};\n","'use client';\n\nimport { useMemo } from 'react';\n\nimport { useError, usePreview } from '~/components/context/states';\nimport LiveError from '~/components/error';\nimport Frame, { type FrameProps } from '~/components/frame';\nimport { cn } from '~/utils/cn';\n\nimport { NO_MODULES, NO_PROPS } from './defaults';\nimport { type Props } from './preview';\nimport { useCompiledModule } from './use-compiled-module';\nimport { useDynamicTailwind } from './use-dynamic-tailwind';\n\nconst Client = ({\n code: _code,\n className,\n showError,\n props = NO_PROPS,\n modules = NO_MODULES,\n frame,\n dynamicTailwind = false,\n provider,\n}: Props) => {\n const { code } = usePreview();\n const { error, setError } = useError();\n const isError = !!showError && !!error;\n\n const classNames = cn(isError && 'hidden', className);\n\n const effectiveCode = _code === undefined ? code : _code;\n\n const module = useCompiledModule(effectiveCode, modules);\n\n // Attached to the wrapper below; scans the rendered DOM so classes coming\n // from an imported component are picked up too. See the hook for details.\n const { ref: contentRef, css: dynamicCSS } = useDynamicTailwind(\n effectiveCode,\n dynamicTailwind,\n );\n\n const componentProps = useMemo(\n () => ({\n ...props,\n }),\n [props],\n );\n\n const renderProvider = (component: React.ReactNode) => {\n return provider ? provider(component) : component;\n };\n\n if (module && module.error) {\n return (\n <>\n <div className={cn('relative h-full w-full', classNames)}>\n <LiveError message={module.error} title=\"Compile Error\" />\n </div>\n <LiveError.Runtime open={isError} />\n </>\n );\n }\n\n const Component = module?.exports?.default;\n\n if (!Component) {\n return null;\n }\n\n return (\n <>\n {frame ? (\n <div className={cn('h-full w-full', classNames)}>\n <Frame {...(frame as FrameProps)}>\n {container => (\n <div ref={contentRef} style={{ display: 'contents' }}>\n <LiveError.Boundary\n resetKeys={[effectiveCode]}\n onError={(e: Error) => setError(e.message)}\n >\n {renderProvider(\n <LiveError.Guard onError={e => setError(e.message)}>\n <Component {...componentProps} container={container} />\n {dynamicTailwind && dynamicCSS && (\n <style>{dynamicCSS}</style>\n )}\n </LiveError.Guard>,\n )}\n </LiveError.Boundary>\n </div>\n )}\n </Frame>\n </div>\n ) : (\n <div\n ref={contentRef}\n className={cn(\n 'relative h-full w-full overflow-x-hidden overflow-y-auto',\n classNames,\n )}\n style={{\n isolation: 'isolate',\n transform: 'translateZ(0)',\n containerType: 'inline-size',\n }}\n >\n <LiveError.Boundary\n resetKeys={[effectiveCode]}\n onError={e => setError(e.message)}\n >\n {renderProvider(\n <LiveError.Guard onError={e => setError(e.message)}>\n <Component {...componentProps} />\n {dynamicTailwind && dynamicCSS && <style>{dynamicCSS}</style>}\n </LiveError.Guard>,\n )}\n </LiveError.Boundary>\n </div>\n )}\n <LiveError.Runtime open={isError} />\n </>\n );\n};\n\nexport default Client;\n","import type React from 'react';\n\nimport type { FrameProps } from '../frame';\nimport Client from './client';\nimport { NO_MODULES, NO_PROPS } from './defaults';\n\nexport interface Props extends React.ComponentPropsWithRef<'div'> {\n code?: string;\n showError?: boolean;\n props?: Record<string, unknown>;\n container?: HTMLElement | null;\n frame?: boolean | FrameProps;\n modules?: Record<string, unknown>;\n dynamicTailwind?: boolean;\n provider?: (children: React.ReactNode) => React.ReactNode;\n}\n\n// A thin wrapper around Client, which does the actual compiling, error\n// handling, and frame wrapping. This used to have its own duplicate\n// compile-and-render branch for the `code` prop that never wrapped its\n// output in <Frame>, so `frame` was silently ignored whenever `code` was\n// passed — see #187. Client already handles `code` (falling back to\n// context when absent) and `frame`, so there is only one render path now.\nconst Preview = ({\n code,\n props = NO_PROPS,\n modules = NO_MODULES,\n dynamicTailwind = false,\n provider,\n ...restProps\n}: Props) => {\n return (\n <Client\n code={code}\n props={props}\n modules={modules}\n dynamicTailwind={dynamicTailwind}\n provider={provider}\n {...restProps}\n />\n );\n};\n\nexport default Preview;\n"],"mappings":";;;;;;;AAGA,MAAa,WAAoC,CAAC;AAClD,MAAa,aAAsC,CAAC;;;ACUpD,MAAM,UAAU,EACd,MAAM,OACN,WACA,WACA,QAAQ,UACR,UAAU,YACV,OACA,kBAAkB,OAClB,eACW;CACX,MAAM,EAAE,SAAS,WAAW;CAC5B,MAAM,EAAE,OAAO,aAAa,SAAS;CACrC,MAAM,UAAU,CAAC,CAAC,aAAa,CAAC,CAAC;CAEjC,MAAM,aAAa,GAAG,WAAW,UAAU,SAAS;CAEpD,MAAM,gBAAgB,UAAU,KAAA,IAAY,OAAO;CAEnD,MAAM,SAAS,kBAAkB,eAAe,OAAO;CAIvD,MAAM,EAAE,KAAK,YAAY,KAAK,eAAe,mBAC3C,eACA,eACF;CAEA,MAAM,iBAAiB,eACd,EACL,GAAG,MACL,IACA,CAAC,KAAK,CACR;CAEA,MAAM,kBAAkB,cAA+B;EACrD,OAAO,WAAW,SAAS,SAAS,IAAI;CAC1C;CAEA,IAAI,UAAU,OAAO,OACnB,OACE,qBAAA,UAAA,EAAA,UAAA,CACE,oBAAC,OAAD;EAAK,WAAW,GAAG,0BAA0B,UAAU;EACrD,UAAA,oBAACA,OAAD;GAAW,SAAS,OAAO;GAAO,OAAM;EAAiB,CAAA;CACtD,CAAA,GACL,oBAACA,MAAU,SAAX,EAAmB,MAAM,QAAU,CAAA,CACnC,EAAA,CAAA;CAIN,MAAM,YAAY,QAAQ,SAAS;CAEnC,IAAI,CAAC,WACH,OAAO;CAGT,OACE,qBAAA,UAAA,EAAA,UAAA,CACG,QACC,oBAAC,OAAD;EAAK,WAAW,GAAG,iBAAiB,UAAU;EAC5C,UAAA,oBAAC,OAAD;GAAO,GAAK;GACT,WAAA,cACC,oBAAC,OAAD;IAAK,KAAK;IAAY,OAAO,EAAE,SAAS,WAAW;IACjD,UAAA,oBAACA,MAAU,UAAX;KACE,WAAW,CAAC,aAAa;KACzB,UAAU,MAAa,SAAS,EAAE,OAAO;KAExC,UAAA,eACC,qBAACA,MAAU,OAAX;MAAiB,UAAS,MAAK,SAAS,EAAE,OAAO;MAAjD,UAAA,CACE,oBAAC,WAAD;OAAW,GAAI;OAA2B;MAAY,CAAA,GACrD,mBAAmB,cAClB,oBAAC,SAAD,EAAA,UAAQ,WAAkB,CAAA,CAEb;KACnB,CAAA,CAAA;IACkB,CAAA;GACjB,CAAA;EAEF,CAAA;CACJ,CAAA,IAEL,oBAAC,OAAD;EACE,KAAK;EACL,WAAW,GACT,4DACA,UACF;EACA,OAAO;GACL,WAAW;GACX,WAAW;GACX,eAAe;EACjB;EAEA,UAAA,oBAACA,MAAU,UAAX;GACE,WAAW,CAAC,aAAa;GACzB,UAAS,MAAK,SAAS,EAAE,OAAO;GAE/B,UAAA,eACC,qBAACA,MAAU,OAAX;IAAiB,UAAS,MAAK,SAAS,EAAE,OAAO;IAAjD,UAAA,CACE,oBAAC,WAAD,EAAW,GAAI,eAAiB,CAAA,GAC/B,mBAAmB,cAAc,oBAAC,SAAD,EAAA,UAAQ,WAAkB,CAAA,CAC7C;GACnB,CAAA,CAAA;EACkB,CAAA;CACjB,CAAA,GAEP,oBAACA,MAAU,SAAX,EAAmB,MAAM,QAAU,CAAA,CACnC,EAAA,CAAA;AAEN;;;ACnGA,MAAM,WAAW,EACf,MACA,QAAQ,UACR,UAAU,YACV,kBAAkB,OAClB,UACA,GAAG,gBACQ;CACX,OACE,oBAAC,QAAD;EACQ;EACC;EACE;EACQ;EACP;EACV,GAAI;CACL,CAAA;AAEL"}
@@ -1,4 +1,4 @@
1
- import { a as generateSectionPreview, l as parseDocument, o as generateSectionPreviews, s as getSections, u as replaceDocumentSections } from "./document-C3eCRQlj.js";
1
+ import { a as generateSectionPreview, l as parseDocument, o as generateSectionPreviews, s as getSections, u as replaceDocumentSections } from "./document-C-hGKMkk.js";
2
2
  import { i as DEFAULT_CONTAINER_ID, u as documentTemplate } from "./constants-CKvdqZtO.js";
3
3
  //#region src/utils/sections.ts
4
4
  const extractSections = (code, options) => {
@@ -21,4 +21,4 @@ const createDocument = ({ containerId = DEFAULT_CONTAINER_ID } = {}) => {
21
21
  //#endregion
22
22
  export { replaceSections as a, generateSections as i, extractSections as n, generateSection as r, createDocument as t };
23
23
 
24
- //# sourceMappingURL=sections-A4TAXUP8.js.map
24
+ //# sourceMappingURL=sections-Ce63wbtG.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sections-A4TAXUP8.js","names":[],"sources":["../src/utils/sections.ts"],"sourcesContent":["import { DEFAULT_CONTAINER_ID, documentTemplate } from '~/constants';\nimport type { Section } from '~/types';\n\nimport {\n type DocumentOptions,\n type SectionOptions,\n createSectionPreviewCache,\n generateSectionPreview,\n generateSectionPreviews,\n getSections,\n parseDocument,\n replaceDocumentSections,\n} from './ast/document';\n\nexport const extractSections = (\n code: string,\n options?: SectionOptions,\n): Section[] => {\n const doc = parseDocument(code, options);\n\n return doc ? getSections(doc, options) : [];\n};\n\nexport const replaceSections = (\n code: string,\n sections: string[],\n options?: DocumentOptions,\n): string => {\n return replaceDocumentSections(code, sections, options);\n};\n\nexport const generateSection = (\n code: string,\n fullCode: string,\n options?: DocumentOptions,\n) => {\n return generateSectionPreview(fullCode, code, options);\n};\n\n// Batched form of generateSection() — computes every section's preview from\n// a single parse of fullCode instead of one parseDocument call per section\n// (see generateSectionPreviews). Unchanged sections come back byte-identical\n// to their previous preview string, which is what lets a caller pass each\n// one down as a stable prop (see Renderer's React.memo).\nexport const generateSections = (\n codes: string[],\n fullCode: string,\n options?: DocumentOptions,\n): string[] => {\n return generateSectionPreviews(fullCode, codes, options);\n};\n\n// A new, empty document `Live.Dnd` can add sections to: an `App` component\n// rendering the container element and nothing else. Start a controlled\n// `Live.Dnd` from this rather than an empty string, which has no container,\n// so nothing dropped on the canvas would land anywhere (#449). Pass the same\n// `containerId` you give `Live.Dnd`.\nexport const createDocument = ({\n containerId = DEFAULT_CONTAINER_ID,\n}: DocumentOptions = {}): string => {\n // The id is written into a JSX attribute as-is, so anything that could end\n // the attribute or the tag is refused rather than producing broken source.\n if (!/^[^\\s\"'<>{}]+$/.test(containerId)) {\n throw new Error(\n `createDocument: containerId must be a plain element id, got ${JSON.stringify(containerId)}`,\n );\n }\n\n return documentTemplate(containerId);\n};\n\n// Incremental counterpart to generateSections() — see createSectionPreviewCache\n// (#131). Pass a fresh instance's `compute` in place of generateSections()\n// where the caller can keep it alive across renders (e.g. Dnd holds one via\n// `useState(() => createSectionPreviewCache())`).\nexport { createSectionPreviewCache };\nexport type {\n DocumentOptions,\n SectionOptions,\n SectionPreviewCache,\n} from './ast/document';\n"],"mappings":";;;AAcA,MAAa,mBACX,MACA,YACc;CACd,MAAM,MAAM,cAAc,MAAM,OAAO;CAEvC,OAAO,MAAM,YAAY,KAAK,OAAO,IAAI,CAAC;AAC5C;AAEA,MAAa,mBACX,MACA,UACA,YACW;CACX,OAAO,wBAAwB,MAAM,UAAU,OAAO;AACxD;AAEA,MAAa,mBACX,MACA,UACA,YACG;CACH,OAAO,uBAAuB,UAAU,MAAM,OAAO;AACvD;AAOA,MAAa,oBACX,OACA,UACA,YACa;CACb,OAAO,wBAAwB,UAAU,OAAO,OAAO;AACzD;AAOA,MAAa,kBAAkB,EAC7B,cAAc,yBACK,CAAC,MAAc;CAGlC,IAAI,CAAC,iBAAiB,KAAK,WAAW,GACpC,MAAM,IAAI,MACR,+DAA+D,KAAK,UAAU,WAAW,GAC3F;CAGF,OAAO,iBAAiB,WAAW;AACrC"}
1
+ {"version":3,"file":"sections-Ce63wbtG.js","names":[],"sources":["../src/utils/sections.ts"],"sourcesContent":["import { DEFAULT_CONTAINER_ID, documentTemplate } from '~/constants';\nimport type { Section } from '~/types';\n\nimport {\n type DocumentOptions,\n type SectionOptions,\n createSectionPreviewCache,\n generateSectionPreview,\n generateSectionPreviews,\n getSections,\n parseDocument,\n replaceDocumentSections,\n} from './ast/document';\n\nexport const extractSections = (\n code: string,\n options?: SectionOptions,\n): Section[] => {\n const doc = parseDocument(code, options);\n\n return doc ? getSections(doc, options) : [];\n};\n\nexport const replaceSections = (\n code: string,\n sections: string[],\n options?: DocumentOptions,\n): string => {\n return replaceDocumentSections(code, sections, options);\n};\n\nexport const generateSection = (\n code: string,\n fullCode: string,\n options?: DocumentOptions,\n) => {\n return generateSectionPreview(fullCode, code, options);\n};\n\n// Batched form of generateSection() — computes every section's preview from\n// a single parse of fullCode instead of one parseDocument call per section\n// (see generateSectionPreviews). Unchanged sections come back byte-identical\n// to their previous preview string, which is what lets a caller pass each\n// one down as a stable prop (see Renderer's React.memo).\nexport const generateSections = (\n codes: string[],\n fullCode: string,\n options?: DocumentOptions,\n): string[] => {\n return generateSectionPreviews(fullCode, codes, options);\n};\n\n// A new, empty document `Live.Dnd` can add sections to: an `App` component\n// rendering the container element and nothing else. Start a controlled\n// `Live.Dnd` from this rather than an empty string, which has no container,\n// so nothing dropped on the canvas would land anywhere (#449). Pass the same\n// `containerId` you give `Live.Dnd`.\nexport const createDocument = ({\n containerId = DEFAULT_CONTAINER_ID,\n}: DocumentOptions = {}): string => {\n // The id is written into a JSX attribute as-is, so anything that could end\n // the attribute or the tag is refused rather than producing broken source.\n if (!/^[^\\s\"'<>{}]+$/.test(containerId)) {\n throw new Error(\n `createDocument: containerId must be a plain element id, got ${JSON.stringify(containerId)}`,\n );\n }\n\n return documentTemplate(containerId);\n};\n\n// Incremental counterpart to generateSections() — see createSectionPreviewCache\n// (#131). Pass a fresh instance's `compute` in place of generateSections()\n// where the caller can keep it alive across renders (e.g. Dnd holds one via\n// `useState(() => createSectionPreviewCache())`).\nexport { createSectionPreviewCache };\nexport type {\n DocumentOptions,\n SectionOptions,\n SectionPreviewCache,\n} from './ast/document';\n"],"mappings":";;;AAcA,MAAa,mBACX,MACA,YACc;CACd,MAAM,MAAM,cAAc,MAAM,OAAO;CAEvC,OAAO,MAAM,YAAY,KAAK,OAAO,IAAI,CAAC;AAC5C;AAEA,MAAa,mBACX,MACA,UACA,YACW;CACX,OAAO,wBAAwB,MAAM,UAAU,OAAO;AACxD;AAEA,MAAa,mBACX,MACA,UACA,YACG;CACH,OAAO,uBAAuB,UAAU,MAAM,OAAO;AACvD;AAOA,MAAa,oBACX,OACA,UACA,YACa;CACb,OAAO,wBAAwB,UAAU,OAAO,OAAO;AACzD;AAOA,MAAa,kBAAkB,EAC7B,cAAc,yBACK,CAAC,MAAc;CAGlC,IAAI,CAAC,iBAAiB,KAAK,WAAW,GACpC,MAAM,IAAI,MACR,+DAA+D,KAAK,UAAU,WAAW,GAC3F;CAGF,OAAO,iBAAiB,WAAW;AACrC"}
@@ -63,7 +63,7 @@ const isSyncedStyle = (node) => {
63
63
  const removeClone = (clone) => {
64
64
  clone.remove();
65
65
  };
66
- const reconcileStyles = (sourceDocument, target, manager, enabled, transformStyle = (content) => content) => {
66
+ const reconcileStyles = (sourceDocument, target, manager, enabled, transformStyle = (content) => content, anchor = null) => {
67
67
  if (!enabled) {
68
68
  manager.clones.forEach(removeClone);
69
69
  manager.clones.clear();
@@ -85,11 +85,35 @@ const reconcileStyles = (sourceDocument, target, manager, enabled, transformStyl
85
85
  clone = createClone(source, target, transformStyle);
86
86
  manager.clones.set(source, clone);
87
87
  } else copyAttributes(source, clone, transformStyle);
88
- const nextSibling = previous ? previous.nextSibling : Array.from(container.childNodes).find(isSyncedStyle) ?? null;
88
+ const nextSibling = previous ? previous.nextSibling : Array.from(container.childNodes).find(isSyncedStyle) ?? (anchor?.parentNode === container ? anchor : null);
89
89
  if (clone !== nextSibling) container.insertBefore(clone, nextSibling);
90
90
  previous = clone;
91
91
  });
92
92
  };
93
+ const createRootAttributeSync = () => ({
94
+ attributes: /* @__PURE__ */ new Set(),
95
+ classes: /* @__PURE__ */ new Set()
96
+ });
97
+ const isMirroredAttribute = (name) => name.startsWith("data-");
98
+ const reconcileRootAttributes = (source, target, sync, enabled) => {
99
+ const attributes = enabled ? Array.from(source.attributes).filter(({ name }) => isMirroredAttribute(name)) : [];
100
+ const classes = enabled ? Array.from(source.classList) : [];
101
+ const attributeNames = new Set(attributes.map(({ name }) => name));
102
+ const classNames = new Set(classes);
103
+ sync.attributes.forEach((name) => {
104
+ if (!attributeNames.has(name)) target.removeAttribute(name);
105
+ });
106
+ attributes.forEach(({ name, value }) => {
107
+ if (target.getAttribute(name) !== value) target.setAttribute(name, value);
108
+ });
109
+ sync.classes.forEach((name) => {
110
+ if (!classNames.has(name)) target.classList.remove(name);
111
+ });
112
+ classes.forEach((name) => target.classList.add(name));
113
+ if (!target.classList.length) target.removeAttribute("class");
114
+ sync.attributes = attributeNames;
115
+ sync.classes = classNames;
116
+ };
93
117
  //#endregion
94
118
  //#region src/components/frame/viewport-units.ts
95
119
  const UNIT_MAP = {
@@ -119,6 +143,7 @@ const rewriteInlineViewportUnits = (root) => {
119
143
  };
120
144
  //#endregion
121
145
  //#region src/components/frame/iframe.tsx
146
+ const CONTAINER_STYLE_ID = "autoheight-container";
122
147
  const EMPTY_STRING_ARRAY = [];
123
148
  const isCssAnimation = (win, animation) => typeof win.CSSAnimation === "function" && animation instanceof win.CSSAnimation;
124
149
  const isCssTransition = (win, animation) => typeof win.CSSTransition === "function" && animation instanceof win.CSSTransition;
@@ -155,10 +180,12 @@ const IFrame = ({ title = "Live Preview", sandbox, style = {}, scripts = EMPTY_S
155
180
  const lastProbeHeightRef = useRef(void 0);
156
181
  const shouldAutoHeight = autoHeight && style.height == null;
157
182
  const styleManagerRef = useRef(createStyleSyncManager());
183
+ const rootAttributeSyncRef = useRef(createRootAttributeSync());
158
184
  const applyStyle = useCallback(() => {
159
185
  const doc = iframeRef.current?.contentDocument;
160
186
  if (!doc) return;
161
- reconcileStyles(document, doc, styleManagerRef.current, syncStyle, convertViewportUnits);
187
+ reconcileStyles(document, doc, styleManagerRef.current, syncStyle, convertViewportUnits, doc.getElementById(CONTAINER_STYLE_ID));
188
+ reconcileRootAttributes(document.documentElement, doc.documentElement, rootAttributeSyncRef.current, syncStyle);
162
189
  }, [syncStyle]);
163
190
  const applyStyleTimeoutRef = useRef(void 0);
164
191
  const debouncedApplyStyle = useCallback(() => {
@@ -174,6 +201,10 @@ const IFrame = ({ title = "Live Preview", sandbox, style = {}, scripts = EMPTY_S
174
201
  subtree: true,
175
202
  attributes: true
176
203
  });
204
+ useMutationObserver(document.documentElement, debouncedApplyStyle, {
205
+ enabled: syncStyle,
206
+ attributes: true
207
+ });
177
208
  useEffect(() => {
178
209
  const $iframe = iframeRef.current;
179
210
  if (!$iframe) return;
@@ -246,7 +277,6 @@ const IFrame = ({ title = "Live Preview", sandbox, style = {}, scripts = EMPTY_S
246
277
  for (let index = stylesheets.length; index < prevStylesheetCountRef.current; index++) doc.getElementById(`injected-stylesheet-${index}`)?.remove();
247
278
  prevStylesheetCountRef.current = stylesheets.length;
248
279
  }, [styles, stylesheets]);
249
- const CONTAINER_STYLE_ID = "autoheight-container";
250
280
  const HIDE_SCROLLBAR_STYLE_ID = "autoheight-hide-scrollbar";
251
281
  const ensureContainerStyle = (doc, probeHeight) => {
252
282
  let styleEl = doc.getElementById(CONTAINER_STYLE_ID);
@@ -527,4 +557,4 @@ const useDynamicTailwind = (code, enabled) => {
527
557
  //#endregion
528
558
  export { Frame as a, useStableModules as i, useCompiledModule as n, baseModules as r, useDynamicTailwind as t };
529
559
 
530
- //# sourceMappingURL=use-dynamic-tailwind-BDfytfYC.js.map
560
+ //# sourceMappingURL=use-dynamic-tailwind-QKcGvUJs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-dynamic-tailwind-QKcGvUJs.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 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 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 // 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,3 +1,3 @@
1
- import { a as generateSectionPreview, c as inspectDocument, i as generateDocumentCode, l as parseDocument, n as createSectionPreviewCache, o as generateSectionPreviews, r as fillSectionIds, s as getSections, t as clearDocumentParseCache, u as replaceDocumentSections } from "../../document-C3eCRQlj.js";
2
- import { A as extractObjectProperties, C as getStructuredValue, D as canLosslesslyEvaluateSource, E as arrayExpressionToCode, F as generateCode, M as parseArrayExpression, N as parseValue, O as createNodeFromValue, P as setEditableValue, S as getCurrentValue, T as parseBindingExpression, _ as updateArrayItemProperty, a as bulkUpdate, c as getChildrenSignatures, d as appendArrayItem, f as duplicateArrayItems, g as removeArrayItems, h as parseItems, i as replaceIds, j as flattenEditableValue, k as extractNodeValue, l as clearExtractCache, m as moveArrayItems, n as clone, o as update, p as moveArrayItem, r as fillIds, s as updateAll, t as validateBindingValue, u as extract, v as updateArrayItemValue, w as parseBinding, x as findEditableChildren, y as canStructurallyEditArray } from "../../ast-CMD20ykx.js";
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, y as generateCode } from "../../document-C-hGKMkk.js";
2
+ import { A as parseValue, C as arrayExpressionToCode, D as extractObjectProperties, E as extractNodeValue, O as flattenEditableValue, S as parseBindingExpression, T as createNodeFromValue, a as getChildrenSignatures, b as getStructuredValue, c as appendArrayItem, d as moveArrayItems, f as parseItems, g as canStructurallyEditArray, h as updateArrayItemValue, i as updateAll, j as setEditableValue, k as parseArrayExpression, l as duplicateArrayItems, m as updateArrayItemProperty, n as bulkUpdate, o as clearExtractCache, p as removeArrayItems, r as update, s as extract, t as validateBindingValue, u as moveArrayItem, v as findEditableChildren, w as canLosslesslyEvaluateSource, x as parseBinding, y as getCurrentValue } from "../../ast-CehbCXwh.js";
3
3
  export { appendArrayItem, arrayExpressionToCode, bulkUpdate, canLosslesslyEvaluateSource, canStructurallyEditArray, clearDocumentParseCache, clearExtractCache, clone, createNodeFromValue, createSectionPreviewCache, duplicateArrayItems, extract, extractNodeValue, extractObjectProperties, fillIds, fillSectionIds, findEditableChildren, flattenEditableValue, generateCode, generateDocumentCode, generateSectionPreview, generateSectionPreviews, getChildrenSignatures, getCurrentValue, getSections, getStructuredValue, inspectDocument, moveArrayItem, moveArrayItems, parseArrayExpression, parseBinding, parseBindingExpression, parseDocument, parseItems, parseValue, removeArrayItems, replaceDocumentSections, replaceIds, setEditableValue, update, updateAll, updateArrayItemProperty, updateArrayItemValue, validateBindingValue };