@np-dev/ui-ai-anotation 0.1.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.
Files changed (52) hide show
  1. package/README.md +245 -0
  2. package/dist/cjs/index.cjs +1550 -0
  3. package/dist/cjs/index.cjs.map +7 -0
  4. package/dist/cjs/index.native.cjs +1004 -0
  5. package/dist/cjs/index.native.cjs.map +7 -0
  6. package/dist/cjs/index.web.cjs +83 -0
  7. package/dist/cjs/index.web.cjs.map +7 -0
  8. package/dist/esm/index.js +1524 -0
  9. package/dist/esm/index.js.map +7 -0
  10. package/dist/esm/index.native.js +1012 -0
  11. package/dist/esm/index.native.js.map +7 -0
  12. package/dist/esm/index.web.js +62 -0
  13. package/dist/esm/index.web.js.map +7 -0
  14. package/dist/types/components/AnnotationInput.d.ts +8 -0
  15. package/dist/types/components/AnnotationList.d.ts +1 -0
  16. package/dist/types/components/Draggable.d.ts +10 -0
  17. package/dist/types/components/Highlighter.d.ts +1 -0
  18. package/dist/types/components/Toolbar.d.ts +1 -0
  19. package/dist/types/index.d.ts +20 -0
  20. package/dist/types/index.web.d.ts +69 -0
  21. package/dist/types/store.d.ts +66 -0
  22. package/dist/types/utils/fiber.d.ts +51 -0
  23. package/dist/types/utils/platform.d.ts +8 -0
  24. package/dist/types/utils/screenshot.d.ts +28 -0
  25. package/package.json +115 -0
  26. package/src/components/AnnotationInput.tsx +269 -0
  27. package/src/components/AnnotationList.tsx +248 -0
  28. package/src/components/Draggable.tsx +73 -0
  29. package/src/components/Highlighter.tsx +497 -0
  30. package/src/components/Toolbar.tsx +213 -0
  31. package/src/components/native/AnnotationInput.tsx +227 -0
  32. package/src/components/native/AnnotationList.tsx +157 -0
  33. package/src/components/native/Draggable.tsx +65 -0
  34. package/src/components/native/Highlighter.tsx +239 -0
  35. package/src/components/native/Toolbar.tsx +192 -0
  36. package/src/components/native/index.ts +6 -0
  37. package/src/components/web/AnnotationInput.tsx +150 -0
  38. package/src/components/web/AnnotationList.tsx +117 -0
  39. package/src/components/web/Draggable.tsx +74 -0
  40. package/src/components/web/Highlighter.tsx +329 -0
  41. package/src/components/web/Toolbar.tsx +198 -0
  42. package/src/components/web/index.ts +6 -0
  43. package/src/extension.tsx +15 -0
  44. package/src/index.native.tsx +50 -0
  45. package/src/index.tsx +41 -0
  46. package/src/index.web.tsx +124 -0
  47. package/src/store.tsx +120 -0
  48. package/src/utils/fiber.native.ts +90 -0
  49. package/src/utils/fiber.ts +255 -0
  50. package/src/utils/platform.ts +33 -0
  51. package/src/utils/screenshot.native.ts +139 -0
  52. package/src/utils/screenshot.ts +162 -0
@@ -0,0 +1,1524 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
7
+
8
+ // src/store.tsx
9
+ import { createContext, useContext, useReducer } from "react";
10
+ import { jsx } from "react/jsx-runtime";
11
+ var initialState = {
12
+ mode: "disabled",
13
+ annotations: [],
14
+ hoveredElement: null,
15
+ hoveredComponentInfo: null,
16
+ isMinimized: false,
17
+ showList: false
18
+ };
19
+ var AnnotationContext = createContext(void 0);
20
+ function reducer(state, action) {
21
+ switch (action.type) {
22
+ case "SET_MODE":
23
+ return { ...state, mode: action.payload };
24
+ case "ADD_ANNOTATION":
25
+ return { ...state, annotations: [...state.annotations, action.payload] };
26
+ case "REMOVE_ANNOTATION":
27
+ return {
28
+ ...state,
29
+ annotations: state.annotations.filter((a) => a.id !== action.payload)
30
+ };
31
+ case "CLEAR_ALL_ANNOTATIONS":
32
+ return {
33
+ ...state,
34
+ annotations: []
35
+ };
36
+ case "SET_HOVERED":
37
+ if (state.hoveredElement === action.payload.element) return state;
38
+ return {
39
+ ...state,
40
+ hoveredElement: action.payload.element,
41
+ hoveredComponentInfo: action.payload.name ? { name: action.payload.name, details: action.payload.details } : null
42
+ };
43
+ case "RESET_HOVER":
44
+ return { ...state, hoveredElement: null, hoveredComponentInfo: null };
45
+ case "TOGGLE_MINIMIZED":
46
+ return { ...state, isMinimized: !state.isMinimized };
47
+ case "TOGGLE_LIST":
48
+ return { ...state, showList: !state.showList };
49
+ default:
50
+ return state;
51
+ }
52
+ }
53
+ function AiAnnotationProvider({ children }) {
54
+ const [state, dispatch] = useReducer(reducer, initialState);
55
+ return /* @__PURE__ */ jsx(AnnotationContext.Provider, { value: { state, dispatch }, children });
56
+ }
57
+ function useAiAnnotation() {
58
+ const context = useContext(AnnotationContext);
59
+ if (!context) {
60
+ throw new Error("useAiAnnotation must be used within an AiAnnotationProvider");
61
+ }
62
+ return context;
63
+ }
64
+
65
+ // src/components/Toolbar.tsx
66
+ import { useState as useState5, useEffect as useEffect4, useRef as useRef2 } from "react";
67
+
68
+ // src/components/Draggable.tsx
69
+ import { useState, useEffect, useRef } from "react";
70
+ import { jsx as jsx2 } from "react/jsx-runtime";
71
+ function Draggable({ children, initialPos = { x: 20, y: 20 } }) {
72
+ const [pos, setPos] = useState(initialPos);
73
+ const [dragging, setDragging] = useState(false);
74
+ const [rel, setRel] = useState({ x: 0, y: 0 });
75
+ const nodeRef = useRef(null);
76
+ const onMouseDown = (e) => {
77
+ if (e.button !== 0) return;
78
+ const node = nodeRef.current;
79
+ if (!node) return;
80
+ const rect = node.getBoundingClientRect();
81
+ setDragging(true);
82
+ setRel({
83
+ x: e.pageX - rect.left - window.scrollX,
84
+ y: e.pageY - rect.top - window.scrollY
85
+ });
86
+ e.preventDefault();
87
+ };
88
+ const onMouseMove = (e) => {
89
+ if (!dragging) return;
90
+ setPos({
91
+ x: e.pageX - rel.x,
92
+ y: e.pageY - rel.y
93
+ });
94
+ e.preventDefault();
95
+ };
96
+ const onMouseUp = () => {
97
+ setDragging(false);
98
+ };
99
+ useEffect(() => {
100
+ if (dragging) {
101
+ document.addEventListener("mousemove", onMouseMove);
102
+ document.addEventListener("mouseup", onMouseUp);
103
+ } else {
104
+ document.removeEventListener("mousemove", onMouseMove);
105
+ document.removeEventListener("mouseup", onMouseUp);
106
+ }
107
+ return () => {
108
+ document.removeEventListener("mousemove", onMouseMove);
109
+ document.removeEventListener("mouseup", onMouseUp);
110
+ };
111
+ }, [dragging]);
112
+ return /* @__PURE__ */ jsx2(
113
+ "div",
114
+ {
115
+ ref: nodeRef,
116
+ style: {
117
+ position: "fixed",
118
+ left: pos.x,
119
+ top: pos.y,
120
+ zIndex: 9999,
121
+ cursor: dragging ? "grabbing" : "grab"
122
+ },
123
+ onMouseDown,
124
+ children
125
+ }
126
+ );
127
+ }
128
+
129
+ // src/components/Toolbar.tsx
130
+ import { GripVertical, MousePointer2, List, Copy, Minus, Maximize2, Ban, Check } from "lucide-react";
131
+
132
+ // src/components/Highlighter.tsx
133
+ import { useEffect as useEffect3, useState as useState3 } from "react";
134
+
135
+ // src/components/AnnotationInput.tsx
136
+ import { useState as useState2, useEffect as useEffect2 } from "react";
137
+ import { X } from "lucide-react";
138
+ import { jsx as jsx3, jsxs } from "react/jsx-runtime";
139
+ function formatComponentDetails(details) {
140
+ const lines = [];
141
+ lines.push("## Element");
142
+ let elementStr = `<${details.elementInfo.tagName}`;
143
+ if (details.elementInfo.id) elementStr += ` id="${details.elementInfo.id}"`;
144
+ if (details.elementInfo.className) elementStr += ` class="${details.elementInfo.className}"`;
145
+ elementStr += ">";
146
+ lines.push(elementStr);
147
+ if (details.elementInfo.textContent) {
148
+ lines.push(`Text: "${details.elementInfo.textContent}"`);
149
+ }
150
+ lines.push(`Child elements: ${details.elementInfo.childElementCount}`);
151
+ if (Object.keys(details.elementInfo.attributes).length > 0) {
152
+ lines.push("");
153
+ lines.push("## Attributes");
154
+ for (const [key, value] of Object.entries(details.elementInfo.attributes)) {
155
+ lines.push(`${key}="${value}"`);
156
+ }
157
+ }
158
+ if (details.parentHierarchy.length > 0) {
159
+ lines.push("");
160
+ lines.push("## Parent Components");
161
+ lines.push(details.parentHierarchy.join(" \u2192 "));
162
+ }
163
+ if (details.childComponents.length > 0) {
164
+ lines.push("");
165
+ lines.push(`## Child Components (${details.childComponents.length})`);
166
+ const childLines = details.childComponents.slice(0, 15).map((child) => {
167
+ let str = child.name;
168
+ if (child.count > 1) str += ` \xD7${child.count}`;
169
+ if (child.hasChildren) str += " (has children)";
170
+ return str;
171
+ });
172
+ lines.push(childLines.join(", "));
173
+ if (details.childComponents.length > 15) {
174
+ lines.push(`... and ${details.childComponents.length - 15} more`);
175
+ }
176
+ }
177
+ return lines.join("\n");
178
+ }
179
+ function AnnotationInput({ onClose, componentName, componentDetails }) {
180
+ const { dispatch } = useAiAnnotation();
181
+ const [note, setNote] = useState2("");
182
+ const [includeDetails, setIncludeDetails] = useState2(true);
183
+ const [isVisible, setIsVisible] = useState2(false);
184
+ const [isClosing, setIsClosing] = useState2(false);
185
+ useEffect2(() => {
186
+ requestAnimationFrame(() => {
187
+ setIsVisible(true);
188
+ });
189
+ }, []);
190
+ useEffect2(() => {
191
+ const handleKeyDown = (e) => {
192
+ if (e.key === "Escape") {
193
+ e.preventDefault();
194
+ e.stopPropagation();
195
+ handleClose();
196
+ }
197
+ };
198
+ document.addEventListener("keydown", handleKeyDown);
199
+ return () => document.removeEventListener("keydown", handleKeyDown);
200
+ }, []);
201
+ const handleClose = () => {
202
+ setIsClosing(true);
203
+ setTimeout(() => {
204
+ onClose();
205
+ }, 200);
206
+ };
207
+ const handleSubmit = (e) => {
208
+ e.preventDefault();
209
+ if (!note.trim()) return;
210
+ let finalNote = note.trim();
211
+ if (includeDetails && componentDetails) {
212
+ const detailsText = formatComponentDetails(componentDetails);
213
+ finalNote = `${note.trim()}
214
+
215
+ ---
216
+ ${detailsText}`;
217
+ }
218
+ dispatch({
219
+ type: "ADD_ANNOTATION",
220
+ payload: {
221
+ id: Date.now().toString(),
222
+ componentName,
223
+ note: finalNote,
224
+ timestamp: Date.now(),
225
+ details: componentDetails
226
+ }
227
+ });
228
+ handleClose();
229
+ };
230
+ const detailsPreview = componentDetails ? /* @__PURE__ */ jsxs("div", { style: {
231
+ fontSize: "0.75rem",
232
+ color: "rgba(255, 255, 255, 0.6)",
233
+ marginTop: "4px"
234
+ }, children: [
235
+ componentDetails.parentHierarchy.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
236
+ "Parents: ",
237
+ componentDetails.parentHierarchy.slice(0, 3).join(" \u2192 "),
238
+ componentDetails.parentHierarchy.length > 3 ? "..." : ""
239
+ ] }),
240
+ componentDetails.childComponents.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
241
+ "Children: ",
242
+ componentDetails.childComponents.slice(0, 3).map((c) => c.name).join(", "),
243
+ componentDetails.childComponents.length > 3 ? "..." : ""
244
+ ] }),
245
+ /* @__PURE__ */ jsxs("div", { children: [
246
+ "Element: <",
247
+ componentDetails.elementInfo.tagName,
248
+ "> (",
249
+ componentDetails.elementInfo.childElementCount,
250
+ " children)"
251
+ ] })
252
+ ] }) : null;
253
+ return /* @__PURE__ */ jsx3(
254
+ "div",
255
+ {
256
+ style: {
257
+ position: "fixed",
258
+ top: 0,
259
+ left: 0,
260
+ right: 0,
261
+ bottom: 0,
262
+ backgroundColor: isVisible && !isClosing ? "rgba(0,0,0,0.5)" : "rgba(0,0,0,0)",
263
+ display: "flex",
264
+ alignItems: "center",
265
+ justifyContent: "center",
266
+ zIndex: 1e4,
267
+ transition: "background-color 0.2s ease"
268
+ },
269
+ "data-ai-annotation-ui": true,
270
+ onClick: handleClose,
271
+ children: /* @__PURE__ */ jsxs(
272
+ "div",
273
+ {
274
+ style: {
275
+ backgroundColor: "#1e1e1e",
276
+ color: "#e5e7eb",
277
+ padding: "20px",
278
+ borderRadius: "8px",
279
+ width: "500px",
280
+ maxWidth: "90%",
281
+ maxHeight: "80vh",
282
+ overflow: "auto",
283
+ boxShadow: "0 4px 6px rgba(0,0,0,0.3)",
284
+ transform: isVisible && !isClosing ? "scale(1) translateY(0)" : "scale(0.95) translateY(-10px)",
285
+ opacity: isVisible && !isClosing ? 1 : 0,
286
+ transition: "transform 0.2s ease, opacity 0.2s ease"
287
+ },
288
+ onClick: (e) => e.stopPropagation(),
289
+ children: [
290
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", marginBottom: "16px" }, children: [
291
+ /* @__PURE__ */ jsx3("h3", { style: { margin: 0, fontSize: "1.1rem" }, children: "Add Annotation" }),
292
+ /* @__PURE__ */ jsx3("button", { onClick: handleClose, style: { background: "none", border: "none", color: "inherit", cursor: "pointer" }, children: /* @__PURE__ */ jsx3(X, { size: 20 }) })
293
+ ] }),
294
+ /* @__PURE__ */ jsxs("div", { style: { marginBottom: "12px", fontSize: "0.9rem", opacity: 0.8 }, children: [
295
+ "Component: ",
296
+ /* @__PURE__ */ jsx3("strong", { children: componentName })
297
+ ] }),
298
+ /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, children: [
299
+ /* @__PURE__ */ jsx3(
300
+ "textarea",
301
+ {
302
+ value: note,
303
+ onChange: (e) => setNote(e.target.value),
304
+ placeholder: "Describe what this component does or what changes you need...",
305
+ style: {
306
+ width: "100%",
307
+ height: "120px",
308
+ padding: "8px",
309
+ borderRadius: "4px",
310
+ backgroundColor: "#2d2d2d",
311
+ border: "1px solid #404040",
312
+ color: "white",
313
+ marginBottom: "12px",
314
+ resize: "vertical",
315
+ fontFamily: "inherit"
316
+ },
317
+ autoFocus: true
318
+ }
319
+ ),
320
+ componentDetails && /* @__PURE__ */ jsx3("div", { style: { marginBottom: "16px" }, children: /* @__PURE__ */ jsxs(
321
+ "label",
322
+ {
323
+ style: {
324
+ display: "flex",
325
+ alignItems: "flex-start",
326
+ gap: "8px",
327
+ cursor: "pointer",
328
+ fontSize: "0.85rem"
329
+ },
330
+ children: [
331
+ /* @__PURE__ */ jsx3(
332
+ "input",
333
+ {
334
+ type: "checkbox",
335
+ checked: includeDetails,
336
+ onChange: (e) => setIncludeDetails(e.target.checked),
337
+ style: { marginTop: "2px" }
338
+ }
339
+ ),
340
+ /* @__PURE__ */ jsxs("div", { children: [
341
+ /* @__PURE__ */ jsx3("div", { style: { color: "#10b981", fontWeight: 500 }, children: "Include component details in annotation" }),
342
+ includeDetails && detailsPreview
343
+ ] })
344
+ ]
345
+ }
346
+ ) }),
347
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "flex-end", gap: "8px" }, children: [
348
+ /* @__PURE__ */ jsx3(
349
+ "button",
350
+ {
351
+ type: "button",
352
+ onClick: handleClose,
353
+ style: {
354
+ padding: "6px 12px",
355
+ borderRadius: "4px",
356
+ backgroundColor: "transparent",
357
+ border: "1px solid #404040",
358
+ color: "white",
359
+ cursor: "pointer"
360
+ },
361
+ children: "Cancel"
362
+ }
363
+ ),
364
+ /* @__PURE__ */ jsx3(
365
+ "button",
366
+ {
367
+ type: "submit",
368
+ style: {
369
+ padding: "6px 12px",
370
+ borderRadius: "4px",
371
+ backgroundColor: "#3b82f6",
372
+ border: "none",
373
+ color: "white",
374
+ cursor: "pointer"
375
+ },
376
+ children: "Save Annotation"
377
+ }
378
+ )
379
+ ] })
380
+ ] })
381
+ ]
382
+ }
383
+ )
384
+ }
385
+ );
386
+ }
387
+
388
+ // src/utils/screenshot.ts
389
+ import html2canvas from "html2canvas";
390
+ function isTauriEnv() {
391
+ return typeof window !== "undefined" && "__TAURI_INTERNALS__" in window;
392
+ }
393
+ async function copyToClipboardTauri(blob) {
394
+ if (typeof window.__TAURI_CLIPBOARD_WRITE_IMAGE__ === "function") {
395
+ const arrayBuffer = await blob.arrayBuffer();
396
+ const bytes = new Uint8Array(arrayBuffer);
397
+ await window.__TAURI_CLIPBOARD_WRITE_IMAGE__(bytes);
398
+ return;
399
+ }
400
+ throw new Error("Tauri clipboard function not available");
401
+ }
402
+ async function copyToClipboardBrowser(blob, dataUrl) {
403
+ try {
404
+ await navigator.clipboard.write([
405
+ new ClipboardItem({
406
+ "image/png": blob
407
+ })
408
+ ]);
409
+ } catch (clipboardError) {
410
+ console.warn("Failed to copy to clipboard:", clipboardError);
411
+ try {
412
+ await navigator.clipboard.writeText(dataUrl);
413
+ } catch {
414
+ }
415
+ }
416
+ }
417
+ async function captureScreenshot(element, options = {}) {
418
+ const {
419
+ scale = 2,
420
+ backgroundColor = null,
421
+ copyToClipboard = true,
422
+ download = false,
423
+ filename = "screenshot.png"
424
+ } = options;
425
+ try {
426
+ const canvas = await html2canvas(element, {
427
+ scale,
428
+ backgroundColor,
429
+ logging: false,
430
+ useCORS: true,
431
+ allowTaint: true,
432
+ // Capture just the element
433
+ x: window.scrollX,
434
+ y: window.scrollY,
435
+ scrollX: -window.scrollX,
436
+ scrollY: -window.scrollY,
437
+ windowWidth: document.documentElement.scrollWidth,
438
+ windowHeight: document.documentElement.scrollHeight,
439
+ // Ignore annotation UI elements
440
+ ignoreElements: (el) => {
441
+ return el.hasAttribute("data-ai-annotation-ui");
442
+ }
443
+ });
444
+ const blob = await new Promise((resolve, reject) => {
445
+ canvas.toBlob((b) => {
446
+ if (b) resolve(b);
447
+ else reject(new Error("Failed to create blob"));
448
+ }, "image/png");
449
+ });
450
+ const dataUrl = canvas.toDataURL("image/png");
451
+ if (copyToClipboard) {
452
+ if (isTauriEnv()) {
453
+ try {
454
+ await copyToClipboardTauri(blob);
455
+ } catch (tauriErr) {
456
+ console.warn("Tauri clipboard failed, falling back to browser API:", tauriErr);
457
+ await copyToClipboardBrowser(blob, dataUrl);
458
+ }
459
+ } else {
460
+ await copyToClipboardBrowser(blob, dataUrl);
461
+ }
462
+ }
463
+ if (download) {
464
+ const link = document.createElement("a");
465
+ link.href = dataUrl;
466
+ link.download = filename;
467
+ document.body.appendChild(link);
468
+ link.click();
469
+ document.body.removeChild(link);
470
+ }
471
+ return {
472
+ success: true,
473
+ dataUrl,
474
+ blob
475
+ };
476
+ } catch (error) {
477
+ const message = error instanceof Error ? error.message : "Unknown error";
478
+ console.error("Screenshot capture failed:", message);
479
+ return {
480
+ success: false,
481
+ error: message
482
+ };
483
+ }
484
+ }
485
+
486
+ // src/utils/fiber.ts
487
+ function getReactFiber(dom) {
488
+ const key = Object.keys(dom).find(
489
+ (key2) => key2.startsWith("__reactFiber$")
490
+ );
491
+ return key ? dom[key] : null;
492
+ }
493
+ function getComponentDisplayName(fiber) {
494
+ let curr = fiber;
495
+ while (curr) {
496
+ const name = curr.type?.displayName || curr.type?.name;
497
+ if (name && typeof curr.type === "function") return name;
498
+ curr = curr.return;
499
+ }
500
+ return "Unknown";
501
+ }
502
+ function getElementFromFiber(fiber) {
503
+ let curr = fiber;
504
+ while (curr) {
505
+ if (curr.stateNode instanceof HTMLElement) {
506
+ return curr.stateNode;
507
+ }
508
+ curr = curr.child;
509
+ }
510
+ return null;
511
+ }
512
+ function getParentHierarchy(fiber, maxDepth = 10) {
513
+ const hierarchy = [];
514
+ let curr = fiber?.return;
515
+ let depth = 0;
516
+ while (curr && depth < maxDepth) {
517
+ const name = curr.type?.displayName || curr.type?.name;
518
+ if (name && typeof curr.type === "function") {
519
+ if (!name.includes("$") && !name.startsWith("_")) {
520
+ hierarchy.push(name);
521
+ }
522
+ }
523
+ curr = curr.return;
524
+ depth++;
525
+ }
526
+ return hierarchy;
527
+ }
528
+ function getChildComponents(fiber, maxDepth = 5) {
529
+ const children = /* @__PURE__ */ new Map();
530
+ function traverse(node, depth) {
531
+ if (!node || depth > maxDepth) return;
532
+ const name = node.type?.displayName || node.type?.name;
533
+ if (name && typeof node.type === "function" && !name.includes("$") && !name.startsWith("_")) {
534
+ const existing = children.get(name);
535
+ const hasChildComponents = checkHasChildComponents(node.child);
536
+ if (existing) {
537
+ existing.count++;
538
+ if (depth < existing.depth) {
539
+ existing.depth = depth;
540
+ }
541
+ existing.hasChildren = existing.hasChildren || hasChildComponents;
542
+ } else {
543
+ children.set(name, { count: 1, depth, hasChildren: hasChildComponents });
544
+ }
545
+ }
546
+ if (node.child) traverse(node.child, depth + 1);
547
+ if (node.sibling) traverse(node.sibling, depth);
548
+ }
549
+ if (fiber?.child) {
550
+ traverse(fiber.child, 1);
551
+ }
552
+ return Array.from(children.entries()).map(([name, info]) => ({
553
+ name,
554
+ depth: info.depth,
555
+ count: info.count,
556
+ hasChildren: info.hasChildren
557
+ })).sort((a, b) => a.depth - b.depth);
558
+ }
559
+ function checkHasChildComponents(fiber, maxDepth = 3) {
560
+ if (!fiber || maxDepth <= 0) return false;
561
+ const name = fiber.type?.displayName || fiber.type?.name;
562
+ if (name && typeof fiber.type === "function" && !name.includes("$")) {
563
+ return true;
564
+ }
565
+ if (fiber.child && checkHasChildComponents(fiber.child, maxDepth - 1)) return true;
566
+ if (fiber.sibling && checkHasChildComponents(fiber.sibling, maxDepth - 1)) return true;
567
+ return false;
568
+ }
569
+ function getElementInfo(element) {
570
+ const importantAttributes = ["data-testid", "role", "aria-label", "type", "name", "href", "src"];
571
+ const attributes = {};
572
+ importantAttributes.forEach((attr) => {
573
+ const value = element.getAttribute(attr);
574
+ if (value) {
575
+ attributes[attr] = value;
576
+ }
577
+ });
578
+ let textContent;
579
+ const directText = Array.from(element.childNodes).filter((node) => node.nodeType === Node.TEXT_NODE).map((node) => node.textContent?.trim()).filter(Boolean).join(" ");
580
+ if (directText) {
581
+ textContent = directText.length > 100 ? directText.substring(0, 100) + "..." : directText;
582
+ }
583
+ return {
584
+ tagName: element.tagName.toLowerCase(),
585
+ id: element.id || void 0,
586
+ className: element.className && typeof element.className === "string" ? element.className.split(" ").slice(0, 5).join(" ") + (element.className.split(" ").length > 5 ? "..." : "") : void 0,
587
+ attributes,
588
+ childElementCount: element.childElementCount,
589
+ textContent
590
+ };
591
+ }
592
+ function getComponentProps(fiber, maxProps = 10) {
593
+ if (!fiber?.memoizedProps) return void 0;
594
+ const props = fiber.memoizedProps;
595
+ const safeProps = {};
596
+ let count = 0;
597
+ for (const key of Object.keys(props)) {
598
+ if (count >= maxProps) {
599
+ safeProps["..."] = `${Object.keys(props).length - maxProps} more props`;
600
+ break;
601
+ }
602
+ if (key === "children") continue;
603
+ const value = props[key];
604
+ const type = typeof value;
605
+ if (type === "function") {
606
+ safeProps[key] = "[Function]";
607
+ } else if (type === "object" && value !== null) {
608
+ if (Array.isArray(value)) {
609
+ safeProps[key] = `[Array(${value.length})]`;
610
+ } else if (value.$$typeof) {
611
+ safeProps[key] = "[ReactElement]";
612
+ } else {
613
+ try {
614
+ const json = JSON.stringify(value);
615
+ safeProps[key] = json.length > 100 ? "[Object]" : value;
616
+ } catch {
617
+ safeProps[key] = "[Object]";
618
+ }
619
+ }
620
+ } else {
621
+ safeProps[key] = value;
622
+ }
623
+ count++;
624
+ }
625
+ return Object.keys(safeProps).length > 0 ? safeProps : void 0;
626
+ }
627
+ function getComponentDetails(element, options) {
628
+ const { includeProps = false, maxParentDepth = 10, maxChildDepth = 5 } = options || {};
629
+ const fiber = getReactFiber(element);
630
+ const name = fiber ? getComponentDisplayName(fiber) : "Unknown";
631
+ let componentFiber = fiber;
632
+ while (componentFiber && typeof componentFiber.type !== "function") {
633
+ componentFiber = componentFiber.return;
634
+ }
635
+ return {
636
+ name,
637
+ parentHierarchy: getParentHierarchy(componentFiber, maxParentDepth),
638
+ childComponents: getChildComponents(componentFiber, maxChildDepth),
639
+ elementInfo: getElementInfo(element),
640
+ props: includeProps ? getComponentProps(componentFiber) : void 0
641
+ };
642
+ }
643
+
644
+ // src/components/Highlighter.tsx
645
+ import { Fragment, jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
646
+ function Highlighter() {
647
+ const { state, dispatch } = useAiAnnotation();
648
+ const { hoveredElement, mode, hoveredComponentInfo } = state;
649
+ const [rect, setRect] = useState3(null);
650
+ const [showInput, setShowInput] = useState3(false);
651
+ const [mousePos, setMousePos] = useState3({ x: 0, y: 0 });
652
+ const [isLocked, setIsLocked] = useState3(false);
653
+ const [lockedPos, setLockedPos] = useState3({ x: 0, y: 0 });
654
+ const [isCapturing, setIsCapturing] = useState3(false);
655
+ const [captureSuccess, setCaptureSuccess] = useState3(false);
656
+ const [showDetails, setShowDetails] = useState3(false);
657
+ const componentName = hoveredComponentInfo?.name || "";
658
+ const componentDetails = hoveredComponentInfo?.details;
659
+ useEffect3(() => {
660
+ if (mode !== "inspecting") return;
661
+ const handleMouseOver = (e) => {
662
+ if (isLocked) return;
663
+ const target = e.target;
664
+ if (target.closest("[data-ai-annotation-ui]") || target.closest("[data-annotation-tooltip]")) {
665
+ return;
666
+ }
667
+ const details = getComponentDetails(target, {
668
+ includeProps: false,
669
+ maxParentDepth: 10,
670
+ maxChildDepth: 5
671
+ });
672
+ dispatch({
673
+ type: "SET_HOVERED",
674
+ payload: { element: target, name: details.name, details }
675
+ });
676
+ };
677
+ document.addEventListener("mouseover", handleMouseOver);
678
+ return () => {
679
+ document.removeEventListener("mouseover", handleMouseOver);
680
+ };
681
+ }, [mode, isLocked, dispatch]);
682
+ useEffect3(() => {
683
+ if (mode !== "inspecting") return;
684
+ const handleGlobalEsc = (e) => {
685
+ if (e.key === "Escape" && !isLocked) {
686
+ e.preventDefault();
687
+ e.stopPropagation();
688
+ dispatch({ type: "SET_MODE", payload: "disabled" });
689
+ }
690
+ };
691
+ document.addEventListener("keydown", handleGlobalEsc);
692
+ return () => document.removeEventListener("keydown", handleGlobalEsc);
693
+ }, [mode, isLocked, dispatch]);
694
+ useEffect3(() => {
695
+ if (mode === "disabled") {
696
+ setRect(null);
697
+ setShowInput(false);
698
+ setIsLocked(false);
699
+ setMousePos({ x: 0, y: 0 });
700
+ setLockedPos({ x: 0, y: 0 });
701
+ setIsCapturing(false);
702
+ setCaptureSuccess(false);
703
+ setShowDetails(false);
704
+ dispatch({ type: "RESET_HOVER" });
705
+ }
706
+ }, [mode, dispatch]);
707
+ useEffect3(() => {
708
+ if (!hoveredElement) {
709
+ setRect(null);
710
+ setShowInput(false);
711
+ setIsLocked(false);
712
+ return;
713
+ }
714
+ const updateRect = () => {
715
+ const newRect = hoveredElement.getBoundingClientRect();
716
+ setRect(newRect);
717
+ };
718
+ updateRect();
719
+ const handleMouseMove = (e) => {
720
+ if (!isLocked) {
721
+ setMousePos({ x: e.clientX, y: e.clientY });
722
+ }
723
+ };
724
+ const handleClick = (e) => {
725
+ const target = e.target;
726
+ if (target.closest("[data-annotation-tooltip]")) {
727
+ return;
728
+ }
729
+ if (target.closest("[data-ai-annotation-ui]")) {
730
+ return;
731
+ }
732
+ if (!isLocked) {
733
+ e.preventDefault();
734
+ e.stopPropagation();
735
+ setIsLocked(true);
736
+ setLockedPos({ x: e.clientX, y: e.clientY });
737
+ } else {
738
+ setIsLocked(false);
739
+ }
740
+ };
741
+ const handleKeyDown = (e) => {
742
+ if (e.key === "Escape" && isLocked) {
743
+ e.preventDefault();
744
+ e.stopPropagation();
745
+ setIsLocked(false);
746
+ setShowInput(false);
747
+ }
748
+ };
749
+ window.addEventListener("scroll", updateRect, true);
750
+ window.addEventListener("resize", updateRect);
751
+ document.addEventListener("mousemove", handleMouseMove);
752
+ document.addEventListener("click", handleClick, true);
753
+ document.addEventListener("keydown", handleKeyDown);
754
+ return () => {
755
+ window.removeEventListener("scroll", updateRect, true);
756
+ window.removeEventListener("resize", updateRect);
757
+ document.removeEventListener("mousemove", handleMouseMove);
758
+ document.removeEventListener("click", handleClick, true);
759
+ document.removeEventListener("keydown", handleKeyDown);
760
+ };
761
+ }, [hoveredElement, isLocked]);
762
+ const handleScreenshot = async (e) => {
763
+ e.stopPropagation();
764
+ if (!hoveredElement || isCapturing) return;
765
+ setIsCapturing(true);
766
+ try {
767
+ const result = await captureScreenshot(hoveredElement, {
768
+ copyToClipboard: true,
769
+ scale: 2
770
+ });
771
+ if (result.success) {
772
+ setCaptureSuccess(true);
773
+ setTimeout(() => {
774
+ setCaptureSuccess(false);
775
+ setIsLocked(false);
776
+ }, 1500);
777
+ }
778
+ } catch (error) {
779
+ console.error("Screenshot failed:", error);
780
+ } finally {
781
+ setIsCapturing(false);
782
+ }
783
+ };
784
+ const handleAddClick = (e) => {
785
+ e.stopPropagation();
786
+ setShowInput(true);
787
+ setShowDetails(false);
788
+ setIsLocked(false);
789
+ };
790
+ const handleDetailsClick = (e) => {
791
+ e.stopPropagation();
792
+ setShowDetails(!showDetails);
793
+ };
794
+ const handleCloseInput = () => {
795
+ setShowInput(false);
796
+ };
797
+ if (mode !== "inspecting" || !rect) return null;
798
+ const tooltipX = isLocked ? lockedPos.x : mousePos.x;
799
+ const tooltipY = isLocked ? lockedPos.y : mousePos.y;
800
+ return /* @__PURE__ */ jsxs2(Fragment, { children: [
801
+ /* @__PURE__ */ jsx4(
802
+ "div",
803
+ {
804
+ "data-annotation-ui": "true",
805
+ style: {
806
+ position: "fixed",
807
+ left: rect.left - 2,
808
+ top: rect.top - 2,
809
+ width: rect.width + 4,
810
+ height: rect.height + 4,
811
+ border: "2px solid #3b82f6",
812
+ borderRadius: 4,
813
+ pointerEvents: "none",
814
+ zIndex: 99998,
815
+ backgroundColor: "rgba(59, 130, 246, 0.1)"
816
+ }
817
+ }
818
+ ),
819
+ componentName && /* @__PURE__ */ jsx4(
820
+ "div",
821
+ {
822
+ "data-annotation-ui": "true",
823
+ style: {
824
+ position: "fixed",
825
+ left: rect.left,
826
+ top: rect.top - 24,
827
+ backgroundColor: "#3b82f6",
828
+ color: "white",
829
+ padding: "2px 8px",
830
+ borderRadius: 4,
831
+ fontSize: 12,
832
+ fontFamily: "monospace",
833
+ pointerEvents: "none",
834
+ zIndex: 99999
835
+ },
836
+ children: componentName
837
+ }
838
+ ),
839
+ !showInput && /* @__PURE__ */ jsxs2(
840
+ "div",
841
+ {
842
+ "data-annotation-ui": "true",
843
+ "data-annotation-tooltip": "true",
844
+ style: {
845
+ position: "fixed",
846
+ left: tooltipX + 16,
847
+ top: tooltipY + 16,
848
+ display: "flex",
849
+ flexDirection: "column",
850
+ gap: 6,
851
+ padding: 6,
852
+ backgroundColor: isLocked ? "rgba(0, 0, 0, 0.95)" : "rgba(0, 0, 0, 0.85)",
853
+ borderRadius: 8,
854
+ boxShadow: isLocked ? "0 4px 12px rgba(0, 0, 0, 0.4), 0 0 0 2px rgba(59, 130, 246, 0.5)" : "0 4px 12px rgba(0, 0, 0, 0.3)",
855
+ zIndex: 1e5,
856
+ pointerEvents: isLocked ? "auto" : "none",
857
+ backdropFilter: "blur(8px)",
858
+ transition: isLocked ? "none" : "left 0.05s ease-out, top 0.05s ease-out",
859
+ maxWidth: showDetails ? 400 : "auto"
860
+ },
861
+ children: [
862
+ /* @__PURE__ */ jsxs2("div", { style: { display: "flex", gap: 6 }, children: [
863
+ /* @__PURE__ */ jsx4(
864
+ "button",
865
+ {
866
+ onClick: handleScreenshot,
867
+ disabled: isCapturing || !isLocked,
868
+ style: {
869
+ width: 32,
870
+ height: 32,
871
+ borderRadius: 6,
872
+ border: "none",
873
+ backgroundColor: captureSuccess ? "#22c55e" : isCapturing ? "#6b7280" : "#8b5cf6",
874
+ color: "white",
875
+ cursor: isLocked ? "pointer" : "default",
876
+ display: "flex",
877
+ alignItems: "center",
878
+ justifyContent: "center",
879
+ fontSize: 16,
880
+ transition: "all 0.15s ease",
881
+ opacity: isLocked ? 1 : 0.7
882
+ },
883
+ title: "Capture screenshot",
884
+ children: captureSuccess ? "\u2713" : isCapturing ? "..." : "\u{1F4F7}"
885
+ }
886
+ ),
887
+ /* @__PURE__ */ jsx4(
888
+ "button",
889
+ {
890
+ onClick: handleAddClick,
891
+ disabled: !isLocked,
892
+ style: {
893
+ width: 32,
894
+ height: 32,
895
+ borderRadius: 6,
896
+ border: "none",
897
+ backgroundColor: "#3b82f6",
898
+ color: "white",
899
+ cursor: isLocked ? "pointer" : "default",
900
+ display: "flex",
901
+ alignItems: "center",
902
+ justifyContent: "center",
903
+ fontSize: 18,
904
+ fontWeight: "bold",
905
+ transition: "all 0.15s ease",
906
+ opacity: isLocked ? 1 : 0.7
907
+ },
908
+ title: "Add annotation",
909
+ children: "+"
910
+ }
911
+ ),
912
+ /* @__PURE__ */ jsx4(
913
+ "button",
914
+ {
915
+ onClick: handleDetailsClick,
916
+ disabled: !isLocked,
917
+ style: {
918
+ width: 32,
919
+ height: 32,
920
+ borderRadius: 6,
921
+ border: showDetails ? "2px solid #10b981" : "none",
922
+ backgroundColor: showDetails ? "#10b981" : "#6b7280",
923
+ color: "white",
924
+ cursor: isLocked ? "pointer" : "default",
925
+ display: "flex",
926
+ alignItems: "center",
927
+ justifyContent: "center",
928
+ fontSize: 14,
929
+ transition: "all 0.15s ease",
930
+ opacity: isLocked ? 1 : 0.7
931
+ },
932
+ title: "Show component details",
933
+ children: "\u24D8"
934
+ }
935
+ )
936
+ ] }),
937
+ !isLocked && /* @__PURE__ */ jsx4(
938
+ "div",
939
+ {
940
+ style: {
941
+ position: "absolute",
942
+ bottom: -20,
943
+ left: "50%",
944
+ transform: "translateX(-50%)",
945
+ fontSize: 10,
946
+ color: "rgba(255, 255, 255, 0.7)",
947
+ whiteSpace: "nowrap",
948
+ pointerEvents: "none"
949
+ },
950
+ children: "Click to lock"
951
+ }
952
+ ),
953
+ showDetails && componentDetails && /* @__PURE__ */ jsxs2(
954
+ "div",
955
+ {
956
+ style: {
957
+ padding: "8px 4px 4px 4px",
958
+ borderTop: "1px solid rgba(255, 255, 255, 0.2)",
959
+ fontSize: 11,
960
+ color: "rgba(255, 255, 255, 0.9)",
961
+ maxHeight: 300,
962
+ overflowY: "auto"
963
+ },
964
+ children: [
965
+ /* @__PURE__ */ jsxs2("div", { style: { marginBottom: 8 }, children: [
966
+ /* @__PURE__ */ jsx4("div", { style: { color: "#60a5fa", fontWeight: "bold", marginBottom: 4 }, children: "Element" }),
967
+ /* @__PURE__ */ jsxs2("div", { style: { fontFamily: "monospace", color: "#f59e0b" }, children: [
968
+ "<",
969
+ componentDetails.elementInfo.tagName,
970
+ componentDetails.elementInfo.id && ` id="${componentDetails.elementInfo.id}"`,
971
+ componentDetails.elementInfo.className && ` class="${componentDetails.elementInfo.className}"`,
972
+ ">"
973
+ ] }),
974
+ componentDetails.elementInfo.textContent && /* @__PURE__ */ jsxs2("div", { style: { color: "rgba(255, 255, 255, 0.6)", marginTop: 2, fontStyle: "italic" }, children: [
975
+ '"',
976
+ componentDetails.elementInfo.textContent,
977
+ '"'
978
+ ] }),
979
+ /* @__PURE__ */ jsxs2("div", { style: { color: "rgba(255, 255, 255, 0.5)", marginTop: 2 }, children: [
980
+ componentDetails.elementInfo.childElementCount,
981
+ " child element(s)"
982
+ ] })
983
+ ] }),
984
+ componentDetails.parentHierarchy.length > 0 && /* @__PURE__ */ jsxs2("div", { style: { marginBottom: 8 }, children: [
985
+ /* @__PURE__ */ jsx4("div", { style: { color: "#60a5fa", fontWeight: "bold", marginBottom: 4 }, children: "Parent Components" }),
986
+ /* @__PURE__ */ jsxs2("div", { style: { fontFamily: "monospace" }, children: [
987
+ componentDetails.parentHierarchy.slice(0, 5).map((parent, i) => /* @__PURE__ */ jsxs2("div", { style: { color: "#a78bfa", paddingLeft: i * 8 }, children: [
988
+ "\u2190 ",
989
+ parent
990
+ ] }, i)),
991
+ componentDetails.parentHierarchy.length > 5 && /* @__PURE__ */ jsxs2("div", { style: { color: "rgba(255, 255, 255, 0.5)" }, children: [
992
+ "... and ",
993
+ componentDetails.parentHierarchy.length - 5,
994
+ " more"
995
+ ] })
996
+ ] })
997
+ ] }),
998
+ componentDetails.childComponents.length > 0 && /* @__PURE__ */ jsxs2("div", { style: { marginBottom: 8 }, children: [
999
+ /* @__PURE__ */ jsxs2("div", { style: { color: "#60a5fa", fontWeight: "bold", marginBottom: 4 }, children: [
1000
+ "Child Components (",
1001
+ componentDetails.childComponents.length,
1002
+ ")"
1003
+ ] }),
1004
+ /* @__PURE__ */ jsxs2("div", { style: { fontFamily: "monospace" }, children: [
1005
+ componentDetails.childComponents.slice(0, 10).map((child, i) => /* @__PURE__ */ jsxs2(
1006
+ "div",
1007
+ {
1008
+ style: {
1009
+ display: "flex",
1010
+ justifyContent: "space-between",
1011
+ gap: 8,
1012
+ color: "#34d399"
1013
+ },
1014
+ children: [
1015
+ /* @__PURE__ */ jsxs2("span", { children: [
1016
+ "\u2192 ",
1017
+ child.name,
1018
+ child.hasChildren && " \u25BE"
1019
+ ] }),
1020
+ child.count > 1 && /* @__PURE__ */ jsxs2("span", { style: { color: "rgba(255, 255, 255, 0.5)" }, children: [
1021
+ "\xD7",
1022
+ child.count
1023
+ ] })
1024
+ ]
1025
+ },
1026
+ i
1027
+ )),
1028
+ componentDetails.childComponents.length > 10 && /* @__PURE__ */ jsxs2("div", { style: { color: "rgba(255, 255, 255, 0.5)" }, children: [
1029
+ "... and ",
1030
+ componentDetails.childComponents.length - 10,
1031
+ " more"
1032
+ ] })
1033
+ ] })
1034
+ ] }),
1035
+ Object.keys(componentDetails.elementInfo.attributes).length > 0 && /* @__PURE__ */ jsxs2("div", { children: [
1036
+ /* @__PURE__ */ jsx4("div", { style: { color: "#60a5fa", fontWeight: "bold", marginBottom: 4 }, children: "Attributes" }),
1037
+ /* @__PURE__ */ jsx4("div", { style: { fontFamily: "monospace" }, children: Object.entries(componentDetails.elementInfo.attributes).map(([key, value]) => /* @__PURE__ */ jsxs2("div", { style: { color: "#fbbf24" }, children: [
1038
+ key,
1039
+ '="',
1040
+ value,
1041
+ '"'
1042
+ ] }, key)) })
1043
+ ] })
1044
+ ]
1045
+ }
1046
+ )
1047
+ ]
1048
+ }
1049
+ ),
1050
+ showInput && /* @__PURE__ */ jsx4(
1051
+ AnnotationInput,
1052
+ {
1053
+ onClose: handleCloseInput,
1054
+ componentName: componentName || "Unknown",
1055
+ componentDetails
1056
+ }
1057
+ )
1058
+ ] });
1059
+ }
1060
+
1061
+ // src/components/AnnotationList.tsx
1062
+ import { useState as useState4 } from "react";
1063
+ import { X as X2, Trash2, Trash, ChevronDown, ChevronRight } from "lucide-react";
1064
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
1065
+ function AnnotationList() {
1066
+ const { state, dispatch } = useAiAnnotation();
1067
+ const [expandedId, setExpandedId] = useState4(null);
1068
+ if (!state.showList) return null;
1069
+ const toggleExpand = (id) => {
1070
+ setExpandedId(expandedId === id ? null : id);
1071
+ };
1072
+ return /* @__PURE__ */ jsx5(
1073
+ "div",
1074
+ {
1075
+ style: {
1076
+ position: "fixed",
1077
+ top: 0,
1078
+ left: 0,
1079
+ right: 0,
1080
+ bottom: 0,
1081
+ backgroundColor: "rgba(0,0,0,0.5)",
1082
+ display: "flex",
1083
+ alignItems: "center",
1084
+ justifyContent: "center",
1085
+ zIndex: 1e4
1086
+ },
1087
+ "data-ai-annotation-ui": true,
1088
+ onClick: () => dispatch({ type: "TOGGLE_LIST" }),
1089
+ children: /* @__PURE__ */ jsxs3(
1090
+ "div",
1091
+ {
1092
+ style: {
1093
+ backgroundColor: "#1e1e1e",
1094
+ color: "#e5e7eb",
1095
+ padding: "20px",
1096
+ borderRadius: "8px",
1097
+ width: "600px",
1098
+ maxWidth: "90%",
1099
+ maxHeight: "80vh",
1100
+ display: "flex",
1101
+ flexDirection: "column",
1102
+ boxShadow: "0 4px 6px rgba(0,0,0,0.3)"
1103
+ },
1104
+ onClick: (e) => e.stopPropagation(),
1105
+ children: [
1106
+ /* @__PURE__ */ jsxs3("div", { style: { display: "flex", justifyContent: "space-between", marginBottom: "16px", alignItems: "center" }, children: [
1107
+ /* @__PURE__ */ jsxs3("h3", { style: { margin: 0 }, children: [
1108
+ "Annotations (",
1109
+ state.annotations.length,
1110
+ ")"
1111
+ ] }),
1112
+ /* @__PURE__ */ jsxs3("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
1113
+ state.annotations.length > 0 && /* @__PURE__ */ jsxs3(
1114
+ "button",
1115
+ {
1116
+ onClick: () => dispatch({ type: "CLEAR_ALL_ANNOTATIONS" }),
1117
+ style: {
1118
+ background: "#ef4444",
1119
+ border: "none",
1120
+ color: "white",
1121
+ cursor: "pointer",
1122
+ padding: "6px 12px",
1123
+ borderRadius: "4px",
1124
+ fontSize: "12px",
1125
+ display: "flex",
1126
+ alignItems: "center",
1127
+ gap: "4px"
1128
+ },
1129
+ title: "Clear all annotations",
1130
+ children: [
1131
+ /* @__PURE__ */ jsx5(Trash, { size: 14 }),
1132
+ "Clear All"
1133
+ ]
1134
+ }
1135
+ ),
1136
+ /* @__PURE__ */ jsx5(
1137
+ "button",
1138
+ {
1139
+ onClick: () => dispatch({ type: "TOGGLE_LIST" }),
1140
+ style: { background: "none", border: "none", color: "inherit", cursor: "pointer" },
1141
+ children: /* @__PURE__ */ jsx5(X2, { size: 20 })
1142
+ }
1143
+ )
1144
+ ] })
1145
+ ] }),
1146
+ /* @__PURE__ */ jsx5("div", { style: { overflowY: "auto", flex: 1, display: "flex", flexDirection: "column", gap: "8px" }, children: state.annotations.length === 0 ? /* @__PURE__ */ jsx5("p", { style: { textAlign: "center", opacity: 0.6, padding: "20px" }, children: "No annotations yet." }) : state.annotations.map((ann) => {
1147
+ const isExpanded = expandedId === ann.id;
1148
+ const hasDetails = !!ann.details;
1149
+ return /* @__PURE__ */ jsxs3(
1150
+ "div",
1151
+ {
1152
+ style: {
1153
+ backgroundColor: "#2d2d2d",
1154
+ borderRadius: "4px",
1155
+ overflow: "hidden"
1156
+ },
1157
+ children: [
1158
+ /* @__PURE__ */ jsxs3(
1159
+ "div",
1160
+ {
1161
+ style: {
1162
+ padding: "12px",
1163
+ display: "flex",
1164
+ justifyContent: "space-between",
1165
+ alignItems: "flex-start",
1166
+ gap: "12px"
1167
+ },
1168
+ children: [
1169
+ /* @__PURE__ */ jsxs3("div", { style: { flex: 1 }, children: [
1170
+ /* @__PURE__ */ jsxs3("div", { style: { display: "flex", alignItems: "center", gap: "8px", marginBottom: "4px" }, children: [
1171
+ hasDetails && /* @__PURE__ */ jsx5(
1172
+ "button",
1173
+ {
1174
+ onClick: () => toggleExpand(ann.id),
1175
+ style: {
1176
+ background: "none",
1177
+ border: "none",
1178
+ color: "#60a5fa",
1179
+ cursor: "pointer",
1180
+ padding: 0,
1181
+ display: "flex",
1182
+ alignItems: "center"
1183
+ },
1184
+ children: isExpanded ? /* @__PURE__ */ jsx5(ChevronDown, { size: 16 }) : /* @__PURE__ */ jsx5(ChevronRight, { size: 16 })
1185
+ }
1186
+ ),
1187
+ /* @__PURE__ */ jsx5("div", { style: { fontWeight: "bold", fontSize: "0.9rem", color: "#60a5fa" }, children: ann.componentName })
1188
+ ] }),
1189
+ /* @__PURE__ */ jsx5("div", { style: { fontSize: "0.9rem", whiteSpace: "pre-wrap", marginLeft: hasDetails ? 24 : 0 }, children: ann.note })
1190
+ ] }),
1191
+ /* @__PURE__ */ jsx5(
1192
+ "button",
1193
+ {
1194
+ onClick: () => dispatch({ type: "REMOVE_ANNOTATION", payload: ann.id }),
1195
+ style: {
1196
+ background: "none",
1197
+ border: "none",
1198
+ color: "#ef4444",
1199
+ cursor: "pointer",
1200
+ padding: "4px",
1201
+ flexShrink: 0
1202
+ },
1203
+ title: "Remove annotation",
1204
+ children: /* @__PURE__ */ jsx5(Trash2, { size: 16 })
1205
+ }
1206
+ )
1207
+ ]
1208
+ }
1209
+ ),
1210
+ isExpanded && ann.details && /* @__PURE__ */ jsxs3(
1211
+ "div",
1212
+ {
1213
+ style: {
1214
+ padding: "12px",
1215
+ paddingTop: 0,
1216
+ borderTop: "1px solid #404040",
1217
+ marginTop: "8px",
1218
+ fontSize: "0.8rem"
1219
+ },
1220
+ children: [
1221
+ /* @__PURE__ */ jsxs3("div", { style: { marginBottom: "12px" }, children: [
1222
+ /* @__PURE__ */ jsx5("div", { style: { color: "#10b981", fontWeight: "bold", marginBottom: "4px" }, children: "Element" }),
1223
+ /* @__PURE__ */ jsxs3("div", { style: { fontFamily: "monospace", color: "#f59e0b", fontSize: "0.75rem" }, children: [
1224
+ "<",
1225
+ ann.details.elementInfo.tagName,
1226
+ ann.details.elementInfo.id && ` id="${ann.details.elementInfo.id}"`,
1227
+ ann.details.elementInfo.className && ` class="${ann.details.elementInfo.className}"`,
1228
+ ">"
1229
+ ] }),
1230
+ ann.details.elementInfo.textContent && /* @__PURE__ */ jsxs3("div", { style: { color: "rgba(255, 255, 255, 0.6)", marginTop: "4px", fontStyle: "italic" }, children: [
1231
+ 'Text: "',
1232
+ ann.details.elementInfo.textContent,
1233
+ '"'
1234
+ ] })
1235
+ ] }),
1236
+ ann.details.parentHierarchy.length > 0 && /* @__PURE__ */ jsxs3("div", { style: { marginBottom: "12px" }, children: [
1237
+ /* @__PURE__ */ jsx5("div", { style: { color: "#10b981", fontWeight: "bold", marginBottom: "4px" }, children: "Parent Components" }),
1238
+ /* @__PURE__ */ jsx5("div", { style: { fontFamily: "monospace", fontSize: "0.75rem" }, children: ann.details.parentHierarchy.map((parent, i) => /* @__PURE__ */ jsxs3("span", { style: { color: "#a78bfa" }, children: [
1239
+ i > 0 && " \u2192 ",
1240
+ parent
1241
+ ] }, i)) })
1242
+ ] }),
1243
+ ann.details.childComponents.length > 0 && /* @__PURE__ */ jsxs3("div", { style: { marginBottom: "12px" }, children: [
1244
+ /* @__PURE__ */ jsxs3("div", { style: { color: "#10b981", fontWeight: "bold", marginBottom: "4px" }, children: [
1245
+ "Child Components (",
1246
+ ann.details.childComponents.length,
1247
+ ")"
1248
+ ] }),
1249
+ /* @__PURE__ */ jsx5("div", { style: { display: "flex", flexWrap: "wrap", gap: "6px" }, children: ann.details.childComponents.map((child, i) => /* @__PURE__ */ jsxs3(
1250
+ "span",
1251
+ {
1252
+ style: {
1253
+ backgroundColor: "#374151",
1254
+ padding: "2px 8px",
1255
+ borderRadius: "4px",
1256
+ fontFamily: "monospace",
1257
+ fontSize: "0.75rem",
1258
+ color: "#34d399"
1259
+ },
1260
+ children: [
1261
+ child.name,
1262
+ child.count > 1 && ` \xD7${child.count}`,
1263
+ child.hasChildren && " \u25BE"
1264
+ ]
1265
+ },
1266
+ i
1267
+ )) })
1268
+ ] }),
1269
+ Object.keys(ann.details.elementInfo.attributes).length > 0 && /* @__PURE__ */ jsxs3("div", { children: [
1270
+ /* @__PURE__ */ jsx5("div", { style: { color: "#10b981", fontWeight: "bold", marginBottom: "4px" }, children: "Attributes" }),
1271
+ /* @__PURE__ */ jsx5("div", { style: { fontFamily: "monospace", fontSize: "0.75rem" }, children: Object.entries(ann.details.elementInfo.attributes).map(([key, value]) => /* @__PURE__ */ jsxs3("div", { style: { color: "#fbbf24" }, children: [
1272
+ key,
1273
+ '="',
1274
+ value,
1275
+ '"'
1276
+ ] }, key)) })
1277
+ ] })
1278
+ ]
1279
+ }
1280
+ )
1281
+ ]
1282
+ },
1283
+ ann.id
1284
+ );
1285
+ }) })
1286
+ ]
1287
+ }
1288
+ )
1289
+ }
1290
+ );
1291
+ }
1292
+
1293
+ // src/components/Toolbar.tsx
1294
+ import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
1295
+ function Toolbar() {
1296
+ const { state, dispatch } = useAiAnnotation();
1297
+ const [showCopied, setShowCopied] = useState5(false);
1298
+ const [isAnimating, setIsAnimating] = useState5(false);
1299
+ const contentRef = useRef2(null);
1300
+ const [contentWidth, setContentWidth] = useState5(null);
1301
+ useEffect4(() => {
1302
+ if (contentRef.current && !state.isMinimized) {
1303
+ requestAnimationFrame(() => {
1304
+ if (contentRef.current) {
1305
+ setContentWidth(contentRef.current.scrollWidth);
1306
+ }
1307
+ });
1308
+ }
1309
+ }, [state.isMinimized, showCopied, state.annotations.length]);
1310
+ const handleToggleMinimized = () => {
1311
+ setIsAnimating(true);
1312
+ dispatch({ type: "TOGGLE_MINIMIZED" });
1313
+ setTimeout(() => setIsAnimating(false), 300);
1314
+ };
1315
+ const handleCopy = () => {
1316
+ if (state.annotations.length === 0) return;
1317
+ const sections = state.annotations.map((a, index) => {
1318
+ const lines = [];
1319
+ lines.push(`${"=".repeat(50)}`);
1320
+ lines.push(`Annotation ${index + 1}: ${a.componentName}`);
1321
+ lines.push(`${"=".repeat(50)}`);
1322
+ lines.push("");
1323
+ lines.push("## Instruction");
1324
+ lines.push(a.note);
1325
+ return lines.join("\n");
1326
+ });
1327
+ const text = sections.join("\n\n");
1328
+ navigator.clipboard.writeText(text).then(() => {
1329
+ setShowCopied(true);
1330
+ setTimeout(() => setShowCopied(false), 2e3);
1331
+ });
1332
+ };
1333
+ const toggleMode = () => {
1334
+ dispatch({
1335
+ type: "SET_MODE",
1336
+ payload: state.mode === "disabled" ? "inspecting" : "disabled"
1337
+ });
1338
+ };
1339
+ return /* @__PURE__ */ jsxs4(Fragment2, { children: [
1340
+ /* @__PURE__ */ jsx6(Highlighter, {}),
1341
+ /* @__PURE__ */ jsx6(AnnotationList, {}),
1342
+ /* @__PURE__ */ jsx6(Draggable, { children: /* @__PURE__ */ jsxs4(
1343
+ "div",
1344
+ {
1345
+ style: {
1346
+ backgroundColor: "#1e1e1e",
1347
+ border: "1px solid #333",
1348
+ borderRadius: "8px",
1349
+ padding: "8px",
1350
+ boxShadow: "0 4px 12px rgba(0,0,0,0.3)",
1351
+ display: "flex",
1352
+ alignItems: "center",
1353
+ gap: "8px",
1354
+ color: "#e5e7eb",
1355
+ transition: "width 0.2s"
1356
+ },
1357
+ "data-ai-annotation-ui": true,
1358
+ children: [
1359
+ /* @__PURE__ */ jsx6("div", { style: { cursor: "grab", color: "#666", display: "flex" }, title: "Drag", children: /* @__PURE__ */ jsx6(GripVertical, { size: 20 }) }),
1360
+ /* @__PURE__ */ jsx6(
1361
+ "div",
1362
+ {
1363
+ ref: contentRef,
1364
+ style: {
1365
+ display: "flex",
1366
+ alignItems: "center",
1367
+ gap: "8px",
1368
+ overflow: "hidden",
1369
+ transition: "max-width 0.3s ease, opacity 0.2s ease",
1370
+ maxWidth: state.isMinimized ? 0 : contentWidth || 300,
1371
+ opacity: state.isMinimized ? 0 : 1,
1372
+ paddingTop: "4px",
1373
+ paddingBottom: "4px",
1374
+ marginTop: "-4px",
1375
+ marginBottom: "-4px"
1376
+ },
1377
+ children: /* @__PURE__ */ jsxs4(Fragment2, { children: [
1378
+ /* @__PURE__ */ jsx6("div", { style: { width: "1px", height: "24px", backgroundColor: "#333" } }),
1379
+ /* @__PURE__ */ jsx6(
1380
+ "button",
1381
+ {
1382
+ onClick: toggleMode,
1383
+ title: state.mode === "inspecting" ? "Disable Inspection" : "Enable Inspection",
1384
+ style: {
1385
+ background: state.mode === "inspecting" ? "#3b82f6" : "transparent",
1386
+ border: "none",
1387
+ borderRadius: "4px",
1388
+ padding: "6px",
1389
+ color: state.mode === "inspecting" ? "white" : "inherit",
1390
+ cursor: "pointer",
1391
+ display: "flex"
1392
+ },
1393
+ children: state.mode === "inspecting" ? /* @__PURE__ */ jsx6(MousePointer2, { size: 18 }) : /* @__PURE__ */ jsx6(Ban, { size: 18 })
1394
+ }
1395
+ ),
1396
+ /* @__PURE__ */ jsxs4(
1397
+ "button",
1398
+ {
1399
+ onClick: () => dispatch({ type: "TOGGLE_LIST" }),
1400
+ title: "List Annotations",
1401
+ style: {
1402
+ background: "transparent",
1403
+ border: "none",
1404
+ borderRadius: "4px",
1405
+ padding: "6px",
1406
+ color: "inherit",
1407
+ cursor: "pointer",
1408
+ display: "flex",
1409
+ position: "relative"
1410
+ },
1411
+ children: [
1412
+ /* @__PURE__ */ jsx6(List, { size: 18 }),
1413
+ state.annotations.length > 0 && /* @__PURE__ */ jsx6("span", { style: {
1414
+ position: "absolute",
1415
+ top: -2,
1416
+ right: -2,
1417
+ background: "#ef4444",
1418
+ color: "white",
1419
+ fontSize: "9px",
1420
+ width: "14px",
1421
+ height: "14px",
1422
+ borderRadius: "50%",
1423
+ display: "flex",
1424
+ alignItems: "center",
1425
+ justifyContent: "center"
1426
+ }, children: state.annotations.length })
1427
+ ]
1428
+ }
1429
+ ),
1430
+ /* @__PURE__ */ jsx6(
1431
+ "button",
1432
+ {
1433
+ onClick: handleCopy,
1434
+ title: "Copy Annotations",
1435
+ style: {
1436
+ background: showCopied ? "#22c55e" : "transparent",
1437
+ border: "none",
1438
+ borderRadius: "4px",
1439
+ padding: "6px",
1440
+ color: showCopied ? "white" : "inherit",
1441
+ cursor: "pointer",
1442
+ display: "flex",
1443
+ alignItems: "center",
1444
+ gap: "4px",
1445
+ transition: "background-color 0.2s, color 0.2s",
1446
+ minWidth: showCopied ? "75px" : "auto"
1447
+ },
1448
+ children: showCopied ? /* @__PURE__ */ jsxs4(Fragment2, { children: [
1449
+ /* @__PURE__ */ jsx6(Check, { size: 18 }),
1450
+ /* @__PURE__ */ jsx6("span", { style: { fontSize: "12px", whiteSpace: "nowrap" }, children: "Copied!" })
1451
+ ] }) : /* @__PURE__ */ jsx6(Copy, { size: 18 })
1452
+ }
1453
+ )
1454
+ ] })
1455
+ }
1456
+ ),
1457
+ /* @__PURE__ */ jsx6("div", { style: { width: "1px", height: "24px", backgroundColor: "#333", marginLeft: state.isMinimized ? 0 : "auto" } }),
1458
+ /* @__PURE__ */ jsx6(
1459
+ "button",
1460
+ {
1461
+ onClick: handleToggleMinimized,
1462
+ title: state.isMinimized ? "Expand" : "Minimize",
1463
+ style: {
1464
+ background: "transparent",
1465
+ border: "none",
1466
+ borderRadius: "4px",
1467
+ padding: "6px",
1468
+ color: "inherit",
1469
+ cursor: "pointer",
1470
+ display: "flex"
1471
+ },
1472
+ children: state.isMinimized ? /* @__PURE__ */ jsx6(Maximize2, { size: 18 }) : /* @__PURE__ */ jsx6(Minus, { size: 18 })
1473
+ }
1474
+ )
1475
+ ]
1476
+ }
1477
+ ) })
1478
+ ] });
1479
+ }
1480
+
1481
+ // src/utils/platform.ts
1482
+ var isWeb = typeof document !== "undefined" && typeof window !== "undefined";
1483
+ var isNative = !isWeb && typeof global !== "undefined";
1484
+ function getPlatform() {
1485
+ if (isWeb) return "web";
1486
+ try {
1487
+ const { Platform } = __require("react-native");
1488
+ if (Platform.OS === "ios") return "ios";
1489
+ if (Platform.OS === "android") return "android";
1490
+ return "native";
1491
+ } catch {
1492
+ return "native";
1493
+ }
1494
+ }
1495
+ function isTauriEnv2() {
1496
+ return isWeb && typeof window !== "undefined" && "__TAURI_INTERNALS__" in window;
1497
+ }
1498
+
1499
+ // src/index.tsx
1500
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
1501
+ function AiAnnotationProvider2({ children }) {
1502
+ return /* @__PURE__ */ jsxs5(AiAnnotationProvider, { children: [
1503
+ children,
1504
+ /* @__PURE__ */ jsx7(Toolbar, {})
1505
+ ] });
1506
+ }
1507
+ export {
1508
+ AiAnnotationProvider2 as AiAnnotationProvider,
1509
+ AnnotationInput,
1510
+ AnnotationList,
1511
+ Draggable,
1512
+ Highlighter,
1513
+ Toolbar,
1514
+ captureScreenshot,
1515
+ getComponentDisplayName,
1516
+ getElementFromFiber,
1517
+ getPlatform,
1518
+ getReactFiber,
1519
+ isNative,
1520
+ isTauriEnv2 as isTauriEnv,
1521
+ isWeb,
1522
+ useAiAnnotation
1523
+ };
1524
+ //# sourceMappingURL=index.js.map