@vibexdotnew/inspector 0.0.1 → 0.0.3

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.
@@ -0,0 +1,994 @@
1
+ "use client";
2
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
3
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
4
+ }) : x)(function(x) {
5
+ if (typeof require !== "undefined") return require.apply(this, arguments);
6
+ throw Error('Dynamic require of "' + x + '" is not supported');
7
+ });
8
+
9
+ // src/expo/components/VisualInspector.tsx
10
+ import { useEffect, useReducer, useRef, useCallback } from "react";
11
+ import { View, Text, StyleSheet, Platform } from "react-native";
12
+ import { jsx, jsxs } from "react/jsx-runtime";
13
+ var CHANNEL = "VIBEX_INSPECTOR";
14
+ var STORAGE_KEY = "vibex_inspector_active";
15
+ var SELECTED_KEY = "vibex_selected_element";
16
+ var DATA_ATTR = "data-vibex-loc";
17
+ var OVERLAY_PADDING = 4;
18
+ var MAX_SELECTIONS = 5;
19
+ var isEmbedded = () => {
20
+ if (Platform.OS !== "web" || typeof window === "undefined") return false;
21
+ try {
22
+ return window.self !== window.top;
23
+ } catch {
24
+ return true;
25
+ }
26
+ };
27
+ var emit = /* @__PURE__ */ (() => {
28
+ let lastPayload = "";
29
+ return (msg) => {
30
+ if (Platform.OS !== "web" || typeof window === "undefined") return;
31
+ const json = JSON.stringify(msg);
32
+ if (json === lastPayload) return;
33
+ lastPayload = json;
34
+ window.parent.postMessage(msg, "*");
35
+ };
36
+ })();
37
+ var padRect = (r) => ({
38
+ top: r.top - OVERLAY_PADDING,
39
+ left: r.left - OVERLAY_PADDING,
40
+ width: r.width + OVERLAY_PADDING * 2,
41
+ height: r.height + OVERLAY_PADDING * 2
42
+ });
43
+ var parseLocation = (id) => {
44
+ const parts = id.split(":");
45
+ if (parts.length < 3) return null;
46
+ const col = parseInt(parts.pop(), 10);
47
+ const line = parseInt(parts.pop(), 10);
48
+ const file = parts.join(":");
49
+ if (isNaN(line) || isNaN(col)) return null;
50
+ return { file, line, col };
51
+ };
52
+ var normalizeStyleValue = (prop, val) => {
53
+ if (prop === "backgroundColor" && (val === "rgba(0, 0, 0, 0)" || val === "transparent")) return "transparent";
54
+ if (prop === "backgroundImage" && val === "none") return "none";
55
+ if (prop === "textDecoration" && val.includes("none")) return "none";
56
+ if (prop === "fontStyle" && val === "normal") return "normal";
57
+ if (prop === "opacity" && val === "1") return "1";
58
+ if ((prop.includes("padding") || prop.includes("margin")) && (val === "0px" || val === "0")) return "0";
59
+ if (prop === "borderRadius" && val === "0px") return "0";
60
+ if (prop === "letterSpacing" && (val === "normal" || val === "0px")) return "normal";
61
+ if (prop === "gap" && (val === "normal" || val === "0px")) return "0";
62
+ return val;
63
+ };
64
+ var getComputedStyles = (el) => {
65
+ const cs = window.getComputedStyle(el);
66
+ const get = (p) => normalizeStyleValue(p, cs.getPropertyValue(p.replace(/([A-Z])/g, "-$1").toLowerCase()));
67
+ return {
68
+ fontSize: get("fontSize"),
69
+ color: get("color"),
70
+ fontWeight: get("fontWeight"),
71
+ fontFamily: get("fontFamily"),
72
+ fontStyle: get("fontStyle"),
73
+ textAlign: get("textAlign"),
74
+ textDecoration: get("textDecoration"),
75
+ lineHeight: get("lineHeight"),
76
+ letterSpacing: get("letterSpacing"),
77
+ backgroundColor: get("backgroundColor"),
78
+ backgroundImage: get("backgroundImage"),
79
+ borderRadius: get("borderRadius"),
80
+ opacity: get("opacity"),
81
+ padding: `${get("paddingTop")} ${get("paddingRight")} ${get("paddingBottom")} ${get("paddingLeft")}`,
82
+ margin: `${get("marginTop")} ${get("marginRight")} ${get("marginBottom")} ${get("marginLeft")}`,
83
+ display: get("display"),
84
+ flexDirection: get("flexDirection"),
85
+ alignItems: get("alignItems"),
86
+ justifyContent: get("justifyContent"),
87
+ gap: get("gap"),
88
+ width: get("width"),
89
+ height: get("height")
90
+ };
91
+ };
92
+ var canEditText = (el) => {
93
+ const tag = el.tagName.toLowerCase();
94
+ const editableTags = ["p", "h1", "h2", "h3", "h4", "h5", "h6", "span", "div", "li", "a", "button", "label", "td", "th"];
95
+ if (el.contentEditable === "true" || tag === "input" || tag === "textarea") return true;
96
+ if (!editableTags.includes(tag) || !el.textContent?.trim()) return false;
97
+ const hasDirectText = Array.from(el.childNodes).some((n) => n.nodeType === Node.TEXT_NODE && n.textContent?.trim());
98
+ return el.childElementCount === 0 || el.childElementCount <= 1 && hasDirectText;
99
+ };
100
+ var getDirectText = (el) => {
101
+ let txt = "";
102
+ for (const n of el.childNodes) {
103
+ if (n.nodeType === Node.TEXT_NODE) txt += n.textContent || "";
104
+ }
105
+ return txt;
106
+ };
107
+ var normalizeImgSrc = (src) => {
108
+ if (!src) return "";
109
+ try {
110
+ const url = new URL(src, location.origin);
111
+ return url.href;
112
+ } catch {
113
+ return src;
114
+ }
115
+ };
116
+ var initialState = {
117
+ active: false,
118
+ hoveredId: null,
119
+ hoveredRect: null,
120
+ hoveredTag: null,
121
+ selectedElements: [],
122
+ isScrolling: false,
123
+ isResizing: false,
124
+ resizeHandle: null
125
+ };
126
+ function reducer(state, action) {
127
+ switch (action.type) {
128
+ case "SET_ACTIVE":
129
+ return { ...state, active: action.value };
130
+ case "SET_HOVER":
131
+ return { ...state, hoveredId: action.id, hoveredRect: action.rect, hoveredTag: action.tag };
132
+ case "ADD_SELECTED":
133
+ return { ...state, selectedElements: [...state.selectedElements, action.selected] };
134
+ case "CLEAR_SELECTIONS":
135
+ return { ...state, selectedElements: [] };
136
+ case "UPDATE_SELECTED_RECTS":
137
+ return {
138
+ ...state,
139
+ selectedElements: state.selectedElements.map((sel) => ({
140
+ ...sel,
141
+ rect: padRect(sel.element.getBoundingClientRect())
142
+ }))
143
+ };
144
+ case "SET_SCROLLING":
145
+ return { ...state, isScrolling: action.value };
146
+ case "SET_RESIZING":
147
+ return { ...state, isResizing: action.value, resizeHandle: action.handle };
148
+ case "CLEAR_HOVER":
149
+ return { ...state, hoveredId: null, hoveredRect: null, hoveredTag: null };
150
+ default:
151
+ return state;
152
+ }
153
+ }
154
+ function VisualInspector() {
155
+ const [state, dispatch2] = useReducer(reducer, initialState, (init) => {
156
+ if (Platform.OS === "web" && typeof window !== "undefined") {
157
+ try {
158
+ const stored = localStorage.getItem(STORAGE_KEY);
159
+ return { ...init, active: stored === "true" };
160
+ } catch {
161
+ return init;
162
+ }
163
+ }
164
+ return init;
165
+ });
166
+ const activeRef = useRef(state.active);
167
+ const selectedElRef = useRef(null);
168
+ const editingElRef = useRef(null);
169
+ const originalTextRef = useRef("");
170
+ const originalSrcRef = useRef("");
171
+ const appliedStylesRef = useRef(/* @__PURE__ */ new Map());
172
+ const scrollTimeoutRef = useRef(null);
173
+ const resizeStartRef = useRef(null);
174
+ useEffect(() => {
175
+ activeRef.current = state.active;
176
+ if (Platform.OS === "web" && typeof window !== "undefined") {
177
+ try {
178
+ localStorage.setItem(STORAGE_KEY, String(state.active));
179
+ } catch {
180
+ }
181
+ }
182
+ emit({ channel: CHANNEL, event: "MODE_CHANGED", active: state.active });
183
+ }, [state.active]);
184
+ useEffect(() => {
185
+ if (!isEmbedded()) return;
186
+ emit({ channel: CHANNEL, event: "READY" });
187
+ if (state.active) {
188
+ emit({ channel: CHANNEL, event: "MODE_CHANGED", active: true });
189
+ }
190
+ }, []);
191
+ const buildElementInfo = useCallback((el) => {
192
+ const id = el.getAttribute(DATA_ATTR) || "";
193
+ const tag = el.getAttribute("data-vibex-name") || el.tagName.toLowerCase();
194
+ const rect = padRect(el.getBoundingClientRect());
195
+ const editable = canEditText(el);
196
+ const styles3 = getComputedStyles(el);
197
+ const className = el.className || "";
198
+ const src = el.tagName.toLowerCase() === "img" ? el.src : void 0;
199
+ return { id, tag, rect, editable, styles: styles3, className, src };
200
+ }, []);
201
+ const handleSelect = useCallback((el, clickPos, currentSelections) => {
202
+ const info = buildElementInfo(el);
203
+ if (currentSelections.some((s) => s.id === info.id)) {
204
+ return;
205
+ }
206
+ if (currentSelections.length >= MAX_SELECTIONS) {
207
+ return;
208
+ }
209
+ selectedElRef.current = el;
210
+ const selected = {
211
+ id: info.id,
212
+ rect: info.rect,
213
+ tag: info.tag,
214
+ element: el
215
+ };
216
+ dispatch2({ type: "ADD_SELECTED", selected });
217
+ dispatch2({ type: "CLEAR_HOVER" });
218
+ const allSelected = [...currentSelections, selected];
219
+ const allInfos = allSelected.map((s) => buildElementInfo(s.element));
220
+ if (Platform.OS === "web" && typeof window !== "undefined") {
221
+ try {
222
+ localStorage.setItem(SELECTED_KEY, JSON.stringify({ ids: allSelected.map((s) => s.id) }));
223
+ } catch {
224
+ }
225
+ }
226
+ emit({ channel: CHANNEL, event: "SELECT", elements: allInfos, position: clickPos });
227
+ if (info.editable && el.contentEditable !== "true") {
228
+ originalTextRef.current = el.childElementCount > 0 ? getDirectText(el) : el.innerText;
229
+ el.contentEditable = "true";
230
+ editingElRef.current = el;
231
+ el.querySelectorAll("*").forEach((child) => {
232
+ child.contentEditable = "false";
233
+ });
234
+ }
235
+ if (el.tagName.toLowerCase() === "img") {
236
+ originalSrcRef.current = normalizeImgSrc(el.src);
237
+ }
238
+ }, [buildElementInfo]);
239
+ const cleanupEditing = useCallback(() => {
240
+ const el = editingElRef.current;
241
+ if (!el) return;
242
+ const id = el.getAttribute(DATA_ATTR);
243
+ if (!id) return;
244
+ const newText = el.childElementCount > 0 ? getDirectText(el) : el.innerText;
245
+ if (newText !== originalTextRef.current) {
246
+ const loc = parseLocation(id);
247
+ if (loc) {
248
+ emit({
249
+ channel: CHANNEL,
250
+ event: "TEXT_EDIT",
251
+ elementId: id,
252
+ before: originalTextRef.current,
253
+ after: newText,
254
+ source: loc
255
+ });
256
+ }
257
+ }
258
+ const styles3 = appliedStylesRef.current.get(id);
259
+ if (styles3 && Object.keys(styles3).length > 0) {
260
+ const loc = parseLocation(id);
261
+ if (loc) {
262
+ emit({
263
+ channel: CHANNEL,
264
+ event: "STYLE_COMMIT",
265
+ elementId: id,
266
+ styles: styles3,
267
+ source: loc,
268
+ className: el.className || ""
269
+ });
270
+ }
271
+ appliedStylesRef.current.delete(id);
272
+ }
273
+ if (el.tagName.toLowerCase() === "img") {
274
+ const newSrc = normalizeImgSrc(el.src);
275
+ if (newSrc !== originalSrcRef.current && originalSrcRef.current) {
276
+ const loc = parseLocation(id);
277
+ if (loc) {
278
+ emit({
279
+ channel: CHANNEL,
280
+ event: "IMAGE_COMMIT",
281
+ elementId: id,
282
+ before: originalSrcRef.current,
283
+ after: newSrc,
284
+ source: loc
285
+ });
286
+ }
287
+ }
288
+ }
289
+ el.contentEditable = "false";
290
+ el.querySelectorAll('[contenteditable="false"]').forEach((child) => {
291
+ child.removeAttribute("contenteditable");
292
+ });
293
+ editingElRef.current = null;
294
+ originalTextRef.current = "";
295
+ originalSrcRef.current = "";
296
+ }, []);
297
+ const handleDeselect = useCallback(() => {
298
+ cleanupEditing();
299
+ selectedElRef.current = null;
300
+ dispatch2({ type: "CLEAR_SELECTIONS" });
301
+ if (Platform.OS === "web" && typeof window !== "undefined") {
302
+ try {
303
+ localStorage.removeItem(SELECTED_KEY);
304
+ } catch {
305
+ }
306
+ }
307
+ emit({ channel: CHANNEL, event: "DESELECT" });
308
+ }, [cleanupEditing]);
309
+ useEffect(() => {
310
+ if (!isEmbedded()) return;
311
+ const onPointerMove = (e) => {
312
+ if (!activeRef.current || state.isResizing || state.isScrolling) return;
313
+ const hit = document.elementFromPoint(e.clientX, e.clientY)?.closest(`[${DATA_ATTR}]`) ?? null;
314
+ if (!hit) {
315
+ if (state.hoveredId) {
316
+ dispatch2({ type: "SET_HOVER", id: null, rect: null, tag: null });
317
+ emit({ channel: CHANNEL, event: "HOVER", element: null });
318
+ }
319
+ return;
320
+ }
321
+ const id = hit.getAttribute(DATA_ATTR);
322
+ const isSelected = state.selectedElements.some((s) => s.id === id);
323
+ if (id === state.hoveredId || isSelected) return;
324
+ const info = buildElementInfo(hit);
325
+ dispatch2({ type: "SET_HOVER", id: info.id, rect: info.rect, tag: info.tag });
326
+ emit({ channel: CHANNEL, event: "HOVER", element: info });
327
+ };
328
+ const onPointerLeave = () => {
329
+ if (!activeRef.current) return;
330
+ dispatch2({ type: "CLEAR_HOVER" });
331
+ emit({ channel: CHANNEL, event: "HOVER", element: null });
332
+ };
333
+ document.addEventListener("pointermove", onPointerMove);
334
+ document.addEventListener("pointerleave", onPointerLeave);
335
+ return () => {
336
+ document.removeEventListener("pointermove", onPointerMove);
337
+ document.removeEventListener("pointerleave", onPointerLeave);
338
+ };
339
+ }, [state.hoveredId, state.selectedElements, state.isResizing, state.isScrolling, buildElementInfo]);
340
+ useEffect(() => {
341
+ if (!isEmbedded()) return;
342
+ const onClick = (e) => {
343
+ if (!activeRef.current) return;
344
+ const target = e.target;
345
+ const link = target.closest("a");
346
+ if (link && !link.isContentEditable) {
347
+ e.preventDefault();
348
+ e.stopPropagation();
349
+ }
350
+ const hit = target.closest(`[${DATA_ATTR}]`);
351
+ if (!hit) {
352
+ handleDeselect();
353
+ return;
354
+ }
355
+ const id = hit.getAttribute(DATA_ATTR);
356
+ const isAlreadySelected = state.selectedElements.some((s) => s.id === id);
357
+ if (isAlreadySelected) return;
358
+ cleanupEditing();
359
+ handleSelect(hit, { x: e.clientX, y: e.clientY }, state.selectedElements);
360
+ };
361
+ document.addEventListener("click", onClick, true);
362
+ return () => document.removeEventListener("click", onClick, true);
363
+ }, [state.selectedElements, handleSelect, handleDeselect, cleanupEditing]);
364
+ useEffect(() => {
365
+ if (!isEmbedded()) return;
366
+ const onScroll = () => {
367
+ if (!activeRef.current) return;
368
+ if (!state.isScrolling) {
369
+ dispatch2({ type: "SET_SCROLLING", value: true });
370
+ emit({ channel: CHANNEL, event: "SCROLL_START" });
371
+ }
372
+ if (scrollTimeoutRef.current) clearTimeout(scrollTimeoutRef.current);
373
+ scrollTimeoutRef.current = window.setTimeout(() => {
374
+ dispatch2({ type: "SET_SCROLLING", value: false });
375
+ emit({ channel: CHANNEL, event: "SCROLL_END" });
376
+ dispatch2({ type: "UPDATE_SELECTED_RECTS" });
377
+ }, 150);
378
+ };
379
+ window.addEventListener("scroll", onScroll, true);
380
+ return () => window.removeEventListener("scroll", onScroll, true);
381
+ }, [state.isScrolling]);
382
+ useEffect(() => {
383
+ if (!isEmbedded()) return;
384
+ const onMessage = (e) => {
385
+ const msg = e.data;
386
+ if (msg?.channel !== CHANNEL) return;
387
+ switch (msg.action) {
388
+ case "ACTIVATE":
389
+ dispatch2({ type: "SET_ACTIVE", value: msg.value });
390
+ if (!msg.value) handleDeselect();
391
+ break;
392
+ case "SCROLL_BY":
393
+ window.scrollBy({ left: msg.dx, top: msg.dy, behavior: "auto" });
394
+ break;
395
+ case "APPLY_STYLES": {
396
+ const elements = document.querySelectorAll(`[${DATA_ATTR}="${msg.elementId}"]`);
397
+ elements.forEach((el) => {
398
+ Object.entries(msg.styles).forEach(([prop, val]) => {
399
+ const cssProp = prop.replace(/([A-Z])/g, "-$1").toLowerCase();
400
+ el.style.setProperty(cssProp, val, "important");
401
+ });
402
+ });
403
+ const existing = appliedStylesRef.current.get(msg.elementId) || {};
404
+ appliedStylesRef.current.set(msg.elementId, { ...existing, ...msg.styles });
405
+ requestAnimationFrame(() => {
406
+ dispatch2({ type: "UPDATE_SELECTED_RECTS" });
407
+ });
408
+ break;
409
+ }
410
+ case "APPLY_IMAGE": {
411
+ const img = document.querySelector(`[${DATA_ATTR}="${msg.elementId}"]`);
412
+ if (img?.tagName.toLowerCase() === "img") {
413
+ img.removeAttribute("srcset");
414
+ img.src = msg.src;
415
+ }
416
+ break;
417
+ }
418
+ case "PREVIEW_FONT": {
419
+ const el = document.querySelector(`[${DATA_ATTR}="${msg.elementId}"]`);
420
+ if (el) {
421
+ const fontKey = msg.font.replace(/[\s']+/g, "+");
422
+ const link = document.createElement("link");
423
+ link.rel = "stylesheet";
424
+ link.href = `https://fonts.googleapis.com/css2?family=${fontKey}:wght@400;500;600;700&display=swap`;
425
+ document.head.appendChild(link);
426
+ el.style.setProperty("font-family", msg.font, "important");
427
+ }
428
+ break;
429
+ }
430
+ case "CLEAR_STYLES": {
431
+ const el = document.querySelector(`[${DATA_ATTR}="${msg.elementId}"]`);
432
+ if (el) {
433
+ el.removeAttribute("style");
434
+ appliedStylesRef.current.delete(msg.elementId);
435
+ }
436
+ break;
437
+ }
438
+ case "RESIZE": {
439
+ const el = document.querySelector(`[${DATA_ATTR}="${msg.elementId}"]`);
440
+ if (el) {
441
+ el.style.setProperty("width", `${msg.width}px`, "important");
442
+ el.style.setProperty("height", `${msg.height}px`, "important");
443
+ const existing = appliedStylesRef.current.get(msg.elementId) || {};
444
+ appliedStylesRef.current.set(msg.elementId, {
445
+ ...existing,
446
+ width: `${msg.width}px`,
447
+ height: `${msg.height}px`
448
+ });
449
+ requestAnimationFrame(() => {
450
+ dispatch2({ type: "UPDATE_SELECTED_RECTS" });
451
+ });
452
+ }
453
+ break;
454
+ }
455
+ case "HOVER_ELEMENT": {
456
+ if (!msg.elementId) {
457
+ dispatch2({ type: "CLEAR_HOVER" });
458
+ return;
459
+ }
460
+ const el = document.querySelector(`[${DATA_ATTR}="${msg.elementId}"]`);
461
+ if (el) {
462
+ const info = buildElementInfo(el);
463
+ dispatch2({ type: "SET_HOVER", id: info.id, rect: info.rect, tag: info.tag });
464
+ }
465
+ break;
466
+ }
467
+ }
468
+ };
469
+ window.addEventListener("message", onMessage);
470
+ return () => window.removeEventListener("message", onMessage);
471
+ }, [handleDeselect, buildElementInfo]);
472
+ useEffect(() => {
473
+ if (!isEmbedded()) return;
474
+ const onKeyDown = (e) => {
475
+ if (!activeRef.current) return;
476
+ if (e.key === "Escape") {
477
+ if (editingElRef.current) {
478
+ editingElRef.current.blur();
479
+ cleanupEditing();
480
+ } else if (state.selectedElements.length > 0) {
481
+ handleDeselect();
482
+ }
483
+ }
484
+ };
485
+ document.addEventListener("keydown", onKeyDown);
486
+ return () => document.removeEventListener("keydown", onKeyDown);
487
+ }, [state.selectedElements, cleanupEditing, handleDeselect]);
488
+ const onResizeStart = useCallback((handle, e) => {
489
+ if (!selectedElRef.current || Platform.OS !== "web") return;
490
+ const nativeEvent = e.nativeEvent;
491
+ const rect = selectedElRef.current.getBoundingClientRect();
492
+ resizeStartRef.current = { x: nativeEvent.pageX, y: nativeEvent.pageY, w: rect.width, h: rect.height };
493
+ dispatch2({ type: "SET_RESIZING", value: true, handle });
494
+ dispatch2({ type: "CLEAR_HOVER" });
495
+ }, []);
496
+ useEffect(() => {
497
+ if (!state.isResizing || !resizeStartRef.current || !state.resizeHandle || Platform.OS !== "web") return;
498
+ const onMouseMove = (e) => {
499
+ if (!selectedElRef.current || !resizeStartRef.current) return;
500
+ const dx = e.clientX - resizeStartRef.current.x;
501
+ const dy = e.clientY - resizeStartRef.current.y;
502
+ const handle = state.resizeHandle;
503
+ let w = resizeStartRef.current.w;
504
+ let h = resizeStartRef.current.h;
505
+ if (handle.includes("e")) w += dx;
506
+ if (handle.includes("w")) w -= dx;
507
+ if (handle.includes("s")) h += dy;
508
+ if (handle.includes("n")) h -= dy;
509
+ w = Math.max(20, w);
510
+ h = Math.max(20, h);
511
+ selectedElRef.current.style.setProperty("width", `${w}px`, "important");
512
+ selectedElRef.current.style.setProperty("height", `${h}px`, "important");
513
+ dispatch2({ type: "UPDATE_SELECTED_RECTS" });
514
+ };
515
+ const onMouseUp = () => {
516
+ if (selectedElRef.current) {
517
+ const id = selectedElRef.current.getAttribute(DATA_ATTR);
518
+ if (id) {
519
+ const cs = window.getComputedStyle(selectedElRef.current);
520
+ const loc = parseLocation(id);
521
+ if (loc) {
522
+ emit({
523
+ channel: CHANNEL,
524
+ event: "RESIZE_COMMIT",
525
+ elementId: id,
526
+ width: cs.width,
527
+ height: cs.height,
528
+ source: loc
529
+ });
530
+ }
531
+ }
532
+ }
533
+ dispatch2({ type: "SET_RESIZING", value: false, handle: null });
534
+ resizeStartRef.current = null;
535
+ };
536
+ document.addEventListener("mousemove", onMouseMove);
537
+ document.addEventListener("mouseup", onMouseUp);
538
+ return () => {
539
+ document.removeEventListener("mousemove", onMouseMove);
540
+ document.removeEventListener("mouseup", onMouseUp);
541
+ };
542
+ }, [state.isResizing, state.resizeHandle]);
543
+ useEffect(() => {
544
+ if (!state.active || Platform.OS !== "web") return;
545
+ const preventSubmit = (e) => {
546
+ e.preventDefault();
547
+ e.stopPropagation();
548
+ };
549
+ document.addEventListener("submit", preventSubmit, true);
550
+ return () => document.removeEventListener("submit", preventSubmit, true);
551
+ }, [state.active]);
552
+ if (Platform.OS !== "web" || !state.active) return null;
553
+ const resizeHandles = ["n", "ne", "e", "se", "s", "sw", "w", "nw"];
554
+ const getHandleStyle = (handle) => {
555
+ const base = {
556
+ position: "absolute",
557
+ width: 8,
558
+ height: 8,
559
+ backgroundColor: "#3b82f6",
560
+ borderWidth: 1,
561
+ borderColor: "white",
562
+ borderRadius: 2,
563
+ zIndex: 10002
564
+ };
565
+ const posMap = {
566
+ n: { top: -4, left: "50%", transform: "translateX(-50%)", cursor: "ns-resize" },
567
+ ne: { top: -4, right: -4, cursor: "nesw-resize" },
568
+ e: { top: "50%", right: -4, transform: "translateY(-50%)", cursor: "ew-resize" },
569
+ se: { bottom: -4, right: -4, cursor: "nwse-resize" },
570
+ s: { bottom: -4, left: "50%", transform: "translateX(-50%)", cursor: "ns-resize" },
571
+ sw: { bottom: -4, left: -4, cursor: "nesw-resize" },
572
+ w: { top: "50%", left: -4, transform: "translateY(-50%)", cursor: "ew-resize" },
573
+ nw: { top: -4, left: -4, cursor: "nwse-resize" }
574
+ };
575
+ return { ...base, ...posMap[handle] };
576
+ };
577
+ return /* @__PURE__ */ jsxs(View, { style: styles.container, pointerEvents: "box-none", children: [
578
+ state.hoveredRect && !state.selectedElements.some((s) => s.id === state.hoveredId) && !state.isScrolling && /* @__PURE__ */ jsx(
579
+ View,
580
+ {
581
+ style: [
582
+ styles.hoverOverlay,
583
+ {
584
+ top: state.hoveredRect.top,
585
+ left: state.hoveredRect.left,
586
+ width: state.hoveredRect.width,
587
+ height: state.hoveredRect.height
588
+ }
589
+ ],
590
+ pointerEvents: "none",
591
+ children: state.hoveredTag && /* @__PURE__ */ jsx(View, { style: styles.hoverTag, children: /* @__PURE__ */ jsx(Text, { style: styles.hoverTagText, children: state.hoveredTag }) })
592
+ }
593
+ ),
594
+ !state.isScrolling && state.selectedElements.map((selected, index) => /* @__PURE__ */ jsxs(
595
+ View,
596
+ {
597
+ style: [
598
+ styles.selectedOverlay,
599
+ {
600
+ top: selected.rect.top,
601
+ left: selected.rect.left,
602
+ width: selected.rect.width,
603
+ height: selected.rect.height,
604
+ zIndex: 10001 + index
605
+ }
606
+ ],
607
+ pointerEvents: "box-none",
608
+ children: [
609
+ selected.tag && /* @__PURE__ */ jsx(View, { style: styles.selectedTag, children: /* @__PURE__ */ jsxs(Text, { style: styles.selectedTagText, children: [
610
+ selected.tag,
611
+ " (",
612
+ index + 1,
613
+ "/",
614
+ state.selectedElements.length,
615
+ ")"
616
+ ] }) }),
617
+ index === state.selectedElements.length - 1 && resizeHandles.map((handle) => /* @__PURE__ */ jsx(
618
+ View,
619
+ {
620
+ style: getHandleStyle(handle),
621
+ onStartShouldSetResponder: () => true,
622
+ onResponderGrant: (e) => onResizeStart(handle, e)
623
+ },
624
+ handle
625
+ ))
626
+ ]
627
+ },
628
+ selected.id
629
+ ))
630
+ ] });
631
+ }
632
+ var styles = StyleSheet.create({
633
+ container: {
634
+ position: "absolute",
635
+ top: 0,
636
+ left: 0,
637
+ right: 0,
638
+ bottom: 0,
639
+ zIndex: 99999
640
+ },
641
+ hoverOverlay: {
642
+ position: "absolute",
643
+ borderWidth: 2,
644
+ borderStyle: "dashed",
645
+ borderColor: "#3b82f6",
646
+ backgroundColor: "rgba(59, 130, 246, 0.08)",
647
+ borderRadius: 4,
648
+ zIndex: 1e4
649
+ },
650
+ hoverTag: {
651
+ position: "absolute",
652
+ top: -22,
653
+ left: 0,
654
+ backgroundColor: "#3b82f6",
655
+ paddingHorizontal: 6,
656
+ paddingVertical: 2,
657
+ borderRadius: 3
658
+ },
659
+ hoverTagText: {
660
+ color: "white",
661
+ fontSize: 11,
662
+ fontFamily: "system-ui, sans-serif"
663
+ },
664
+ selectedOverlay: {
665
+ position: "absolute",
666
+ borderWidth: 2,
667
+ borderColor: "#3b82f6",
668
+ backgroundColor: "rgba(59, 130, 246, 0.04)",
669
+ borderRadius: 4
670
+ },
671
+ selectedTag: {
672
+ position: "absolute",
673
+ top: -22,
674
+ left: 0,
675
+ backgroundColor: "#3b82f6",
676
+ paddingHorizontal: 6,
677
+ paddingVertical: 2,
678
+ borderRadius: 3
679
+ },
680
+ selectedTagText: {
681
+ color: "white",
682
+ fontSize: 11,
683
+ fontWeight: "500",
684
+ fontFamily: "system-ui, sans-serif"
685
+ }
686
+ });
687
+
688
+ // src/expo/components/ErrorBoundary.tsx
689
+ import { Component } from "react";
690
+ import { Platform as Platform2, StyleSheet as StyleSheet2, Text as Text2, View as View2, TouchableOpacity } from "react-native";
691
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
692
+ var ERROR_DEDUPE_WINDOW_MS = 5e3;
693
+ var reportedErrors = /* @__PURE__ */ new Set();
694
+ function generateFingerprint(message, stack) {
695
+ const stackLine = stack?.split("\\n")[1] || "";
696
+ return `${message}: ${stackLine}`.slice(0, 200);
697
+ }
698
+ function shouldReport(fingerprint) {
699
+ if (reportedErrors.has(fingerprint)) {
700
+ return false;
701
+ }
702
+ reportedErrors.add(fingerprint);
703
+ setTimeout(() => reportedErrors.delete(fingerprint), ERROR_DEDUPE_WINDOW_MS);
704
+ return true;
705
+ }
706
+ function dispatch(data) {
707
+ if (Platform2.OS === "web" && typeof window !== "undefined" && window.parent !== window) {
708
+ try {
709
+ window.parent.postMessage(data, "*");
710
+ } catch {
711
+ }
712
+ } else if (Platform2.OS !== "web") {
713
+ try {
714
+ const { requireOptionalNativeModule } = __require("expo-modules-core");
715
+ const Bridge = requireOptionalNativeModule("FlexBridge");
716
+ Bridge?.sendMessage?.({ type: "runtime-error", payload: JSON.stringify(data) });
717
+ } catch {
718
+ }
719
+ }
720
+ }
721
+ function reportException(message, origin, details) {
722
+ const fingerprint = generateFingerprint(message, details?.stack);
723
+ if (!shouldReport(fingerprint)) return;
724
+ dispatch({
725
+ type: "VIBEX_EXCEPTION",
726
+ details: {
727
+ message,
728
+ stack: details?.stack,
729
+ file: details?.file,
730
+ line: details?.line,
731
+ column: details?.column,
732
+ componentStack: details?.componentStack,
733
+ name: details?.name,
734
+ origin
735
+ },
736
+ capturedAt: Date.now()
737
+ });
738
+ }
739
+ function reportBoundaryError(error, componentStack) {
740
+ dispatch({
741
+ type: "vibex-boundary-error",
742
+ details: {
743
+ message: error.message,
744
+ stack: error.stack,
745
+ name: error.name,
746
+ componentStack
747
+ },
748
+ capturedAt: Date.now(),
749
+ client: Platform2.OS === "web" && typeof navigator !== "undefined" ? navigator.userAgent : `react-native-${Platform2.OS}`
750
+ });
751
+ }
752
+ function requestAIFix(error) {
753
+ dispatch({
754
+ type: "vibex-fix-with-ai",
755
+ details: {
756
+ message: error.message,
757
+ stack: error.stack,
758
+ name: error.name
759
+ },
760
+ capturedAt: Date.now()
761
+ });
762
+ }
763
+ function setupGlobalHandlers() {
764
+ if (Platform2.OS !== "web" || typeof window === "undefined") return;
765
+ window.addEventListener("error", (evt) => {
766
+ evt.preventDefault();
767
+ reportException(
768
+ evt.message || "Unknown error",
769
+ "runtime",
770
+ {
771
+ stack: evt.error?.stack,
772
+ file: evt.filename,
773
+ line: evt.lineno,
774
+ column: evt.colno
775
+ }
776
+ );
777
+ }, true);
778
+ window.addEventListener("unhandledrejection", (evt) => {
779
+ evt.preventDefault();
780
+ reportException(
781
+ evt.reason?.message ?? String(evt.reason),
782
+ "promise",
783
+ { stack: evt.reason?.stack }
784
+ );
785
+ }, true);
786
+ }
787
+ function interceptConsoleErrors() {
788
+ const original = console.error;
789
+ console.error = (...args) => {
790
+ original.apply(console, args);
791
+ const message = args.map((a) => {
792
+ if (a instanceof Error) return a.message;
793
+ if (typeof a === "object") {
794
+ try {
795
+ return JSON.stringify(a);
796
+ } catch {
797
+ return String(a);
798
+ }
799
+ }
800
+ return String(a);
801
+ }).join(" ");
802
+ if (/error|exception|failed|crash|fatal/i.test(message)) {
803
+ reportException(message, "console");
804
+ }
805
+ };
806
+ return () => {
807
+ console.error = original;
808
+ };
809
+ }
810
+ var ErrorBoundary = class extends Component {
811
+ cleanupConsole = null;
812
+ constructor(props) {
813
+ super(props);
814
+ this.state = { hasError: false, error: null, errorInfo: null };
815
+ }
816
+ componentDidMount() {
817
+ setupGlobalHandlers();
818
+ this.cleanupConsole = interceptConsoleErrors();
819
+ }
820
+ componentWillUnmount() {
821
+ this.cleanupConsole?.();
822
+ }
823
+ static getDerivedStateFromError(error) {
824
+ return { hasError: true, error };
825
+ }
826
+ componentDidCatch(error, errorInfo) {
827
+ this.setState({ errorInfo });
828
+ reportException(error.message, "boundary", {
829
+ stack: error.stack,
830
+ name: error.name,
831
+ componentStack: errorInfo.componentStack || void 0
832
+ });
833
+ reportBoundaryError(error, errorInfo.componentStack || void 0);
834
+ this.props.onError?.(error, errorInfo);
835
+ }
836
+ handleRetry = () => {
837
+ this.setState({ hasError: false, error: null, errorInfo: null });
838
+ };
839
+ handleFixWithAI = () => {
840
+ if (this.state.error) {
841
+ requestAIFix(this.state.error);
842
+ }
843
+ };
844
+ render() {
845
+ if (this.state.hasError) {
846
+ if (this.props.fallback) return this.props.fallback;
847
+ const error = this.state.error;
848
+ return /* @__PURE__ */ jsxs2(View2, { style: styles2.container, children: [
849
+ /* @__PURE__ */ jsxs2(View2, { style: styles2.card, children: [
850
+ /* @__PURE__ */ jsx2(View2, { style: styles2.accentBar }),
851
+ /* @__PURE__ */ jsxs2(View2, { style: styles2.content, children: [
852
+ /* @__PURE__ */ jsx2(View2, { style: styles2.header, children: /* @__PURE__ */ jsxs2(View2, { children: [
853
+ /* @__PURE__ */ jsx2(Text2, { style: styles2.label, children: "Runtime Error" }),
854
+ /* @__PURE__ */ jsx2(Text2, { style: styles2.title, children: error?.name || "Error" })
855
+ ] }) }),
856
+ /* @__PURE__ */ jsx2(Text2, { style: styles2.message, children: error?.message || "An unexpected error occurred" }),
857
+ /* @__PURE__ */ jsxs2(View2, { style: styles2.actions, children: [
858
+ /* @__PURE__ */ jsx2(TouchableOpacity, { style: styles2.primaryButton, onPress: this.handleFixWithAI, children: /* @__PURE__ */ jsx2(Text2, { style: styles2.primaryButtonText, children: "Fix with AI" }) }),
859
+ /* @__PURE__ */ jsx2(TouchableOpacity, { style: styles2.secondaryButton, onPress: this.handleRetry, children: /* @__PURE__ */ jsx2(Text2, { style: styles2.secondaryButtonText, children: "Retry" }) })
860
+ ] }),
861
+ __DEV__ && error?.stack && /* @__PURE__ */ jsx2(View2, { style: styles2.stackContainer, children: /* @__PURE__ */ jsx2(Text2, { style: styles2.stackTrace, children: error.stack }) })
862
+ ] })
863
+ ] }),
864
+ /* @__PURE__ */ jsx2(Text2, { style: styles2.footer, children: "Vibex" })
865
+ ] });
866
+ }
867
+ return this.props.children;
868
+ }
869
+ };
870
+ var styles2 = StyleSheet2.create({
871
+ container: {
872
+ flex: 1,
873
+ backgroundColor: "#fafafa",
874
+ alignItems: "center",
875
+ justifyContent: "center",
876
+ padding: 16
877
+ },
878
+ card: {
879
+ width: "100%",
880
+ maxWidth: 480,
881
+ flexDirection: "row",
882
+ borderRadius: 12,
883
+ borderWidth: 1,
884
+ borderColor: "#e5e5e5",
885
+ backgroundColor: "#fff",
886
+ overflow: "hidden"
887
+ },
888
+ accentBar: {
889
+ width: 6,
890
+ backgroundColor: "#dc2626"
891
+ },
892
+ content: {
893
+ flex: 1,
894
+ padding: 20
895
+ },
896
+ header: {
897
+ flexDirection: "row",
898
+ justifyContent: "space-between",
899
+ alignItems: "flex-start",
900
+ marginBottom: 16
901
+ },
902
+ label: {
903
+ fontSize: 10,
904
+ fontWeight: "600",
905
+ letterSpacing: 0.5,
906
+ textTransform: "uppercase",
907
+ color: "#737373",
908
+ marginBottom: 4
909
+ },
910
+ title: {
911
+ fontSize: 18,
912
+ fontWeight: "500",
913
+ color: "#171717"
914
+ },
915
+ message: {
916
+ fontSize: 14,
917
+ color: "#737373",
918
+ lineHeight: 20,
919
+ marginBottom: 20
920
+ },
921
+ actions: {
922
+ flexDirection: "row",
923
+ alignItems: "center",
924
+ gap: 12
925
+ },
926
+ primaryButton: {
927
+ backgroundColor: "#171717",
928
+ paddingHorizontal: 16,
929
+ paddingVertical: 10,
930
+ borderRadius: 6,
931
+ flexDirection: "row",
932
+ alignItems: "center",
933
+ gap: 8
934
+ },
935
+ primaryButtonText: {
936
+ color: "#fff",
937
+ fontSize: 14,
938
+ fontWeight: "500"
939
+ },
940
+ secondaryButton: {
941
+ paddingHorizontal: 12,
942
+ paddingVertical: 10
943
+ },
944
+ secondaryButtonText: {
945
+ color: "#737373",
946
+ fontSize: 14
947
+ },
948
+ stackContainer: {
949
+ marginTop: 16,
950
+ paddingTop: 16,
951
+ borderTopWidth: 1,
952
+ borderTopColor: "#e5e5e5"
953
+ },
954
+ stackTrace: {
955
+ fontSize: 11,
956
+ fontFamily: Platform2.OS === "ios" ? "Menlo" : "monospace",
957
+ color: "#737373",
958
+ lineHeight: 16,
959
+ maxHeight: 350,
960
+ overflow: "scroll"
961
+ },
962
+ footer: {
963
+ marginTop: 16,
964
+ fontSize: 12,
965
+ color: "#a3a3a3"
966
+ }
967
+ });
968
+
969
+ // src/expo/config.ts
970
+ import { fileURLToPath } from "url";
971
+ import { dirname, join } from "path";
972
+ var __filename = fileURLToPath(import.meta.url);
973
+ var __dirname = dirname(__filename);
974
+ var transformerPath = join(__dirname, "..", "..", "expo", "metro-transformer.js");
975
+ function withInspector(metroConfig, options = {}) {
976
+ const { enableInProduction = false } = options;
977
+ if (process.env.NODE_ENV === "production" && !enableInProduction) {
978
+ return metroConfig;
979
+ }
980
+ return {
981
+ ...metroConfig,
982
+ transformer: {
983
+ ...metroConfig.transformer,
984
+ babelTransformerPath: transformerPath
985
+ }
986
+ };
987
+ }
988
+ export {
989
+ ErrorBoundary,
990
+ VisualInspector,
991
+ transformerPath,
992
+ withInspector
993
+ };
994
+ //# sourceMappingURL=index.js.map