@jbpark/live-editor 1.8.1 → 1.10.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,19 +26,17 @@ 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
@@ -55,16 +53,18 @@ const ContextProvider = ({ children }) => {
55
53
  clearCompilationCache();
56
54
  };
57
55
  }, []);
56
+ const previewValue = useMemo(() => ({
57
+ code,
58
+ setCode
59
+ }), [code, setCode]);
60
+ const errorValue = useMemo(() => ({
61
+ error,
62
+ setError
63
+ }), [error, setError]);
58
64
  return /* @__PURE__ */ jsx(PreviewContext.Provider, {
59
- value: {
60
- code,
61
- setCode
62
- },
65
+ value: previewValue,
63
66
  children: /* @__PURE__ */ jsx(ErrorContext.Provider, {
64
- value: {
65
- error,
66
- setError
67
- },
67
+ value: errorValue,
68
68
  children
69
69
  })
70
70
  });
@@ -119,6 +119,21 @@ const IFrame = ({ title = "Live Preview", sandbox, style = {}, scripts = EMPTY_S
119
119
  doc.head.appendChild(fragment);
120
120
  }
121
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
+ });
122
137
  useEffect(() => {
123
138
  const $iframe = iframeRef.current;
124
139
  if (!$iframe) return;
@@ -152,28 +167,11 @@ const IFrame = ({ title = "Live Preview", sandbox, style = {}, scripts = EMPTY_S
152
167
  onLoaded?.();
153
168
  };
154
169
  $iframe.addEventListener("load", onLoad);
155
- let timeoutId;
156
- let observer = null;
157
- if (syncStyle) {
158
- observer = new MutationObserver(() => {
159
- clearTimeout(timeoutId);
160
- timeoutId = window.setTimeout(applyStyle, 50);
161
- });
162
- observer.observe(document.head, {
163
- childList: true,
164
- subtree: true,
165
- attributes: true,
166
- attributeFilter: ["href"]
167
- });
168
- }
169
170
  if ($iframe.contentDocument?.readyState === "complete") onLoad();
170
171
  return () => {
171
172
  $iframe.removeEventListener("load", onLoad);
172
- observer?.disconnect();
173
- clearTimeout(timeoutId);
174
173
  };
175
174
  }, [
176
- syncStyle,
177
175
  scripts,
178
176
  onLoaded,
179
177
  applyStyle
@@ -207,66 +205,73 @@ const IFrame = ({ title = "Live Preview", sandbox, style = {}, scripts = EMPTY_S
207
205
  for (let index = stylesheets.length; index < prevStylesheetCountRef.current; index++) doc.getElementById(`injected-stylesheet-${index}`)?.remove();
208
206
  prevStylesheetCountRef.current = stylesheets.length;
209
207
  }, [styles, stylesheets]);
210
- useEffect(() => {
208
+ const updateHeight = useCallback(() => {
211
209
  if (!shouldAutoHeight || !mountNode || !iframeRef.current) return;
212
210
  const iframe = iframeRef.current;
213
- const updateHeight = () => {
214
- const scrollParent = iframe.closest("[data-frame-container]");
215
- const savedScrollTop = scrollParent?.scrollTop ?? 0;
216
- iframe.style.height = "0px";
217
- let childrenHeight = 0;
218
- Array.from(mountNode.children).forEach((child) => {
219
- const el = child;
220
- childrenHeight = Math.max(childrenHeight, el.scrollHeight);
221
- });
222
- const win = iframe.contentDocument?.defaultView;
223
- mountNode.querySelectorAll("[data-floating-ui-focusable]").forEach((popup) => {
224
- 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) {
225
237
  let offsetY = 0;
226
- const transform = popup.style.transform;
238
+ const transform = el.style.transform;
227
239
  if (transform) {
228
240
  const match = transform.match(/translate\([^,]+,\s*([+-]?\d+(?:\.\d+)?)/);
229
241
  if (match?.[1]) offsetY = parseFloat(match[1]);
230
242
  }
231
- const estimatedHeight = offsetY + popup.offsetHeight;
243
+ const estimatedHeight = offsetY + el.offsetHeight;
232
244
  childrenHeight = Math.max(childrenHeight, estimatedHeight);
233
245
  }
234
- });
235
- if (win) Array.from(mountNode.children).forEach((child) => {
236
- const el = child;
237
- const style = win.getComputedStyle(el);
238
- if (style.position === "fixed" || style.position === "absolute") {
239
- if (el.offsetHeight > 0) {
240
- let offsetY = 0;
241
- const transform = el.style.transform;
242
- if (transform) {
243
- const match = transform.match(/translate\([^,]+,\s*([+-]?\d+(?:\.\d+)?)/);
244
- if (match?.[1]) offsetY = parseFloat(match[1]);
245
- }
246
- const estimatedHeight = offsetY + el.offsetHeight;
247
- childrenHeight = Math.max(childrenHeight, estimatedHeight);
248
- }
249
- }
250
- });
251
- const contentHeight = Math.max(mountNode.scrollHeight, childrenHeight);
252
- if (contentHeight > 0) iframe.style.height = `${Math.ceil(contentHeight)}px`;
253
- if (scrollParent) scrollParent.scrollTop = savedScrollTop;
254
- };
255
- updateHeight();
256
- const resizeObserver = new ResizeObserver(updateHeight);
257
- resizeObserver.observe(mountNode);
258
- const mutationObserver = new MutationObserver(updateHeight);
259
- mutationObserver.observe(mountNode, {
260
- childList: true,
261
- subtree: true,
262
- attributes: true,
263
- characterData: true
246
+ }
264
247
  });
265
- return () => {
266
- resizeObserver.disconnect();
267
- mutationObserver.disconnect();
268
- };
269
- }, [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
+ });
270
275
  const content = mountNode ? createPortal(children(mountNode), mountNode) : null;
271
276
  return /* @__PURE__ */ jsx("iframe", {
272
277
  ref: iframeRef,
@@ -330,24 +335,34 @@ const Frame = ({ mode, children, ...restProps }) => {
330
335
 
331
336
  //#endregion
332
337
  //#region src/components/dnd/draggable.tsx
333
- const Draggable = ({ item, onAdd }) => {
334
- const { attributes, listeners, setNodeRef, transform, isDragging } = useDraggable({
338
+ const DraggableItem = ({ item, children }) => {
339
+ const { attributes, listeners, setNodeRef, isDragging } = useDraggable({
335
340
  id: item.id,
336
341
  data: {
337
342
  type: "new-item",
338
343
  item
339
344
  }
340
345
  });
341
- return /* @__PURE__ */ jsx(Card, {
346
+ return children({
342
347
  ref: setNodeRef,
343
- style: transform ? { opacity: isDragging ? .5 : 1 } : void 0,
344
- ...listeners,
345
- ...attributes,
346
- className: cn("cursor-grab", "outline-none", "hover:border-blue-300 hover:shadow-md", isDragging && "opacity-50"),
347
- onDoubleClick: () => onAdd?.(item),
348
- children: item.name
348
+ dragProps: {
349
+ ...listeners,
350
+ ...attributes
351
+ },
352
+ isDragging
349
353
  });
350
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
+ });
351
366
 
352
367
  //#endregion
353
368
  //#region src/components/dnd/droppable.tsx
@@ -459,7 +474,7 @@ const Overlay = ({ sections, renderProps }) => {
459
474
  if (!active) return null;
460
475
  if (active.data.current?.type === "new-item") {
461
476
  const item = active.data.current.item;
462
- return /* @__PURE__ */ jsx(Draggable, { item });
477
+ return /* @__PURE__ */ jsx(DefaultDraggableItem, { item });
463
478
  }
464
479
  const section = sections.find((s) => s.id === active.id);
465
480
  if (section) {
@@ -11336,8 +11351,8 @@ GutterMarker.prototype.startSide = GutterMarker.prototype.endSide = -1;
11336
11351
  GutterMarker.prototype.point = true;
11337
11352
 
11338
11353
  //#endregion
11339
- //#region src/components/editor/core.tsx
11340
- const prettierInitialOptions = {
11354
+ //#region src/components/editor/use-format-code.ts
11355
+ const DEFAULT_PRETTIER_OPTIONS = {
11341
11356
  tabWidth: 2,
11342
11357
  singleQuote: true,
11343
11358
  trailingComma: "all",
@@ -11345,13 +11360,12 @@ const prettierInitialOptions = {
11345
11360
  arrowParens: "avoid",
11346
11361
  printWidth: 60
11347
11362
  };
11348
- const Core = ({ value, theme, height, className, prettierOptions, fragment, raw, onChange, onSave: _onSave, onError, ...props }) => {
11349
- const editorRef = useRef(null);
11363
+ const useFormatCode = ({ fragment, prettierOptions } = {}) => {
11350
11364
  const prettierConfig = useMemo(() => ({
11351
- ...prettierInitialOptions,
11365
+ ...DEFAULT_PRETTIER_OPTIONS,
11352
11366
  ...prettierOptions
11353
11367
  }), [prettierOptions]);
11354
- const formatCode = useCallback(async (code) => {
11368
+ return useCallback(async (code) => {
11355
11369
  const isTypeScript = detectTypeScript(code);
11356
11370
  const source = fragment ? `<>${code}</>` : code;
11357
11371
  const formatted = await prettier.format(source, {
@@ -11366,6 +11380,16 @@ const Core = ({ value, theme, height, className, prettierOptions, fragment, raw,
11366
11380
  if (fragment) return formatted.replace(/^<>\n?/, "").replace(/\n?<\/>;?\s*$/, "").replace(/^ {2}/gm, "").trim();
11367
11381
  return formatted;
11368
11382
  }, [prettierConfig, fragment]);
11383
+ };
11384
+
11385
+ //#endregion
11386
+ //#region src/components/editor/core.tsx
11387
+ const Core = ({ value, theme, height, className, prettierOptions, fragment, raw, onChange, onSave: _onSave, onError, ...props }) => {
11388
+ const editorRef = useRef(null);
11389
+ const formatCode = useFormatCode({
11390
+ fragment,
11391
+ prettierOptions
11392
+ });
11369
11393
  const onSave = useCallback(async (val) => {
11370
11394
  try {
11371
11395
  const currentView = editorRef.current?.view;
@@ -12606,7 +12630,7 @@ const conditionalModifiers = (args) => {
12606
12630
  if (active?.data.current?.type === "new-item") return args.transform;
12607
12631
  return restrictToVerticalAxis(args);
12608
12632
  };
12609
- const Dnd = ({ value: _value, props, modules = {}, onChange: _onChange, className, items = [], frame, provider, ...restProps }) => {
12633
+ const Dnd$1 = ({ value: _value, props, modules = {}, onChange: _onChange, className, items = [], frame, provider, renderPalette, renderPanel, ...restProps }) => {
12610
12634
  const [selectedId, setSelectedId] = useState(null);
12611
12635
  const [mobilePaletteOpen, setMobilePaletteOpen] = useState(false);
12612
12636
  const { breakpoint } = useResponsiveSize();
@@ -12706,14 +12730,35 @@ const Dnd = ({ value: _value, props, modules = {}, onChange: _onChange, classNam
12706
12730
  useEffect(() => {
12707
12731
  if (frame?.scripts?.length) preloadScripts(frame.scripts);
12708
12732
  }, [frame?.scripts]);
12709
- const renderPaletteItems = (onAdd) => /* @__PURE__ */ jsx(Space, {
12710
- orientation: "vertical",
12711
- align: "start",
12712
- children: (items?.length ? items : DRAGGABLE_ITEMS).map((item) => /* @__PURE__ */ jsx(Draggable, {
12713
- item,
12714
- onAdd
12715
- }, item.id))
12716
- });
12733
+ const renderPaletteItems = (onAdd) => {
12734
+ const paletteItems = items?.length ? items : DRAGGABLE_ITEMS;
12735
+ if (renderPalette) return renderPalette({
12736
+ items: paletteItems,
12737
+ onAdd,
12738
+ DraggableItem
12739
+ });
12740
+ return /* @__PURE__ */ jsx(Space, {
12741
+ orientation: "vertical",
12742
+ align: "start",
12743
+ children: paletteItems.map((item) => /* @__PURE__ */ jsx(DefaultDraggableItem, {
12744
+ item,
12745
+ onAdd
12746
+ }, item.id))
12747
+ });
12748
+ };
12749
+ const renderPanelContent = () => {
12750
+ const selectedItem = sections.find((s) => s.id === selectedId);
12751
+ if (renderPanel) return renderPanel({
12752
+ item: selectedItem,
12753
+ onChange,
12754
+ onDelete
12755
+ });
12756
+ return /* @__PURE__ */ jsx(Panel, {
12757
+ item: selectedItem,
12758
+ onChange,
12759
+ onDelete
12760
+ });
12761
+ };
12717
12762
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(DndContext, {
12718
12763
  sensors,
12719
12764
  collisionDetection: closestCenter,
@@ -12772,11 +12817,7 @@ const Dnd = ({ value: _value, props, modules = {}, onChange: _onChange, classNam
12772
12817
  }),
12773
12818
  /* @__PURE__ */ jsx("div", {
12774
12819
  className: "hidden w-1/5 md:block",
12775
- children: /* @__PURE__ */ jsx(Panel, {
12776
- item: sections.find((s) => s.id === selectedId),
12777
- onChange,
12778
- onDelete
12779
- })
12820
+ children: renderPanelContent()
12780
12821
  }),
12781
12822
  /* @__PURE__ */ jsx(Button, {
12782
12823
  type: "primary",
@@ -12803,11 +12844,7 @@ const Dnd = ({ value: _value, props, modules = {}, onChange: _onChange, classNam
12803
12844
  direction: "bottom",
12804
12845
  size: "large",
12805
12846
  title: "Properties",
12806
- children: /* @__PURE__ */ jsx(Panel, {
12807
- item: sections.find((s) => s.id === selectedId),
12808
- onChange,
12809
- onDelete
12810
- })
12847
+ children: renderPanelContent()
12811
12848
  })
12812
12849
  ]
12813
12850
  }), /* @__PURE__ */ jsx(DragOverlay, { children: /* @__PURE__ */ jsx(Overlay, {
@@ -12822,9 +12859,14 @@ const Dnd = ({ value: _value, props, modules = {}, onChange: _onChange, classNam
12822
12859
  }) });
12823
12860
  };
12824
12861
 
12862
+ //#endregion
12863
+ //#region src/components/dnd/index.ts
12864
+ const Dnd = Dnd$1;
12865
+ Dnd.DraggableItem = DraggableItem;
12866
+
12825
12867
  //#endregion
12826
12868
  //#region src/components/editor/editor.tsx
12827
- const Editor$1 = ({ defaultValue, value: _value, debounce = 1e3, onChange: _onChange, ...props }) => {
12869
+ const Editor$1 = ({ defaultValue, value: _value, debounce = 1e3, onChange: _onChange, renderEditor, fragment, prettierOptions, ...props }) => {
12828
12870
  const { setCode } = usePreview();
12829
12871
  const { setError } = useError();
12830
12872
  const value = _value.trim() === "" ? defaultValue || DEFAULT_TEMPLATE : _value;
@@ -12837,14 +12879,26 @@ const Editor$1 = ({ defaultValue, value: _value, debounce = 1e3, onChange: _onCh
12837
12879
  const onError = useCallback((error) => {
12838
12880
  setError(error);
12839
12881
  }, [setError]);
12840
- useDebounce(() => {
12841
- setCode(value);
12842
- }, { delay: debounce }, [value]);
12882
+ const formatCode = useFormatCode({
12883
+ fragment,
12884
+ prettierOptions
12885
+ });
12886
+ const debouncedValue = useDebouncedValue(value, debounce);
12887
+ useEffect(() => {
12888
+ setCode(debouncedValue);
12889
+ }, [debouncedValue, setCode]);
12890
+ if (renderEditor) return renderEditor({
12891
+ value,
12892
+ onChange,
12893
+ formatCode
12894
+ });
12843
12895
  return /* @__PURE__ */ jsx(Core, {
12844
12896
  value,
12845
12897
  onChange,
12846
12898
  onSave,
12847
12899
  onError,
12900
+ fragment,
12901
+ prettierOptions,
12848
12902
  ...props
12849
12903
  });
12850
12904
  };
@@ -12948,21 +13002,20 @@ const Guard = ({ children, onError }) => {
12948
13002
  const [error, setError] = useState(null);
12949
13003
  const containerRef = useRef(null);
12950
13004
  const errorHandled = useRef(false);
13005
+ useEventListener("error", (event) => {
13006
+ if (errorHandled.current) return;
13007
+ errorHandled.current = true;
13008
+ const errorMessage = event.error?.message || event.message || "Unknown error";
13009
+ console.error("⚡ [Guard] Event handler error:", errorMessage);
13010
+ event.preventDefault();
13011
+ setError(errorMessage);
13012
+ onError?.(event.error || new window.Error(errorMessage));
13013
+ setTimeout(() => {
13014
+ errorHandled.current = false;
13015
+ }, 100);
13016
+ }, { capture: true });
12951
13017
  useEffect(() => {
12952
13018
  if (!containerRef.current) return;
12953
- const handleWindowError = (event) => {
12954
- if (errorHandled.current) return;
12955
- errorHandled.current = true;
12956
- const errorMessage = event.error?.message || event.message || "Unknown error";
12957
- console.error("⚡ [Guard] Event handler error:", errorMessage);
12958
- event.preventDefault();
12959
- setError(errorMessage);
12960
- onError?.(event.error || new window.Error(errorMessage));
12961
- setTimeout(() => {
12962
- errorHandled.current = false;
12963
- }, 100);
12964
- return true;
12965
- };
12966
13019
  const handleUnhandledRejection = (event) => {
12967
13020
  if (errorHandled.current) return;
12968
13021
  errorHandled.current = true;
@@ -12974,10 +13027,8 @@ const Guard = ({ children, onError }) => {
12974
13027
  errorHandled.current = false;
12975
13028
  }, 100);
12976
13029
  };
12977
- window.addEventListener("error", handleWindowError, true);
12978
13030
  window.addEventListener("unhandledrejection", handleUnhandledRejection, true);
12979
13031
  return () => {
12980
- window.removeEventListener("error", handleWindowError, true);
12981
13032
  window.removeEventListener("unhandledrejection", handleUnhandledRejection, true);
12982
13033
  };
12983
13034
  }, [onError]);
@@ -13000,16 +13051,12 @@ const Guard = ({ children, onError }) => {
13000
13051
  //#region src/components/error/runtime.tsx
13001
13052
  const Runtime = ({ open = true, reset }) => {
13002
13053
  const { error: message, setError } = useError();
13054
+ useEventListener("error", (e) => {
13055
+ setError(e.message);
13056
+ e.preventDefault();
13057
+ });
13003
13058
  useEffect(() => {
13004
- const onError = (e) => {
13005
- setError(e.message);
13006
- e.preventDefault();
13007
- };
13008
- window.addEventListener("error", onError);
13009
- return () => {
13010
- setError(null);
13011
- window.removeEventListener("error", onError);
13012
- };
13059
+ return () => setError(null);
13013
13060
  }, [setError]);
13014
13061
  if (!open) return null;
13015
13062
  return /* @__PURE__ */ jsx(Error$2, {