@ewjdev/anyclick-react 0.1.0 → 1.1.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.
package/dist/index.mjs CHANGED
@@ -1,9 +1,10 @@
1
1
  "use client";
2
2
 
3
- // src/FeedbackProvider.tsx
3
+ // src/AnyclickProvider.tsx
4
4
  import {
5
5
  useCallback as useCallback2,
6
6
  useEffect as useEffect2,
7
+ useLayoutEffect,
7
8
  useMemo,
8
9
  useRef as useRef2,
9
10
  useState as useState3
@@ -12,9 +13,17 @@ import { createFeedbackClient } from "@ewjdev/anyclick-core";
12
13
 
13
14
  // src/context.ts
14
15
  import { createContext, useContext } from "react";
15
- var FeedbackContext = createContext(null);
16
+ var AnyclickContext = createContext(null);
17
+ var FeedbackContext = AnyclickContext;
18
+ function useAnyclick() {
19
+ const context = useContext(AnyclickContext);
20
+ if (!context) {
21
+ throw new Error("useAnyclick must be used within an AnyclickProvider");
22
+ }
23
+ return context;
24
+ }
16
25
  function useFeedback() {
17
- const context = useContext(FeedbackContext);
26
+ const context = useContext(AnyclickContext);
18
27
  if (!context) {
19
28
  throw new Error("useFeedback must be used within a FeedbackProvider");
20
29
  }
@@ -30,6 +39,25 @@ import {
30
39
  } from "@ewjdev/anyclick-core";
31
40
 
32
41
  // src/styles.ts
42
+ var menuCSSVariables = {
43
+ // Background colors
44
+ "--anyclick-menu-bg": "#ffffff",
45
+ "--anyclick-menu-hover": "#f5f5f5",
46
+ // Text colors
47
+ "--anyclick-menu-text": "#333333",
48
+ "--anyclick-menu-text-muted": "#666666",
49
+ // Border colors
50
+ "--anyclick-menu-border": "#e5e5e5",
51
+ // Accent/action colors
52
+ "--anyclick-menu-accent": "#0066cc",
53
+ "--anyclick-menu-accent-text": "#ffffff",
54
+ // Input colors
55
+ "--anyclick-menu-input-bg": "#ffffff",
56
+ "--anyclick-menu-input-border": "#dddddd",
57
+ // Cancel button
58
+ "--anyclick-menu-cancel-bg": "#f0f0f0",
59
+ "--anyclick-menu-cancel-text": "#666666"
60
+ };
33
61
  var menuStyles = {
34
62
  overlay: {
35
63
  position: "fixed",
@@ -40,17 +68,19 @@ var menuStyles = {
40
68
  position: "fixed",
41
69
  zIndex: 9999,
42
70
  minWidth: "200px",
43
- backgroundColor: "#ffffff",
71
+ backgroundColor: "var(--anyclick-menu-bg, #ffffff)",
44
72
  borderRadius: "8px",
45
73
  boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15), 0 0 0 1px rgba(0, 0, 0, 0.05)",
46
74
  overflow: "hidden",
47
75
  fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
48
- fontSize: "14px"
76
+ fontSize: "14px",
77
+ // Set default CSS variables
78
+ ...menuCSSVariables
49
79
  },
50
80
  header: {
51
81
  padding: "12px 16px",
52
- borderBottom: "1px solid #e5e5e5",
53
- color: "#666",
82
+ borderBottom: "1px solid var(--anyclick-menu-border, #e5e5e5)",
83
+ color: "var(--anyclick-menu-text-muted, #666)",
54
84
  fontSize: "12px",
55
85
  fontWeight: 500,
56
86
  textTransform: "uppercase",
@@ -66,15 +96,15 @@ var menuStyles = {
66
96
  padding: "10px 16px",
67
97
  cursor: "pointer",
68
98
  transition: "background-color 0.15s",
69
- color: "#333",
99
+ color: "var(--anyclick-menu-text, #333)",
70
100
  border: "none",
71
- background: "none",
101
+ backgroundColor: "transparent",
72
102
  width: "100%",
73
103
  textAlign: "left",
74
104
  fontSize: "14px"
75
105
  },
76
106
  itemHover: {
77
- backgroundColor: "#f5f5f5"
107
+ backgroundColor: "var(--anyclick-menu-hover, #f5f5f5)"
78
108
  },
79
109
  itemIcon: {
80
110
  display: "flex",
@@ -86,19 +116,21 @@ var menuStyles = {
86
116
  },
87
117
  commentSection: {
88
118
  padding: "12px 16px",
89
- borderTop: "1px solid #e5e5e5"
119
+ borderTop: "1px solid var(--anyclick-menu-border, #e5e5e5)"
90
120
  },
91
121
  commentInput: {
92
122
  width: "100%",
93
123
  minHeight: "60px",
94
124
  padding: "8px 12px",
95
- border: "1px solid #ddd",
125
+ border: "1px solid var(--anyclick-menu-input-border, #ddd)",
96
126
  borderRadius: "6px",
97
127
  fontSize: "14px",
98
128
  fontFamily: "inherit",
99
129
  resize: "vertical",
100
130
  outline: "none",
101
- boxSizing: "border-box"
131
+ boxSizing: "border-box",
132
+ backgroundColor: "var(--anyclick-menu-input-bg, #ffffff)",
133
+ color: "var(--anyclick-menu-text, #333)"
102
134
  },
103
135
  buttonRow: {
104
136
  display: "flex",
@@ -116,15 +148,15 @@ var menuStyles = {
116
148
  border: "none"
117
149
  },
118
150
  cancelButton: {
119
- backgroundColor: "#f0f0f0",
120
- color: "#666",
151
+ backgroundColor: "var(--anyclick-menu-cancel-bg, #f0f0f0)",
152
+ color: "var(--anyclick-menu-cancel-text, #666)",
121
153
  display: "flex",
122
154
  alignItems: "center",
123
155
  gap: "2px"
124
156
  },
125
157
  submitButton: {
126
- backgroundColor: "#0066cc",
127
- color: "#ffffff",
158
+ backgroundColor: "var(--anyclick-menu-accent, #0066cc)",
159
+ color: "var(--anyclick-menu-accent-text, #ffffff)",
128
160
  display: "flex",
129
161
  alignItems: "center",
130
162
  gap: "2px"
@@ -168,6 +200,242 @@ var darkMenuStyles = {
168
200
  color: "#ccc"
169
201
  }
170
202
  };
203
+ var screenshotPreviewStyles = {
204
+ container: {
205
+ width: "100%",
206
+ display: "flex",
207
+ flexDirection: "column",
208
+ gap: "8px"
209
+ },
210
+ containerExpanded: {
211
+ position: "fixed",
212
+ top: "50%",
213
+ left: "50%",
214
+ transform: "translate(-50%, -50%)",
215
+ width: "90vw",
216
+ maxWidth: "800px",
217
+ maxHeight: "90vh",
218
+ backgroundColor: "#fff",
219
+ borderRadius: "12px",
220
+ boxShadow: "0 25px 50px -12px rgba(0, 0, 0, 0.25)",
221
+ padding: "16px",
222
+ zIndex: 1e4
223
+ },
224
+ loadingContainer: {
225
+ display: "flex",
226
+ flexDirection: "column",
227
+ alignItems: "center",
228
+ justifyContent: "center",
229
+ gap: "12px",
230
+ padding: "24px"
231
+ },
232
+ loadingText: {
233
+ fontSize: "13px",
234
+ color: "#6b7280"
235
+ },
236
+ emptyContainer: {
237
+ display: "flex",
238
+ flexDirection: "column",
239
+ alignItems: "center",
240
+ justifyContent: "center",
241
+ gap: "8px",
242
+ padding: "24px"
243
+ },
244
+ emptyText: {
245
+ fontSize: "13px",
246
+ color: "#6b7280",
247
+ fontWeight: "500"
248
+ },
249
+ emptySubtext: {
250
+ fontSize: "11px",
251
+ color: "#9ca3af",
252
+ textAlign: "center"
253
+ },
254
+ emptyActions: {
255
+ display: "flex",
256
+ gap: "8px",
257
+ marginTop: "8px"
258
+ },
259
+ retakeButtonOutline: {
260
+ display: "flex",
261
+ alignItems: "center",
262
+ gap: "6px",
263
+ padding: "8px 12px",
264
+ fontSize: "12px",
265
+ color: "#6b7280",
266
+ backgroundColor: "transparent",
267
+ border: "1px solid #e5e7eb",
268
+ borderRadius: "6px",
269
+ cursor: "pointer"
270
+ },
271
+ continueButton: {
272
+ display: "flex",
273
+ alignItems: "center",
274
+ gap: "6px",
275
+ padding: "8px 12px",
276
+ fontSize: "12px",
277
+ color: "#fff",
278
+ backgroundColor: "#3b82f6",
279
+ border: "none",
280
+ borderRadius: "6px",
281
+ cursor: "pointer",
282
+ fontWeight: "500"
283
+ },
284
+ header: {
285
+ display: "flex",
286
+ justifyContent: "space-between",
287
+ alignItems: "center",
288
+ padding: "0 4px"
289
+ },
290
+ headerTitle: {
291
+ fontSize: "12px",
292
+ fontWeight: "600",
293
+ color: "#374151",
294
+ textTransform: "uppercase",
295
+ letterSpacing: "0.05em"
296
+ },
297
+ headerActions: {
298
+ display: "flex",
299
+ alignItems: "center",
300
+ gap: "8px"
301
+ },
302
+ sizeLabel: {
303
+ fontSize: "11px",
304
+ color: "#9ca3af"
305
+ },
306
+ iconButton: {
307
+ display: "flex",
308
+ alignItems: "center",
309
+ justifyContent: "center",
310
+ width: "24px",
311
+ height: "24px",
312
+ border: "none",
313
+ backgroundColor: "transparent",
314
+ borderRadius: "4px",
315
+ cursor: "pointer",
316
+ color: "#6b7280"
317
+ },
318
+ tabContainer: {
319
+ display: "flex",
320
+ gap: "4px",
321
+ borderBottom: "1px solid #e5e7eb",
322
+ paddingBottom: "8px"
323
+ },
324
+ tab: {
325
+ display: "flex",
326
+ alignItems: "center",
327
+ gap: "4px",
328
+ padding: "2px 5px",
329
+ fontSize: "12px",
330
+ color: "#6b7280",
331
+ backgroundColor: "transparent",
332
+ border: "none",
333
+ borderRadius: "4px",
334
+ cursor: "pointer",
335
+ transition: "all 0.15s ease"
336
+ },
337
+ tabActive: {
338
+ backgroundColor: "#eff6ff",
339
+ color: "#3b82f6",
340
+ fontWeight: "500"
341
+ },
342
+ tabError: {
343
+ color: "#ef4444"
344
+ },
345
+ tabSize: {
346
+ fontSize: "10px",
347
+ color: "#9ca3af"
348
+ },
349
+ previewContainer: {
350
+ position: "relative",
351
+ width: "100%",
352
+ height: "150px",
353
+ backgroundColor: "#f9fafb",
354
+ borderRadius: "8px",
355
+ overflow: "hidden",
356
+ display: "flex",
357
+ alignItems: "center",
358
+ justifyContent: "center"
359
+ },
360
+ previewContainerExpanded: {
361
+ height: "60vh",
362
+ maxHeight: "500px"
363
+ },
364
+ previewImage: {
365
+ maxWidth: "100%",
366
+ maxHeight: "100%",
367
+ objectFit: "contain"
368
+ },
369
+ noPreview: {
370
+ display: "flex",
371
+ flexDirection: "column",
372
+ alignItems: "center",
373
+ gap: "8px",
374
+ fontSize: "12px",
375
+ color: "#9ca3af"
376
+ },
377
+ errorPreview: {
378
+ display: "flex",
379
+ flexDirection: "column",
380
+ alignItems: "center",
381
+ justifyContent: "center",
382
+ gap: "8px",
383
+ padding: "16px",
384
+ textAlign: "center"
385
+ },
386
+ errorTitle: {
387
+ fontSize: "14px",
388
+ fontWeight: "600",
389
+ color: "#ef4444"
390
+ },
391
+ errorMessage: {
392
+ fontSize: "12px",
393
+ color: "#6b7280",
394
+ maxWidth: "250px",
395
+ lineHeight: "1.4"
396
+ },
397
+ dimensionsInfo: {
398
+ fontSize: "11px",
399
+ color: "#9ca3af",
400
+ textAlign: "center"
401
+ },
402
+ actions: {
403
+ display: "flex",
404
+ justifyContent: "space-between",
405
+ alignItems: "center",
406
+ paddingTop: "8px",
407
+ borderTop: "1px solid #e5e7eb"
408
+ },
409
+ actionsRight: {
410
+ display: "flex",
411
+ gap: "8px"
412
+ },
413
+ retakeButton: {
414
+ display: "flex",
415
+ alignItems: "center",
416
+ gap: "6px",
417
+ padding: "8px 16px",
418
+ fontSize: "13px",
419
+ color: "#fff",
420
+ backgroundColor: "#3b82f6",
421
+ border: "none",
422
+ borderRadius: "6px",
423
+ cursor: "pointer",
424
+ fontWeight: "500"
425
+ },
426
+ retakeButtonSmall: {
427
+ display: "flex",
428
+ alignItems: "center",
429
+ gap: "4px",
430
+ padding: "4px 8px",
431
+ fontSize: "11px",
432
+ color: "#6b7280",
433
+ backgroundColor: "transparent",
434
+ border: "1px solid #e5e7eb",
435
+ borderRadius: "4px",
436
+ cursor: "pointer"
437
+ }
438
+ };
171
439
 
172
440
  // src/highlight.ts
173
441
  var HIGHLIGHT_TARGET_CLASS = "uifeedback-highlight-target";
@@ -202,7 +470,12 @@ var defaultContainerSelectors = [
202
470
  "footer"
203
471
  ];
204
472
  function generateHighlightCSS(colors) {
205
- const { targetColor, containerColor, targetShadowOpacity, containerShadowOpacity } = colors;
473
+ const {
474
+ targetColor,
475
+ containerColor,
476
+ targetShadowOpacity,
477
+ containerShadowOpacity
478
+ } = colors;
206
479
  const hexToRgba = (hex, alpha) => {
207
480
  const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
208
481
  if (!result) return `rgba(0, 0, 0, ${alpha})`;
@@ -210,7 +483,7 @@ function generateHighlightCSS(colors) {
210
483
  };
211
484
  return `
212
485
  .${HIGHLIGHT_TARGET_CLASS} {
213
- outline: 2px solid ${targetColor} !important;
486
+ outline: 2px dashed ${targetColor} !important;
214
487
  outline-offset: 2px !important;
215
488
  box-shadow: 0 0 0 4px ${hexToRgba(targetColor, targetShadowOpacity)}, 0 4px 12px ${hexToRgba(targetColor, targetShadowOpacity * 0.6)} !important;
216
489
  border-radius: 4px !important;
@@ -425,8 +698,16 @@ var ChevronLeft = createLucideIcon("chevron-left", __iconNode3);
425
698
  var __iconNode4 = [["path", { d: "m9 18 6-6-6-6", key: "mthhwq" }]];
426
699
  var ChevronRight = createLucideIcon("chevron-right", __iconNode4);
427
700
 
428
- // ../../node_modules/lucide-react/dist/esm/icons/expand.js
701
+ // ../../node_modules/lucide-react/dist/esm/icons/circle-alert.js
429
702
  var __iconNode5 = [
703
+ ["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
704
+ ["line", { x1: "12", x2: "12", y1: "8", y2: "12", key: "1pkeuh" }],
705
+ ["line", { x1: "12", x2: "12.01", y1: "16", y2: "16", key: "4dfq90" }]
706
+ ];
707
+ var CircleAlert = createLucideIcon("circle-alert", __iconNode5);
708
+
709
+ // ../../node_modules/lucide-react/dist/esm/icons/expand.js
710
+ var __iconNode6 = [
430
711
  ["path", { d: "m15 15 6 6", key: "1s409w" }],
431
712
  ["path", { d: "m15 9 6-6", key: "ko1vev" }],
432
713
  ["path", { d: "M21 16v5h-5", key: "1ck2sf" }],
@@ -436,10 +717,10 @@ var __iconNode5 = [
436
717
  ["path", { d: "M3 8V3h5", key: "1ln10m" }],
437
718
  ["path", { d: "M9 9 3 3", key: "v551iv" }]
438
719
  ];
439
- var Expand = createLucideIcon("expand", __iconNode5);
720
+ var Expand = createLucideIcon("expand", __iconNode6);
440
721
 
441
722
  // ../../node_modules/lucide-react/dist/esm/icons/flag.js
442
- var __iconNode6 = [
723
+ var __iconNode7 = [
443
724
  [
444
725
  "path",
445
726
  {
@@ -448,47 +729,58 @@ var __iconNode6 = [
448
729
  }
449
730
  ]
450
731
  ];
451
- var Flag = createLucideIcon("flag", __iconNode6);
732
+ var Flag = createLucideIcon("flag", __iconNode7);
733
+
734
+ // ../../node_modules/lucide-react/dist/esm/icons/grip-vertical.js
735
+ var __iconNode8 = [
736
+ ["circle", { cx: "9", cy: "12", r: "1", key: "1vctgf" }],
737
+ ["circle", { cx: "9", cy: "5", r: "1", key: "hp0tcf" }],
738
+ ["circle", { cx: "9", cy: "19", r: "1", key: "fkjjf6" }],
739
+ ["circle", { cx: "15", cy: "12", r: "1", key: "1tmaij" }],
740
+ ["circle", { cx: "15", cy: "5", r: "1", key: "19l28e" }],
741
+ ["circle", { cx: "15", cy: "19", r: "1", key: "f4zoj3" }]
742
+ ];
743
+ var GripVertical = createLucideIcon("grip-vertical", __iconNode8);
452
744
 
453
745
  // ../../node_modules/lucide-react/dist/esm/icons/image.js
454
- var __iconNode7 = [
746
+ var __iconNode9 = [
455
747
  ["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", ry: "2", key: "1m3agn" }],
456
748
  ["circle", { cx: "9", cy: "9", r: "2", key: "af1f0g" }],
457
749
  ["path", { d: "m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21", key: "1xmnt7" }]
458
750
  ];
459
- var Image = createLucideIcon("image", __iconNode7);
751
+ var Image = createLucideIcon("image", __iconNode9);
460
752
 
461
753
  // ../../node_modules/lucide-react/dist/esm/icons/loader-circle.js
462
- var __iconNode8 = [["path", { d: "M21 12a9 9 0 1 1-6.219-8.56", key: "13zald" }]];
463
- var LoaderCircle = createLucideIcon("loader-circle", __iconNode8);
754
+ var __iconNode10 = [["path", { d: "M21 12a9 9 0 1 1-6.219-8.56", key: "13zald" }]];
755
+ var LoaderCircle = createLucideIcon("loader-circle", __iconNode10);
464
756
 
465
757
  // ../../node_modules/lucide-react/dist/esm/icons/plus.js
466
- var __iconNode9 = [
758
+ var __iconNode11 = [
467
759
  ["path", { d: "M5 12h14", key: "1ays0h" }],
468
760
  ["path", { d: "M12 5v14", key: "s699le" }]
469
761
  ];
470
- var Plus = createLucideIcon("plus", __iconNode9);
762
+ var Plus = createLucideIcon("plus", __iconNode11);
471
763
 
472
764
  // ../../node_modules/lucide-react/dist/esm/icons/refresh-cw.js
473
- var __iconNode10 = [
765
+ var __iconNode12 = [
474
766
  ["path", { d: "M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8", key: "v9h5vc" }],
475
767
  ["path", { d: "M21 3v5h-5", key: "1q7to0" }],
476
768
  ["path", { d: "M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16", key: "3uifl3" }],
477
769
  ["path", { d: "M8 16H3v5", key: "1cv678" }]
478
770
  ];
479
- var RefreshCw = createLucideIcon("refresh-cw", __iconNode10);
771
+ var RefreshCw = createLucideIcon("refresh-cw", __iconNode12);
480
772
 
481
773
  // ../../node_modules/lucide-react/dist/esm/icons/shrink.js
482
- var __iconNode11 = [
774
+ var __iconNode13 = [
483
775
  ["path", { d: "m15 15 6 6m-6-6v4.8m0-4.8h4.8", key: "17vawe" }],
484
776
  ["path", { d: "M9 19.8V15m0 0H4.2M9 15l-6 6", key: "chjx8e" }],
485
777
  ["path", { d: "M15 4.2V9m0 0h4.8M15 9l6-6", key: "lav6yq" }],
486
778
  ["path", { d: "M9 4.2V9m0 0H4.2M9 9 3 3", key: "1pxi2q" }]
487
779
  ];
488
- var Shrink = createLucideIcon("shrink", __iconNode11);
780
+ var Shrink = createLucideIcon("shrink", __iconNode13);
489
781
 
490
782
  // ../../node_modules/lucide-react/dist/esm/icons/thumbs-up.js
491
- var __iconNode12 = [
783
+ var __iconNode14 = [
492
784
  ["path", { d: "M7 10v12", key: "1qc93n" }],
493
785
  [
494
786
  "path",
@@ -498,14 +790,14 @@ var __iconNode12 = [
498
790
  }
499
791
  ]
500
792
  ];
501
- var ThumbsUp = createLucideIcon("thumbs-up", __iconNode12);
793
+ var ThumbsUp = createLucideIcon("thumbs-up", __iconNode14);
502
794
 
503
795
  // ../../node_modules/lucide-react/dist/esm/icons/x.js
504
- var __iconNode13 = [
796
+ var __iconNode15 = [
505
797
  ["path", { d: "M18 6 6 18", key: "1bl5f8" }],
506
798
  ["path", { d: "m6 6 12 12", key: "d8bk6v" }]
507
799
  ];
508
- var X = createLucideIcon("x", __iconNode13);
800
+ var X = createLucideIcon("x", __iconNode15);
509
801
 
510
802
  // src/ScreenshotPreview.tsx
511
803
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
@@ -520,7 +812,7 @@ function ScreenshotPreview({
520
812
  const [activeTab, setActiveTab] = useState("element");
521
813
  const [isExpanded, setIsExpanded] = useState(false);
522
814
  if (isLoading) {
523
- return /* @__PURE__ */ jsx("div", { style: styles.container, children: /* @__PURE__ */ jsxs("div", { style: styles.loadingContainer, children: [
815
+ return /* @__PURE__ */ jsx("div", { style: screenshotPreviewStyles.container, children: /* @__PURE__ */ jsxs("div", { style: screenshotPreviewStyles.loadingContainer, children: [
524
816
  /* @__PURE__ */ jsx(
525
817
  LoaderCircle,
526
818
  {
@@ -528,76 +820,117 @@ function ScreenshotPreview({
528
820
  style: { color: "#3b82f6" }
529
821
  }
530
822
  ),
531
- /* @__PURE__ */ jsx("span", { style: styles.loadingText, children: "Capturing screenshots..." })
823
+ /* @__PURE__ */ jsx("span", { style: screenshotPreviewStyles.loadingText, children: "Capturing screenshots..." })
532
824
  ] }) });
533
825
  }
534
826
  if (!screenshots) {
535
- return /* @__PURE__ */ jsx("div", { style: styles.container, children: /* @__PURE__ */ jsxs("div", { style: styles.emptyContainer, children: [
827
+ return /* @__PURE__ */ jsx("div", { style: screenshotPreviewStyles.container, children: /* @__PURE__ */ jsxs("div", { style: screenshotPreviewStyles.emptyContainer, children: [
536
828
  /* @__PURE__ */ jsx(Image, { className: "w-8 h-8", style: { color: "#9ca3af" } }),
537
- /* @__PURE__ */ jsx("span", { style: styles.emptyText, children: "No screenshots captured" }),
538
- /* @__PURE__ */ jsxs(
539
- "button",
540
- {
541
- type: "button",
542
- onClick: onRetake,
543
- style: styles.retakeButton,
544
- disabled: isSubmitting,
545
- children: [
546
- /* @__PURE__ */ jsx(RefreshCw, { className: "w-4 h-4" }),
547
- "Capture Screenshots"
548
- ]
549
- }
550
- )
551
- ] }) });
552
- }
829
+ /* @__PURE__ */ jsx("span", { style: screenshotPreviewStyles.emptyText, children: "Screenshots unavailable" }),
830
+ /* @__PURE__ */ jsx("span", { style: screenshotPreviewStyles.emptySubtext, children: "Some elements can't be captured (e.g., gradient text)" }),
831
+ /* @__PURE__ */ jsxs("div", { style: screenshotPreviewStyles.emptyActions, children: [
832
+ /* @__PURE__ */ jsxs(
833
+ "button",
834
+ {
835
+ type: "button",
836
+ onClick: onRetake,
837
+ style: screenshotPreviewStyles.retakeButtonOutline,
838
+ disabled: isSubmitting,
839
+ children: [
840
+ /* @__PURE__ */ jsx(RefreshCw, { className: "w-4 h-4" }),
841
+ "Try Again"
842
+ ]
843
+ }
844
+ ),
845
+ /* @__PURE__ */ jsxs(
846
+ "button",
847
+ {
848
+ type: "button",
849
+ onClick: () => onConfirm({ capturedAt: (/* @__PURE__ */ new Date()).toISOString() }),
850
+ style: screenshotPreviewStyles.continueButton,
851
+ disabled: isSubmitting,
852
+ children: [
853
+ /* @__PURE__ */ jsx(Check, { className: "w-4 h-4" }),
854
+ "Continue Without"
855
+ ]
856
+ }
857
+ )
858
+ ] })
859
+ ] }) });
860
+ }
861
+ const getError = (key) => {
862
+ return screenshots.errors?.[key];
863
+ };
553
864
  const allTabs = [
554
- { key: "element", label: "Element", data: screenshots.element },
865
+ {
866
+ key: "element",
867
+ label: "Element",
868
+ data: screenshots.element,
869
+ error: getError("element")
870
+ },
555
871
  {
556
872
  key: "container",
557
873
  label: "Container",
558
- data: screenshots.container
874
+ data: screenshots.container,
875
+ error: getError("container")
559
876
  },
560
- { key: "viewport", label: "Viewport", data: screenshots.viewport }
877
+ {
878
+ key: "viewport",
879
+ label: "Viewport",
880
+ data: screenshots.viewport,
881
+ error: getError("viewport")
882
+ }
561
883
  ];
562
- const tabs = allTabs.filter((tab) => tab.data);
884
+ const tabs = allTabs.filter((tab) => tab.data || tab.error);
563
885
  const activeScreenshot = activeTab === "element" ? screenshots.element : activeTab === "container" ? screenshots.container : screenshots.viewport;
886
+ const activeError = getError(activeTab);
564
887
  const totalSize = estimateTotalSize(screenshots);
888
+ console.log({ styles: screenshotPreviewStyles });
565
889
  return /* @__PURE__ */ jsxs(
566
890
  "div",
567
891
  {
568
892
  style: {
569
- ...styles.container,
570
- ...isExpanded ? styles.containerExpanded : {}
893
+ ...screenshotPreviewStyles.container,
894
+ ...isExpanded ? screenshotPreviewStyles.containerExpanded : {},
895
+ padding: "8px"
571
896
  },
572
897
  children: [
573
- /* @__PURE__ */ jsxs("div", { style: styles.header, children: [
574
- /* @__PURE__ */ jsx("span", { style: styles.headerTitle, children: "Screenshot Preview" }),
575
- /* @__PURE__ */ jsxs("div", { style: styles.headerActions, children: [
576
- /* @__PURE__ */ jsx("span", { style: styles.sizeLabel, children: formatBytes(totalSize) }),
898
+ /* @__PURE__ */ jsxs("div", { style: screenshotPreviewStyles.header, children: [
899
+ /* @__PURE__ */ jsx("span", { style: screenshotPreviewStyles.headerTitle, children: "Review Screenshots" }),
900
+ /* @__PURE__ */ jsxs("div", { style: screenshotPreviewStyles.headerActions, children: [
901
+ /* @__PURE__ */ jsx("span", { style: screenshotPreviewStyles.sizeLabel, children: formatBytes(totalSize) }),
577
902
  /* @__PURE__ */ jsx(
578
903
  "button",
579
904
  {
580
905
  type: "button",
581
906
  onClick: () => setIsExpanded(!isExpanded),
582
- style: styles.iconButton,
907
+ style: screenshotPreviewStyles.iconButton,
583
908
  title: isExpanded ? "Collapse" : "Expand",
584
909
  children: isExpanded ? /* @__PURE__ */ jsx(Shrink, { className: "w-4 h-4" }) : /* @__PURE__ */ jsx(Expand, { className: "w-4 h-4" })
585
910
  }
586
911
  )
587
912
  ] })
588
913
  ] }),
589
- /* @__PURE__ */ jsx("div", { style: styles.tabContainer, children: tabs.map((tab) => /* @__PURE__ */ jsxs(
914
+ /* @__PURE__ */ jsx("div", { style: screenshotPreviewStyles.tabContainer, children: tabs.map((tab) => /* @__PURE__ */ jsxs(
590
915
  "button",
591
916
  {
592
917
  type: "button",
593
918
  onClick: () => setActiveTab(tab.key),
594
919
  style: {
595
- ...styles.tab,
596
- ...activeTab === tab.key ? styles.tabActive : {}
920
+ ...screenshotPreviewStyles.tab,
921
+ ...activeTab === tab.key ? screenshotPreviewStyles.tabActive : {},
922
+ ...tab.error && !tab.data ? screenshotPreviewStyles.tabError : {}
597
923
  },
598
924
  children: [
925
+ tab.error && !tab.data && /* @__PURE__ */ jsx(
926
+ CircleAlert,
927
+ {
928
+ className: "w-3 h-3",
929
+ style: { color: "#ef4444" }
930
+ }
931
+ ),
599
932
  tab.label,
600
- tab.data && /* @__PURE__ */ jsx("span", { style: styles.tabSize, children: formatBytes(tab.data.sizeBytes) })
933
+ tab.data && /* @__PURE__ */ jsx("span", { style: screenshotPreviewStyles.tabSize, children: formatBytes(tab.data.sizeBytes) })
601
934
  ]
602
935
  },
603
936
  tab.key
@@ -606,17 +939,21 @@ function ScreenshotPreview({
606
939
  "div",
607
940
  {
608
941
  style: {
609
- ...styles.previewContainer,
610
- ...isExpanded ? styles.previewContainerExpanded : {}
942
+ ...screenshotPreviewStyles.previewContainer,
943
+ ...isExpanded ? screenshotPreviewStyles.previewContainerExpanded : {}
611
944
  },
612
945
  children: activeScreenshot ? /* @__PURE__ */ jsx(
613
946
  "img",
614
947
  {
615
948
  src: activeScreenshot.dataUrl,
616
949
  alt: `${activeTab} screenshot`,
617
- style: styles.previewImage
950
+ style: screenshotPreviewStyles.previewImage
618
951
  }
619
- ) : /* @__PURE__ */ jsxs("div", { style: styles.noPreview, children: [
952
+ ) : activeError ? /* @__PURE__ */ jsxs("div", { style: screenshotPreviewStyles.errorPreview, children: [
953
+ /* @__PURE__ */ jsx(CircleAlert, { className: "w-8 h-8", style: { color: "#ef4444" } }),
954
+ /* @__PURE__ */ jsx("span", { style: screenshotPreviewStyles.errorTitle, children: "Capture Failed" }),
955
+ /* @__PURE__ */ jsx("span", { style: screenshotPreviewStyles.errorMessage, children: activeError.message })
956
+ ] }) : /* @__PURE__ */ jsxs("div", { style: screenshotPreviewStyles.noPreview, children: [
620
957
  /* @__PURE__ */ jsx(Image, { className: "w-6 h-6", style: { color: "#9ca3af" } }),
621
958
  /* @__PURE__ */ jsxs("span", { children: [
622
959
  "No ",
@@ -626,27 +963,27 @@ function ScreenshotPreview({
626
963
  ] })
627
964
  }
628
965
  ),
629
- activeScreenshot && /* @__PURE__ */ jsxs("div", { style: styles.dimensionsInfo, children: [
966
+ activeScreenshot && /* @__PURE__ */ jsxs("div", { style: screenshotPreviewStyles.dimensionsInfo, children: [
630
967
  activeScreenshot.width,
631
968
  " \xD7 ",
632
969
  activeScreenshot.height,
633
970
  "px"
634
971
  ] }),
635
- /* @__PURE__ */ jsxs("div", { style: styles.actions, children: [
972
+ /* @__PURE__ */ jsxs("div", { style: screenshotPreviewStyles.actions, children: [
636
973
  /* @__PURE__ */ jsxs(
637
974
  "button",
638
975
  {
639
976
  type: "button",
640
977
  onClick: onRetake,
641
978
  disabled: isSubmitting,
642
- style: styles.retakeButtonSmall,
979
+ style: screenshotPreviewStyles.retakeButtonSmall,
643
980
  children: [
644
981
  /* @__PURE__ */ jsx(RefreshCw, { className: "w-3 h-3" }),
645
982
  "Retake"
646
983
  ]
647
984
  }
648
985
  ),
649
- /* @__PURE__ */ jsxs("div", { style: styles.actionsRight, children: [
986
+ /* @__PURE__ */ jsxs("div", { style: screenshotPreviewStyles.actionsRight, children: [
650
987
  /* @__PURE__ */ jsxs(
651
988
  "button",
652
989
  {
@@ -687,186 +1024,20 @@ function ScreenshotPreview({
687
1024
  }
688
1025
  );
689
1026
  }
690
- var styles = {
691
- container: {
692
- width: "100%",
693
- display: "flex",
694
- flexDirection: "column",
695
- gap: "8px"
696
- },
697
- containerExpanded: {
698
- position: "fixed",
699
- top: "50%",
700
- left: "50%",
701
- transform: "translate(-50%, -50%)",
702
- width: "90vw",
703
- maxWidth: "800px",
704
- maxHeight: "90vh",
705
- backgroundColor: "#fff",
706
- borderRadius: "12px",
707
- boxShadow: "0 25px 50px -12px rgba(0, 0, 0, 0.25)",
708
- padding: "16px",
709
- zIndex: 1e4
710
- },
711
- loadingContainer: {
712
- display: "flex",
713
- flexDirection: "column",
714
- alignItems: "center",
715
- justifyContent: "center",
716
- gap: "12px",
717
- padding: "24px"
718
- },
719
- loadingText: {
720
- fontSize: "13px",
721
- color: "#6b7280"
722
- },
723
- emptyContainer: {
724
- display: "flex",
725
- flexDirection: "column",
726
- alignItems: "center",
727
- justifyContent: "center",
728
- gap: "8px",
729
- padding: "24px"
730
- },
731
- emptyText: {
732
- fontSize: "13px",
733
- color: "#9ca3af"
734
- },
735
- header: {
736
- display: "flex",
737
- justifyContent: "space-between",
738
- alignItems: "center",
739
- padding: "0 4px"
740
- },
741
- headerTitle: {
742
- fontSize: "12px",
743
- fontWeight: "600",
744
- color: "#374151",
745
- textTransform: "uppercase",
746
- letterSpacing: "0.05em"
747
- },
748
- headerActions: {
749
- display: "flex",
750
- alignItems: "center",
751
- gap: "8px"
752
- },
753
- sizeLabel: {
754
- fontSize: "11px",
755
- color: "#9ca3af"
756
- },
757
- iconButton: {
758
- display: "flex",
759
- alignItems: "center",
760
- justifyContent: "center",
761
- width: "24px",
762
- height: "24px",
763
- border: "none",
764
- background: "transparent",
765
- borderRadius: "4px",
766
- cursor: "pointer",
767
- color: "#6b7280"
768
- },
769
- tabContainer: {
770
- display: "flex",
771
- gap: "4px",
772
- borderBottom: "1px solid #e5e7eb",
773
- paddingBottom: "8px"
774
- },
775
- tab: {
776
- display: "flex",
777
- alignItems: "center",
778
- gap: "4px",
779
- padding: "6px 10px",
780
- fontSize: "12px",
781
- color: "#6b7280",
782
- background: "transparent",
783
- border: "none",
784
- borderRadius: "4px",
785
- cursor: "pointer",
786
- transition: "all 0.15s ease"
787
- },
788
- tabActive: {
789
- backgroundColor: "#eff6ff",
790
- color: "#3b82f6",
791
- fontWeight: "500"
792
- },
793
- tabSize: {
794
- fontSize: "10px",
795
- color: "#9ca3af"
796
- },
797
- previewContainer: {
798
- position: "relative",
799
- width: "100%",
800
- height: "150px",
801
- backgroundColor: "#f9fafb",
802
- borderRadius: "8px",
803
- overflow: "hidden",
804
- display: "flex",
805
- alignItems: "center",
806
- justifyContent: "center"
807
- },
808
- previewContainerExpanded: {
809
- height: "60vh",
810
- maxHeight: "500px"
811
- },
812
- previewImage: {
813
- maxWidth: "100%",
814
- maxHeight: "100%",
815
- objectFit: "contain"
816
- },
817
- noPreview: {
818
- display: "flex",
819
- flexDirection: "column",
820
- alignItems: "center",
821
- gap: "8px",
822
- fontSize: "12px",
823
- color: "#9ca3af"
824
- },
825
- dimensionsInfo: {
826
- fontSize: "11px",
827
- color: "#9ca3af",
828
- textAlign: "center"
829
- },
830
- actions: {
831
- display: "flex",
832
- justifyContent: "space-between",
833
- alignItems: "center",
834
- paddingTop: "8px",
835
- borderTop: "1px solid #e5e7eb"
836
- },
837
- actionsRight: {
838
- display: "flex",
839
- gap: "8px"
840
- },
841
- retakeButton: {
842
- display: "flex",
843
- alignItems: "center",
844
- gap: "6px",
845
- padding: "8px 16px",
846
- fontSize: "13px",
847
- color: "#fff",
848
- backgroundColor: "#3b82f6",
849
- border: "none",
850
- borderRadius: "6px",
851
- cursor: "pointer",
852
- fontWeight: "500"
853
- },
854
- retakeButtonSmall: {
855
- display: "flex",
856
- alignItems: "center",
857
- gap: "4px",
858
- padding: "4px 8px",
859
- fontSize: "11px",
860
- color: "#6b7280",
861
- backgroundColor: "transparent",
862
- border: "1px solid #e5e7eb",
863
- borderRadius: "4px",
864
- cursor: "pointer"
865
- }
866
- };
867
1027
 
868
1028
  // src/ContextMenu.tsx
869
1029
  import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
1030
+ var VIEWPORT_PADDING = 10;
1031
+ var screenshotIndicatorStyle = {
1032
+ display: "flex",
1033
+ alignItems: "center",
1034
+ gap: "6px",
1035
+ padding: "8px 12px",
1036
+ fontSize: "11px",
1037
+ color: "var(--anyclick-menu-text-muted, #9ca3af)",
1038
+ borderTop: "1px solid var(--anyclick-menu-border, #f3f4f6)",
1039
+ marginTop: "4px"
1040
+ };
870
1041
  var defaultIcons = {
871
1042
  issue: /* @__PURE__ */ jsx2(Flag, { className: "w-4 h-4" }),
872
1043
  feature: /* @__PURE__ */ jsx2(Plus, { className: "w-4 h-4" }),
@@ -879,6 +1050,7 @@ function MenuItem({
879
1050
  hasChildren
880
1051
  }) {
881
1052
  const [isHovered, setIsHovered] = useState2(false);
1053
+ const [isPressed, setIsPressed] = useState2(false);
882
1054
  return /* @__PURE__ */ jsxs2(
883
1055
  "button",
884
1056
  {
@@ -886,11 +1058,25 @@ function MenuItem({
886
1058
  onClick,
887
1059
  disabled,
888
1060
  onMouseEnter: () => setIsHovered(true),
889
- onMouseLeave: () => setIsHovered(false),
1061
+ onMouseLeave: () => {
1062
+ setIsHovered(false);
1063
+ setIsPressed(false);
1064
+ },
1065
+ onTouchStart: () => setIsPressed(true),
1066
+ onTouchEnd: () => setIsPressed(false),
1067
+ onTouchCancel: () => setIsPressed(false),
890
1068
  style: {
891
1069
  ...menuStyles.item,
892
- ...isHovered ? menuStyles.itemHover : {},
893
- ...disabled ? { opacity: 0.5, cursor: "not-allowed" } : {}
1070
+ // Apply hover/pressed state for both mouse and touch
1071
+ ...isHovered || isPressed ? menuStyles.itemHover : {},
1072
+ ...disabled ? { opacity: 0.5, cursor: "not-allowed" } : {},
1073
+ // Ensure minimum touch target size (44px is recommended)
1074
+ minHeight: "44px",
1075
+ // Prevent text selection on touch
1076
+ WebkitUserSelect: "none",
1077
+ userSelect: "none",
1078
+ // Prevent touch callout on iOS
1079
+ WebkitTouchCallout: "none"
894
1080
  },
895
1081
  children: [
896
1082
  /* @__PURE__ */ jsx2("span", { style: menuStyles.itemIcon, children: item.icon ?? defaultIcons[item.type] }),
@@ -908,18 +1094,31 @@ function MenuItem({
908
1094
  }
909
1095
  function BackButton({ onClick }) {
910
1096
  const [isHovered, setIsHovered] = useState2(false);
1097
+ const [isPressed, setIsPressed] = useState2(false);
911
1098
  return /* @__PURE__ */ jsxs2(
912
1099
  "button",
913
1100
  {
914
1101
  type: "button",
915
1102
  onClick,
916
1103
  onMouseEnter: () => setIsHovered(true),
917
- onMouseLeave: () => setIsHovered(false),
1104
+ onMouseLeave: () => {
1105
+ setIsHovered(false);
1106
+ setIsPressed(false);
1107
+ },
1108
+ onTouchStart: () => setIsPressed(true),
1109
+ onTouchEnd: () => setIsPressed(false),
1110
+ onTouchCancel: () => setIsPressed(false),
918
1111
  style: {
919
1112
  ...menuStyles.item,
920
- ...isHovered ? menuStyles.itemHover : {},
1113
+ ...isHovered || isPressed ? menuStyles.itemHover : {},
921
1114
  borderBottom: "1px solid #e5e5e5",
922
- marginBottom: "4px"
1115
+ marginBottom: "4px",
1116
+ // Ensure minimum touch target size
1117
+ minHeight: "44px",
1118
+ // Prevent text selection on touch
1119
+ WebkitUserSelect: "none",
1120
+ userSelect: "none",
1121
+ WebkitTouchCallout: "none"
923
1122
  },
924
1123
  children: [
925
1124
  /* @__PURE__ */ jsx2(ChevronLeft, { className: "w-4 h-4", style: { opacity: 0.5 } }),
@@ -989,6 +1188,25 @@ function CommentForm({
989
1188
  ] })
990
1189
  ] });
991
1190
  }
1191
+ function calculateInViewPosition(requestedX, requestedY, menuWidth, menuHeight) {
1192
+ const viewportWidth = window.innerWidth;
1193
+ const viewportHeight = window.innerHeight;
1194
+ let x = requestedX;
1195
+ let y = requestedY;
1196
+ if (x + menuWidth > viewportWidth - VIEWPORT_PADDING) {
1197
+ x = viewportWidth - menuWidth - VIEWPORT_PADDING;
1198
+ }
1199
+ if (x < VIEWPORT_PADDING) {
1200
+ x = VIEWPORT_PADDING;
1201
+ }
1202
+ if (y + menuHeight > viewportHeight - VIEWPORT_PADDING) {
1203
+ y = viewportHeight - menuHeight - VIEWPORT_PADDING;
1204
+ }
1205
+ if (y < VIEWPORT_PADDING) {
1206
+ y = VIEWPORT_PADDING;
1207
+ }
1208
+ return { x, y };
1209
+ }
992
1210
  function ContextMenu({
993
1211
  visible,
994
1212
  position,
@@ -1001,7 +1219,8 @@ function ContextMenu({
1001
1219
  style,
1002
1220
  className,
1003
1221
  highlightConfig,
1004
- screenshotConfig
1222
+ screenshotConfig,
1223
+ positionMode = "inView"
1005
1224
  }) {
1006
1225
  const [selectedType, setSelectedType] = useState2(null);
1007
1226
  const [currentView, setCurrentView] = useState2("menu");
@@ -1010,6 +1229,13 @@ function ContextMenu({
1010
1229
  const [screenshots, setScreenshots] = useState2(null);
1011
1230
  const [isCapturing, setIsCapturing] = useState2(false);
1012
1231
  const menuRef = useRef(null);
1232
+ const [adjustedPosition, setAdjustedPosition] = useState2(position);
1233
+ const [isDragging, setIsDragging] = useState2(false);
1234
+ const [dragOffset, setDragOffset] = useState2({
1235
+ x: 0,
1236
+ y: 0
1237
+ });
1238
+ const dragStartRef = useRef(null);
1013
1239
  const mergedScreenshotConfig = {
1014
1240
  ...DEFAULT_SCREENSHOT_CONFIG,
1015
1241
  ...screenshotConfig
@@ -1041,6 +1267,9 @@ function ContextMenu({
1041
1267
  setSubmenuStack([]);
1042
1268
  setScreenshots(null);
1043
1269
  setIsCapturing(false);
1270
+ setIsDragging(false);
1271
+ setDragOffset({ x: 0, y: 0 });
1272
+ dragStartRef.current = null;
1044
1273
  }
1045
1274
  }, [visible]);
1046
1275
  useEffect(() => {
@@ -1061,6 +1290,9 @@ function ContextMenu({
1061
1290
  const handlePointerDown = (event) => {
1062
1291
  if (!menuRef.current) return;
1063
1292
  const target = event.target;
1293
+ if (target.closest?.("[data-drag-handle]")) {
1294
+ return;
1295
+ }
1064
1296
  if (!menuRef.current.contains(target)) {
1065
1297
  onClose();
1066
1298
  }
@@ -1071,24 +1303,71 @@ function ContextMenu({
1071
1303
  };
1072
1304
  }, [visible, onClose]);
1073
1305
  useEffect(() => {
1074
- if (visible && menuRef.current) {
1075
- const rect = menuRef.current.getBoundingClientRect();
1076
- const viewportWidth = window.innerWidth;
1077
- const viewportHeight = window.innerHeight;
1078
- let adjustedX = position.x;
1079
- let adjustedY = position.y;
1080
- if (position.x + rect.width > viewportWidth) {
1081
- adjustedX = viewportWidth - rect.width - 10;
1082
- }
1083
- if (position.y + rect.height > viewportHeight) {
1084
- adjustedY = viewportHeight - rect.height - 10;
1085
- }
1086
- if (adjustedX !== position.x || adjustedY !== position.y) {
1087
- menuRef.current.style.left = `${adjustedX}px`;
1088
- menuRef.current.style.top = `${adjustedY}px`;
1306
+ if (visible) {
1307
+ setAdjustedPosition(position);
1308
+ setDragOffset({ x: 0, y: 0 });
1309
+ }
1310
+ }, [visible, position.x, position.y]);
1311
+ useEffect(() => {
1312
+ if (!visible || !menuRef.current) return;
1313
+ const updatePosition = () => {
1314
+ const menuElement = menuRef.current;
1315
+ if (!menuElement) return;
1316
+ const rect = menuElement.getBoundingClientRect();
1317
+ const baseX = position.x + dragOffset.x;
1318
+ const baseY = position.y + dragOffset.y;
1319
+ if (positionMode === "static") {
1320
+ setAdjustedPosition({ x: baseX, y: baseY });
1321
+ } else if (positionMode === "inView" || positionMode === "dynamic") {
1322
+ const adjusted = calculateInViewPosition(
1323
+ baseX,
1324
+ baseY,
1325
+ rect.width,
1326
+ rect.height
1327
+ );
1328
+ setAdjustedPosition(adjusted);
1089
1329
  }
1330
+ };
1331
+ requestAnimationFrame(updatePosition);
1332
+ window.addEventListener("resize", updatePosition);
1333
+ return () => window.removeEventListener("resize", updatePosition);
1334
+ }, [visible, position, positionMode, dragOffset, currentView]);
1335
+ useEffect(() => {
1336
+ if (!visible || positionMode !== "dynamic") return;
1337
+ const handlePointerMove = (event) => {
1338
+ if (!isDragging || !dragStartRef.current) return;
1339
+ const deltaX = event.clientX - dragStartRef.current.x;
1340
+ const deltaY = event.clientY - dragStartRef.current.y;
1341
+ setDragOffset((prev) => ({
1342
+ x: prev.x + deltaX,
1343
+ y: prev.y + deltaY
1344
+ }));
1345
+ dragStartRef.current = { x: event.clientX, y: event.clientY };
1346
+ };
1347
+ const handlePointerUp = () => {
1348
+ setIsDragging(false);
1349
+ dragStartRef.current = null;
1350
+ };
1351
+ if (isDragging) {
1352
+ document.addEventListener("pointermove", handlePointerMove);
1353
+ document.addEventListener("pointerup", handlePointerUp);
1354
+ document.addEventListener("pointercancel", handlePointerUp);
1355
+ return () => {
1356
+ document.removeEventListener("pointermove", handlePointerMove);
1357
+ document.removeEventListener("pointerup", handlePointerUp);
1358
+ document.removeEventListener("pointercancel", handlePointerUp);
1359
+ };
1090
1360
  }
1091
- }, [visible, position]);
1361
+ }, [visible, positionMode, isDragging]);
1362
+ const handleDragStart = useCallback(
1363
+ (event) => {
1364
+ if (positionMode !== "dynamic") return;
1365
+ event.preventDefault();
1366
+ setIsDragging(true);
1367
+ dragStartRef.current = { x: event.clientX, y: event.clientY };
1368
+ },
1369
+ [positionMode]
1370
+ );
1092
1371
  useEffect(() => {
1093
1372
  const handleKeyDown = (e) => {
1094
1373
  if (e.key === "Escape") {
@@ -1110,6 +1389,23 @@ function ContextMenu({
1110
1389
  return () => document.removeEventListener("keydown", handleKeyDown);
1111
1390
  }
1112
1391
  }, [visible, currentView, submenuStack.length, onClose]);
1392
+ useEffect(() => {
1393
+ const menuElement = menuRef.current;
1394
+ if (!visible || !menuElement) return;
1395
+ const preventTouchDefault = (e) => {
1396
+ const target = e.target;
1397
+ if (target.tagName === "TEXTAREA" || target.tagName === "INPUT" || target.isContentEditable) {
1398
+ return;
1399
+ }
1400
+ e.preventDefault();
1401
+ };
1402
+ menuElement.addEventListener("touchmove", preventTouchDefault, {
1403
+ passive: false
1404
+ });
1405
+ return () => {
1406
+ menuElement.removeEventListener("touchmove", preventTouchDefault);
1407
+ };
1408
+ }, [visible]);
1113
1409
  if (!visible || !targetElement) {
1114
1410
  return null;
1115
1411
  }
@@ -1167,6 +1463,14 @@ function ContextMenu({
1167
1463
  captureScreenshots();
1168
1464
  };
1169
1465
  const containerWidth = currentView === "screenshot-preview" ? 360 : void 0;
1466
+ if (process.env.NODE_ENV === "development" && visible) {
1467
+ console.log("[ContextMenu] Style Debug", {
1468
+ styleExists: !!style,
1469
+ styleKeys: style ? Object.keys(style) : [],
1470
+ styleValues: style,
1471
+ className
1472
+ });
1473
+ }
1170
1474
  return /* @__PURE__ */ jsxs2(
1171
1475
  "div",
1172
1476
  {
@@ -1174,15 +1478,61 @@ function ContextMenu({
1174
1478
  className,
1175
1479
  style: {
1176
1480
  ...menuStyles.container,
1177
- left: position.x,
1178
- top: position.y,
1481
+ left: adjustedPosition.x,
1482
+ top: adjustedPosition.y,
1179
1483
  ...containerWidth ? { width: containerWidth, minWidth: containerWidth } : {},
1484
+ // Touch-specific styles
1485
+ WebkitUserSelect: "none",
1486
+ userSelect: "none",
1487
+ WebkitTouchCallout: "none",
1488
+ touchAction: "none",
1489
+ // Prevent default touch behaviors
1490
+ // Cursor style for dragging
1491
+ ...isDragging ? { cursor: "grabbing" } : {},
1180
1492
  ...style
1181
1493
  },
1182
1494
  role: "menu",
1183
1495
  "aria-label": "Feedback options",
1184
1496
  children: [
1185
- /* @__PURE__ */ jsx2("div", { style: menuStyles.header, children: currentView === "screenshot-preview" ? "Review Screenshots" : "Send Feedback" }),
1497
+ currentView !== "screenshot-preview" && /* @__PURE__ */ jsxs2(
1498
+ "div",
1499
+ {
1500
+ style: {
1501
+ ...menuStyles.header,
1502
+ display: "flex",
1503
+ alignItems: "center",
1504
+ justifyContent: "space-between"
1505
+ },
1506
+ children: [
1507
+ /* @__PURE__ */ jsx2("span", { children: "Send Feedback" }),
1508
+ positionMode === "dynamic" && /* @__PURE__ */ jsx2(
1509
+ "div",
1510
+ {
1511
+ "data-drag-handle": true,
1512
+ onPointerDown: handleDragStart,
1513
+ style: {
1514
+ cursor: isDragging ? "grabbing" : "grab",
1515
+ padding: "4px",
1516
+ marginRight: "-4px",
1517
+ borderRadius: "4px",
1518
+ display: "flex",
1519
+ alignItems: "center",
1520
+ opacity: 0.5,
1521
+ transition: "opacity 0.15s"
1522
+ },
1523
+ onMouseEnter: (e) => {
1524
+ e.currentTarget.style.opacity = "1";
1525
+ },
1526
+ onMouseLeave: (e) => {
1527
+ e.currentTarget.style.opacity = "0.5";
1528
+ },
1529
+ title: "Drag to move",
1530
+ children: /* @__PURE__ */ jsx2(GripVertical, { className: "w-4 h-4" })
1531
+ }
1532
+ )
1533
+ ]
1534
+ }
1535
+ ),
1186
1536
  currentView === "menu" && /* @__PURE__ */ jsxs2("div", { style: menuStyles.itemList, children: [
1187
1537
  submenuStack.length > 0 && /* @__PURE__ */ jsx2(BackButton, { onClick: handleBack }),
1188
1538
  currentItems.map((item) => /* @__PURE__ */ jsx2(
@@ -1223,25 +1573,203 @@ function ContextMenu({
1223
1573
  }
1224
1574
  );
1225
1575
  }
1226
- var screenshotIndicatorStyle = {
1227
- display: "flex",
1228
- alignItems: "center",
1229
- gap: "6px",
1230
- padding: "8px 12px",
1231
- fontSize: "11px",
1232
- color: "#9ca3af",
1233
- borderTop: "1px solid #f3f4f6",
1234
- marginTop: "4px"
1235
- };
1236
1576
 
1237
- // src/FeedbackProvider.tsx
1577
+ // src/store.ts
1578
+ import { create } from "zustand";
1579
+ var providerIdCounter = 0;
1580
+ function generateProviderId() {
1581
+ return `anyclick-provider-${++providerIdCounter}`;
1582
+ }
1583
+ var useProviderStore = create((set, get) => ({
1584
+ providers: /* @__PURE__ */ new Map(),
1585
+ registerProvider: (provider) => {
1586
+ set((state) => {
1587
+ const newProviders = new Map(state.providers);
1588
+ newProviders.set(provider.id, provider);
1589
+ return { providers: newProviders };
1590
+ });
1591
+ },
1592
+ unregisterProvider: (id) => {
1593
+ set((state) => {
1594
+ const newProviders = new Map(state.providers);
1595
+ newProviders.delete(id);
1596
+ return { providers: newProviders };
1597
+ });
1598
+ },
1599
+ updateProvider: (id, updates) => {
1600
+ set((state) => {
1601
+ const newProviders = new Map(state.providers);
1602
+ const existing = newProviders.get(id);
1603
+ if (existing) {
1604
+ newProviders.set(id, { ...existing, ...updates });
1605
+ }
1606
+ return { providers: newProviders };
1607
+ });
1608
+ },
1609
+ findProvidersForElement: (element) => {
1610
+ const { providers } = get();
1611
+ const matching = [];
1612
+ for (const provider of providers.values()) {
1613
+ if (provider.disabled) continue;
1614
+ const container = provider.containerRef.current;
1615
+ if (provider.scoped && container) {
1616
+ if (container.contains(element)) {
1617
+ matching.push(provider);
1618
+ }
1619
+ } else if (!provider.scoped) {
1620
+ matching.push(provider);
1621
+ }
1622
+ }
1623
+ return matching.sort((a, b) => b.depth - a.depth);
1624
+ },
1625
+ findParentProvider: (container, excludeId) => {
1626
+ const { providers } = get();
1627
+ if (!container) return null;
1628
+ let nearestParent = null;
1629
+ let nearestDepth = -1;
1630
+ for (const provider of providers.values()) {
1631
+ if (provider.id === excludeId) continue;
1632
+ const providerContainer = provider.containerRef.current;
1633
+ if (!providerContainer) continue;
1634
+ if (providerContainer.contains(container)) {
1635
+ if (provider.depth > nearestDepth) {
1636
+ nearestParent = provider;
1637
+ nearestDepth = provider.depth;
1638
+ }
1639
+ }
1640
+ }
1641
+ return nearestParent;
1642
+ },
1643
+ getMergedTheme: (providerId) => {
1644
+ const { providers } = get();
1645
+ const provider = providers.get(providerId);
1646
+ if (!provider) {
1647
+ if (process.env.NODE_ENV === "development") {
1648
+ console.log(`[Store:getMergedTheme] Provider not found: ${providerId}`);
1649
+ }
1650
+ return {};
1651
+ }
1652
+ const ancestors = [];
1653
+ let current = provider;
1654
+ while (current) {
1655
+ ancestors.unshift(current);
1656
+ current = current.parentId ? providers.get(current.parentId) : void 0;
1657
+ }
1658
+ if (process.env.NODE_ENV === "development") {
1659
+ console.log(`[Store:getMergedTheme] Provider: ${providerId}`, {
1660
+ ancestorCount: ancestors.length,
1661
+ ancestorIds: ancestors.map((a) => a.id),
1662
+ providerHasTheme: !!provider.theme,
1663
+ providerThemeMenuStyle: provider.theme?.menuStyle ? Object.keys(provider.theme.menuStyle) : []
1664
+ });
1665
+ }
1666
+ const mergedTheme = {};
1667
+ for (const ancestor of ancestors) {
1668
+ if (ancestor.theme) {
1669
+ Object.assign(mergedTheme, ancestor.theme);
1670
+ if (ancestor.theme.highlightConfig) {
1671
+ mergedTheme.highlightConfig = {
1672
+ ...mergedTheme.highlightConfig,
1673
+ ...ancestor.theme.highlightConfig,
1674
+ colors: {
1675
+ ...mergedTheme.highlightConfig?.colors,
1676
+ ...ancestor.theme.highlightConfig.colors
1677
+ }
1678
+ };
1679
+ }
1680
+ if (ancestor.theme.screenshotConfig) {
1681
+ mergedTheme.screenshotConfig = {
1682
+ ...mergedTheme.screenshotConfig,
1683
+ ...ancestor.theme.screenshotConfig
1684
+ };
1685
+ }
1686
+ }
1687
+ }
1688
+ if (process.env.NODE_ENV === "development") {
1689
+ console.log(`[Store:getMergedTheme] Result for ${providerId}`, {
1690
+ hasMenuStyle: !!mergedTheme.menuStyle,
1691
+ menuStyleKeys: mergedTheme.menuStyle ? Object.keys(mergedTheme.menuStyle) : []
1692
+ });
1693
+ }
1694
+ return mergedTheme;
1695
+ },
1696
+ isDisabledByAncestor: (providerId) => {
1697
+ const { providers } = get();
1698
+ const provider = providers.get(providerId);
1699
+ if (!provider) return false;
1700
+ let current = provider;
1701
+ while (current) {
1702
+ if (current.disabled || current.theme?.disabled) {
1703
+ return true;
1704
+ }
1705
+ current = current.parentId ? providers.get(current.parentId) : void 0;
1706
+ }
1707
+ return false;
1708
+ },
1709
+ isElementInDisabledScope: (element) => {
1710
+ const { providers } = get();
1711
+ for (const provider of providers.values()) {
1712
+ if (!provider.scoped) continue;
1713
+ if (!provider.disabled && !provider.theme?.disabled) continue;
1714
+ const container = provider.containerRef.current;
1715
+ if (!container) continue;
1716
+ if (container.contains(element)) {
1717
+ return true;
1718
+ }
1719
+ }
1720
+ return false;
1721
+ },
1722
+ isElementInAnyScopedProvider: (element) => {
1723
+ const { providers } = get();
1724
+ for (const provider of providers.values()) {
1725
+ if (!provider.scoped) continue;
1726
+ const container = provider.containerRef.current;
1727
+ if (!container) continue;
1728
+ if (container.contains(element)) {
1729
+ if (process.env.NODE_ENV === "development") {
1730
+ console.log(
1731
+ `[Store:isElementInAnyScopedProvider] Element is in scoped provider: ${provider.id}`,
1732
+ {
1733
+ elementTag: element.tagName,
1734
+ providerId: provider.id,
1735
+ providerDisabled: provider.disabled
1736
+ }
1737
+ );
1738
+ }
1739
+ return true;
1740
+ }
1741
+ }
1742
+ return false;
1743
+ }
1744
+ }));
1745
+ function dispatchContextMenuEvent(event, element) {
1746
+ const store = useProviderStore.getState();
1747
+ const providers = store.findProvidersForElement(element);
1748
+ const menuEvent = {
1749
+ clientX: event.clientX,
1750
+ clientY: event.clientY,
1751
+ originalEvent: event,
1752
+ isTouch: false
1753
+ };
1754
+ for (const provider of providers) {
1755
+ if (store.isDisabledByAncestor(provider.id)) {
1756
+ continue;
1757
+ }
1758
+ if (provider.onContextMenu) {
1759
+ provider.onContextMenu(menuEvent, element);
1760
+ break;
1761
+ }
1762
+ }
1763
+ }
1764
+
1765
+ // src/AnyclickProvider.tsx
1238
1766
  import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
1239
1767
  var defaultMenuItems = [
1240
1768
  { type: "issue", label: "Report an issue", showComment: true },
1241
1769
  { type: "feature", label: "Request a feature", showComment: true },
1242
1770
  { type: "like", label: "I like this!", showComment: false }
1243
1771
  ];
1244
- function FeedbackProvider({
1772
+ function AnyclickProvider({
1245
1773
  adapter,
1246
1774
  children,
1247
1775
  targetFilter,
@@ -1258,7 +1786,11 @@ function FeedbackProvider({
1258
1786
  menuClassName,
1259
1787
  disabled = false,
1260
1788
  highlightConfig,
1261
- screenshotConfig
1789
+ screenshotConfig,
1790
+ scoped = false,
1791
+ theme,
1792
+ touchHoldDurationMs,
1793
+ touchMoveThreshold
1262
1794
  }) {
1263
1795
  const [isSubmitting, setIsSubmitting] = useState3(false);
1264
1796
  const [menuVisible, setMenuVisible] = useState3(false);
@@ -1267,8 +1799,203 @@ function FeedbackProvider({
1267
1799
  const [containerElement, setContainerElement] = useState3(
1268
1800
  null
1269
1801
  );
1802
+ const providerIdRef = useRef2(generateProviderId());
1803
+ const providerId = providerIdRef.current;
1804
+ const containerRef = useRef2(null);
1805
+ const [containerReady, setContainerReady] = useState3(!scoped);
1270
1806
  const clientRef = useRef2(null);
1807
+ const setContainerRef = useCallback2(
1808
+ (node) => {
1809
+ if (process.env.NODE_ENV === "development") {
1810
+ console.log(`[AnyclickProvider:${providerId}] Container ref callback`, {
1811
+ scoped,
1812
+ nodeReceived: !!node,
1813
+ hadPreviousNode: !!containerRef.current,
1814
+ clientExists: !!clientRef.current
1815
+ });
1816
+ }
1817
+ containerRef.current = node;
1818
+ if (scoped && node) {
1819
+ setContainerReady(true);
1820
+ if (clientRef.current) {
1821
+ clientRef.current.setContainer(node);
1822
+ }
1823
+ }
1824
+ },
1825
+ [scoped, providerId]
1826
+ );
1827
+ let parentContext = null;
1828
+ try {
1829
+ parentContext = useAnyclick();
1830
+ } catch {
1831
+ parentContext = null;
1832
+ }
1833
+ const {
1834
+ registerProvider,
1835
+ unregisterProvider,
1836
+ updateProvider,
1837
+ getMergedTheme,
1838
+ isDisabledByAncestor,
1839
+ findParentProvider,
1840
+ isElementInDisabledScope,
1841
+ isElementInAnyScopedProvider
1842
+ } = useProviderStore();
1843
+ const parentId = parentContext?.providerId ?? null;
1844
+ const depth = parentContext ? parentContext.scoped ? 1 : 0 : 0;
1845
+ const actualDepth = useMemo(() => {
1846
+ if (!parentContext) return 0;
1847
+ let d = 0;
1848
+ let currentId = parentId;
1849
+ const providers = useProviderStore.getState().providers;
1850
+ while (currentId) {
1851
+ d++;
1852
+ const parent = providers.get(currentId);
1853
+ currentId = parent?.parentId ?? null;
1854
+ }
1855
+ return d;
1856
+ }, [parentId]);
1857
+ const isDisabledByTheme = theme === null || theme?.disabled === true;
1858
+ const effectiveDisabled = disabled || isDisabledByTheme;
1859
+ const localTheme = useMemo(() => {
1860
+ if (theme === null) {
1861
+ return { disabled: true };
1862
+ }
1863
+ const explicitThemeProps = {};
1864
+ if (menuStyle) explicitThemeProps.menuStyle = menuStyle;
1865
+ if (menuClassName) explicitThemeProps.menuClassName = menuClassName;
1866
+ if (highlightConfig) explicitThemeProps.highlightConfig = highlightConfig;
1867
+ if (screenshotConfig)
1868
+ explicitThemeProps.screenshotConfig = screenshotConfig;
1869
+ return {
1870
+ ...explicitThemeProps,
1871
+ ...theme
1872
+ };
1873
+ }, [theme, menuStyle, menuClassName, highlightConfig, screenshotConfig]);
1874
+ const handleContextMenu = useCallback2(
1875
+ (event, element) => {
1876
+ if (!scoped && isElementInDisabledScope(element)) {
1877
+ if (process.env.NODE_ENV === "development") {
1878
+ console.log(
1879
+ `[AnyclickProvider:${providerId}] Allowing native menu - element is in disabled scope`,
1880
+ {
1881
+ targetTag: element.tagName
1882
+ }
1883
+ );
1884
+ }
1885
+ return false;
1886
+ }
1887
+ if (!scoped && event.isTouch && isElementInAnyScopedProvider(element)) {
1888
+ if (process.env.NODE_ENV === "development") {
1889
+ console.log(
1890
+ `[AnyclickProvider:${providerId}] Deferring to scoped provider for touch event`,
1891
+ {
1892
+ targetTag: element.tagName
1893
+ }
1894
+ );
1895
+ }
1896
+ return false;
1897
+ }
1898
+ const mergedTheme2 = getMergedTheme(providerId);
1899
+ if (process.env.NODE_ENV === "development") {
1900
+ console.log(
1901
+ `[AnyclickProvider:${providerId}] handleContextMenu called`,
1902
+ {
1903
+ scoped,
1904
+ targetTag: element.tagName,
1905
+ mergedThemeColors: mergedTheme2.highlightConfig?.colors,
1906
+ position: { x: event.clientX, y: event.clientY },
1907
+ isTouch: event.isTouch
1908
+ }
1909
+ );
1910
+ }
1911
+ setTargetElement(element);
1912
+ const container = findContainerParent(
1913
+ element,
1914
+ mergedTheme2.highlightConfig ?? highlightConfig
1915
+ );
1916
+ setContainerElement(container);
1917
+ setMenuPosition({ x: event.clientX, y: event.clientY });
1918
+ setMenuVisible(true);
1919
+ return true;
1920
+ },
1921
+ [
1922
+ providerId,
1923
+ getMergedTheme,
1924
+ highlightConfig,
1925
+ scoped,
1926
+ isElementInDisabledScope,
1927
+ isElementInAnyScopedProvider
1928
+ ]
1929
+ );
1930
+ useLayoutEffect(() => {
1931
+ const providerInstance = {
1932
+ id: providerId,
1933
+ containerRef,
1934
+ scoped,
1935
+ theme: localTheme,
1936
+ disabled: effectiveDisabled,
1937
+ parentId,
1938
+ depth: actualDepth,
1939
+ onContextMenu: handleContextMenu
1940
+ };
1941
+ registerProvider(providerInstance);
1942
+ return () => {
1943
+ unregisterProvider(providerId);
1944
+ };
1945
+ }, [
1946
+ providerId,
1947
+ scoped,
1948
+ localTheme,
1949
+ effectiveDisabled,
1950
+ parentId,
1951
+ actualDepth,
1952
+ handleContextMenu,
1953
+ registerProvider,
1954
+ unregisterProvider
1955
+ ]);
1271
1956
  useEffect2(() => {
1957
+ updateProvider(providerId, {
1958
+ theme: localTheme,
1959
+ disabled: effectiveDisabled,
1960
+ onContextMenu: handleContextMenu
1961
+ });
1962
+ }, [
1963
+ providerId,
1964
+ localTheme,
1965
+ effectiveDisabled,
1966
+ handleContextMenu,
1967
+ updateProvider
1968
+ ]);
1969
+ useEffect2(() => {
1970
+ if (isDisabledByAncestor(providerId)) {
1971
+ if (process.env.NODE_ENV === "development") {
1972
+ console.log(
1973
+ `[AnyclickProvider:${providerId}] Skipping - disabled by ancestor`
1974
+ );
1975
+ }
1976
+ return;
1977
+ }
1978
+ if (scoped && !containerReady) {
1979
+ if (process.env.NODE_ENV === "development") {
1980
+ console.log(
1981
+ `[AnyclickProvider:${providerId}] Waiting for container to be ready`,
1982
+ {
1983
+ scoped,
1984
+ containerReady
1985
+ }
1986
+ );
1987
+ }
1988
+ return;
1989
+ }
1990
+ if (process.env.NODE_ENV === "development") {
1991
+ console.log(`[AnyclickProvider:${providerId}] Creating client`, {
1992
+ scoped,
1993
+ containerReady,
1994
+ hasContainer: !!containerRef.current,
1995
+ effectiveDisabled,
1996
+ localThemeColors: localTheme.highlightConfig?.colors
1997
+ });
1998
+ }
1272
1999
  const client = createFeedbackClient({
1273
2000
  adapter,
1274
2001
  targetFilter,
@@ -1276,19 +2003,17 @@ function FeedbackProvider({
1276
2003
  maxOuterHTMLLength,
1277
2004
  maxAncestors,
1278
2005
  cooldownMs,
1279
- stripAttributes
2006
+ stripAttributes,
2007
+ // For scoped providers, pass the container
2008
+ container: scoped ? containerRef.current : null,
2009
+ touchHoldDurationMs,
2010
+ touchMoveThreshold
1280
2011
  });
1281
2012
  client.onSubmitSuccess = onSubmitSuccess;
1282
2013
  client.onSubmitError = onSubmitError;
1283
- client.onContextMenu = (event, element) => {
1284
- setTargetElement(element);
1285
- const container = findContainerParent(element, highlightConfig);
1286
- setContainerElement(container);
1287
- setMenuPosition({ x: event.clientX, y: event.clientY });
1288
- setMenuVisible(true);
1289
- };
2014
+ client.onContextMenu = handleContextMenu;
1290
2015
  clientRef.current = client;
1291
- if (!disabled) {
2016
+ if (!effectiveDisabled) {
1292
2017
  client.attach();
1293
2018
  }
1294
2019
  return () => {
@@ -1304,8 +2029,14 @@ function FeedbackProvider({
1304
2029
  stripAttributes,
1305
2030
  onSubmitSuccess,
1306
2031
  onSubmitError,
1307
- disabled,
1308
- highlightConfig
2032
+ effectiveDisabled,
2033
+ scoped,
2034
+ containerReady,
2035
+ providerId,
2036
+ isDisabledByAncestor,
2037
+ handleContextMenu,
2038
+ touchHoldDurationMs,
2039
+ touchMoveThreshold
1309
2040
  ]);
1310
2041
  const submitFeedback = useCallback2(
1311
2042
  async (element, type, comment, screenshots) => {
@@ -1330,12 +2061,16 @@ function FeedbackProvider({
1330
2061
  const openMenu = useCallback2(
1331
2062
  (element, position) => {
1332
2063
  setTargetElement(element);
1333
- const container = findContainerParent(element, highlightConfig);
2064
+ const mergedTheme2 = getMergedTheme(providerId);
2065
+ const container = findContainerParent(
2066
+ element,
2067
+ mergedTheme2.highlightConfig ?? highlightConfig
2068
+ );
1334
2069
  setContainerElement(container);
1335
2070
  setMenuPosition(position);
1336
2071
  setMenuVisible(true);
1337
2072
  },
1338
- [highlightConfig]
2073
+ [providerId, getMergedTheme, highlightConfig]
1339
2074
  );
1340
2075
  const closeMenu = useCallback2(() => {
1341
2076
  setMenuVisible(false);
@@ -1350,22 +2085,73 @@ function FeedbackProvider({
1350
2085
  },
1351
2086
  [targetElement, submitFeedback]
1352
2087
  );
2088
+ const inheritedTheme = getMergedTheme(providerId);
2089
+ const mergedTheme = useMemo(
2090
+ () => ({
2091
+ ...inheritedTheme,
2092
+ ...localTheme,
2093
+ // Deep merge for nested configs
2094
+ highlightConfig: {
2095
+ ...inheritedTheme.highlightConfig,
2096
+ ...localTheme.highlightConfig,
2097
+ colors: {
2098
+ ...inheritedTheme.highlightConfig?.colors,
2099
+ ...localTheme.highlightConfig?.colors
2100
+ }
2101
+ },
2102
+ screenshotConfig: {
2103
+ ...inheritedTheme.screenshotConfig,
2104
+ ...localTheme.screenshotConfig
2105
+ }
2106
+ }),
2107
+ [inheritedTheme, localTheme]
2108
+ );
2109
+ const effectiveMenuStyle = mergedTheme.menuStyle ?? menuStyle;
2110
+ const effectiveMenuClassName = mergedTheme.menuClassName ?? menuClassName;
2111
+ if (process.env.NODE_ENV === "development") {
2112
+ console.log(`[AnyclickProvider:${providerId}] Theme Debug`, {
2113
+ scoped,
2114
+ localThemeHasMenuStyle: !!localTheme.menuStyle,
2115
+ localThemeMenuStyleKeys: localTheme.menuStyle ? Object.keys(localTheme.menuStyle) : [],
2116
+ mergedThemeHasMenuStyle: !!mergedTheme.menuStyle,
2117
+ mergedThemeMenuStyleKeys: mergedTheme.menuStyle ? Object.keys(mergedTheme.menuStyle) : [],
2118
+ effectiveMenuStyleExists: !!effectiveMenuStyle,
2119
+ effectiveMenuStyleKeys: effectiveMenuStyle ? Object.keys(effectiveMenuStyle) : [],
2120
+ menuStyleProp: !!menuStyle
2121
+ });
2122
+ }
2123
+ const effectiveHighlightConfig = mergedTheme.highlightConfig ?? highlightConfig;
2124
+ const effectiveScreenshotConfig = mergedTheme.screenshotConfig ?? screenshotConfig;
1353
2125
  const contextValue = useMemo(
1354
2126
  () => ({
1355
- isEnabled: !disabled,
2127
+ isEnabled: !effectiveDisabled && !isDisabledByAncestor(providerId),
1356
2128
  isSubmitting,
1357
2129
  submitFeedback,
1358
2130
  openMenu,
1359
- closeMenu
2131
+ closeMenu,
2132
+ theme: mergedTheme,
2133
+ scoped,
2134
+ providerId
1360
2135
  }),
1361
- [disabled, isSubmitting, submitFeedback, openMenu, closeMenu]
2136
+ [
2137
+ effectiveDisabled,
2138
+ providerId,
2139
+ isDisabledByAncestor,
2140
+ isSubmitting,
2141
+ submitFeedback,
2142
+ openMenu,
2143
+ closeMenu,
2144
+ mergedTheme,
2145
+ scoped
2146
+ ]
1362
2147
  );
1363
- return /* @__PURE__ */ jsxs3(FeedbackContext.Provider, { value: contextValue, children: [
1364
- children,
2148
+ const content = scoped ? /* @__PURE__ */ jsx3("div", { ref: setContainerRef, style: { display: "contents" }, children }) : children;
2149
+ return /* @__PURE__ */ jsxs3(AnyclickContext.Provider, { value: contextValue, children: [
2150
+ content,
1365
2151
  /* @__PURE__ */ jsx3(
1366
2152
  ContextMenu,
1367
2153
  {
1368
- visible: menuVisible && !disabled,
2154
+ visible: menuVisible && !effectiveDisabled,
1369
2155
  position: menuPosition,
1370
2156
  targetElement,
1371
2157
  containerElement,
@@ -1373,14 +2159,15 @@ function FeedbackProvider({
1373
2159
  onSelect: handleMenuSelect,
1374
2160
  onClose: closeMenu,
1375
2161
  isSubmitting,
1376
- style: menuStyle,
1377
- className: menuClassName,
1378
- highlightConfig,
1379
- screenshotConfig
2162
+ style: effectiveMenuStyle,
2163
+ className: effectiveMenuClassName,
2164
+ highlightConfig: effectiveHighlightConfig,
2165
+ screenshotConfig: effectiveScreenshotConfig
1380
2166
  }
1381
2167
  )
1382
2168
  ] });
1383
2169
  }
2170
+ var FeedbackProvider = AnyclickProvider;
1384
2171
 
1385
2172
  // src/types.ts
1386
2173
  function filterMenuItemsByRole(items, userContext) {
@@ -1409,6 +2196,8 @@ import {
1409
2196
  DEFAULT_SENSITIVE_SELECTORS
1410
2197
  } from "@ewjdev/anyclick-core";
1411
2198
  export {
2199
+ AnyclickContext,
2200
+ AnyclickProvider,
1412
2201
  ContextMenu,
1413
2202
  DEFAULT_SCREENSHOT_CONFIG2 as DEFAULT_SCREENSHOT_CONFIG,
1414
2203
  DEFAULT_SENSITIVE_SELECTORS,
@@ -1422,15 +2211,20 @@ export {
1422
2211
  darkMenuStyles,
1423
2212
  defaultContainerSelectors,
1424
2213
  defaultHighlightColors,
2214
+ dispatchContextMenuEvent,
1425
2215
  estimateTotalSize2 as estimateTotalSize,
1426
2216
  filterMenuItemsByRole,
1427
2217
  findContainerParent,
1428
2218
  formatBytes2 as formatBytes,
2219
+ generateProviderId,
1429
2220
  highlightContainer,
1430
2221
  highlightTarget,
1431
2222
  isScreenshotSupported2 as isScreenshotSupported,
2223
+ menuCSSVariables,
1432
2224
  menuStyles,
1433
- useFeedback
2225
+ useAnyclick,
2226
+ useFeedback,
2227
+ useProviderStore
1434
2228
  };
1435
2229
  /*! Bundled license information:
1436
2230
 
@@ -1442,8 +2236,10 @@ lucide-react/dist/esm/icons/camera.js:
1442
2236
  lucide-react/dist/esm/icons/check.js:
1443
2237
  lucide-react/dist/esm/icons/chevron-left.js:
1444
2238
  lucide-react/dist/esm/icons/chevron-right.js:
2239
+ lucide-react/dist/esm/icons/circle-alert.js:
1445
2240
  lucide-react/dist/esm/icons/expand.js:
1446
2241
  lucide-react/dist/esm/icons/flag.js:
2242
+ lucide-react/dist/esm/icons/grip-vertical.js:
1447
2243
  lucide-react/dist/esm/icons/image.js:
1448
2244
  lucide-react/dist/esm/icons/loader-circle.js:
1449
2245
  lucide-react/dist/esm/icons/plus.js: