@jbpark/live-editor 1.8.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -9,7 +9,7 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
9
9
  import { DndContext, DragOverlay, PointerSensor, closestCenter, useDndContext, useDraggable, useDroppable, useSensor, useSensors } from "@dnd-kit/core";
10
10
  import { restrictToVerticalAxis } from "@dnd-kit/modifiers";
11
11
  import { SortableContext, arrayMove, useSortable, verticalListSortingStrategy } from "@dnd-kit/sortable";
12
- import { useDebounce, useMultiSelect, useResponsiveSize } from "@jbpark/use-hooks";
12
+ import { useDebouncedValue, useEventListener, useMultiSelect, useMutationObserver, useResizeObserver, useResponsiveSize } from "@jbpark/use-hooks";
13
13
  import { AlertCircle, ArrowDown, ArrowLeft, ArrowRight, ArrowUp, Bell, Bookmark, Calendar, Camera, Check, ChevronDown, ChevronRight, Clock, Copy, Download, Edit, Eye, Filter, Heart, Home, Image, Info, LayoutGrid, Link, Mail, Map as Map$1, Menu, MessageCircle, Phone, Plus, Search, Settings, Share, ShoppingCart, Star, Trash, Upload as Upload$1, User, X } from "lucide-react";
14
14
  import { v4 } from "uuid";
15
15
  import { createPortal } from "react-dom";
@@ -26,41 +26,45 @@ import { EditorContent, useEditor } from "@tiptap/react";
26
26
  import StarterKit from "@tiptap/starter-kit";
27
27
 
28
28
  //#region src/components/context/states.ts
29
- const PreviewContext = createContext({
30
- code: "",
31
- setCode: () => {}
32
- });
33
- const ErrorContext = createContext({
34
- error: null,
35
- setError: () => {}
36
- });
29
+ const PreviewContext = createContext(void 0);
30
+ const ErrorContext = createContext(void 0);
37
31
  const usePreview = () => {
38
- return useContext(PreviewContext);
32
+ const context = useContext(PreviewContext);
33
+ if (context === void 0) throw new Error("usePreview must be used within <Live> (ContextProvider)");
34
+ return context;
39
35
  };
40
36
  const useError = () => {
41
- return useContext(ErrorContext);
37
+ const context = useContext(ErrorContext);
38
+ if (context === void 0) throw new Error("useError must be used within <Live> (ContextProvider)");
39
+ return context;
42
40
  };
43
41
 
44
42
  //#endregion
45
43
  //#region src/components/context/context.tsx
46
44
  const ContextProvider = ({ children }) => {
47
- const [code, setCode] = useState(DEFAULT_TEMPLATE);
45
+ const [code, setCodeState] = useState(DEFAULT_TEMPLATE);
48
46
  const [error, setError] = useState(null);
47
+ const setCode = useCallback((next) => {
48
+ setError(null);
49
+ setCodeState(next);
50
+ }, []);
49
51
  useEffect(() => {
50
52
  return () => {
51
53
  clearCompilationCache();
52
54
  };
53
55
  }, []);
56
+ const previewValue = useMemo(() => ({
57
+ code,
58
+ setCode
59
+ }), [code, setCode]);
60
+ const errorValue = useMemo(() => ({
61
+ error,
62
+ setError
63
+ }), [error, setError]);
54
64
  return /* @__PURE__ */ jsx(PreviewContext.Provider, {
55
- value: {
56
- code,
57
- setCode
58
- },
65
+ value: previewValue,
59
66
  children: /* @__PURE__ */ jsx(ErrorContext.Provider, {
60
- value: {
61
- error,
62
- setError
63
- },
67
+ value: errorValue,
64
68
  children
65
69
  })
66
70
  });
@@ -72,7 +76,9 @@ const EMPTY_STRING_ARRAY = [];
72
76
  const IFrame = ({ title = "Live Preview", sandbox, style = {}, scripts = EMPTY_STRING_ARRAY, styles = EMPTY_STRING_ARRAY, stylesheets = EMPTY_STRING_ARRAY, autoHeight = false, syncStyle = false, children, onLoaded, ...props }) => {
73
77
  const iframeRef = useRef(null);
74
78
  const [mountNode, setMountNode] = useState(null);
75
- const scriptsLoadedRef = useRef(false);
79
+ const loadedScriptsRef = useRef(/* @__PURE__ */ new Set());
80
+ const prevStyleCountRef = useRef(0);
81
+ const prevStylesheetCountRef = useRef(0);
76
82
  const shouldAutoHeight = autoHeight && style.height == null;
77
83
  const styleManagerRef = useRef({
78
84
  copiedLinks: /* @__PURE__ */ new Set(),
@@ -113,6 +119,21 @@ const IFrame = ({ title = "Live Preview", sandbox, style = {}, scripts = EMPTY_S
113
119
  doc.head.appendChild(fragment);
114
120
  }
115
121
  }, [syncStyle]);
122
+ const applyStyleTimeoutRef = useRef(void 0);
123
+ const debouncedApplyStyle = useCallback(() => {
124
+ clearTimeout(applyStyleTimeoutRef.current);
125
+ applyStyleTimeoutRef.current = window.setTimeout(applyStyle, 50);
126
+ }, [applyStyle]);
127
+ useEffect(() => {
128
+ return () => clearTimeout(applyStyleTimeoutRef.current);
129
+ }, []);
130
+ useMutationObserver(document.head, debouncedApplyStyle, {
131
+ enabled: syncStyle,
132
+ childList: true,
133
+ subtree: true,
134
+ attributes: true,
135
+ attributeFilter: ["href"]
136
+ });
116
137
  useEffect(() => {
117
138
  const $iframe = iframeRef.current;
118
139
  if (!$iframe) return;
@@ -129,9 +150,10 @@ const IFrame = ({ title = "Live Preview", sandbox, style = {}, scripts = EMPTY_S
129
150
  }
130
151
  setMountNode(node);
131
152
  applyStyle();
132
- if (scripts.length && !scriptsLoadedRef.current) {
133
- scriptsLoadedRef.current = true;
134
- Promise.all(scripts.map(getCachedScriptBlob)).then((blobUrls) => {
153
+ const pendingScripts = scripts.filter((src) => !loadedScriptsRef.current.has(src));
154
+ if (pendingScripts.length) {
155
+ pendingScripts.forEach((src) => loadedScriptsRef.current.add(src));
156
+ Promise.all(pendingScripts.map(getCachedScriptBlob)).then((blobUrls) => {
135
157
  if (!doc.head) return;
136
158
  const fragment = doc.createDocumentFragment();
137
159
  blobUrls.forEach((blobUrl) => {
@@ -145,28 +167,11 @@ const IFrame = ({ title = "Live Preview", sandbox, style = {}, scripts = EMPTY_S
145
167
  onLoaded?.();
146
168
  };
147
169
  $iframe.addEventListener("load", onLoad);
148
- let timeoutId;
149
- let observer = null;
150
- if (syncStyle) {
151
- observer = new MutationObserver(() => {
152
- clearTimeout(timeoutId);
153
- timeoutId = window.setTimeout(applyStyle, 50);
154
- });
155
- observer.observe(document.head, {
156
- childList: true,
157
- subtree: true,
158
- attributes: true,
159
- attributeFilter: ["href"]
160
- });
161
- }
162
170
  if ($iframe.contentDocument?.readyState === "complete") onLoad();
163
171
  return () => {
164
172
  $iframe.removeEventListener("load", onLoad);
165
- observer?.disconnect();
166
- clearTimeout(timeoutId);
167
173
  };
168
174
  }, [
169
- syncStyle,
170
175
  scripts,
171
176
  onLoaded,
172
177
  applyStyle
@@ -184,6 +189,8 @@ const IFrame = ({ title = "Live Preview", sandbox, style = {}, scripts = EMPTY_S
184
189
  }
185
190
  if (styleEl.textContent !== css) styleEl.textContent = css;
186
191
  });
192
+ for (let index = styles.length; index < prevStyleCountRef.current; index++) doc.getElementById(`injected-style-${index}`)?.remove();
193
+ prevStyleCountRef.current = styles.length;
187
194
  stylesheets.forEach((href, index) => {
188
195
  const linkId = `injected-stylesheet-${index}`;
189
196
  let linkEl = doc.getElementById(linkId);
@@ -195,67 +202,76 @@ const IFrame = ({ title = "Live Preview", sandbox, style = {}, scripts = EMPTY_S
195
202
  }
196
203
  if (linkEl.href !== href) linkEl.href = href;
197
204
  });
205
+ for (let index = stylesheets.length; index < prevStylesheetCountRef.current; index++) doc.getElementById(`injected-stylesheet-${index}`)?.remove();
206
+ prevStylesheetCountRef.current = stylesheets.length;
198
207
  }, [styles, stylesheets]);
199
- useEffect(() => {
208
+ const updateHeight = useCallback(() => {
200
209
  if (!shouldAutoHeight || !mountNode || !iframeRef.current) return;
201
210
  const iframe = iframeRef.current;
202
- const updateHeight = () => {
203
- const scrollParent = iframe.closest("[data-frame-container]");
204
- const savedScrollTop = scrollParent?.scrollTop ?? 0;
205
- iframe.style.height = "0px";
206
- let childrenHeight = 0;
207
- Array.from(mountNode.children).forEach((child) => {
208
- const el = child;
209
- childrenHeight = Math.max(childrenHeight, el.scrollHeight);
210
- });
211
- const win = iframe.contentDocument?.defaultView;
212
- mountNode.querySelectorAll("[data-floating-ui-focusable]").forEach((popup) => {
213
- if (popup.offsetHeight > 0) {
211
+ const scrollParent = iframe.closest("[data-frame-container]");
212
+ const savedScrollTop = scrollParent?.scrollTop ?? 0;
213
+ iframe.style.height = "0px";
214
+ let childrenHeight = 0;
215
+ Array.from(mountNode.children).forEach((child) => {
216
+ const el = child;
217
+ childrenHeight = Math.max(childrenHeight, el.scrollHeight);
218
+ });
219
+ const win = iframe.contentDocument?.defaultView;
220
+ mountNode.querySelectorAll("[data-floating-ui-focusable]").forEach((popup) => {
221
+ if (popup.offsetHeight > 0) {
222
+ let offsetY = 0;
223
+ const transform = popup.style.transform;
224
+ if (transform) {
225
+ const match = transform.match(/translate\([^,]+,\s*([+-]?\d+(?:\.\d+)?)/);
226
+ if (match?.[1]) offsetY = parseFloat(match[1]);
227
+ }
228
+ const estimatedHeight = offsetY + popup.offsetHeight;
229
+ childrenHeight = Math.max(childrenHeight, estimatedHeight);
230
+ }
231
+ });
232
+ if (win) Array.from(mountNode.children).forEach((child) => {
233
+ const el = child;
234
+ const style = win.getComputedStyle(el);
235
+ if (style.position === "fixed" || style.position === "absolute") {
236
+ if (el.offsetHeight > 0) {
214
237
  let offsetY = 0;
215
- const transform = popup.style.transform;
238
+ const transform = el.style.transform;
216
239
  if (transform) {
217
240
  const match = transform.match(/translate\([^,]+,\s*([+-]?\d+(?:\.\d+)?)/);
218
241
  if (match?.[1]) offsetY = parseFloat(match[1]);
219
242
  }
220
- const estimatedHeight = offsetY + popup.offsetHeight;
243
+ const estimatedHeight = offsetY + el.offsetHeight;
221
244
  childrenHeight = Math.max(childrenHeight, estimatedHeight);
222
245
  }
223
- });
224
- if (win) Array.from(mountNode.children).forEach((child) => {
225
- const el = child;
226
- const style = win.getComputedStyle(el);
227
- if (style.position === "fixed" || style.position === "absolute") {
228
- if (el.offsetHeight > 0) {
229
- let offsetY = 0;
230
- const transform = el.style.transform;
231
- if (transform) {
232
- const match = transform.match(/translate\([^,]+,\s*([+-]?\d+(?:\.\d+)?)/);
233
- if (match?.[1]) offsetY = parseFloat(match[1]);
234
- }
235
- const estimatedHeight = offsetY + el.offsetHeight;
236
- childrenHeight = Math.max(childrenHeight, estimatedHeight);
237
- }
238
- }
239
- });
240
- const contentHeight = Math.max(mountNode.scrollHeight, childrenHeight);
241
- if (contentHeight > 0) iframe.style.height = `${Math.ceil(contentHeight)}px`;
242
- if (scrollParent) scrollParent.scrollTop = savedScrollTop;
243
- };
244
- updateHeight();
245
- const resizeObserver = new ResizeObserver(updateHeight);
246
- resizeObserver.observe(mountNode);
247
- const mutationObserver = new MutationObserver(updateHeight);
248
- mutationObserver.observe(mountNode, {
249
- childList: true,
250
- subtree: true,
251
- attributes: true,
252
- characterData: true
246
+ }
253
247
  });
254
- return () => {
255
- resizeObserver.disconnect();
256
- mutationObserver.disconnect();
257
- };
258
- }, [mountNode, shouldAutoHeight]);
248
+ const contentHeight = Math.max(mountNode.scrollHeight, childrenHeight);
249
+ if (contentHeight > 0) iframe.style.height = `${Math.ceil(contentHeight)}px`;
250
+ if (scrollParent) scrollParent.scrollTop = savedScrollTop;
251
+ }, [shouldAutoHeight, mountNode]);
252
+ useEffect(() => {
253
+ updateHeight();
254
+ }, [updateHeight]);
255
+ const [resizeRef, resizeSize] = useResizeObserver();
256
+ useEffect(() => {
257
+ if (!shouldAutoHeight || !mountNode) return;
258
+ resizeRef(mountNode);
259
+ return () => resizeRef(null);
260
+ }, [
261
+ shouldAutoHeight,
262
+ mountNode,
263
+ resizeRef
264
+ ]);
265
+ useEffect(() => {
266
+ updateHeight();
267
+ }, [resizeSize]);
268
+ useMutationObserver(mountNode, updateHeight, {
269
+ enabled: shouldAutoHeight,
270
+ childList: true,
271
+ subtree: true,
272
+ attributes: true,
273
+ characterData: true
274
+ });
259
275
  const content = mountNode ? createPortal(children(mountNode), mountNode) : null;
260
276
  return /* @__PURE__ */ jsx("iframe", {
261
277
  ref: iframeRef,
@@ -295,41 +311,6 @@ const Shadow = ({ children }) => {
295
311
  renderTargetRef.current = target;
296
312
  setRenderTarget(target);
297
313
  }
298
- const onClick = (e) => {
299
- const eventInit = {
300
- bubbles: true,
301
- cancelable: true,
302
- composed: true
303
- };
304
- let newEvent;
305
- if (e instanceof MouseEvent) newEvent = new MouseEvent(e.type, {
306
- ...eventInit,
307
- clientX: e.clientX,
308
- clientY: e.clientY,
309
- button: e.button
310
- });
311
- else if (e instanceof PointerEvent) newEvent = new PointerEvent(e.type, {
312
- ...eventInit,
313
- clientX: e.clientX,
314
- clientY: e.clientY,
315
- pointerId: e.pointerId
316
- });
317
- else newEvent = new Event(e.type, eventInit);
318
- hostRef.current?.dispatchEvent(newEvent);
319
- };
320
- const eventTypes = [
321
- "click",
322
- "pointerdown",
323
- "pointerup"
324
- ];
325
- eventTypes.forEach((type) => {
326
- target.addEventListener(type, onClick, true);
327
- });
328
- return () => {
329
- eventTypes.forEach((type) => {
330
- target?.removeEventListener(type, onClick, true);
331
- });
332
- };
333
314
  }, []);
334
315
  useLayoutEffect(() => {
335
316
  if (renderTargetRef.current && !renderTarget) setRenderTarget(renderTargetRef.current);
@@ -354,24 +335,34 @@ const Frame = ({ mode, children, ...restProps }) => {
354
335
 
355
336
  //#endregion
356
337
  //#region src/components/dnd/draggable.tsx
357
- const Draggable = ({ item, onAdd }) => {
358
- const { attributes, listeners, setNodeRef, transform, isDragging } = useDraggable({
338
+ const DraggableItem = ({ item, children }) => {
339
+ const { attributes, listeners, setNodeRef, isDragging } = useDraggable({
359
340
  id: item.id,
360
341
  data: {
361
342
  type: "new-item",
362
343
  item
363
344
  }
364
345
  });
365
- return /* @__PURE__ */ jsx(Card, {
346
+ return children({
366
347
  ref: setNodeRef,
367
- style: transform ? { opacity: isDragging ? .5 : 1 } : void 0,
368
- ...listeners,
369
- ...attributes,
370
- className: cn("cursor-grab", "outline-none", "hover:border-blue-300 hover:shadow-md", isDragging && "opacity-50"),
371
- onDoubleClick: () => onAdd?.(item),
372
- children: item.name
348
+ dragProps: {
349
+ ...listeners,
350
+ ...attributes
351
+ },
352
+ isDragging
373
353
  });
374
354
  };
355
+ const DefaultDraggableItem = ({ item, onAdd }) => /* @__PURE__ */ jsx(DraggableItem, {
356
+ item,
357
+ children: ({ ref, dragProps, isDragging }) => /* @__PURE__ */ jsx(Card, {
358
+ ref,
359
+ style: { opacity: isDragging ? .5 : 1 },
360
+ ...dragProps,
361
+ className: cn("cursor-grab", "outline-none", "hover:border-blue-300 hover:shadow-md", isDragging && "opacity-50"),
362
+ onDoubleClick: onAdd ? () => onAdd(item) : void 0,
363
+ children: item.name
364
+ })
365
+ });
375
366
 
376
367
  //#endregion
377
368
  //#region src/components/dnd/droppable.tsx
@@ -402,7 +393,16 @@ const Renderer = ({ preview, headers, modules, frame, provider }) => {
402
393
  ...baseModules,
403
394
  ...modules
404
395
  }), [modules]);
405
- const module = useMemo(() => compile(preview, memoizedModules), [preview, memoizedModules]);
396
+ const module = useMemo(() => {
397
+ try {
398
+ return compile(preview, memoizedModules);
399
+ } catch (e) {
400
+ return {
401
+ exports: {},
402
+ error: e instanceof Error ? e.message : "Module transformation error"
403
+ };
404
+ }
405
+ }, [preview, memoizedModules]);
406
406
  const renderProvider = (component) => {
407
407
  return provider ? provider(component) : component;
408
408
  };
@@ -474,7 +474,7 @@ const Overlay = ({ sections, renderProps }) => {
474
474
  if (!active) return null;
475
475
  if (active.data.current?.type === "new-item") {
476
476
  const item = active.data.current.item;
477
- return /* @__PURE__ */ jsx(Draggable, { item });
477
+ return /* @__PURE__ */ jsx(DefaultDraggableItem, { item });
478
478
  }
479
479
  const section = sections.find((s) => s.id === active.id);
480
480
  if (section) {
@@ -12621,7 +12621,7 @@ const conditionalModifiers = (args) => {
12621
12621
  if (active?.data.current?.type === "new-item") return args.transform;
12622
12622
  return restrictToVerticalAxis(args);
12623
12623
  };
12624
- const Dnd = ({ value: _value, props, modules = {}, onChange: _onChange, className, items = [], frame, provider, ...restProps }) => {
12624
+ const Dnd$1 = ({ value: _value, props, modules = {}, onChange: _onChange, className, items = [], frame, provider, renderPalette, renderPanel, ...restProps }) => {
12625
12625
  const [selectedId, setSelectedId] = useState(null);
12626
12626
  const [mobilePaletteOpen, setMobilePaletteOpen] = useState(false);
12627
12627
  const { breakpoint } = useResponsiveSize();
@@ -12721,14 +12721,35 @@ const Dnd = ({ value: _value, props, modules = {}, onChange: _onChange, classNam
12721
12721
  useEffect(() => {
12722
12722
  if (frame?.scripts?.length) preloadScripts(frame.scripts);
12723
12723
  }, [frame?.scripts]);
12724
- const renderPaletteItems = (onAdd) => /* @__PURE__ */ jsx(Space, {
12725
- orientation: "vertical",
12726
- align: "start",
12727
- children: (items?.length ? items : DRAGGABLE_ITEMS).map((item) => /* @__PURE__ */ jsx(Draggable, {
12728
- item,
12729
- onAdd
12730
- }, item.id))
12731
- });
12724
+ const renderPaletteItems = (onAdd) => {
12725
+ const paletteItems = items?.length ? items : DRAGGABLE_ITEMS;
12726
+ if (renderPalette) return renderPalette({
12727
+ items: paletteItems,
12728
+ onAdd,
12729
+ DraggableItem
12730
+ });
12731
+ return /* @__PURE__ */ jsx(Space, {
12732
+ orientation: "vertical",
12733
+ align: "start",
12734
+ children: paletteItems.map((item) => /* @__PURE__ */ jsx(DefaultDraggableItem, {
12735
+ item,
12736
+ onAdd
12737
+ }, item.id))
12738
+ });
12739
+ };
12740
+ const renderPanelContent = () => {
12741
+ const selectedItem = sections.find((s) => s.id === selectedId);
12742
+ if (renderPanel) return renderPanel({
12743
+ item: selectedItem,
12744
+ onChange,
12745
+ onDelete
12746
+ });
12747
+ return /* @__PURE__ */ jsx(Panel, {
12748
+ item: selectedItem,
12749
+ onChange,
12750
+ onDelete
12751
+ });
12752
+ };
12732
12753
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(DndContext, {
12733
12754
  sensors,
12734
12755
  collisionDetection: closestCenter,
@@ -12787,11 +12808,7 @@ const Dnd = ({ value: _value, props, modules = {}, onChange: _onChange, classNam
12787
12808
  }),
12788
12809
  /* @__PURE__ */ jsx("div", {
12789
12810
  className: "hidden w-1/5 md:block",
12790
- children: /* @__PURE__ */ jsx(Panel, {
12791
- item: sections.find((s) => s.id === selectedId),
12792
- onChange,
12793
- onDelete
12794
- })
12811
+ children: renderPanelContent()
12795
12812
  }),
12796
12813
  /* @__PURE__ */ jsx(Button, {
12797
12814
  type: "primary",
@@ -12818,11 +12835,7 @@ const Dnd = ({ value: _value, props, modules = {}, onChange: _onChange, classNam
12818
12835
  direction: "bottom",
12819
12836
  size: "large",
12820
12837
  title: "Properties",
12821
- children: /* @__PURE__ */ jsx(Panel, {
12822
- item: sections.find((s) => s.id === selectedId),
12823
- onChange,
12824
- onDelete
12825
- })
12838
+ children: renderPanelContent()
12826
12839
  })
12827
12840
  ]
12828
12841
  }), /* @__PURE__ */ jsx(DragOverlay, { children: /* @__PURE__ */ jsx(Overlay, {
@@ -12837,6 +12850,11 @@ const Dnd = ({ value: _value, props, modules = {}, onChange: _onChange, classNam
12837
12850
  }) });
12838
12851
  };
12839
12852
 
12853
+ //#endregion
12854
+ //#region src/components/dnd/index.ts
12855
+ const Dnd = Dnd$1;
12856
+ Dnd.DraggableItem = DraggableItem;
12857
+
12840
12858
  //#endregion
12841
12859
  //#region src/components/editor/editor.tsx
12842
12860
  const Editor$1 = ({ defaultValue, value: _value, debounce = 1e3, onChange: _onChange, ...props }) => {
@@ -12852,9 +12870,10 @@ const Editor$1 = ({ defaultValue, value: _value, debounce = 1e3, onChange: _onCh
12852
12870
  const onError = useCallback((error) => {
12853
12871
  setError(error);
12854
12872
  }, [setError]);
12855
- useDebounce(() => {
12856
- setCode(value);
12857
- }, { delay: debounce }, [value]);
12873
+ const debouncedValue = useDebouncedValue(value, debounce);
12874
+ useEffect(() => {
12875
+ setCode(debouncedValue);
12876
+ }, [debouncedValue, setCode]);
12858
12877
  return /* @__PURE__ */ jsx(Core, {
12859
12878
  value,
12860
12879
  onChange,
@@ -12935,6 +12954,15 @@ var ErrorBoundary = class extends React.Component {
12935
12954
  console.error("🚨 [Boundary] Rendering error:", error, errorInfo);
12936
12955
  this.props.onError?.(error, errorInfo);
12937
12956
  }
12957
+ componentDidUpdate(prevProps) {
12958
+ if (!this.state.hasError) return;
12959
+ const prevKeys = prevProps.resetKeys ?? [];
12960
+ const nextKeys = this.props.resetKeys ?? [];
12961
+ if (prevKeys.length !== nextKeys.length || nextKeys.some((key, i) => key !== prevKeys[i])) this.setState({
12962
+ hasError: false,
12963
+ error: void 0
12964
+ });
12965
+ }
12938
12966
  render() {
12939
12967
  if (this.state.hasError) return this.props.fallback ? /* @__PURE__ */ jsx(Fragment, { children: this.props.fallback(this.state.error?.message) }) : /* @__PURE__ */ jsx(Error$2, {
12940
12968
  message: this.state.error?.message,
@@ -12954,21 +12982,20 @@ const Guard = ({ children, onError }) => {
12954
12982
  const [error, setError] = useState(null);
12955
12983
  const containerRef = useRef(null);
12956
12984
  const errorHandled = useRef(false);
12985
+ useEventListener("error", (event) => {
12986
+ if (errorHandled.current) return;
12987
+ errorHandled.current = true;
12988
+ const errorMessage = event.error?.message || event.message || "Unknown error";
12989
+ console.error("⚡ [Guard] Event handler error:", errorMessage);
12990
+ event.preventDefault();
12991
+ setError(errorMessage);
12992
+ onError?.(event.error || new window.Error(errorMessage));
12993
+ setTimeout(() => {
12994
+ errorHandled.current = false;
12995
+ }, 100);
12996
+ }, { capture: true });
12957
12997
  useEffect(() => {
12958
12998
  if (!containerRef.current) return;
12959
- const handleWindowError = (event) => {
12960
- if (errorHandled.current) return;
12961
- errorHandled.current = true;
12962
- const errorMessage = event.error?.message || event.message || "Unknown error";
12963
- console.error("⚡ [Guard] Event handler error:", errorMessage);
12964
- event.preventDefault();
12965
- setError(errorMessage);
12966
- onError?.(event.error || new window.Error(errorMessage));
12967
- setTimeout(() => {
12968
- errorHandled.current = false;
12969
- }, 100);
12970
- return true;
12971
- };
12972
12999
  const handleUnhandledRejection = (event) => {
12973
13000
  if (errorHandled.current) return;
12974
13001
  errorHandled.current = true;
@@ -12980,10 +13007,8 @@ const Guard = ({ children, onError }) => {
12980
13007
  errorHandled.current = false;
12981
13008
  }, 100);
12982
13009
  };
12983
- window.addEventListener("error", handleWindowError, true);
12984
13010
  window.addEventListener("unhandledrejection", handleUnhandledRejection, true);
12985
13011
  return () => {
12986
- window.removeEventListener("error", handleWindowError, true);
12987
13012
  window.removeEventListener("unhandledrejection", handleUnhandledRejection, true);
12988
13013
  };
12989
13014
  }, [onError]);
@@ -13006,16 +13031,12 @@ const Guard = ({ children, onError }) => {
13006
13031
  //#region src/components/error/runtime.tsx
13007
13032
  const Runtime = ({ open = true, reset }) => {
13008
13033
  const { error: message, setError } = useError();
13034
+ useEventListener("error", (e) => {
13035
+ setError(e.message);
13036
+ e.preventDefault();
13037
+ });
13009
13038
  useEffect(() => {
13010
- const onError = (e) => {
13011
- setError(e.message);
13012
- e.preventDefault();
13013
- };
13014
- window.addEventListener("error", onError);
13015
- return () => {
13016
- setError(null);
13017
- window.removeEventListener("error", onError);
13018
- };
13039
+ return () => setError(null);
13019
13040
  }, [setError]);
13020
13041
  if (!open) return null;
13021
13042
  return /* @__PURE__ */ jsx(Error$2, {
@@ -13043,9 +13064,10 @@ const Client = ({ code: _code = "", className, showError, props = {}, modules =
13043
13064
  ...baseModules,
13044
13065
  ...modules
13045
13066
  };
13067
+ const effectiveCode = _code || code;
13046
13068
  let module = null;
13047
- if (_code || code) try {
13048
- module = compile(_code || code, mergedModules);
13069
+ if (effectiveCode) try {
13070
+ module = compile(effectiveCode, mergedModules);
13049
13071
  } catch (e) {
13050
13072
  module = {
13051
13073
  exports: {},
@@ -13070,6 +13092,7 @@ const Client = ({ code: _code = "", className, showError, props = {}, modules =
13070
13092
  children: /* @__PURE__ */ jsx(Frame, {
13071
13093
  ...frame,
13072
13094
  children: (container) => /* @__PURE__ */ jsx(Error$1.Boundary, {
13095
+ resetKeys: [effectiveCode],
13073
13096
  onError: (e) => setError(e.message),
13074
13097
  children: renderProvider(/* @__PURE__ */ jsx(Error$1.Guard, {
13075
13098
  onError: (e) => setError(e.message),
@@ -13088,6 +13111,7 @@ const Client = ({ code: _code = "", className, showError, props = {}, modules =
13088
13111
  containerType: "inline-size"
13089
13112
  },
13090
13113
  children: /* @__PURE__ */ jsx(Error$1.Boundary, {
13114
+ resetKeys: [effectiveCode],
13091
13115
  onError: (e) => setError(e.message),
13092
13116
  children: renderProvider(/* @__PURE__ */ jsx(Error$1.Guard, {
13093
13117
  onError: (e) => setError(e.message),
@@ -13102,12 +13126,23 @@ const Client = ({ code: _code = "", className, showError, props = {}, modules =
13102
13126
  const Preview = ({ code, props = {}, modules = {}, dynamicTailwind = false, provider, ...restProps }) => {
13103
13127
  const [runtimeError, setRuntimeError] = useState(null);
13104
13128
  const [dynamicCSS, setDynamicCSS] = useState("");
13129
+ const [prevCode, setPrevCode] = useState(code);
13130
+ if (code !== prevCode) {
13131
+ setPrevCode(code);
13132
+ setRuntimeError(null);
13133
+ }
13105
13134
  const renderProvider = (component) => {
13106
13135
  return provider ? provider(component) : component;
13107
13136
  };
13108
13137
  useEffect(() => {
13109
13138
  if (!code || !dynamicTailwind) return;
13110
- generateTailwindCSS(code).then(setDynamicCSS);
13139
+ let cancelled = false;
13140
+ generateTailwindCSS(code).then((css) => {
13141
+ if (!cancelled) setDynamicCSS(css);
13142
+ });
13143
+ return () => {
13144
+ cancelled = true;
13145
+ };
13111
13146
  }, [code, dynamicTailwind]);
13112
13147
  if (runtimeError) return /* @__PURE__ */ jsx(Error$1, {
13113
13148
  title: "Runtime Error",
@@ -13116,10 +13151,18 @@ const Preview = ({ code, props = {}, modules = {}, dynamicTailwind = false, prov
13116
13151
  onReset: () => setRuntimeError(null)
13117
13152
  });
13118
13153
  if (code) {
13119
- const module = compile(code, {
13120
- ...baseModules,
13121
- ...modules
13122
- });
13154
+ let module;
13155
+ try {
13156
+ module = compile(code, {
13157
+ ...baseModules,
13158
+ ...modules
13159
+ });
13160
+ } catch (e) {
13161
+ module = {
13162
+ exports: {},
13163
+ error: e instanceof Error ? e.message : "Module transformation error"
13164
+ };
13165
+ }
13123
13166
  if (module.error) return /* @__PURE__ */ jsx(Error$1, {
13124
13167
  title: "Compile Error",
13125
13168
  message: module.error,
@@ -13131,13 +13174,14 @@ const Preview = ({ code, props = {}, modules = {}, dynamicTailwind = false, prov
13131
13174
  className: "mx-5 mt-25"
13132
13175
  });
13133
13176
  return /* @__PURE__ */ jsx(Error$1.Boundary, {
13177
+ resetKeys: [code],
13134
13178
  fallback: (message) => /* @__PURE__ */ jsx(Error$1, {
13135
13179
  title: "Rendering Error",
13136
13180
  message
13137
13181
  }),
13138
13182
  children: renderProvider(/* @__PURE__ */ jsxs(Error$1.Guard, {
13139
13183
  onError: (e) => setRuntimeError(e.message),
13140
- children: [/* @__PURE__ */ jsx(Component, { ...props }), dynamicCSS && /* @__PURE__ */ jsx("style", { children: dynamicCSS })]
13184
+ children: [/* @__PURE__ */ jsx(Component, { ...props }), dynamicTailwind && dynamicCSS && /* @__PURE__ */ jsx("style", { children: dynamicCSS })]
13141
13185
  }))
13142
13186
  });
13143
13187
  }