@jbpark/live-editor 2.0.2 → 2.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{ast-BKQmEBj9.js → ast-BhbOxomw.js} +2 -2
- package/dist/{ast-BKQmEBj9.js.map → ast-BhbOxomw.js.map} +1 -1
- package/dist/{boundary-b-U70XU_.js → boundary-DOBK2HnX.js} +2 -2
- package/dist/{boundary-b-U70XU_.js.map → boundary-DOBK2HnX.js.map} +1 -1
- package/dist/{context-Yb81ZxnN.js → context-DOMX_RsU.js} +3 -3
- package/dist/{context-Yb81ZxnN.js.map → context-DOMX_RsU.js.map} +1 -1
- package/dist/{core-mdFWxqB2.js → core-D-ZvVhdn.js} +2 -2
- package/dist/{core-mdFWxqB2.js.map → core-D-ZvVhdn.js.map} +1 -1
- package/dist/dnd/index.js +1 -1
- package/dist/{dnd-Cyuif73L.js → dnd-E8LbmRfE.js} +26 -12
- package/dist/dnd-E8LbmRfE.js.map +1 -0
- package/dist/{document-DEx4WyCn.js → document-Ddw-tfnt.js} +37 -155
- package/dist/{document-DEx4WyCn.js.map → document-Ddw-tfnt.js.map} +1 -1
- package/dist/editor/index.js +2 -2
- package/dist/{editor-jzeeLkwi.js → editor-ka05eWyz.js} +3 -3
- package/dist/{editor-jzeeLkwi.js.map → editor-ka05eWyz.js.map} +1 -1
- package/dist/error/index.js +2 -2
- package/dist/{error-C47PVZgY.js → error-fzPGTaFe.js} +2 -2
- package/dist/{error-C47PVZgY.js.map → error-fzPGTaFe.js.map} +1 -1
- package/dist/index.js +5 -5
- package/dist/preview/index.js +1 -1
- package/dist/{preview-8OZDsfGT.js → preview-EsfzEjis.js} +4 -4
- package/dist/{preview-8OZDsfGT.js.map → preview-EsfzEjis.js.map} +1 -1
- package/dist/provider/index.js +1 -1
- package/dist/{use-dynamic-tailwind-_3tC-S5j.js → use-dynamic-tailwind-CRMGuKnH.js} +2 -2
- package/dist/{use-dynamic-tailwind-_3tC-S5j.js.map → use-dynamic-tailwind-CRMGuKnH.js.map} +1 -1
- package/dist/utils/ast/index.js +2 -2
- package/dist/utils/index.js +2 -2
- package/dist/{utils-CXq1KaM3.js → utils-B6zpA7mT.js} +2 -2
- package/dist/{utils-CXq1KaM3.js.map → utils-B6zpA7mT.js.map} +1 -1
- package/package.json +7 -2
- package/dist/dnd-Cyuif73L.js.map +0 -1
package/dist/editor/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { t as Core } from "../core-
|
|
2
|
-
import { t as Editor } from "../editor-
|
|
1
|
+
import { t as Core } from "../core-D-ZvVhdn.js";
|
|
2
|
+
import { t as Editor } from "../editor-ka05eWyz.js";
|
|
3
3
|
export { Core, Editor as default };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { y as DEFAULT_TEMPLATE } from "./document-
|
|
1
|
+
import { y as DEFAULT_TEMPLATE } from "./document-Ddw-tfnt.js";
|
|
2
2
|
import { i as usePreview, r as useError } from "./states-Ci1AvoQ9.js";
|
|
3
|
-
import { n as useFormatCode, t as Core } from "./core-
|
|
3
|
+
import { n as useFormatCode, t as Core } from "./core-D-ZvVhdn.js";
|
|
4
4
|
import { useCallback, useEffect } from "react";
|
|
5
5
|
import { jsx } from "react/jsx-runtime";
|
|
6
6
|
import { useDebouncedValue } from "@jbpark/use-hooks";
|
|
@@ -48,4 +48,4 @@ Editor.Core = Core;
|
|
|
48
48
|
//#endregion
|
|
49
49
|
export { Editor as t };
|
|
50
50
|
|
|
51
|
-
//# sourceMappingURL=editor-
|
|
51
|
+
//# sourceMappingURL=editor-ka05eWyz.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"editor-
|
|
1
|
+
{"version":3,"file":"editor-ka05eWyz.js","names":["Editor","EditorImpl"],"sources":["../src/components/editor/editor.tsx","../src/components/editor/index.ts"],"sourcesContent":["import { useCallback, useEffect } from 'react';\n\nimport { useDebouncedValue } from '@jbpark/use-hooks';\n\nimport { useError, usePreview } from '~/components/context/states';\nimport { DEFAULT_TEMPLATE } from '~/constants';\n\nimport Core, { type Props as CoreProps } from './core';\nimport { useFormatCode } from './use-format-code';\n\nexport interface EditorRenderData {\n value: string;\n onChange: (value: string) => void;\n // Same prettier-based formatting Core's own Cmd+S uses - reused rather\n // than reimplemented, so a custom editor can offer equivalent\n // format-on-save behavior without duplicating the prettier wiring.\n formatCode: (code: string) => Promise<string>;\n}\n\nexport interface Props extends Omit<CoreProps, 'onSave' | 'onError'> {\n defaultValue?: string;\n debounce?: number;\n // Full replacement for the built-in CodeMirror editor. Editor still owns\n // syncing `value` to the shared preview code (debounced), regardless of\n // which UI renders it - only the editing surface itself is customizable.\n renderEditor?: (data: EditorRenderData) => React.ReactNode;\n}\n\nconst Editor = ({\n defaultValue,\n value: _value,\n debounce = 1000,\n onChange: _onChange,\n renderEditor,\n fragment,\n prettierOptions,\n ...props\n}: Props) => {\n const { setCode } = usePreview();\n const { setError } = useError();\n\n const value =\n _value.trim() === '' ? defaultValue || DEFAULT_TEMPLATE : _value;\n\n const onChange = useCallback(\n (value: string) => {\n _onChange?.(value);\n },\n [_onChange],\n );\n\n const onSave = useCallback(\n (formattedCode: string) => {\n setCode(formattedCode);\n },\n [setCode],\n );\n\n const onError = useCallback(\n (error: string | null) => {\n setError(error);\n },\n [setError],\n );\n\n const formatCode = useFormatCode({ fragment, prettierOptions });\n\n const debouncedValue = useDebouncedValue(value, debounce);\n\n useEffect(() => {\n setCode(debouncedValue);\n }, [debouncedValue, setCode]);\n\n if (renderEditor) {\n return renderEditor({ value, onChange, formatCode });\n }\n\n return (\n <Core\n value={value}\n onChange={onChange}\n onSave={onSave}\n onError={onError}\n fragment={fragment}\n prettierOptions={prettierOptions}\n {...props}\n />\n );\n};\n\nexport default Editor;\n","import Core, { type Props as CoreProps } from './core';\nimport EditorImpl, { type EditorRenderData, type Props } from './editor';\n\ntype EditorComponent = typeof EditorImpl & { Core: typeof Core };\n\nconst Editor = EditorImpl as EditorComponent;\n\nEditor.Core = Core;\n\nexport { Core };\nexport type { Props, CoreProps, EditorRenderData };\nexport default Editor;\n"],"mappings":";;;;;;;AA4BA,MAAMA,YAAU,EACd,cACA,OAAO,QACP,WAAW,KACX,UAAU,WACV,cACA,UACA,iBACA,GAAG,YACQ;CACX,MAAM,EAAE,YAAY,WAAW;CAC/B,MAAM,EAAE,aAAa,SAAS;CAE9B,MAAM,QACJ,OAAO,KAAK,MAAM,KAAK,gBAAgB,mBAAmB;CAE5D,MAAM,WAAW,aACd,UAAkB;EACjB,YAAY,KAAK;CACnB,GACA,CAAC,SAAS,CACZ;CAEA,MAAM,SAAS,aACZ,kBAA0B;EACzB,QAAQ,aAAa;CACvB,GACA,CAAC,OAAO,CACV;CAEA,MAAM,UAAU,aACb,UAAyB;EACxB,SAAS,KAAK;CAChB,GACA,CAAC,QAAQ,CACX;CAEA,MAAM,aAAa,cAAc;EAAE;EAAU;CAAgB,CAAC;CAE9D,MAAM,iBAAiB,kBAAkB,OAAO,QAAQ;CAExD,gBAAgB;EACd,QAAQ,cAAc;CACxB,GAAG,CAAC,gBAAgB,OAAO,CAAC;CAE5B,IAAI,cACF,OAAO,aAAa;EAAE;EAAO;EAAU;CAAW,CAAC;CAGrD,OACE,oBAAC,MAAD;EACS;EACG;EACF;EACC;EACC;EACO;EACjB,GAAI;CACL,CAAA;AAEL;;;ACnFA,MAAM,SAASC;AAEf,OAAO,OAAO"}
|
package/dist/error/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { t as ErrorBoundary } from "../boundary-
|
|
2
|
-
import { n as Runtime, r as Guard, t as Error } from "../error-
|
|
1
|
+
import { t as ErrorBoundary } from "../boundary-DOBK2HnX.js";
|
|
2
|
+
import { n as Runtime, r as Guard, t as Error } from "../error-fzPGTaFe.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-
|
|
2
|
+
import { n as Error$1, t as ErrorBoundary } from "./boundary-DOBK2HnX.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-
|
|
82
|
+
//# sourceMappingURL=error-fzPGTaFe.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error-
|
|
1
|
+
{"version":3,"file":"error-fzPGTaFe.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"}
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { t as ContextProvider } from "./context-
|
|
2
|
-
import { t as Dnd } from "./dnd-
|
|
3
|
-
import { t as Editor } from "./editor-
|
|
4
|
-
import { t as Error } from "./error-
|
|
5
|
-
import { t as Preview } from "./preview-
|
|
1
|
+
import { t as ContextProvider } from "./context-DOMX_RsU.js";
|
|
2
|
+
import { t as Dnd } from "./dnd-E8LbmRfE.js";
|
|
3
|
+
import { t as Editor } from "./editor-ka05eWyz.js";
|
|
4
|
+
import { t as Error } from "./error-fzPGTaFe.js";
|
|
5
|
+
import { t as Preview } from "./preview-EsfzEjis.js";
|
|
6
6
|
import { jsx } from "react/jsx-runtime";
|
|
7
7
|
//#region src/index.tsx
|
|
8
8
|
const App = ({ children }) => {
|
package/dist/preview/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as Preview } from "../preview-
|
|
1
|
+
import { t as Preview } from "../preview-EsfzEjis.js";
|
|
2
2
|
export { Preview as default };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { r as cn } from "./utils-
|
|
1
|
+
import { r as cn } from "./utils-B6zpA7mT.js";
|
|
2
2
|
import { i as usePreview, r as useError } from "./states-Ci1AvoQ9.js";
|
|
3
|
-
import { n as useCompiledModule, r as Frame, t as useDynamicTailwind } from "./use-dynamic-tailwind-
|
|
4
|
-
import { t as Error } from "./error-
|
|
3
|
+
import { n as useCompiledModule, r as Frame, t as useDynamicTailwind } from "./use-dynamic-tailwind-CRMGuKnH.js";
|
|
4
|
+
import { t as Error } from "./error-fzPGTaFe.js";
|
|
5
5
|
import { useMemo } from "react";
|
|
6
6
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
7
7
|
//#region src/components/preview/client.tsx
|
|
@@ -79,4 +79,4 @@ const Preview = ({ code, props = {}, modules = {}, dynamicTailwind = false, prov
|
|
|
79
79
|
//#endregion
|
|
80
80
|
export { Preview as t };
|
|
81
81
|
|
|
82
|
-
//# sourceMappingURL=preview-
|
|
82
|
+
//# sourceMappingURL=preview-EsfzEjis.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preview-
|
|
1
|
+
{"version":3,"file":"preview-EsfzEjis.js","names":["LiveError"],"sources":["../src/components/preview/client.tsx","../src/components/preview/preview.tsx"],"sourcesContent":["'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';\n\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 = {},\n 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 || 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';\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 = {},\n 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":";;;;;;;AAaA,MAAM,UAAU,EACd,MAAM,QAAQ,IACd,WACA,WACA,QAAQ,CAAC,GACT,UAAU,CAAC,GACX,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,SAAS;CAE/B,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,CAAC,GACT,UAAU,CAAC,GACX,kBAAkB,OAClB,UACA,GAAG,gBACQ;CACX,OACE,oBAAC,QAAD;EACQ;EACC;EACE;EACQ;EACP;EACV,GAAI;CACL,CAAA;AAEL"}
|
package/dist/provider/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as ContextProvider } from "../context-
|
|
1
|
+
import { t as ContextProvider } from "../context-DOMX_RsU.js";
|
|
2
2
|
export { ContextProvider as default };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as compile, l as getCachedScriptBlob, t as baseModules } from "./utils-
|
|
1
|
+
import { i as compile, l as getCachedScriptBlob, t as baseModules } from "./utils-B6zpA7mT.js";
|
|
2
2
|
import { t as generateTailwindCSSFromDOM } from "./tailwind-CbjdLyVX.js";
|
|
3
3
|
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
4
4
|
import { jsx } from "react/jsx-runtime";
|
|
@@ -456,4 +456,4 @@ const useDynamicTailwind = (code, enabled) => {
|
|
|
456
456
|
//#endregion
|
|
457
457
|
export { useCompiledModule as n, Frame as r, useDynamicTailwind as t };
|
|
458
458
|
|
|
459
|
-
//# sourceMappingURL=use-dynamic-tailwind-
|
|
459
|
+
//# sourceMappingURL=use-dynamic-tailwind-CRMGuKnH.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-dynamic-tailwind-_3tC-S5j.js","names":[],"sources":["../src/components/frame/measure.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-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` already excludes the scroll container's own border, but\n// not any padding/border on wrapper elements *between* the iframe and\n// that container (this codebase's own Sortable/Renderer/Frame don't add\n// any today, but a consumer's own `provider`/`renderPanel` wrapper\n// could) — `wrapperInsets` is the sum of those, added up by the caller\n// while walking from the iframe to the scroll container.\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).\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.\nexport const isVisuallyHidden = (computed: {\n visibility: string;\n opacity: string;\n}): boolean => computed.visibility === 'hidden' || computed.opacity === '0';\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","// 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, useRef, useState } from 'react';\nimport type { ReactNode } from 'react';\nimport { createPortal } from 'react-dom';\n\nimport { useMutationObserver, useResizeObserver } from '@jbpark/use-hooks';\n\nimport { getCachedScriptBlob } from '~/utils';\n\nimport {\n FALLBACK_PROBE_HEIGHT,\n computeProbeHeight,\n estimatePositionedElementHeight,\n isVisuallyHidden,\n} from './measure';\nimport {\n convertViewportUnits,\n rewriteInlineViewportUnits,\n} from './viewport-units';\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\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 const shouldAutoHeight = autoHeight && style.height == null;\n\n const styleManagerRef = useRef<{\n copiedLinks: Set<string>;\n copiedStyles: Set<string>;\n }>({\n copiedLinks: new Set(),\n copiedStyles: new Set(),\n });\n\n const applyStyle = useCallback(() => {\n const doc = iframeRef.current?.contentDocument;\n\n if (!doc || !syncStyle) {\n return;\n }\n\n const manager = styleManagerRef.current;\n\n const links = document.querySelectorAll<HTMLLinkElement>(\n 'link[rel=\"stylesheet\"]',\n );\n const newLinks = Array.from(links)\n .map(link => link.href)\n .filter(href => !manager.copiedLinks.has(href));\n\n if (newLinks.length) {\n const fragment = doc.createDocumentFragment();\n newLinks.forEach(href => {\n const link = doc.createElement('link');\n link.rel = 'stylesheet';\n link.href = href;\n fragment.appendChild(link);\n manager.copiedLinks.add(href);\n });\n doc.head.appendChild(fragment);\n }\n\n const styles = document.querySelectorAll<HTMLStyleElement>('style');\n const newStyles = Array.from(styles)\n .map(style => style.textContent || '')\n .filter(content => {\n if (!content) {\n return false;\n }\n const hash = content.length + content.slice(0, 50);\n if (manager.copiedStyles.has(hash)) {\n return false;\n }\n manager.copiedStyles.add(hash);\n return true;\n });\n\n if (newStyles.length) {\n const fragment = doc.createDocumentFragment();\n newStyles.forEach(content => {\n const style = doc.createElement('style');\n // Host styles can legitimately use vh/svh/etc themselves (e.g. a\n // shared design-system stylesheet) — converted the same way as\n // the styles/stylesheets props below, so they resolve against\n // the preview's own probe height instead of the iframe's, once\n // autoHeight's container context (see ensureContainerStyle) is\n // active.\n style.textContent = convertViewportUnits(content);\n fragment.appendChild(style);\n });\n doc.head.appendChild(fragment);\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 attributeFilter: ['href'],\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 Promise.all(pendingScripts.map(getCachedScriptBlob)).then(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 // 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.\n const CONTAINER_STYLE_ID = 'autoheight-container';\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 styleEl.textContent = `html { container-type: size !important; height: ${probeHeight}px !important; }`;\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.\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.\n const MEASUREMENT_OVERRIDE_STYLE_ID = 'autoheight-measurement-overrides';\n\n const withMeasurementOverrides = (doc: Document, measure: () => void) => {\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 styleEl.textContent = [\n '*, *::before, *::after { transition: none !important; }',\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 }\n\n styleEl.media = 'all';\n measure();\n styleEl.media = 'not all';\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 let wrapperInsets = 0;\n let node = iframe.parentElement;\n\n while (node && node !== scrollParent) {\n const style = win.getComputedStyle(node);\n\n wrapperInsets +=\n parseFloat(style.borderTopWidth) +\n parseFloat(style.borderBottomWidth) +\n parseFloat(style.paddingTop) +\n parseFloat(style.paddingBottom);\n\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 withMeasurementOverrides(doc, () => {\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 if (isVisuallyHidden(style)) {\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 }, [shouldAutoHeight, mountNode]);\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 const content = mountNode\n ? createPortal(children(mountNode), mountNode)\n : null;\n\n return (\n <iframe\n ref={iframeRef}\n style={{\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 { useCallback, useLayoutEffect, useRef, useState } from 'react';\nimport type { ReactNode } from 'react';\nimport { createPortal } from 'react-dom';\n\nimport { useMutationObserver } from '@jbpark/use-hooks';\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<{\n copiedLinks: Set<string>;\n copiedStyles: Set<string>;\n }>({\n copiedLinks: new Set(),\n copiedStyles: new Set(),\n });\n\n const applyStyle = useCallback(() => {\n const shadowRoot = shadowRootRef.current;\n\n if (!shadowRoot || !syncStyle) {\n return;\n }\n\n const manager = styleManagerRef.current;\n\n const links = document.querySelectorAll<HTMLLinkElement>(\n 'link[rel=\"stylesheet\"]',\n );\n const newLinks = Array.from(links)\n .map(link => link.href)\n .filter(href => !manager.copiedLinks.has(href));\n\n if (newLinks.length) {\n const fragment = document.createDocumentFragment();\n newLinks.forEach(href => {\n const link = document.createElement('link');\n link.rel = 'stylesheet';\n link.href = href;\n fragment.appendChild(link);\n manager.copiedLinks.add(href);\n });\n shadowRoot.appendChild(fragment);\n }\n\n const styleTags = document.querySelectorAll<HTMLStyleElement>('style');\n const newStyles = Array.from(styleTags)\n .map(style => style.textContent || '')\n .filter(content => {\n if (!content) {\n return false;\n }\n const hash = content.length + content.slice(0, 50);\n if (manager.copiedStyles.has(hash)) {\n return false;\n }\n manager.copiedStyles.add(hash);\n return true;\n });\n\n if (newStyles.length) {\n const fragment = document.createDocumentFragment();\n newStyles.forEach(content => {\n const style = document.createElement('style');\n style.textContent = content;\n fragment.appendChild(style);\n });\n shadowRoot.appendChild(fragment);\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 — 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 useMutationObserver(document.head, debouncedApplyStyle, {\n enabled: syncStyle,\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: ['href'],\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 { useMemo } from 'react';\n\nimport type { Module } from '~/types';\nimport { baseModules, compile } from '~/utils';\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 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\nimport { generateTailwindCSSFromDOM } from '~/utils/tailwind';\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.\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 generateTailwindCSSFromDOM(element).then(next => {\n if (!cancelled) {\n setCss(next);\n }\n });\n\n return () => {\n cancelled = true;\n };\n }, [code, enabled, element]);\n\n return { ref, css };\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"use-dynamic-tailwind-CRMGuKnH.js","names":[],"sources":["../src/components/frame/measure.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-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` already excludes the scroll container's own border, but\n// not any padding/border on wrapper elements *between* the iframe and\n// that container (this codebase's own Sortable/Renderer/Frame don't add\n// any today, but a consumer's own `provider`/`renderPanel` wrapper\n// could) — `wrapperInsets` is the sum of those, added up by the caller\n// while walking from the iframe to the scroll container.\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).\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.\nexport const isVisuallyHidden = (computed: {\n visibility: string;\n opacity: string;\n}): boolean => computed.visibility === 'hidden' || computed.opacity === '0';\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","// 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, useRef, useState } from 'react';\nimport type { ReactNode } from 'react';\nimport { createPortal } from 'react-dom';\n\nimport { useMutationObserver, useResizeObserver } from '@jbpark/use-hooks';\n\nimport { getCachedScriptBlob } from '~/utils';\n\nimport {\n FALLBACK_PROBE_HEIGHT,\n computeProbeHeight,\n estimatePositionedElementHeight,\n isVisuallyHidden,\n} from './measure';\nimport {\n convertViewportUnits,\n rewriteInlineViewportUnits,\n} from './viewport-units';\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\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 const shouldAutoHeight = autoHeight && style.height == null;\n\n const styleManagerRef = useRef<{\n copiedLinks: Set<string>;\n copiedStyles: Set<string>;\n }>({\n copiedLinks: new Set(),\n copiedStyles: new Set(),\n });\n\n const applyStyle = useCallback(() => {\n const doc = iframeRef.current?.contentDocument;\n\n if (!doc || !syncStyle) {\n return;\n }\n\n const manager = styleManagerRef.current;\n\n const links = document.querySelectorAll<HTMLLinkElement>(\n 'link[rel=\"stylesheet\"]',\n );\n const newLinks = Array.from(links)\n .map(link => link.href)\n .filter(href => !manager.copiedLinks.has(href));\n\n if (newLinks.length) {\n const fragment = doc.createDocumentFragment();\n newLinks.forEach(href => {\n const link = doc.createElement('link');\n link.rel = 'stylesheet';\n link.href = href;\n fragment.appendChild(link);\n manager.copiedLinks.add(href);\n });\n doc.head.appendChild(fragment);\n }\n\n const styles = document.querySelectorAll<HTMLStyleElement>('style');\n const newStyles = Array.from(styles)\n .map(style => style.textContent || '')\n .filter(content => {\n if (!content) {\n return false;\n }\n const hash = content.length + content.slice(0, 50);\n if (manager.copiedStyles.has(hash)) {\n return false;\n }\n manager.copiedStyles.add(hash);\n return true;\n });\n\n if (newStyles.length) {\n const fragment = doc.createDocumentFragment();\n newStyles.forEach(content => {\n const style = doc.createElement('style');\n // Host styles can legitimately use vh/svh/etc themselves (e.g. a\n // shared design-system stylesheet) — converted the same way as\n // the styles/stylesheets props below, so they resolve against\n // the preview's own probe height instead of the iframe's, once\n // autoHeight's container context (see ensureContainerStyle) is\n // active.\n style.textContent = convertViewportUnits(content);\n fragment.appendChild(style);\n });\n doc.head.appendChild(fragment);\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 attributeFilter: ['href'],\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 Promise.all(pendingScripts.map(getCachedScriptBlob)).then(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 // 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.\n const CONTAINER_STYLE_ID = 'autoheight-container';\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 styleEl.textContent = `html { container-type: size !important; height: ${probeHeight}px !important; }`;\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.\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.\n const MEASUREMENT_OVERRIDE_STYLE_ID = 'autoheight-measurement-overrides';\n\n const withMeasurementOverrides = (doc: Document, measure: () => void) => {\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 styleEl.textContent = [\n '*, *::before, *::after { transition: none !important; }',\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 }\n\n styleEl.media = 'all';\n measure();\n styleEl.media = 'not all';\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 let wrapperInsets = 0;\n let node = iframe.parentElement;\n\n while (node && node !== scrollParent) {\n const style = win.getComputedStyle(node);\n\n wrapperInsets +=\n parseFloat(style.borderTopWidth) +\n parseFloat(style.borderBottomWidth) +\n parseFloat(style.paddingTop) +\n parseFloat(style.paddingBottom);\n\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 withMeasurementOverrides(doc, () => {\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 if (isVisuallyHidden(style)) {\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 }, [shouldAutoHeight, mountNode]);\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 const content = mountNode\n ? createPortal(children(mountNode), mountNode)\n : null;\n\n return (\n <iframe\n ref={iframeRef}\n style={{\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 { useCallback, useLayoutEffect, useRef, useState } from 'react';\nimport type { ReactNode } from 'react';\nimport { createPortal } from 'react-dom';\n\nimport { useMutationObserver } from '@jbpark/use-hooks';\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<{\n copiedLinks: Set<string>;\n copiedStyles: Set<string>;\n }>({\n copiedLinks: new Set(),\n copiedStyles: new Set(),\n });\n\n const applyStyle = useCallback(() => {\n const shadowRoot = shadowRootRef.current;\n\n if (!shadowRoot || !syncStyle) {\n return;\n }\n\n const manager = styleManagerRef.current;\n\n const links = document.querySelectorAll<HTMLLinkElement>(\n 'link[rel=\"stylesheet\"]',\n );\n const newLinks = Array.from(links)\n .map(link => link.href)\n .filter(href => !manager.copiedLinks.has(href));\n\n if (newLinks.length) {\n const fragment = document.createDocumentFragment();\n newLinks.forEach(href => {\n const link = document.createElement('link');\n link.rel = 'stylesheet';\n link.href = href;\n fragment.appendChild(link);\n manager.copiedLinks.add(href);\n });\n shadowRoot.appendChild(fragment);\n }\n\n const styleTags = document.querySelectorAll<HTMLStyleElement>('style');\n const newStyles = Array.from(styleTags)\n .map(style => style.textContent || '')\n .filter(content => {\n if (!content) {\n return false;\n }\n const hash = content.length + content.slice(0, 50);\n if (manager.copiedStyles.has(hash)) {\n return false;\n }\n manager.copiedStyles.add(hash);\n return true;\n });\n\n if (newStyles.length) {\n const fragment = document.createDocumentFragment();\n newStyles.forEach(content => {\n const style = document.createElement('style');\n style.textContent = content;\n fragment.appendChild(style);\n });\n shadowRoot.appendChild(fragment);\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 — 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 useMutationObserver(document.head, debouncedApplyStyle, {\n enabled: syncStyle,\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: ['href'],\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 { useMemo } from 'react';\n\nimport type { Module } from '~/types';\nimport { baseModules, compile } from '~/utils';\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 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\nimport { generateTailwindCSSFromDOM } from '~/utils/tailwind';\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.\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 generateTailwindCSSFromDOM(element).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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"}
|
package/dist/utils/ast/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { a as generateSectionPreview, c as parseDocument, i as generateDocumentCode, l as replaceDocumentSections, n as createSectionPreviewCache, o as generateSectionPreviews, r as fillSectionIds, s as getSections, t as clearDocumentParseCache } from "../../document-
|
|
2
|
-
import { A as parseValue, C as parseBindingExpression, D as extractObjectProperties, E as extractNodeValue, M as generateCode, O as flattenEditableValue, S as parseBinding, T as createNodeFromValue, a as extract, b as getCurrentValue, c as moveArrayItem, d as removeArrayItems, f as updateArrayItemProperty, g as replaceIds, h as fillIds, i as clearExtractCache, j as setEditableValue, k as parseArrayExpression, l as moveArrayItems, m as clone, n as bulkUpdate, o as appendArrayItem, p as updateArrayItemValue, r as update, s as duplicateArrayItems, t as validateBindingValue, u as parseItems, w as arrayExpressionToCode, x as getStructuredValue, y as findEditableChildren } from "../../ast-
|
|
1
|
+
import { a as generateSectionPreview, c as parseDocument, i as generateDocumentCode, l as replaceDocumentSections, n as createSectionPreviewCache, o as generateSectionPreviews, r as fillSectionIds, s as getSections, t as clearDocumentParseCache } from "../../document-Ddw-tfnt.js";
|
|
2
|
+
import { A as parseValue, C as parseBindingExpression, D as extractObjectProperties, E as extractNodeValue, M as generateCode, O as flattenEditableValue, S as parseBinding, T as createNodeFromValue, a as extract, b as getCurrentValue, c as moveArrayItem, d as removeArrayItems, f as updateArrayItemProperty, g as replaceIds, h as fillIds, i as clearExtractCache, j as setEditableValue, k as parseArrayExpression, l as moveArrayItems, m as clone, n as bulkUpdate, o as appendArrayItem, p as updateArrayItemValue, r as update, s as duplicateArrayItems, t as validateBindingValue, u as parseItems, w as arrayExpressionToCode, x as getStructuredValue, y as findEditableChildren } from "../../ast-BhbOxomw.js";
|
|
3
3
|
export { appendArrayItem, arrayExpressionToCode, bulkUpdate, clearDocumentParseCache, clearExtractCache, clone, createNodeFromValue, createSectionPreviewCache, duplicateArrayItems, extract, extractNodeValue, extractObjectProperties, fillIds, fillSectionIds, findEditableChildren, flattenEditableValue, generateCode, generateDocumentCode, generateSectionPreview, generateSectionPreviews, getCurrentValue, getSections, getStructuredValue, moveArrayItem, moveArrayItems, parseArrayExpression, parseBinding, parseBindingExpression, parseDocument, parseItems, parseValue, removeArrayItems, replaceDocumentSections, replaceIds, setEditableValue, update, updateArrayItemProperty, updateArrayItemValue, validateBindingValue };
|
package/dist/utils/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { n as createSectionPreviewCache } from "../document-
|
|
2
|
-
import { a as detectTypeScript, c as generateSections, d as replaceSections, f as transformCode, i as compile, l as getCachedScriptBlob, n as clearCompilationCache, o as extractSections, r as cn, s as generateSection, t as baseModules, u as preloadScripts } from "../utils-
|
|
1
|
+
import { n as createSectionPreviewCache } from "../document-Ddw-tfnt.js";
|
|
2
|
+
import { a as detectTypeScript, c as generateSections, d as replaceSections, f as transformCode, i as compile, l as getCachedScriptBlob, n as clearCompilationCache, o as extractSections, r as cn, s as generateSection, t as baseModules, u as preloadScripts } from "../utils-B6zpA7mT.js";
|
|
3
3
|
export { baseModules, clearCompilationCache, cn, compile, createSectionPreviewCache, detectTypeScript, extractSections, generateSection, generateSections, getCachedScriptBlob, preloadScripts, replaceSections, transformCode };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { S as TS_PATTERNS, _ as CONFIG, a as generateSectionPreview, c as parseDocument, d as createBoundedCache, l as replaceDocumentSections, o as generateSectionPreviews, s as getSections, x as REGEX } from "./document-
|
|
1
|
+
import { S as TS_PATTERNS, _ as CONFIG, a as generateSectionPreview, c as parseDocument, d as createBoundedCache, l as replaceDocumentSections, o as generateSectionPreviews, s as getSections, x as REGEX } from "./document-Ddw-tfnt.js";
|
|
2
2
|
import React from "react";
|
|
3
3
|
import * as Babel from "@babel/standalone";
|
|
4
4
|
import * as ui from "@jbpark/ui-kit";
|
|
@@ -3507,4 +3507,4 @@ const preloadScripts = (scripts) => {
|
|
|
3507
3507
|
//#endregion
|
|
3508
3508
|
export { detectTypeScript as a, generateSections as c, replaceSections as d, transformCode as f, compile as i, getCachedScriptBlob as l, clearCompilationCache as n, extractSections as o, cn as r, generateSection as s, baseModules as t, preloadScripts as u };
|
|
3509
3509
|
|
|
3510
|
-
//# sourceMappingURL=utils-
|
|
3510
|
+
//# sourceMappingURL=utils-B6zpA7mT.js.map
|