@vibexdotnew/inspector 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,929 @@
1
+ "use client";
2
+ "use strict";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/next/index.ts
22
+ var next_exports = {};
23
+ __export(next_exports, {
24
+ Observer: () => Observer,
25
+ VisualInspector: () => VisualInspector,
26
+ loaderPath: () => loaderPath,
27
+ withInspector: () => withInspector
28
+ });
29
+ module.exports = __toCommonJS(next_exports);
30
+
31
+ // src/next/components/VisualInspector.tsx
32
+ var import_react = require("react");
33
+ var import_jsx_runtime = require("react/jsx-runtime");
34
+ var CHANNEL = "VIBEX_INSPECTOR";
35
+ var STORAGE_KEY = "vibex_inspector_active";
36
+ var SELECTED_KEY = "vibex_selected_element";
37
+ var DATA_ATTR = "data-vibex-loc";
38
+ var OVERLAY_PADDING = 4;
39
+ var MAX_SELECTIONS = 5;
40
+ var isEmbedded = () => {
41
+ try {
42
+ return window.self !== window.top;
43
+ } catch {
44
+ return true;
45
+ }
46
+ };
47
+ var emit = /* @__PURE__ */ (() => {
48
+ let lastPayload = "";
49
+ return (msg) => {
50
+ const json = JSON.stringify(msg);
51
+ if (json === lastPayload) return;
52
+ lastPayload = json;
53
+ window.parent.postMessage(msg, "*");
54
+ };
55
+ })();
56
+ var padRect = (r) => ({
57
+ top: r.top - OVERLAY_PADDING,
58
+ left: r.left - OVERLAY_PADDING,
59
+ width: r.width + OVERLAY_PADDING * 2,
60
+ height: r.height + OVERLAY_PADDING * 2
61
+ });
62
+ var parseLocation = (id) => {
63
+ const parts = id.split(":");
64
+ if (parts.length < 3) return null;
65
+ const col = parseInt(parts.pop(), 10);
66
+ const line = parseInt(parts.pop(), 10);
67
+ const file = parts.join(":");
68
+ if (isNaN(line) || isNaN(col)) return null;
69
+ return { file, line, col };
70
+ };
71
+ var normalizeStyleValue = (prop, val) => {
72
+ if (prop === "backgroundColor" && (val === "rgba(0, 0, 0, 0)" || val === "transparent")) return "transparent";
73
+ if (prop === "backgroundImage" && val === "none") return "none";
74
+ if (prop === "textDecoration" && val.includes("none")) return "none";
75
+ if (prop === "fontStyle" && val === "normal") return "normal";
76
+ if (prop === "opacity" && val === "1") return "1";
77
+ if ((prop.includes("padding") || prop.includes("margin")) && (val === "0px" || val === "0")) return "0";
78
+ if (prop === "borderRadius" && val === "0px") return "0";
79
+ if (prop === "letterSpacing" && (val === "normal" || val === "0px")) return "normal";
80
+ if (prop === "gap" && (val === "normal" || val === "0px")) return "0";
81
+ return val;
82
+ };
83
+ var getComputedStyles = (el) => {
84
+ const cs = window.getComputedStyle(el);
85
+ const get = (p) => normalizeStyleValue(p, cs.getPropertyValue(p.replace(/([A-Z])/g, "-$1").toLowerCase()));
86
+ return {
87
+ fontSize: get("fontSize"),
88
+ color: get("color"),
89
+ fontWeight: get("fontWeight"),
90
+ fontFamily: get("fontFamily"),
91
+ fontStyle: get("fontStyle"),
92
+ textAlign: get("textAlign"),
93
+ textDecoration: get("textDecoration"),
94
+ lineHeight: get("lineHeight"),
95
+ letterSpacing: get("letterSpacing"),
96
+ backgroundColor: get("backgroundColor"),
97
+ backgroundImage: get("backgroundImage"),
98
+ borderRadius: get("borderRadius"),
99
+ opacity: get("opacity"),
100
+ padding: `${get("paddingTop")} ${get("paddingRight")} ${get("paddingBottom")} ${get("paddingLeft")}`,
101
+ margin: `${get("marginTop")} ${get("marginRight")} ${get("marginBottom")} ${get("marginLeft")}`,
102
+ display: get("display"),
103
+ flexDirection: get("flexDirection"),
104
+ alignItems: get("alignItems"),
105
+ justifyContent: get("justifyContent"),
106
+ gap: get("gap"),
107
+ width: get("width"),
108
+ height: get("height")
109
+ };
110
+ };
111
+ var canEditText = (el) => {
112
+ const tag = el.tagName.toLowerCase();
113
+ const editableTags = ["p", "h1", "h2", "h3", "h4", "h5", "h6", "span", "div", "li", "a", "button", "label", "td", "th"];
114
+ if (el.contentEditable === "true" || tag === "input" || tag === "textarea") return true;
115
+ if (!editableTags.includes(tag) || !el.textContent?.trim()) return false;
116
+ const hasDirectText = Array.from(el.childNodes).some((n) => n.nodeType === Node.TEXT_NODE && n.textContent?.trim());
117
+ return el.childElementCount === 0 || el.childElementCount <= 1 && hasDirectText;
118
+ };
119
+ var getDirectText = (el) => {
120
+ let txt = "";
121
+ for (const n of el.childNodes) {
122
+ if (n.nodeType === Node.TEXT_NODE) txt += n.textContent || "";
123
+ }
124
+ return txt;
125
+ };
126
+ var normalizeImgSrc = (src) => {
127
+ if (!src) return "";
128
+ try {
129
+ const url = new URL(src, location.origin);
130
+ if (url.pathname === "/_next/image") {
131
+ const real = url.searchParams.get("url");
132
+ if (real) return decodeURIComponent(real);
133
+ }
134
+ return url.href;
135
+ } catch {
136
+ return src;
137
+ }
138
+ };
139
+ var initialState = {
140
+ active: false,
141
+ hoveredId: null,
142
+ hoveredRect: null,
143
+ hoveredTag: null,
144
+ selectedElements: [],
145
+ isScrolling: false,
146
+ isResizing: false,
147
+ resizeHandle: null
148
+ };
149
+ function reducer(state, action) {
150
+ switch (action.type) {
151
+ case "SET_ACTIVE":
152
+ return { ...state, active: action.value };
153
+ case "SET_HOVER":
154
+ return { ...state, hoveredId: action.id, hoveredRect: action.rect, hoveredTag: action.tag };
155
+ case "ADD_SELECTED":
156
+ return { ...state, selectedElements: [...state.selectedElements, action.selected] };
157
+ case "REMOVE_SELECTED":
158
+ return { ...state, selectedElements: state.selectedElements.filter((sel) => sel.id !== action.elementId) };
159
+ case "CLEAR_SELECTIONS":
160
+ return { ...state, selectedElements: [] };
161
+ case "UPDATE_SELECTED_RECTS":
162
+ return {
163
+ ...state,
164
+ selectedElements: state.selectedElements.map((sel) => ({
165
+ ...sel,
166
+ rect: padRect(sel.element.getBoundingClientRect())
167
+ }))
168
+ };
169
+ case "SET_SCROLLING":
170
+ return { ...state, isScrolling: action.value };
171
+ case "SET_RESIZING":
172
+ return { ...state, isResizing: action.value, resizeHandle: action.handle };
173
+ case "CLEAR_HOVER":
174
+ return { ...state, hoveredId: null, hoveredRect: null, hoveredTag: null };
175
+ default:
176
+ return state;
177
+ }
178
+ }
179
+ function VisualInspector() {
180
+ const [state, dispatch] = (0, import_react.useReducer)(reducer, initialState, (init) => {
181
+ if (typeof window !== "undefined") {
182
+ const stored = localStorage.getItem(STORAGE_KEY);
183
+ return { ...init, active: stored === "true" };
184
+ }
185
+ return init;
186
+ });
187
+ const activeRef = (0, import_react.useRef)(state.active);
188
+ const selectedElRef = (0, import_react.useRef)(null);
189
+ const editingElRef = (0, import_react.useRef)(null);
190
+ const originalTextRef = (0, import_react.useRef)("");
191
+ const originalSrcRef = (0, import_react.useRef)("");
192
+ const appliedStylesRef = (0, import_react.useRef)(/* @__PURE__ */ new Map());
193
+ const scrollTimeoutRef = (0, import_react.useRef)(null);
194
+ const resizeStartRef = (0, import_react.useRef)(null);
195
+ (0, import_react.useEffect)(() => {
196
+ activeRef.current = state.active;
197
+ localStorage.setItem(STORAGE_KEY, String(state.active));
198
+ emit({ channel: CHANNEL, event: "MODE_CHANGED", active: state.active });
199
+ }, [state.active]);
200
+ (0, import_react.useEffect)(() => {
201
+ if (!isEmbedded()) return;
202
+ emit({ channel: CHANNEL, event: "READY" });
203
+ if (state.active) {
204
+ emit({ channel: CHANNEL, event: "MODE_CHANGED", active: true });
205
+ }
206
+ }, []);
207
+ const buildElementInfo = (0, import_react.useCallback)((el) => {
208
+ const id = el.getAttribute(DATA_ATTR) || "";
209
+ const tag = el.getAttribute("data-vibex-name") || el.tagName.toLowerCase();
210
+ const rect = padRect(el.getBoundingClientRect());
211
+ const editable = canEditText(el);
212
+ const styles = getComputedStyles(el);
213
+ const className = el.className || "";
214
+ const src = el.tagName.toLowerCase() === "img" ? el.src : void 0;
215
+ return { id, tag, rect, editable, styles, className, src };
216
+ }, []);
217
+ const handleSelect = (0, import_react.useCallback)((el, clickPos, currentSelections) => {
218
+ const info = buildElementInfo(el);
219
+ if (currentSelections.some((s) => s.id === info.id)) {
220
+ return;
221
+ }
222
+ if (currentSelections.length >= MAX_SELECTIONS) {
223
+ alert(`Selection limit reached! You can only select up to ${MAX_SELECTIONS} elements.`);
224
+ return;
225
+ }
226
+ selectedElRef.current = el;
227
+ const selected = {
228
+ id: info.id,
229
+ rect: info.rect,
230
+ tag: info.tag,
231
+ element: el
232
+ };
233
+ dispatch({ type: "ADD_SELECTED", selected });
234
+ dispatch({ type: "CLEAR_HOVER" });
235
+ const allSelected = [...currentSelections, selected];
236
+ const allInfos = allSelected.map((s) => buildElementInfo(s.element));
237
+ localStorage.setItem(SELECTED_KEY, JSON.stringify({ ids: allSelected.map((s) => s.id) }));
238
+ emit({ channel: CHANNEL, event: "SELECT", elements: allInfos, position: clickPos });
239
+ if (info.editable && el.contentEditable !== "true") {
240
+ originalTextRef.current = el.childElementCount > 0 ? getDirectText(el) : el.innerText;
241
+ el.contentEditable = "true";
242
+ editingElRef.current = el;
243
+ el.querySelectorAll("*").forEach((child) => {
244
+ child.contentEditable = "false";
245
+ });
246
+ }
247
+ if (el.tagName.toLowerCase() === "img") {
248
+ originalSrcRef.current = normalizeImgSrc(el.src);
249
+ }
250
+ }, [buildElementInfo]);
251
+ const cleanupEditing = (0, import_react.useCallback)(() => {
252
+ const el = editingElRef.current;
253
+ if (!el) return;
254
+ const id = el.getAttribute(DATA_ATTR);
255
+ if (!id) return;
256
+ const newText = el.childElementCount > 0 ? getDirectText(el) : el.innerText;
257
+ if (newText !== originalTextRef.current) {
258
+ const loc = parseLocation(id);
259
+ if (loc) {
260
+ emit({
261
+ channel: CHANNEL,
262
+ event: "TEXT_EDIT",
263
+ elementId: id,
264
+ before: originalTextRef.current,
265
+ after: newText,
266
+ source: loc
267
+ });
268
+ }
269
+ }
270
+ const styles = appliedStylesRef.current.get(id);
271
+ if (styles && Object.keys(styles).length > 0) {
272
+ const loc = parseLocation(id);
273
+ if (loc) {
274
+ emit({
275
+ channel: CHANNEL,
276
+ event: "STYLE_COMMIT",
277
+ elementId: id,
278
+ styles,
279
+ source: loc,
280
+ className: el.className || ""
281
+ });
282
+ }
283
+ appliedStylesRef.current.delete(id);
284
+ }
285
+ if (el.tagName.toLowerCase() === "img") {
286
+ const newSrc = normalizeImgSrc(el.src);
287
+ if (newSrc !== originalSrcRef.current && originalSrcRef.current) {
288
+ const loc = parseLocation(id);
289
+ if (loc) {
290
+ emit({
291
+ channel: CHANNEL,
292
+ event: "IMAGE_COMMIT",
293
+ elementId: id,
294
+ before: originalSrcRef.current,
295
+ after: newSrc,
296
+ source: loc
297
+ });
298
+ }
299
+ }
300
+ }
301
+ el.contentEditable = "false";
302
+ el.querySelectorAll('[contenteditable="false"]').forEach((child) => {
303
+ child.removeAttribute("contenteditable");
304
+ });
305
+ editingElRef.current = null;
306
+ originalTextRef.current = "";
307
+ originalSrcRef.current = "";
308
+ }, []);
309
+ const handleDeselect = (0, import_react.useCallback)(() => {
310
+ cleanupEditing();
311
+ selectedElRef.current = null;
312
+ dispatch({ type: "CLEAR_SELECTIONS" });
313
+ localStorage.removeItem(SELECTED_KEY);
314
+ emit({ channel: CHANNEL, event: "DESELECT" });
315
+ }, [cleanupEditing]);
316
+ (0, import_react.useEffect)(() => {
317
+ if (!isEmbedded()) return;
318
+ const onPointerMove = (e) => {
319
+ if (!activeRef.current || state.isResizing || state.isScrolling) return;
320
+ const hit = document.elementFromPoint(e.clientX, e.clientY)?.closest(`[${DATA_ATTR}]`) ?? null;
321
+ if (!hit) {
322
+ if (state.hoveredId) {
323
+ dispatch({ type: "SET_HOVER", id: null, rect: null, tag: null });
324
+ emit({ channel: CHANNEL, event: "HOVER", element: null });
325
+ }
326
+ return;
327
+ }
328
+ const id = hit.getAttribute(DATA_ATTR);
329
+ const isSelected = state.selectedElements.some((s) => s.id === id);
330
+ if (id === state.hoveredId || isSelected) return;
331
+ const info = buildElementInfo(hit);
332
+ dispatch({ type: "SET_HOVER", id: info.id, rect: info.rect, tag: info.tag });
333
+ emit({ channel: CHANNEL, event: "HOVER", element: info });
334
+ };
335
+ const onPointerLeave = () => {
336
+ if (!activeRef.current) return;
337
+ dispatch({ type: "CLEAR_HOVER" });
338
+ emit({ channel: CHANNEL, event: "HOVER", element: null });
339
+ };
340
+ document.addEventListener("pointermove", onPointerMove);
341
+ document.addEventListener("pointerleave", onPointerLeave);
342
+ return () => {
343
+ document.removeEventListener("pointermove", onPointerMove);
344
+ document.removeEventListener("pointerleave", onPointerLeave);
345
+ };
346
+ }, [state.hoveredId, state.selectedElements, state.isResizing, state.isScrolling, buildElementInfo]);
347
+ (0, import_react.useEffect)(() => {
348
+ if (!isEmbedded()) return;
349
+ const onClick = (e) => {
350
+ if (!activeRef.current) return;
351
+ const target = e.target;
352
+ const link = target.closest("a");
353
+ if (link && !link.isContentEditable) {
354
+ e.preventDefault();
355
+ e.stopPropagation();
356
+ }
357
+ const hit = target.closest(`[${DATA_ATTR}]`);
358
+ if (!hit) {
359
+ handleDeselect();
360
+ return;
361
+ }
362
+ const id = hit.getAttribute(DATA_ATTR);
363
+ const isAlreadySelected = state.selectedElements.some((s) => s.id === id);
364
+ if (isAlreadySelected) return;
365
+ cleanupEditing();
366
+ handleSelect(hit, { x: e.clientX, y: e.clientY }, state.selectedElements);
367
+ };
368
+ document.addEventListener("click", onClick, true);
369
+ return () => document.removeEventListener("click", onClick, true);
370
+ }, [state.selectedElements, handleSelect, handleDeselect, cleanupEditing]);
371
+ (0, import_react.useEffect)(() => {
372
+ if (!isEmbedded()) return;
373
+ const onScroll = () => {
374
+ if (!activeRef.current) return;
375
+ if (!state.isScrolling) {
376
+ dispatch({ type: "SET_SCROLLING", value: true });
377
+ emit({ channel: CHANNEL, event: "SCROLL_START" });
378
+ }
379
+ if (scrollTimeoutRef.current) clearTimeout(scrollTimeoutRef.current);
380
+ scrollTimeoutRef.current = window.setTimeout(() => {
381
+ dispatch({ type: "SET_SCROLLING", value: false });
382
+ emit({ channel: CHANNEL, event: "SCROLL_END" });
383
+ dispatch({ type: "UPDATE_SELECTED_RECTS" });
384
+ }, 150);
385
+ };
386
+ window.addEventListener("scroll", onScroll, true);
387
+ return () => window.removeEventListener("scroll", onScroll, true);
388
+ }, [state.isScrolling]);
389
+ (0, import_react.useEffect)(() => {
390
+ if (!isEmbedded()) return;
391
+ const onMessage = (e) => {
392
+ const msg = e.data;
393
+ if (msg?.channel !== CHANNEL) return;
394
+ switch (msg.action) {
395
+ case "ACTIVATE":
396
+ dispatch({ type: "SET_ACTIVE", value: msg.value });
397
+ if (!msg.value) handleDeselect();
398
+ break;
399
+ case "SCROLL_BY":
400
+ window.scrollBy({ left: msg.dx, top: msg.dy, behavior: "auto" });
401
+ break;
402
+ case "APPLY_STYLES": {
403
+ const elements = document.querySelectorAll(`[${DATA_ATTR}="${msg.elementId}"]`);
404
+ elements.forEach((el) => {
405
+ Object.entries(msg.styles).forEach(([prop, val]) => {
406
+ const cssProp = prop.replace(/([A-Z])/g, "-$1").toLowerCase();
407
+ el.style.setProperty(cssProp, val, "important");
408
+ });
409
+ });
410
+ const existing = appliedStylesRef.current.get(msg.elementId) || {};
411
+ appliedStylesRef.current.set(msg.elementId, { ...existing, ...msg.styles });
412
+ requestAnimationFrame(() => {
413
+ dispatch({ type: "UPDATE_SELECTED_RECTS" });
414
+ });
415
+ break;
416
+ }
417
+ case "APPLY_IMAGE": {
418
+ const img = document.querySelector(`[${DATA_ATTR}="${msg.elementId}"]`);
419
+ if (img?.tagName.toLowerCase() === "img") {
420
+ img.removeAttribute("srcset");
421
+ img.src = msg.src;
422
+ }
423
+ break;
424
+ }
425
+ case "PREVIEW_FONT": {
426
+ const el = document.querySelector(`[${DATA_ATTR}="${msg.elementId}"]`);
427
+ if (el) {
428
+ const fontKey = msg.font.replace(/[\s']+/g, "+");
429
+ const link = document.createElement("link");
430
+ link.rel = "stylesheet";
431
+ link.href = `https://fonts.googleapis.com/css2?family=${fontKey}:wght@400;500;600;700&display=swap`;
432
+ document.head.appendChild(link);
433
+ el.style.setProperty("font-family", msg.font, "important");
434
+ }
435
+ break;
436
+ }
437
+ case "CLEAR_STYLES": {
438
+ const el = document.querySelector(`[${DATA_ATTR}="${msg.elementId}"]`);
439
+ if (el) {
440
+ el.removeAttribute("style");
441
+ appliedStylesRef.current.delete(msg.elementId);
442
+ }
443
+ break;
444
+ }
445
+ case "RESIZE": {
446
+ const el = document.querySelector(`[${DATA_ATTR}="${msg.elementId}"]`);
447
+ if (el) {
448
+ el.style.setProperty("width", `${msg.width}px`, "important");
449
+ el.style.setProperty("height", `${msg.height}px`, "important");
450
+ const existing = appliedStylesRef.current.get(msg.elementId) || {};
451
+ appliedStylesRef.current.set(msg.elementId, {
452
+ ...existing,
453
+ width: `${msg.width}px`,
454
+ height: `${msg.height}px`
455
+ });
456
+ requestAnimationFrame(() => {
457
+ dispatch({ type: "UPDATE_SELECTED_RECTS" });
458
+ });
459
+ }
460
+ break;
461
+ }
462
+ case "HOVER_ELEMENT": {
463
+ if (!msg.elementId) {
464
+ dispatch({ type: "CLEAR_HOVER" });
465
+ return;
466
+ }
467
+ const el = document.querySelector(`[${DATA_ATTR}="${msg.elementId}"]`);
468
+ if (el) {
469
+ const info = buildElementInfo(el);
470
+ dispatch({ type: "SET_HOVER", id: info.id, rect: info.rect, tag: info.tag });
471
+ }
472
+ break;
473
+ }
474
+ case "REMOVE_SELECTED": {
475
+ const removedElement = state.selectedElements.find((s) => s.id === msg.elementId);
476
+ if (removedElement?.element === editingElRef.current) {
477
+ cleanupEditing();
478
+ }
479
+ dispatch({ type: "REMOVE_SELECTED", elementId: msg.elementId });
480
+ const remaining = state.selectedElements.filter((s) => s.id !== msg.elementId);
481
+ if (remaining.length > 0) {
482
+ localStorage.setItem(SELECTED_KEY, JSON.stringify({ ids: remaining.map((s) => s.id) }));
483
+ } else {
484
+ localStorage.removeItem(SELECTED_KEY);
485
+ }
486
+ if (remaining.length > 0) {
487
+ const allInfos = remaining.map((s) => buildElementInfo(s.element));
488
+ emit({ channel: CHANNEL, event: "SELECT", elements: allInfos, position: { x: 0, y: 0 } });
489
+ } else {
490
+ emit({ channel: CHANNEL, event: "DESELECT" });
491
+ }
492
+ break;
493
+ }
494
+ }
495
+ };
496
+ window.addEventListener("message", onMessage);
497
+ return () => window.removeEventListener("message", onMessage);
498
+ }, [handleDeselect, buildElementInfo, state.selectedElements, cleanupEditing]);
499
+ (0, import_react.useEffect)(() => {
500
+ if (!isEmbedded()) return;
501
+ const onKeyDown = (e) => {
502
+ if (!activeRef.current) return;
503
+ if (e.key === "Escape") {
504
+ if (editingElRef.current) {
505
+ editingElRef.current.blur();
506
+ cleanupEditing();
507
+ } else if (state.selectedElements.length > 0) {
508
+ handleDeselect();
509
+ }
510
+ }
511
+ };
512
+ document.addEventListener("keydown", onKeyDown);
513
+ return () => document.removeEventListener("keydown", onKeyDown);
514
+ }, [state.selectedElements, cleanupEditing, handleDeselect]);
515
+ const onResizeStart = (0, import_react.useCallback)((e, handle) => {
516
+ if (!selectedElRef.current) return;
517
+ e.preventDefault();
518
+ e.stopPropagation();
519
+ const rect = selectedElRef.current.getBoundingClientRect();
520
+ resizeStartRef.current = { x: e.clientX, y: e.clientY, w: rect.width, h: rect.height };
521
+ dispatch({ type: "SET_RESIZING", value: true, handle });
522
+ dispatch({ type: "CLEAR_HOVER" });
523
+ }, []);
524
+ (0, import_react.useEffect)(() => {
525
+ if (!state.isResizing || !resizeStartRef.current || !state.resizeHandle) return;
526
+ const onMouseMove = (e) => {
527
+ if (!selectedElRef.current || !resizeStartRef.current) return;
528
+ const dx = e.clientX - resizeStartRef.current.x;
529
+ const dy = e.clientY - resizeStartRef.current.y;
530
+ const handle = state.resizeHandle;
531
+ let w = resizeStartRef.current.w;
532
+ let h = resizeStartRef.current.h;
533
+ if (handle.includes("e")) w += dx;
534
+ if (handle.includes("w")) w -= dx;
535
+ if (handle.includes("s")) h += dy;
536
+ if (handle.includes("n")) h -= dy;
537
+ w = Math.max(20, w);
538
+ h = Math.max(20, h);
539
+ selectedElRef.current.style.setProperty("width", `${w}px`, "important");
540
+ selectedElRef.current.style.setProperty("height", `${h}px`, "important");
541
+ dispatch({ type: "UPDATE_SELECTED_RECTS" });
542
+ };
543
+ const onMouseUp = () => {
544
+ if (selectedElRef.current) {
545
+ const id = selectedElRef.current.getAttribute(DATA_ATTR);
546
+ if (id) {
547
+ const cs = window.getComputedStyle(selectedElRef.current);
548
+ const loc = parseLocation(id);
549
+ if (loc) {
550
+ emit({
551
+ channel: CHANNEL,
552
+ event: "RESIZE_COMMIT",
553
+ elementId: id,
554
+ width: cs.width,
555
+ height: cs.height,
556
+ source: loc
557
+ });
558
+ }
559
+ }
560
+ }
561
+ dispatch({ type: "SET_RESIZING", value: false, handle: null });
562
+ resizeStartRef.current = null;
563
+ };
564
+ document.addEventListener("mousemove", onMouseMove);
565
+ document.addEventListener("mouseup", onMouseUp);
566
+ return () => {
567
+ document.removeEventListener("mousemove", onMouseMove);
568
+ document.removeEventListener("mouseup", onMouseUp);
569
+ };
570
+ }, [state.isResizing, state.resizeHandle]);
571
+ (0, import_react.useEffect)(() => {
572
+ if (!state.active) return;
573
+ const preventSubmit = (e) => {
574
+ e.preventDefault();
575
+ e.stopPropagation();
576
+ };
577
+ document.addEventListener("submit", preventSubmit, true);
578
+ return () => document.removeEventListener("submit", preventSubmit, true);
579
+ }, [state.active]);
580
+ if (!state.active) return null;
581
+ const resizeHandles = ["n", "ne", "e", "se", "s", "sw", "w", "nw"];
582
+ const getHandleStyle = (handle) => {
583
+ const base = {
584
+ position: "absolute",
585
+ width: 8,
586
+ height: 8,
587
+ background: "#3b82f6",
588
+ border: "1px solid white",
589
+ borderRadius: 2,
590
+ zIndex: 10002
591
+ };
592
+ const posMap = {
593
+ n: { top: -4, left: "50%", transform: "translateX(-50%)", cursor: "ns-resize" },
594
+ ne: { top: -4, right: -4, cursor: "nesw-resize" },
595
+ e: { top: "50%", right: -4, transform: "translateY(-50%)", cursor: "ew-resize" },
596
+ se: { bottom: -4, right: -4, cursor: "nwse-resize" },
597
+ s: { bottom: -4, left: "50%", transform: "translateX(-50%)", cursor: "ns-resize" },
598
+ sw: { bottom: -4, left: -4, cursor: "nesw-resize" },
599
+ w: { top: "50%", left: -4, transform: "translateY(-50%)", cursor: "ew-resize" },
600
+ nw: { top: -4, left: -4, cursor: "nwse-resize" }
601
+ };
602
+ return { ...base, ...posMap[handle] };
603
+ };
604
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
605
+ state.hoveredRect && !state.selectedElements.some((s) => s.id === state.hoveredId) && !state.isScrolling && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
606
+ "div",
607
+ {
608
+ style: {
609
+ position: "fixed",
610
+ top: state.hoveredRect.top,
611
+ left: state.hoveredRect.left,
612
+ width: state.hoveredRect.width,
613
+ height: state.hoveredRect.height,
614
+ border: "2px dashed #3b82f6",
615
+ background: "rgba(59, 130, 246, 0.08)",
616
+ pointerEvents: "none",
617
+ zIndex: 1e4,
618
+ borderRadius: 4
619
+ },
620
+ children: state.hoveredTag && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
621
+ "span",
622
+ {
623
+ style: {
624
+ position: "absolute",
625
+ top: -22,
626
+ left: 0,
627
+ background: "#3b82f6",
628
+ color: "white",
629
+ fontSize: 11,
630
+ padding: "2px 6px",
631
+ borderRadius: 3,
632
+ fontFamily: "system-ui, sans-serif"
633
+ },
634
+ children: state.hoveredTag
635
+ }
636
+ )
637
+ }
638
+ ),
639
+ !state.isScrolling && state.selectedElements.map((selected, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
640
+ "div",
641
+ {
642
+ style: {
643
+ position: "fixed",
644
+ top: selected.rect.top,
645
+ left: selected.rect.left,
646
+ width: selected.rect.width,
647
+ height: selected.rect.height,
648
+ border: "2px solid #3b82f6",
649
+ background: "rgba(59, 130, 246, 0.04)",
650
+ pointerEvents: "none",
651
+ zIndex: 10001 + index,
652
+ borderRadius: 4
653
+ },
654
+ children: [
655
+ selected.tag && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
656
+ "span",
657
+ {
658
+ style: {
659
+ position: "absolute",
660
+ top: -22,
661
+ left: 0,
662
+ background: "#3b82f6",
663
+ color: "white",
664
+ fontSize: 11,
665
+ padding: "2px 6px",
666
+ borderRadius: 3,
667
+ fontFamily: "system-ui, sans-serif",
668
+ fontWeight: 500
669
+ },
670
+ children: [
671
+ selected.tag,
672
+ " (",
673
+ index + 1,
674
+ "/",
675
+ state.selectedElements.length,
676
+ ")"
677
+ ]
678
+ }
679
+ ),
680
+ index === state.selectedElements.length - 1 && resizeHandles.map((handle) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
681
+ "div",
682
+ {
683
+ style: { ...getHandleStyle(handle), pointerEvents: "auto" },
684
+ onMouseDown: (e) => onResizeStart(e, handle)
685
+ },
686
+ handle
687
+ ))
688
+ ]
689
+ },
690
+ selected.id
691
+ )),
692
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: `
693
+ [contenteditable="true"]:focus {
694
+ outline: none !important;
695
+ }
696
+ [contenteditable="true"]::selection {
697
+ background: rgba(59, 130, 246, 0.3);
698
+ }
699
+ ` }),
700
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: `
701
+ nextjs-portal {
702
+ display: none !important;
703
+ }
704
+ ` })
705
+ ] });
706
+ }
707
+
708
+ // src/next/components/Observer.tsx
709
+ var import_react2 = require("react");
710
+ var import_navigation = require("next/navigation");
711
+ var import_jsx_runtime2 = require("react/jsx-runtime");
712
+ function Observer({
713
+ error,
714
+ reset,
715
+ trackNavigation = true,
716
+ appMeta
717
+ }) {
718
+ const previousOverlayContent = (0, import_react2.useRef)("");
719
+ const monitorInterval = (0, import_react2.useRef)(void 0);
720
+ const isInitialNav = (0, import_react2.useRef)(true);
721
+ const pathname = (0, import_navigation.usePathname)();
722
+ const searchParams = (0, import_navigation.useSearchParams)();
723
+ if (!trackNavigation) return;
724
+ const isEmbedded2 = window.parent !== window;
725
+ (0, import_react2.useEffect)(() => {
726
+ console.log(
727
+ { pathname, searchParams, trackNavigation, appMeta, isEmbedded: isEmbedded2 }
728
+ );
729
+ if (!isEmbedded2) return;
730
+ const queryObj = {};
731
+ searchParams.forEach((val, key) => {
732
+ queryObj[key] = val;
733
+ });
734
+ const href = searchParams.toString() ? `${pathname}?${searchParams.toString()}` : pathname;
735
+ window.parent.postMessage(
736
+ {
737
+ type: "VIBEX_NAVIGATION",
738
+ route: {
739
+ path: pathname,
740
+ query: Object.keys(queryObj).length > 0 ? queryObj : null,
741
+ href
742
+ },
743
+ context: {
744
+ initial: isInitialNav.current,
745
+ timestamp: Date.now(),
746
+ ...appMeta
747
+ }
748
+ },
749
+ "*"
750
+ );
751
+ isInitialNav.current = false;
752
+ }, [pathname, searchParams, trackNavigation, appMeta]);
753
+ (0, import_react2.useEffect)(() => {
754
+ const isEmbedded3 = window.parent !== window;
755
+ if (!isEmbedded3) return;
756
+ const dispatch = (data) => window.parent.postMessage(data, "*");
757
+ const handleRuntimeError = (evt) => dispatch({
758
+ type: "VIBEX_EXCEPTION",
759
+ details: {
760
+ message: evt.message,
761
+ stack: evt.error?.stack,
762
+ file: evt.filename,
763
+ line: evt.lineno,
764
+ column: evt.colno,
765
+ origin: "runtime"
766
+ },
767
+ capturedAt: Date.now()
768
+ });
769
+ const handlePromiseError = (evt) => dispatch({
770
+ type: "VIBEX_EXCEPTION",
771
+ details: {
772
+ message: evt.reason?.message ?? String(evt.reason),
773
+ stack: evt.reason?.stack,
774
+ origin: "promise"
775
+ },
776
+ capturedAt: Date.now()
777
+ });
778
+ const checkDevOverlay = () => {
779
+ const overlayEl = document.querySelector("[data-nextjs-dialog-overlay]");
780
+ const contentEl = overlayEl?.querySelector(
781
+ "h1, h2, .error-message, [data-nextjs-dialog-body]"
782
+ ) ?? null;
783
+ const content = contentEl?.textContent ?? contentEl?.innerHTML ?? "";
784
+ if (content && content !== previousOverlayContent.current) {
785
+ previousOverlayContent.current = content;
786
+ dispatch({
787
+ type: "VIBEX_EXCEPTION",
788
+ details: { message: content, origin: "dev-overlay" },
789
+ capturedAt: Date.now()
790
+ });
791
+ }
792
+ };
793
+ window.addEventListener("error", handleRuntimeError);
794
+ window.addEventListener("unhandledrejection", handlePromiseError);
795
+ monitorInterval.current = setInterval(checkDevOverlay, 1e3);
796
+ return () => {
797
+ window.removeEventListener("error", handleRuntimeError);
798
+ window.removeEventListener("unhandledrejection", handlePromiseError);
799
+ monitorInterval.current && clearInterval(monitorInterval.current);
800
+ };
801
+ }, []);
802
+ (0, import_react2.useEffect)(() => {
803
+ if (!error) return;
804
+ window.parent.postMessage(
805
+ {
806
+ type: "vibex-boundary-error",
807
+ details: {
808
+ message: error.message,
809
+ stack: error.stack,
810
+ digest: error.digest,
811
+ name: error.name
812
+ },
813
+ capturedAt: Date.now(),
814
+ client: navigator.userAgent
815
+ },
816
+ "*"
817
+ );
818
+ }, [error]);
819
+ if (!error) return null;
820
+ const requestAIFix = () => {
821
+ window.parent.postMessage(
822
+ {
823
+ type: "vibex-fix-with-ai",
824
+ details: {
825
+ message: error.message,
826
+ stack: error.stack,
827
+ digest: error.digest,
828
+ name: error.name
829
+ },
830
+ capturedAt: Date.now()
831
+ },
832
+ "*"
833
+ );
834
+ };
835
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("html", { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("body", { className: "min-h-screen bg-background text-foreground flex items-end sm:items-center justify-center p-4 sm:p-8", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "w-full max-w-lg", children: [
836
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex rounded-xl overflow-hidden border border-border shadow-sm", children: [
837
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "w-1.5 bg-destructive shrink-0" }),
838
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex-1 p-5 sm:p-6 space-y-5", children: [
839
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-start justify-between gap-4", children: [
840
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "space-y-1", children: [
841
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-xs font-medium tracking-wide text-muted-foreground uppercase", children: "Runtime Error" }),
842
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h1", { className: "text-lg font-medium leading-snug", children: error.name || "Error" })
843
+ ] }),
844
+ error.digest && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("code", { className: "text-[10px] px-2 py-1 rounded bg-muted text-muted-foreground font-mono shrink-0", children: error.digest })
845
+ ] }),
846
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-sm text-muted-foreground leading-relaxed", children: error.message }),
847
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-3 pt-1", children: [
848
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
849
+ "button",
850
+ {
851
+ onClick: requestAIFix,
852
+ className: "inline-flex items-center gap-2 py-2 px-4 bg-primary text-primary-foreground text-sm font-medium rounded-md hover:bg-primary/90 transition-colors",
853
+ children: [
854
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { className: "w-4 h-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M13 10V3L4 14h7v7l9-11h-7z" }) }),
855
+ "Fix with AI"
856
+ ]
857
+ }
858
+ ),
859
+ reset && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
860
+ "button",
861
+ {
862
+ onClick: reset,
863
+ className: "text-sm text-muted-foreground hover:text-foreground transition-colors",
864
+ children: "Retry"
865
+ }
866
+ )
867
+ ] }),
868
+ process.env.NODE_ENV === "development" && error.stack && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "pt-3 border-t border-border", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("pre", { className: "text-[11px] font-mono text-muted-foreground leading-relaxed overflow-x-auto whitespace-pre-wrap break-all", children: error.stack }) })
869
+ ] })
870
+ ] }),
871
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "mt-4 text-center text-xs text-muted-foreground/60", children: "Vibex Inspector" })
872
+ ] }) }) });
873
+ }
874
+
875
+ // src/next/loader.ts
876
+ var import_url = require("url");
877
+ var import_path = require("path");
878
+ var import_meta = {};
879
+ var __filename = (0, import_url.fileURLToPath)(import_meta.url);
880
+ var __dirname = (0, import_path.dirname)(__filename);
881
+ var loaderPath = (0, import_path.join)(__dirname, "..", "..", "next", "element-tagger.js");
882
+
883
+ // src/next/config.ts
884
+ function withInspector(nextConfig = {}, options = {}) {
885
+ const {
886
+ extensions = ["jsx", "tsx"],
887
+ enableInProduction = false
888
+ } = options;
889
+ if (process.env.NODE_ENV === "production" && !enableInProduction) {
890
+ return nextConfig;
891
+ }
892
+ const extPattern = extensions.length === 1 ? extensions[0] : `{${extensions.join(",")}}`;
893
+ const filePattern = `*.${extPattern}`;
894
+ return {
895
+ ...nextConfig,
896
+ // Add Turbopack loader rules
897
+ turbopack: {
898
+ ...nextConfig.turbopack,
899
+ rules: {
900
+ ...nextConfig.turbopack?.rules,
901
+ [filePattern]: {
902
+ loaders: [loaderPath]
903
+ }
904
+ }
905
+ },
906
+ // Add Webpack loader rules (for non-Turbopack builds)
907
+ webpack: (config, context) => {
908
+ if (context.dev || enableInProduction) {
909
+ config.module.rules.push({
910
+ test: new RegExp(`\\.(${extensions.join("|")})$`),
911
+ exclude: /node_modules/,
912
+ use: [loaderPath]
913
+ });
914
+ }
915
+ if (typeof nextConfig.webpack === "function") {
916
+ return nextConfig.webpack(config, context);
917
+ }
918
+ return config;
919
+ }
920
+ };
921
+ }
922
+ // Annotate the CommonJS export names for ESM import in node:
923
+ 0 && (module.exports = {
924
+ Observer,
925
+ VisualInspector,
926
+ loaderPath,
927
+ withInspector
928
+ });
929
+ //# sourceMappingURL=index.cjs.map