@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.js CHANGED
@@ -21,6 +21,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  // src/index.ts
22
22
  var index_exports = {};
23
23
  __export(index_exports, {
24
+ AnyclickContext: () => AnyclickContext,
25
+ AnyclickProvider: () => AnyclickProvider,
24
26
  ContextMenu: () => ContextMenu,
25
27
  DEFAULT_SCREENSHOT_CONFIG: () => import_anyclick_core4.DEFAULT_SCREENSHOT_CONFIG,
26
28
  DEFAULT_SENSITIVE_SELECTORS: () => import_anyclick_core4.DEFAULT_SENSITIVE_SELECTORS,
@@ -34,27 +36,40 @@ __export(index_exports, {
34
36
  darkMenuStyles: () => darkMenuStyles,
35
37
  defaultContainerSelectors: () => defaultContainerSelectors,
36
38
  defaultHighlightColors: () => defaultHighlightColors,
39
+ dispatchContextMenuEvent: () => dispatchContextMenuEvent,
37
40
  estimateTotalSize: () => import_anyclick_core4.estimateTotalSize,
38
41
  filterMenuItemsByRole: () => filterMenuItemsByRole,
39
42
  findContainerParent: () => findContainerParent,
40
43
  formatBytes: () => import_anyclick_core4.formatBytes,
44
+ generateProviderId: () => generateProviderId,
41
45
  highlightContainer: () => highlightContainer,
42
46
  highlightTarget: () => highlightTarget,
43
47
  isScreenshotSupported: () => import_anyclick_core4.isScreenshotSupported,
48
+ menuCSSVariables: () => menuCSSVariables,
44
49
  menuStyles: () => menuStyles,
45
- useFeedback: () => useFeedback
50
+ useAnyclick: () => useAnyclick,
51
+ useFeedback: () => useFeedback,
52
+ useProviderStore: () => useProviderStore
46
53
  });
47
54
  module.exports = __toCommonJS(index_exports);
48
55
 
49
- // src/FeedbackProvider.tsx
56
+ // src/AnyclickProvider.tsx
50
57
  var import_react6 = require("react");
51
58
  var import_anyclick_core3 = require("@ewjdev/anyclick-core");
52
59
 
53
60
  // src/context.ts
54
61
  var import_react = require("react");
55
- var FeedbackContext = (0, import_react.createContext)(null);
62
+ var AnyclickContext = (0, import_react.createContext)(null);
63
+ var FeedbackContext = AnyclickContext;
64
+ function useAnyclick() {
65
+ const context = (0, import_react.useContext)(AnyclickContext);
66
+ if (!context) {
67
+ throw new Error("useAnyclick must be used within an AnyclickProvider");
68
+ }
69
+ return context;
70
+ }
56
71
  function useFeedback() {
57
- const context = (0, import_react.useContext)(FeedbackContext);
72
+ const context = (0, import_react.useContext)(AnyclickContext);
58
73
  if (!context) {
59
74
  throw new Error("useFeedback must be used within a FeedbackProvider");
60
75
  }
@@ -66,6 +81,25 @@ var import_react5 = require("react");
66
81
  var import_anyclick_core2 = require("@ewjdev/anyclick-core");
67
82
 
68
83
  // src/styles.ts
84
+ var menuCSSVariables = {
85
+ // Background colors
86
+ "--anyclick-menu-bg": "#ffffff",
87
+ "--anyclick-menu-hover": "#f5f5f5",
88
+ // Text colors
89
+ "--anyclick-menu-text": "#333333",
90
+ "--anyclick-menu-text-muted": "#666666",
91
+ // Border colors
92
+ "--anyclick-menu-border": "#e5e5e5",
93
+ // Accent/action colors
94
+ "--anyclick-menu-accent": "#0066cc",
95
+ "--anyclick-menu-accent-text": "#ffffff",
96
+ // Input colors
97
+ "--anyclick-menu-input-bg": "#ffffff",
98
+ "--anyclick-menu-input-border": "#dddddd",
99
+ // Cancel button
100
+ "--anyclick-menu-cancel-bg": "#f0f0f0",
101
+ "--anyclick-menu-cancel-text": "#666666"
102
+ };
69
103
  var menuStyles = {
70
104
  overlay: {
71
105
  position: "fixed",
@@ -76,17 +110,19 @@ var menuStyles = {
76
110
  position: "fixed",
77
111
  zIndex: 9999,
78
112
  minWidth: "200px",
79
- backgroundColor: "#ffffff",
113
+ backgroundColor: "var(--anyclick-menu-bg, #ffffff)",
80
114
  borderRadius: "8px",
81
115
  boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15), 0 0 0 1px rgba(0, 0, 0, 0.05)",
82
116
  overflow: "hidden",
83
117
  fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
84
- fontSize: "14px"
118
+ fontSize: "14px",
119
+ // Set default CSS variables
120
+ ...menuCSSVariables
85
121
  },
86
122
  header: {
87
123
  padding: "12px 16px",
88
- borderBottom: "1px solid #e5e5e5",
89
- color: "#666",
124
+ borderBottom: "1px solid var(--anyclick-menu-border, #e5e5e5)",
125
+ color: "var(--anyclick-menu-text-muted, #666)",
90
126
  fontSize: "12px",
91
127
  fontWeight: 500,
92
128
  textTransform: "uppercase",
@@ -102,15 +138,15 @@ var menuStyles = {
102
138
  padding: "10px 16px",
103
139
  cursor: "pointer",
104
140
  transition: "background-color 0.15s",
105
- color: "#333",
141
+ color: "var(--anyclick-menu-text, #333)",
106
142
  border: "none",
107
- background: "none",
143
+ backgroundColor: "transparent",
108
144
  width: "100%",
109
145
  textAlign: "left",
110
146
  fontSize: "14px"
111
147
  },
112
148
  itemHover: {
113
- backgroundColor: "#f5f5f5"
149
+ backgroundColor: "var(--anyclick-menu-hover, #f5f5f5)"
114
150
  },
115
151
  itemIcon: {
116
152
  display: "flex",
@@ -122,19 +158,21 @@ var menuStyles = {
122
158
  },
123
159
  commentSection: {
124
160
  padding: "12px 16px",
125
- borderTop: "1px solid #e5e5e5"
161
+ borderTop: "1px solid var(--anyclick-menu-border, #e5e5e5)"
126
162
  },
127
163
  commentInput: {
128
164
  width: "100%",
129
165
  minHeight: "60px",
130
166
  padding: "8px 12px",
131
- border: "1px solid #ddd",
167
+ border: "1px solid var(--anyclick-menu-input-border, #ddd)",
132
168
  borderRadius: "6px",
133
169
  fontSize: "14px",
134
170
  fontFamily: "inherit",
135
171
  resize: "vertical",
136
172
  outline: "none",
137
- boxSizing: "border-box"
173
+ boxSizing: "border-box",
174
+ backgroundColor: "var(--anyclick-menu-input-bg, #ffffff)",
175
+ color: "var(--anyclick-menu-text, #333)"
138
176
  },
139
177
  buttonRow: {
140
178
  display: "flex",
@@ -152,15 +190,15 @@ var menuStyles = {
152
190
  border: "none"
153
191
  },
154
192
  cancelButton: {
155
- backgroundColor: "#f0f0f0",
156
- color: "#666",
193
+ backgroundColor: "var(--anyclick-menu-cancel-bg, #f0f0f0)",
194
+ color: "var(--anyclick-menu-cancel-text, #666)",
157
195
  display: "flex",
158
196
  alignItems: "center",
159
197
  gap: "2px"
160
198
  },
161
199
  submitButton: {
162
- backgroundColor: "#0066cc",
163
- color: "#ffffff",
200
+ backgroundColor: "var(--anyclick-menu-accent, #0066cc)",
201
+ color: "var(--anyclick-menu-accent-text, #ffffff)",
164
202
  display: "flex",
165
203
  alignItems: "center",
166
204
  gap: "2px"
@@ -204,6 +242,242 @@ var darkMenuStyles = {
204
242
  color: "#ccc"
205
243
  }
206
244
  };
245
+ var screenshotPreviewStyles = {
246
+ container: {
247
+ width: "100%",
248
+ display: "flex",
249
+ flexDirection: "column",
250
+ gap: "8px"
251
+ },
252
+ containerExpanded: {
253
+ position: "fixed",
254
+ top: "50%",
255
+ left: "50%",
256
+ transform: "translate(-50%, -50%)",
257
+ width: "90vw",
258
+ maxWidth: "800px",
259
+ maxHeight: "90vh",
260
+ backgroundColor: "#fff",
261
+ borderRadius: "12px",
262
+ boxShadow: "0 25px 50px -12px rgba(0, 0, 0, 0.25)",
263
+ padding: "16px",
264
+ zIndex: 1e4
265
+ },
266
+ loadingContainer: {
267
+ display: "flex",
268
+ flexDirection: "column",
269
+ alignItems: "center",
270
+ justifyContent: "center",
271
+ gap: "12px",
272
+ padding: "24px"
273
+ },
274
+ loadingText: {
275
+ fontSize: "13px",
276
+ color: "#6b7280"
277
+ },
278
+ emptyContainer: {
279
+ display: "flex",
280
+ flexDirection: "column",
281
+ alignItems: "center",
282
+ justifyContent: "center",
283
+ gap: "8px",
284
+ padding: "24px"
285
+ },
286
+ emptyText: {
287
+ fontSize: "13px",
288
+ color: "#6b7280",
289
+ fontWeight: "500"
290
+ },
291
+ emptySubtext: {
292
+ fontSize: "11px",
293
+ color: "#9ca3af",
294
+ textAlign: "center"
295
+ },
296
+ emptyActions: {
297
+ display: "flex",
298
+ gap: "8px",
299
+ marginTop: "8px"
300
+ },
301
+ retakeButtonOutline: {
302
+ display: "flex",
303
+ alignItems: "center",
304
+ gap: "6px",
305
+ padding: "8px 12px",
306
+ fontSize: "12px",
307
+ color: "#6b7280",
308
+ backgroundColor: "transparent",
309
+ border: "1px solid #e5e7eb",
310
+ borderRadius: "6px",
311
+ cursor: "pointer"
312
+ },
313
+ continueButton: {
314
+ display: "flex",
315
+ alignItems: "center",
316
+ gap: "6px",
317
+ padding: "8px 12px",
318
+ fontSize: "12px",
319
+ color: "#fff",
320
+ backgroundColor: "#3b82f6",
321
+ border: "none",
322
+ borderRadius: "6px",
323
+ cursor: "pointer",
324
+ fontWeight: "500"
325
+ },
326
+ header: {
327
+ display: "flex",
328
+ justifyContent: "space-between",
329
+ alignItems: "center",
330
+ padding: "0 4px"
331
+ },
332
+ headerTitle: {
333
+ fontSize: "12px",
334
+ fontWeight: "600",
335
+ color: "#374151",
336
+ textTransform: "uppercase",
337
+ letterSpacing: "0.05em"
338
+ },
339
+ headerActions: {
340
+ display: "flex",
341
+ alignItems: "center",
342
+ gap: "8px"
343
+ },
344
+ sizeLabel: {
345
+ fontSize: "11px",
346
+ color: "#9ca3af"
347
+ },
348
+ iconButton: {
349
+ display: "flex",
350
+ alignItems: "center",
351
+ justifyContent: "center",
352
+ width: "24px",
353
+ height: "24px",
354
+ border: "none",
355
+ backgroundColor: "transparent",
356
+ borderRadius: "4px",
357
+ cursor: "pointer",
358
+ color: "#6b7280"
359
+ },
360
+ tabContainer: {
361
+ display: "flex",
362
+ gap: "4px",
363
+ borderBottom: "1px solid #e5e7eb",
364
+ paddingBottom: "8px"
365
+ },
366
+ tab: {
367
+ display: "flex",
368
+ alignItems: "center",
369
+ gap: "4px",
370
+ padding: "2px 5px",
371
+ fontSize: "12px",
372
+ color: "#6b7280",
373
+ backgroundColor: "transparent",
374
+ border: "none",
375
+ borderRadius: "4px",
376
+ cursor: "pointer",
377
+ transition: "all 0.15s ease"
378
+ },
379
+ tabActive: {
380
+ backgroundColor: "#eff6ff",
381
+ color: "#3b82f6",
382
+ fontWeight: "500"
383
+ },
384
+ tabError: {
385
+ color: "#ef4444"
386
+ },
387
+ tabSize: {
388
+ fontSize: "10px",
389
+ color: "#9ca3af"
390
+ },
391
+ previewContainer: {
392
+ position: "relative",
393
+ width: "100%",
394
+ height: "150px",
395
+ backgroundColor: "#f9fafb",
396
+ borderRadius: "8px",
397
+ overflow: "hidden",
398
+ display: "flex",
399
+ alignItems: "center",
400
+ justifyContent: "center"
401
+ },
402
+ previewContainerExpanded: {
403
+ height: "60vh",
404
+ maxHeight: "500px"
405
+ },
406
+ previewImage: {
407
+ maxWidth: "100%",
408
+ maxHeight: "100%",
409
+ objectFit: "contain"
410
+ },
411
+ noPreview: {
412
+ display: "flex",
413
+ flexDirection: "column",
414
+ alignItems: "center",
415
+ gap: "8px",
416
+ fontSize: "12px",
417
+ color: "#9ca3af"
418
+ },
419
+ errorPreview: {
420
+ display: "flex",
421
+ flexDirection: "column",
422
+ alignItems: "center",
423
+ justifyContent: "center",
424
+ gap: "8px",
425
+ padding: "16px",
426
+ textAlign: "center"
427
+ },
428
+ errorTitle: {
429
+ fontSize: "14px",
430
+ fontWeight: "600",
431
+ color: "#ef4444"
432
+ },
433
+ errorMessage: {
434
+ fontSize: "12px",
435
+ color: "#6b7280",
436
+ maxWidth: "250px",
437
+ lineHeight: "1.4"
438
+ },
439
+ dimensionsInfo: {
440
+ fontSize: "11px",
441
+ color: "#9ca3af",
442
+ textAlign: "center"
443
+ },
444
+ actions: {
445
+ display: "flex",
446
+ justifyContent: "space-between",
447
+ alignItems: "center",
448
+ paddingTop: "8px",
449
+ borderTop: "1px solid #e5e7eb"
450
+ },
451
+ actionsRight: {
452
+ display: "flex",
453
+ gap: "8px"
454
+ },
455
+ retakeButton: {
456
+ display: "flex",
457
+ alignItems: "center",
458
+ gap: "6px",
459
+ padding: "8px 16px",
460
+ fontSize: "13px",
461
+ color: "#fff",
462
+ backgroundColor: "#3b82f6",
463
+ border: "none",
464
+ borderRadius: "6px",
465
+ cursor: "pointer",
466
+ fontWeight: "500"
467
+ },
468
+ retakeButtonSmall: {
469
+ display: "flex",
470
+ alignItems: "center",
471
+ gap: "4px",
472
+ padding: "4px 8px",
473
+ fontSize: "11px",
474
+ color: "#6b7280",
475
+ backgroundColor: "transparent",
476
+ border: "1px solid #e5e7eb",
477
+ borderRadius: "4px",
478
+ cursor: "pointer"
479
+ }
480
+ };
207
481
 
208
482
  // src/highlight.ts
209
483
  var HIGHLIGHT_TARGET_CLASS = "uifeedback-highlight-target";
@@ -238,7 +512,12 @@ var defaultContainerSelectors = [
238
512
  "footer"
239
513
  ];
240
514
  function generateHighlightCSS(colors) {
241
- const { targetColor, containerColor, targetShadowOpacity, containerShadowOpacity } = colors;
515
+ const {
516
+ targetColor,
517
+ containerColor,
518
+ targetShadowOpacity,
519
+ containerShadowOpacity
520
+ } = colors;
242
521
  const hexToRgba = (hex, alpha) => {
243
522
  const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
244
523
  if (!result) return `rgba(0, 0, 0, ${alpha})`;
@@ -246,7 +525,7 @@ function generateHighlightCSS(colors) {
246
525
  };
247
526
  return `
248
527
  .${HIGHLIGHT_TARGET_CLASS} {
249
- outline: 2px solid ${targetColor} !important;
528
+ outline: 2px dashed ${targetColor} !important;
250
529
  outline-offset: 2px !important;
251
530
  box-shadow: 0 0 0 4px ${hexToRgba(targetColor, targetShadowOpacity)}, 0 4px 12px ${hexToRgba(targetColor, targetShadowOpacity * 0.6)} !important;
252
531
  border-radius: 4px !important;
@@ -461,8 +740,16 @@ var ChevronLeft = createLucideIcon("chevron-left", __iconNode3);
461
740
  var __iconNode4 = [["path", { d: "m9 18 6-6-6-6", key: "mthhwq" }]];
462
741
  var ChevronRight = createLucideIcon("chevron-right", __iconNode4);
463
742
 
464
- // ../../node_modules/lucide-react/dist/esm/icons/expand.js
743
+ // ../../node_modules/lucide-react/dist/esm/icons/circle-alert.js
465
744
  var __iconNode5 = [
745
+ ["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
746
+ ["line", { x1: "12", x2: "12", y1: "8", y2: "12", key: "1pkeuh" }],
747
+ ["line", { x1: "12", x2: "12.01", y1: "16", y2: "16", key: "4dfq90" }]
748
+ ];
749
+ var CircleAlert = createLucideIcon("circle-alert", __iconNode5);
750
+
751
+ // ../../node_modules/lucide-react/dist/esm/icons/expand.js
752
+ var __iconNode6 = [
466
753
  ["path", { d: "m15 15 6 6", key: "1s409w" }],
467
754
  ["path", { d: "m15 9 6-6", key: "ko1vev" }],
468
755
  ["path", { d: "M21 16v5h-5", key: "1ck2sf" }],
@@ -472,10 +759,10 @@ var __iconNode5 = [
472
759
  ["path", { d: "M3 8V3h5", key: "1ln10m" }],
473
760
  ["path", { d: "M9 9 3 3", key: "v551iv" }]
474
761
  ];
475
- var Expand = createLucideIcon("expand", __iconNode5);
762
+ var Expand = createLucideIcon("expand", __iconNode6);
476
763
 
477
764
  // ../../node_modules/lucide-react/dist/esm/icons/flag.js
478
- var __iconNode6 = [
765
+ var __iconNode7 = [
479
766
  [
480
767
  "path",
481
768
  {
@@ -484,47 +771,58 @@ var __iconNode6 = [
484
771
  }
485
772
  ]
486
773
  ];
487
- var Flag = createLucideIcon("flag", __iconNode6);
774
+ var Flag = createLucideIcon("flag", __iconNode7);
775
+
776
+ // ../../node_modules/lucide-react/dist/esm/icons/grip-vertical.js
777
+ var __iconNode8 = [
778
+ ["circle", { cx: "9", cy: "12", r: "1", key: "1vctgf" }],
779
+ ["circle", { cx: "9", cy: "5", r: "1", key: "hp0tcf" }],
780
+ ["circle", { cx: "9", cy: "19", r: "1", key: "fkjjf6" }],
781
+ ["circle", { cx: "15", cy: "12", r: "1", key: "1tmaij" }],
782
+ ["circle", { cx: "15", cy: "5", r: "1", key: "19l28e" }],
783
+ ["circle", { cx: "15", cy: "19", r: "1", key: "f4zoj3" }]
784
+ ];
785
+ var GripVertical = createLucideIcon("grip-vertical", __iconNode8);
488
786
 
489
787
  // ../../node_modules/lucide-react/dist/esm/icons/image.js
490
- var __iconNode7 = [
788
+ var __iconNode9 = [
491
789
  ["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", ry: "2", key: "1m3agn" }],
492
790
  ["circle", { cx: "9", cy: "9", r: "2", key: "af1f0g" }],
493
791
  ["path", { d: "m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21", key: "1xmnt7" }]
494
792
  ];
495
- var Image = createLucideIcon("image", __iconNode7);
793
+ var Image = createLucideIcon("image", __iconNode9);
496
794
 
497
795
  // ../../node_modules/lucide-react/dist/esm/icons/loader-circle.js
498
- var __iconNode8 = [["path", { d: "M21 12a9 9 0 1 1-6.219-8.56", key: "13zald" }]];
499
- var LoaderCircle = createLucideIcon("loader-circle", __iconNode8);
796
+ var __iconNode10 = [["path", { d: "M21 12a9 9 0 1 1-6.219-8.56", key: "13zald" }]];
797
+ var LoaderCircle = createLucideIcon("loader-circle", __iconNode10);
500
798
 
501
799
  // ../../node_modules/lucide-react/dist/esm/icons/plus.js
502
- var __iconNode9 = [
800
+ var __iconNode11 = [
503
801
  ["path", { d: "M5 12h14", key: "1ays0h" }],
504
802
  ["path", { d: "M12 5v14", key: "s699le" }]
505
803
  ];
506
- var Plus = createLucideIcon("plus", __iconNode9);
804
+ var Plus = createLucideIcon("plus", __iconNode11);
507
805
 
508
806
  // ../../node_modules/lucide-react/dist/esm/icons/refresh-cw.js
509
- var __iconNode10 = [
807
+ var __iconNode12 = [
510
808
  ["path", { d: "M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8", key: "v9h5vc" }],
511
809
  ["path", { d: "M21 3v5h-5", key: "1q7to0" }],
512
810
  ["path", { d: "M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16", key: "3uifl3" }],
513
811
  ["path", { d: "M8 16H3v5", key: "1cv678" }]
514
812
  ];
515
- var RefreshCw = createLucideIcon("refresh-cw", __iconNode10);
813
+ var RefreshCw = createLucideIcon("refresh-cw", __iconNode12);
516
814
 
517
815
  // ../../node_modules/lucide-react/dist/esm/icons/shrink.js
518
- var __iconNode11 = [
816
+ var __iconNode13 = [
519
817
  ["path", { d: "m15 15 6 6m-6-6v4.8m0-4.8h4.8", key: "17vawe" }],
520
818
  ["path", { d: "M9 19.8V15m0 0H4.2M9 15l-6 6", key: "chjx8e" }],
521
819
  ["path", { d: "M15 4.2V9m0 0h4.8M15 9l6-6", key: "lav6yq" }],
522
820
  ["path", { d: "M9 4.2V9m0 0H4.2M9 9 3 3", key: "1pxi2q" }]
523
821
  ];
524
- var Shrink = createLucideIcon("shrink", __iconNode11);
822
+ var Shrink = createLucideIcon("shrink", __iconNode13);
525
823
 
526
824
  // ../../node_modules/lucide-react/dist/esm/icons/thumbs-up.js
527
- var __iconNode12 = [
825
+ var __iconNode14 = [
528
826
  ["path", { d: "M7 10v12", key: "1qc93n" }],
529
827
  [
530
828
  "path",
@@ -534,14 +832,14 @@ var __iconNode12 = [
534
832
  }
535
833
  ]
536
834
  ];
537
- var ThumbsUp = createLucideIcon("thumbs-up", __iconNode12);
835
+ var ThumbsUp = createLucideIcon("thumbs-up", __iconNode14);
538
836
 
539
837
  // ../../node_modules/lucide-react/dist/esm/icons/x.js
540
- var __iconNode13 = [
838
+ var __iconNode15 = [
541
839
  ["path", { d: "M18 6 6 18", key: "1bl5f8" }],
542
840
  ["path", { d: "m6 6 12 12", key: "d8bk6v" }]
543
841
  ];
544
- var X = createLucideIcon("x", __iconNode13);
842
+ var X = createLucideIcon("x", __iconNode15);
545
843
 
546
844
  // src/ScreenshotPreview.tsx
547
845
  var import_jsx_runtime = require("react/jsx-runtime");
@@ -556,7 +854,7 @@ function ScreenshotPreview({
556
854
  const [activeTab, setActiveTab] = (0, import_react4.useState)("element");
557
855
  const [isExpanded, setIsExpanded] = (0, import_react4.useState)(false);
558
856
  if (isLoading) {
559
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.container, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.loadingContainer, children: [
857
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: screenshotPreviewStyles.container, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: screenshotPreviewStyles.loadingContainer, children: [
560
858
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
561
859
  LoaderCircle,
562
860
  {
@@ -564,76 +862,117 @@ function ScreenshotPreview({
564
862
  style: { color: "#3b82f6" }
565
863
  }
566
864
  ),
567
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.loadingText, children: "Capturing screenshots..." })
865
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: screenshotPreviewStyles.loadingText, children: "Capturing screenshots..." })
568
866
  ] }) });
569
867
  }
570
868
  if (!screenshots) {
571
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.container, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.emptyContainer, children: [
869
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: screenshotPreviewStyles.container, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: screenshotPreviewStyles.emptyContainer, children: [
572
870
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Image, { className: "w-8 h-8", style: { color: "#9ca3af" } }),
573
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.emptyText, children: "No screenshots captured" }),
574
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
575
- "button",
576
- {
577
- type: "button",
578
- onClick: onRetake,
579
- style: styles.retakeButton,
580
- disabled: isSubmitting,
581
- children: [
582
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RefreshCw, { className: "w-4 h-4" }),
583
- "Capture Screenshots"
584
- ]
585
- }
586
- )
587
- ] }) });
588
- }
871
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: screenshotPreviewStyles.emptyText, children: "Screenshots unavailable" }),
872
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: screenshotPreviewStyles.emptySubtext, children: "Some elements can't be captured (e.g., gradient text)" }),
873
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: screenshotPreviewStyles.emptyActions, children: [
874
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
875
+ "button",
876
+ {
877
+ type: "button",
878
+ onClick: onRetake,
879
+ style: screenshotPreviewStyles.retakeButtonOutline,
880
+ disabled: isSubmitting,
881
+ children: [
882
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RefreshCw, { className: "w-4 h-4" }),
883
+ "Try Again"
884
+ ]
885
+ }
886
+ ),
887
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
888
+ "button",
889
+ {
890
+ type: "button",
891
+ onClick: () => onConfirm({ capturedAt: (/* @__PURE__ */ new Date()).toISOString() }),
892
+ style: screenshotPreviewStyles.continueButton,
893
+ disabled: isSubmitting,
894
+ children: [
895
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Check, { className: "w-4 h-4" }),
896
+ "Continue Without"
897
+ ]
898
+ }
899
+ )
900
+ ] })
901
+ ] }) });
902
+ }
903
+ const getError = (key) => {
904
+ return screenshots.errors?.[key];
905
+ };
589
906
  const allTabs = [
590
- { key: "element", label: "Element", data: screenshots.element },
907
+ {
908
+ key: "element",
909
+ label: "Element",
910
+ data: screenshots.element,
911
+ error: getError("element")
912
+ },
591
913
  {
592
914
  key: "container",
593
915
  label: "Container",
594
- data: screenshots.container
916
+ data: screenshots.container,
917
+ error: getError("container")
595
918
  },
596
- { key: "viewport", label: "Viewport", data: screenshots.viewport }
919
+ {
920
+ key: "viewport",
921
+ label: "Viewport",
922
+ data: screenshots.viewport,
923
+ error: getError("viewport")
924
+ }
597
925
  ];
598
- const tabs = allTabs.filter((tab) => tab.data);
926
+ const tabs = allTabs.filter((tab) => tab.data || tab.error);
599
927
  const activeScreenshot = activeTab === "element" ? screenshots.element : activeTab === "container" ? screenshots.container : screenshots.viewport;
928
+ const activeError = getError(activeTab);
600
929
  const totalSize = (0, import_anyclick_core.estimateTotalSize)(screenshots);
930
+ console.log({ styles: screenshotPreviewStyles });
601
931
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
602
932
  "div",
603
933
  {
604
934
  style: {
605
- ...styles.container,
606
- ...isExpanded ? styles.containerExpanded : {}
935
+ ...screenshotPreviewStyles.container,
936
+ ...isExpanded ? screenshotPreviewStyles.containerExpanded : {},
937
+ padding: "8px"
607
938
  },
608
939
  children: [
609
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.header, children: [
610
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.headerTitle, children: "Screenshot Preview" }),
611
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.headerActions, children: [
612
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.sizeLabel, children: (0, import_anyclick_core.formatBytes)(totalSize) }),
940
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: screenshotPreviewStyles.header, children: [
941
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: screenshotPreviewStyles.headerTitle, children: "Review Screenshots" }),
942
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: screenshotPreviewStyles.headerActions, children: [
943
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: screenshotPreviewStyles.sizeLabel, children: (0, import_anyclick_core.formatBytes)(totalSize) }),
613
944
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
614
945
  "button",
615
946
  {
616
947
  type: "button",
617
948
  onClick: () => setIsExpanded(!isExpanded),
618
- style: styles.iconButton,
949
+ style: screenshotPreviewStyles.iconButton,
619
950
  title: isExpanded ? "Collapse" : "Expand",
620
951
  children: isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Shrink, { className: "w-4 h-4" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Expand, { className: "w-4 h-4" })
621
952
  }
622
953
  )
623
954
  ] })
624
955
  ] }),
625
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.tabContainer, children: tabs.map((tab) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
956
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: screenshotPreviewStyles.tabContainer, children: tabs.map((tab) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
626
957
  "button",
627
958
  {
628
959
  type: "button",
629
960
  onClick: () => setActiveTab(tab.key),
630
961
  style: {
631
- ...styles.tab,
632
- ...activeTab === tab.key ? styles.tabActive : {}
962
+ ...screenshotPreviewStyles.tab,
963
+ ...activeTab === tab.key ? screenshotPreviewStyles.tabActive : {},
964
+ ...tab.error && !tab.data ? screenshotPreviewStyles.tabError : {}
633
965
  },
634
966
  children: [
967
+ tab.error && !tab.data && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
968
+ CircleAlert,
969
+ {
970
+ className: "w-3 h-3",
971
+ style: { color: "#ef4444" }
972
+ }
973
+ ),
635
974
  tab.label,
636
- tab.data && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.tabSize, children: (0, import_anyclick_core.formatBytes)(tab.data.sizeBytes) })
975
+ tab.data && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: screenshotPreviewStyles.tabSize, children: (0, import_anyclick_core.formatBytes)(tab.data.sizeBytes) })
637
976
  ]
638
977
  },
639
978
  tab.key
@@ -642,17 +981,21 @@ function ScreenshotPreview({
642
981
  "div",
643
982
  {
644
983
  style: {
645
- ...styles.previewContainer,
646
- ...isExpanded ? styles.previewContainerExpanded : {}
984
+ ...screenshotPreviewStyles.previewContainer,
985
+ ...isExpanded ? screenshotPreviewStyles.previewContainerExpanded : {}
647
986
  },
648
987
  children: activeScreenshot ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
649
988
  "img",
650
989
  {
651
990
  src: activeScreenshot.dataUrl,
652
991
  alt: `${activeTab} screenshot`,
653
- style: styles.previewImage
992
+ style: screenshotPreviewStyles.previewImage
654
993
  }
655
- ) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.noPreview, children: [
994
+ ) : activeError ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: screenshotPreviewStyles.errorPreview, children: [
995
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CircleAlert, { className: "w-8 h-8", style: { color: "#ef4444" } }),
996
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: screenshotPreviewStyles.errorTitle, children: "Capture Failed" }),
997
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: screenshotPreviewStyles.errorMessage, children: activeError.message })
998
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: screenshotPreviewStyles.noPreview, children: [
656
999
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Image, { className: "w-6 h-6", style: { color: "#9ca3af" } }),
657
1000
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
658
1001
  "No ",
@@ -662,27 +1005,27 @@ function ScreenshotPreview({
662
1005
  ] })
663
1006
  }
664
1007
  ),
665
- activeScreenshot && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.dimensionsInfo, children: [
1008
+ activeScreenshot && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: screenshotPreviewStyles.dimensionsInfo, children: [
666
1009
  activeScreenshot.width,
667
1010
  " \xD7 ",
668
1011
  activeScreenshot.height,
669
1012
  "px"
670
1013
  ] }),
671
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.actions, children: [
1014
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: screenshotPreviewStyles.actions, children: [
672
1015
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
673
1016
  "button",
674
1017
  {
675
1018
  type: "button",
676
1019
  onClick: onRetake,
677
1020
  disabled: isSubmitting,
678
- style: styles.retakeButtonSmall,
1021
+ style: screenshotPreviewStyles.retakeButtonSmall,
679
1022
  children: [
680
1023
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RefreshCw, { className: "w-3 h-3" }),
681
1024
  "Retake"
682
1025
  ]
683
1026
  }
684
1027
  ),
685
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.actionsRight, children: [
1028
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: screenshotPreviewStyles.actionsRight, children: [
686
1029
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
687
1030
  "button",
688
1031
  {
@@ -723,186 +1066,20 @@ function ScreenshotPreview({
723
1066
  }
724
1067
  );
725
1068
  }
726
- var styles = {
727
- container: {
728
- width: "100%",
729
- display: "flex",
730
- flexDirection: "column",
731
- gap: "8px"
732
- },
733
- containerExpanded: {
734
- position: "fixed",
735
- top: "50%",
736
- left: "50%",
737
- transform: "translate(-50%, -50%)",
738
- width: "90vw",
739
- maxWidth: "800px",
740
- maxHeight: "90vh",
741
- backgroundColor: "#fff",
742
- borderRadius: "12px",
743
- boxShadow: "0 25px 50px -12px rgba(0, 0, 0, 0.25)",
744
- padding: "16px",
745
- zIndex: 1e4
746
- },
747
- loadingContainer: {
748
- display: "flex",
749
- flexDirection: "column",
750
- alignItems: "center",
751
- justifyContent: "center",
752
- gap: "12px",
753
- padding: "24px"
754
- },
755
- loadingText: {
756
- fontSize: "13px",
757
- color: "#6b7280"
758
- },
759
- emptyContainer: {
760
- display: "flex",
761
- flexDirection: "column",
762
- alignItems: "center",
763
- justifyContent: "center",
764
- gap: "8px",
765
- padding: "24px"
766
- },
767
- emptyText: {
768
- fontSize: "13px",
769
- color: "#9ca3af"
770
- },
771
- header: {
772
- display: "flex",
773
- justifyContent: "space-between",
774
- alignItems: "center",
775
- padding: "0 4px"
776
- },
777
- headerTitle: {
778
- fontSize: "12px",
779
- fontWeight: "600",
780
- color: "#374151",
781
- textTransform: "uppercase",
782
- letterSpacing: "0.05em"
783
- },
784
- headerActions: {
785
- display: "flex",
786
- alignItems: "center",
787
- gap: "8px"
788
- },
789
- sizeLabel: {
790
- fontSize: "11px",
791
- color: "#9ca3af"
792
- },
793
- iconButton: {
794
- display: "flex",
795
- alignItems: "center",
796
- justifyContent: "center",
797
- width: "24px",
798
- height: "24px",
799
- border: "none",
800
- background: "transparent",
801
- borderRadius: "4px",
802
- cursor: "pointer",
803
- color: "#6b7280"
804
- },
805
- tabContainer: {
806
- display: "flex",
807
- gap: "4px",
808
- borderBottom: "1px solid #e5e7eb",
809
- paddingBottom: "8px"
810
- },
811
- tab: {
812
- display: "flex",
813
- alignItems: "center",
814
- gap: "4px",
815
- padding: "6px 10px",
816
- fontSize: "12px",
817
- color: "#6b7280",
818
- background: "transparent",
819
- border: "none",
820
- borderRadius: "4px",
821
- cursor: "pointer",
822
- transition: "all 0.15s ease"
823
- },
824
- tabActive: {
825
- backgroundColor: "#eff6ff",
826
- color: "#3b82f6",
827
- fontWeight: "500"
828
- },
829
- tabSize: {
830
- fontSize: "10px",
831
- color: "#9ca3af"
832
- },
833
- previewContainer: {
834
- position: "relative",
835
- width: "100%",
836
- height: "150px",
837
- backgroundColor: "#f9fafb",
838
- borderRadius: "8px",
839
- overflow: "hidden",
840
- display: "flex",
841
- alignItems: "center",
842
- justifyContent: "center"
843
- },
844
- previewContainerExpanded: {
845
- height: "60vh",
846
- maxHeight: "500px"
847
- },
848
- previewImage: {
849
- maxWidth: "100%",
850
- maxHeight: "100%",
851
- objectFit: "contain"
852
- },
853
- noPreview: {
854
- display: "flex",
855
- flexDirection: "column",
856
- alignItems: "center",
857
- gap: "8px",
858
- fontSize: "12px",
859
- color: "#9ca3af"
860
- },
861
- dimensionsInfo: {
862
- fontSize: "11px",
863
- color: "#9ca3af",
864
- textAlign: "center"
865
- },
866
- actions: {
867
- display: "flex",
868
- justifyContent: "space-between",
869
- alignItems: "center",
870
- paddingTop: "8px",
871
- borderTop: "1px solid #e5e7eb"
872
- },
873
- actionsRight: {
874
- display: "flex",
875
- gap: "8px"
876
- },
877
- retakeButton: {
878
- display: "flex",
879
- alignItems: "center",
880
- gap: "6px",
881
- padding: "8px 16px",
882
- fontSize: "13px",
883
- color: "#fff",
884
- backgroundColor: "#3b82f6",
885
- border: "none",
886
- borderRadius: "6px",
887
- cursor: "pointer",
888
- fontWeight: "500"
889
- },
890
- retakeButtonSmall: {
891
- display: "flex",
892
- alignItems: "center",
893
- gap: "4px",
894
- padding: "4px 8px",
895
- fontSize: "11px",
896
- color: "#6b7280",
897
- backgroundColor: "transparent",
898
- border: "1px solid #e5e7eb",
899
- borderRadius: "4px",
900
- cursor: "pointer"
901
- }
902
- };
903
1069
 
904
1070
  // src/ContextMenu.tsx
905
1071
  var import_jsx_runtime2 = require("react/jsx-runtime");
1072
+ var VIEWPORT_PADDING = 10;
1073
+ var screenshotIndicatorStyle = {
1074
+ display: "flex",
1075
+ alignItems: "center",
1076
+ gap: "6px",
1077
+ padding: "8px 12px",
1078
+ fontSize: "11px",
1079
+ color: "var(--anyclick-menu-text-muted, #9ca3af)",
1080
+ borderTop: "1px solid var(--anyclick-menu-border, #f3f4f6)",
1081
+ marginTop: "4px"
1082
+ };
906
1083
  var defaultIcons = {
907
1084
  issue: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Flag, { className: "w-4 h-4" }),
908
1085
  feature: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Plus, { className: "w-4 h-4" }),
@@ -915,6 +1092,7 @@ function MenuItem({
915
1092
  hasChildren
916
1093
  }) {
917
1094
  const [isHovered, setIsHovered] = (0, import_react5.useState)(false);
1095
+ const [isPressed, setIsPressed] = (0, import_react5.useState)(false);
918
1096
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
919
1097
  "button",
920
1098
  {
@@ -922,11 +1100,25 @@ function MenuItem({
922
1100
  onClick,
923
1101
  disabled,
924
1102
  onMouseEnter: () => setIsHovered(true),
925
- onMouseLeave: () => setIsHovered(false),
1103
+ onMouseLeave: () => {
1104
+ setIsHovered(false);
1105
+ setIsPressed(false);
1106
+ },
1107
+ onTouchStart: () => setIsPressed(true),
1108
+ onTouchEnd: () => setIsPressed(false),
1109
+ onTouchCancel: () => setIsPressed(false),
926
1110
  style: {
927
1111
  ...menuStyles.item,
928
- ...isHovered ? menuStyles.itemHover : {},
929
- ...disabled ? { opacity: 0.5, cursor: "not-allowed" } : {}
1112
+ // Apply hover/pressed state for both mouse and touch
1113
+ ...isHovered || isPressed ? menuStyles.itemHover : {},
1114
+ ...disabled ? { opacity: 0.5, cursor: "not-allowed" } : {},
1115
+ // Ensure minimum touch target size (44px is recommended)
1116
+ minHeight: "44px",
1117
+ // Prevent text selection on touch
1118
+ WebkitUserSelect: "none",
1119
+ userSelect: "none",
1120
+ // Prevent touch callout on iOS
1121
+ WebkitTouchCallout: "none"
930
1122
  },
931
1123
  children: [
932
1124
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: menuStyles.itemIcon, children: item.icon ?? defaultIcons[item.type] }),
@@ -944,18 +1136,31 @@ function MenuItem({
944
1136
  }
945
1137
  function BackButton({ onClick }) {
946
1138
  const [isHovered, setIsHovered] = (0, import_react5.useState)(false);
1139
+ const [isPressed, setIsPressed] = (0, import_react5.useState)(false);
947
1140
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
948
1141
  "button",
949
1142
  {
950
1143
  type: "button",
951
1144
  onClick,
952
1145
  onMouseEnter: () => setIsHovered(true),
953
- onMouseLeave: () => setIsHovered(false),
1146
+ onMouseLeave: () => {
1147
+ setIsHovered(false);
1148
+ setIsPressed(false);
1149
+ },
1150
+ onTouchStart: () => setIsPressed(true),
1151
+ onTouchEnd: () => setIsPressed(false),
1152
+ onTouchCancel: () => setIsPressed(false),
954
1153
  style: {
955
1154
  ...menuStyles.item,
956
- ...isHovered ? menuStyles.itemHover : {},
1155
+ ...isHovered || isPressed ? menuStyles.itemHover : {},
957
1156
  borderBottom: "1px solid #e5e5e5",
958
- marginBottom: "4px"
1157
+ marginBottom: "4px",
1158
+ // Ensure minimum touch target size
1159
+ minHeight: "44px",
1160
+ // Prevent text selection on touch
1161
+ WebkitUserSelect: "none",
1162
+ userSelect: "none",
1163
+ WebkitTouchCallout: "none"
959
1164
  },
960
1165
  children: [
961
1166
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ChevronLeft, { className: "w-4 h-4", style: { opacity: 0.5 } }),
@@ -1025,6 +1230,25 @@ function CommentForm({
1025
1230
  ] })
1026
1231
  ] });
1027
1232
  }
1233
+ function calculateInViewPosition(requestedX, requestedY, menuWidth, menuHeight) {
1234
+ const viewportWidth = window.innerWidth;
1235
+ const viewportHeight = window.innerHeight;
1236
+ let x = requestedX;
1237
+ let y = requestedY;
1238
+ if (x + menuWidth > viewportWidth - VIEWPORT_PADDING) {
1239
+ x = viewportWidth - menuWidth - VIEWPORT_PADDING;
1240
+ }
1241
+ if (x < VIEWPORT_PADDING) {
1242
+ x = VIEWPORT_PADDING;
1243
+ }
1244
+ if (y + menuHeight > viewportHeight - VIEWPORT_PADDING) {
1245
+ y = viewportHeight - menuHeight - VIEWPORT_PADDING;
1246
+ }
1247
+ if (y < VIEWPORT_PADDING) {
1248
+ y = VIEWPORT_PADDING;
1249
+ }
1250
+ return { x, y };
1251
+ }
1028
1252
  function ContextMenu({
1029
1253
  visible,
1030
1254
  position,
@@ -1037,7 +1261,8 @@ function ContextMenu({
1037
1261
  style,
1038
1262
  className,
1039
1263
  highlightConfig,
1040
- screenshotConfig
1264
+ screenshotConfig,
1265
+ positionMode = "inView"
1041
1266
  }) {
1042
1267
  const [selectedType, setSelectedType] = (0, import_react5.useState)(null);
1043
1268
  const [currentView, setCurrentView] = (0, import_react5.useState)("menu");
@@ -1046,6 +1271,13 @@ function ContextMenu({
1046
1271
  const [screenshots, setScreenshots] = (0, import_react5.useState)(null);
1047
1272
  const [isCapturing, setIsCapturing] = (0, import_react5.useState)(false);
1048
1273
  const menuRef = (0, import_react5.useRef)(null);
1274
+ const [adjustedPosition, setAdjustedPosition] = (0, import_react5.useState)(position);
1275
+ const [isDragging, setIsDragging] = (0, import_react5.useState)(false);
1276
+ const [dragOffset, setDragOffset] = (0, import_react5.useState)({
1277
+ x: 0,
1278
+ y: 0
1279
+ });
1280
+ const dragStartRef = (0, import_react5.useRef)(null);
1049
1281
  const mergedScreenshotConfig = {
1050
1282
  ...import_anyclick_core2.DEFAULT_SCREENSHOT_CONFIG,
1051
1283
  ...screenshotConfig
@@ -1077,6 +1309,9 @@ function ContextMenu({
1077
1309
  setSubmenuStack([]);
1078
1310
  setScreenshots(null);
1079
1311
  setIsCapturing(false);
1312
+ setIsDragging(false);
1313
+ setDragOffset({ x: 0, y: 0 });
1314
+ dragStartRef.current = null;
1080
1315
  }
1081
1316
  }, [visible]);
1082
1317
  (0, import_react5.useEffect)(() => {
@@ -1097,6 +1332,9 @@ function ContextMenu({
1097
1332
  const handlePointerDown = (event) => {
1098
1333
  if (!menuRef.current) return;
1099
1334
  const target = event.target;
1335
+ if (target.closest?.("[data-drag-handle]")) {
1336
+ return;
1337
+ }
1100
1338
  if (!menuRef.current.contains(target)) {
1101
1339
  onClose();
1102
1340
  }
@@ -1107,24 +1345,71 @@ function ContextMenu({
1107
1345
  };
1108
1346
  }, [visible, onClose]);
1109
1347
  (0, import_react5.useEffect)(() => {
1110
- if (visible && menuRef.current) {
1111
- const rect = menuRef.current.getBoundingClientRect();
1112
- const viewportWidth = window.innerWidth;
1113
- const viewportHeight = window.innerHeight;
1114
- let adjustedX = position.x;
1115
- let adjustedY = position.y;
1116
- if (position.x + rect.width > viewportWidth) {
1117
- adjustedX = viewportWidth - rect.width - 10;
1118
- }
1119
- if (position.y + rect.height > viewportHeight) {
1120
- adjustedY = viewportHeight - rect.height - 10;
1121
- }
1122
- if (adjustedX !== position.x || adjustedY !== position.y) {
1123
- menuRef.current.style.left = `${adjustedX}px`;
1124
- menuRef.current.style.top = `${adjustedY}px`;
1348
+ if (visible) {
1349
+ setAdjustedPosition(position);
1350
+ setDragOffset({ x: 0, y: 0 });
1351
+ }
1352
+ }, [visible, position.x, position.y]);
1353
+ (0, import_react5.useEffect)(() => {
1354
+ if (!visible || !menuRef.current) return;
1355
+ const updatePosition = () => {
1356
+ const menuElement = menuRef.current;
1357
+ if (!menuElement) return;
1358
+ const rect = menuElement.getBoundingClientRect();
1359
+ const baseX = position.x + dragOffset.x;
1360
+ const baseY = position.y + dragOffset.y;
1361
+ if (positionMode === "static") {
1362
+ setAdjustedPosition({ x: baseX, y: baseY });
1363
+ } else if (positionMode === "inView" || positionMode === "dynamic") {
1364
+ const adjusted = calculateInViewPosition(
1365
+ baseX,
1366
+ baseY,
1367
+ rect.width,
1368
+ rect.height
1369
+ );
1370
+ setAdjustedPosition(adjusted);
1125
1371
  }
1372
+ };
1373
+ requestAnimationFrame(updatePosition);
1374
+ window.addEventListener("resize", updatePosition);
1375
+ return () => window.removeEventListener("resize", updatePosition);
1376
+ }, [visible, position, positionMode, dragOffset, currentView]);
1377
+ (0, import_react5.useEffect)(() => {
1378
+ if (!visible || positionMode !== "dynamic") return;
1379
+ const handlePointerMove = (event) => {
1380
+ if (!isDragging || !dragStartRef.current) return;
1381
+ const deltaX = event.clientX - dragStartRef.current.x;
1382
+ const deltaY = event.clientY - dragStartRef.current.y;
1383
+ setDragOffset((prev) => ({
1384
+ x: prev.x + deltaX,
1385
+ y: prev.y + deltaY
1386
+ }));
1387
+ dragStartRef.current = { x: event.clientX, y: event.clientY };
1388
+ };
1389
+ const handlePointerUp = () => {
1390
+ setIsDragging(false);
1391
+ dragStartRef.current = null;
1392
+ };
1393
+ if (isDragging) {
1394
+ document.addEventListener("pointermove", handlePointerMove);
1395
+ document.addEventListener("pointerup", handlePointerUp);
1396
+ document.addEventListener("pointercancel", handlePointerUp);
1397
+ return () => {
1398
+ document.removeEventListener("pointermove", handlePointerMove);
1399
+ document.removeEventListener("pointerup", handlePointerUp);
1400
+ document.removeEventListener("pointercancel", handlePointerUp);
1401
+ };
1126
1402
  }
1127
- }, [visible, position]);
1403
+ }, [visible, positionMode, isDragging]);
1404
+ const handleDragStart = (0, import_react5.useCallback)(
1405
+ (event) => {
1406
+ if (positionMode !== "dynamic") return;
1407
+ event.preventDefault();
1408
+ setIsDragging(true);
1409
+ dragStartRef.current = { x: event.clientX, y: event.clientY };
1410
+ },
1411
+ [positionMode]
1412
+ );
1128
1413
  (0, import_react5.useEffect)(() => {
1129
1414
  const handleKeyDown = (e) => {
1130
1415
  if (e.key === "Escape") {
@@ -1146,6 +1431,23 @@ function ContextMenu({
1146
1431
  return () => document.removeEventListener("keydown", handleKeyDown);
1147
1432
  }
1148
1433
  }, [visible, currentView, submenuStack.length, onClose]);
1434
+ (0, import_react5.useEffect)(() => {
1435
+ const menuElement = menuRef.current;
1436
+ if (!visible || !menuElement) return;
1437
+ const preventTouchDefault = (e) => {
1438
+ const target = e.target;
1439
+ if (target.tagName === "TEXTAREA" || target.tagName === "INPUT" || target.isContentEditable) {
1440
+ return;
1441
+ }
1442
+ e.preventDefault();
1443
+ };
1444
+ menuElement.addEventListener("touchmove", preventTouchDefault, {
1445
+ passive: false
1446
+ });
1447
+ return () => {
1448
+ menuElement.removeEventListener("touchmove", preventTouchDefault);
1449
+ };
1450
+ }, [visible]);
1149
1451
  if (!visible || !targetElement) {
1150
1452
  return null;
1151
1453
  }
@@ -1203,6 +1505,14 @@ function ContextMenu({
1203
1505
  captureScreenshots();
1204
1506
  };
1205
1507
  const containerWidth = currentView === "screenshot-preview" ? 360 : void 0;
1508
+ if (process.env.NODE_ENV === "development" && visible) {
1509
+ console.log("[ContextMenu] Style Debug", {
1510
+ styleExists: !!style,
1511
+ styleKeys: style ? Object.keys(style) : [],
1512
+ styleValues: style,
1513
+ className
1514
+ });
1515
+ }
1206
1516
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1207
1517
  "div",
1208
1518
  {
@@ -1210,15 +1520,61 @@ function ContextMenu({
1210
1520
  className,
1211
1521
  style: {
1212
1522
  ...menuStyles.container,
1213
- left: position.x,
1214
- top: position.y,
1523
+ left: adjustedPosition.x,
1524
+ top: adjustedPosition.y,
1215
1525
  ...containerWidth ? { width: containerWidth, minWidth: containerWidth } : {},
1526
+ // Touch-specific styles
1527
+ WebkitUserSelect: "none",
1528
+ userSelect: "none",
1529
+ WebkitTouchCallout: "none",
1530
+ touchAction: "none",
1531
+ // Prevent default touch behaviors
1532
+ // Cursor style for dragging
1533
+ ...isDragging ? { cursor: "grabbing" } : {},
1216
1534
  ...style
1217
1535
  },
1218
1536
  role: "menu",
1219
1537
  "aria-label": "Feedback options",
1220
1538
  children: [
1221
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: menuStyles.header, children: currentView === "screenshot-preview" ? "Review Screenshots" : "Send Feedback" }),
1539
+ currentView !== "screenshot-preview" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1540
+ "div",
1541
+ {
1542
+ style: {
1543
+ ...menuStyles.header,
1544
+ display: "flex",
1545
+ alignItems: "center",
1546
+ justifyContent: "space-between"
1547
+ },
1548
+ children: [
1549
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: "Send Feedback" }),
1550
+ positionMode === "dynamic" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1551
+ "div",
1552
+ {
1553
+ "data-drag-handle": true,
1554
+ onPointerDown: handleDragStart,
1555
+ style: {
1556
+ cursor: isDragging ? "grabbing" : "grab",
1557
+ padding: "4px",
1558
+ marginRight: "-4px",
1559
+ borderRadius: "4px",
1560
+ display: "flex",
1561
+ alignItems: "center",
1562
+ opacity: 0.5,
1563
+ transition: "opacity 0.15s"
1564
+ },
1565
+ onMouseEnter: (e) => {
1566
+ e.currentTarget.style.opacity = "1";
1567
+ },
1568
+ onMouseLeave: (e) => {
1569
+ e.currentTarget.style.opacity = "0.5";
1570
+ },
1571
+ title: "Drag to move",
1572
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(GripVertical, { className: "w-4 h-4" })
1573
+ }
1574
+ )
1575
+ ]
1576
+ }
1577
+ ),
1222
1578
  currentView === "menu" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: menuStyles.itemList, children: [
1223
1579
  submenuStack.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(BackButton, { onClick: handleBack }),
1224
1580
  currentItems.map((item) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
@@ -1259,25 +1615,203 @@ function ContextMenu({
1259
1615
  }
1260
1616
  );
1261
1617
  }
1262
- var screenshotIndicatorStyle = {
1263
- display: "flex",
1264
- alignItems: "center",
1265
- gap: "6px",
1266
- padding: "8px 12px",
1267
- fontSize: "11px",
1268
- color: "#9ca3af",
1269
- borderTop: "1px solid #f3f4f6",
1270
- marginTop: "4px"
1271
- };
1272
1618
 
1273
- // src/FeedbackProvider.tsx
1619
+ // src/store.ts
1620
+ var import_zustand = require("zustand");
1621
+ var providerIdCounter = 0;
1622
+ function generateProviderId() {
1623
+ return `anyclick-provider-${++providerIdCounter}`;
1624
+ }
1625
+ var useProviderStore = (0, import_zustand.create)((set, get) => ({
1626
+ providers: /* @__PURE__ */ new Map(),
1627
+ registerProvider: (provider) => {
1628
+ set((state) => {
1629
+ const newProviders = new Map(state.providers);
1630
+ newProviders.set(provider.id, provider);
1631
+ return { providers: newProviders };
1632
+ });
1633
+ },
1634
+ unregisterProvider: (id) => {
1635
+ set((state) => {
1636
+ const newProviders = new Map(state.providers);
1637
+ newProviders.delete(id);
1638
+ return { providers: newProviders };
1639
+ });
1640
+ },
1641
+ updateProvider: (id, updates) => {
1642
+ set((state) => {
1643
+ const newProviders = new Map(state.providers);
1644
+ const existing = newProviders.get(id);
1645
+ if (existing) {
1646
+ newProviders.set(id, { ...existing, ...updates });
1647
+ }
1648
+ return { providers: newProviders };
1649
+ });
1650
+ },
1651
+ findProvidersForElement: (element) => {
1652
+ const { providers } = get();
1653
+ const matching = [];
1654
+ for (const provider of providers.values()) {
1655
+ if (provider.disabled) continue;
1656
+ const container = provider.containerRef.current;
1657
+ if (provider.scoped && container) {
1658
+ if (container.contains(element)) {
1659
+ matching.push(provider);
1660
+ }
1661
+ } else if (!provider.scoped) {
1662
+ matching.push(provider);
1663
+ }
1664
+ }
1665
+ return matching.sort((a, b) => b.depth - a.depth);
1666
+ },
1667
+ findParentProvider: (container, excludeId) => {
1668
+ const { providers } = get();
1669
+ if (!container) return null;
1670
+ let nearestParent = null;
1671
+ let nearestDepth = -1;
1672
+ for (const provider of providers.values()) {
1673
+ if (provider.id === excludeId) continue;
1674
+ const providerContainer = provider.containerRef.current;
1675
+ if (!providerContainer) continue;
1676
+ if (providerContainer.contains(container)) {
1677
+ if (provider.depth > nearestDepth) {
1678
+ nearestParent = provider;
1679
+ nearestDepth = provider.depth;
1680
+ }
1681
+ }
1682
+ }
1683
+ return nearestParent;
1684
+ },
1685
+ getMergedTheme: (providerId) => {
1686
+ const { providers } = get();
1687
+ const provider = providers.get(providerId);
1688
+ if (!provider) {
1689
+ if (process.env.NODE_ENV === "development") {
1690
+ console.log(`[Store:getMergedTheme] Provider not found: ${providerId}`);
1691
+ }
1692
+ return {};
1693
+ }
1694
+ const ancestors = [];
1695
+ let current = provider;
1696
+ while (current) {
1697
+ ancestors.unshift(current);
1698
+ current = current.parentId ? providers.get(current.parentId) : void 0;
1699
+ }
1700
+ if (process.env.NODE_ENV === "development") {
1701
+ console.log(`[Store:getMergedTheme] Provider: ${providerId}`, {
1702
+ ancestorCount: ancestors.length,
1703
+ ancestorIds: ancestors.map((a) => a.id),
1704
+ providerHasTheme: !!provider.theme,
1705
+ providerThemeMenuStyle: provider.theme?.menuStyle ? Object.keys(provider.theme.menuStyle) : []
1706
+ });
1707
+ }
1708
+ const mergedTheme = {};
1709
+ for (const ancestor of ancestors) {
1710
+ if (ancestor.theme) {
1711
+ Object.assign(mergedTheme, ancestor.theme);
1712
+ if (ancestor.theme.highlightConfig) {
1713
+ mergedTheme.highlightConfig = {
1714
+ ...mergedTheme.highlightConfig,
1715
+ ...ancestor.theme.highlightConfig,
1716
+ colors: {
1717
+ ...mergedTheme.highlightConfig?.colors,
1718
+ ...ancestor.theme.highlightConfig.colors
1719
+ }
1720
+ };
1721
+ }
1722
+ if (ancestor.theme.screenshotConfig) {
1723
+ mergedTheme.screenshotConfig = {
1724
+ ...mergedTheme.screenshotConfig,
1725
+ ...ancestor.theme.screenshotConfig
1726
+ };
1727
+ }
1728
+ }
1729
+ }
1730
+ if (process.env.NODE_ENV === "development") {
1731
+ console.log(`[Store:getMergedTheme] Result for ${providerId}`, {
1732
+ hasMenuStyle: !!mergedTheme.menuStyle,
1733
+ menuStyleKeys: mergedTheme.menuStyle ? Object.keys(mergedTheme.menuStyle) : []
1734
+ });
1735
+ }
1736
+ return mergedTheme;
1737
+ },
1738
+ isDisabledByAncestor: (providerId) => {
1739
+ const { providers } = get();
1740
+ const provider = providers.get(providerId);
1741
+ if (!provider) return false;
1742
+ let current = provider;
1743
+ while (current) {
1744
+ if (current.disabled || current.theme?.disabled) {
1745
+ return true;
1746
+ }
1747
+ current = current.parentId ? providers.get(current.parentId) : void 0;
1748
+ }
1749
+ return false;
1750
+ },
1751
+ isElementInDisabledScope: (element) => {
1752
+ const { providers } = get();
1753
+ for (const provider of providers.values()) {
1754
+ if (!provider.scoped) continue;
1755
+ if (!provider.disabled && !provider.theme?.disabled) continue;
1756
+ const container = provider.containerRef.current;
1757
+ if (!container) continue;
1758
+ if (container.contains(element)) {
1759
+ return true;
1760
+ }
1761
+ }
1762
+ return false;
1763
+ },
1764
+ isElementInAnyScopedProvider: (element) => {
1765
+ const { providers } = get();
1766
+ for (const provider of providers.values()) {
1767
+ if (!provider.scoped) continue;
1768
+ const container = provider.containerRef.current;
1769
+ if (!container) continue;
1770
+ if (container.contains(element)) {
1771
+ if (process.env.NODE_ENV === "development") {
1772
+ console.log(
1773
+ `[Store:isElementInAnyScopedProvider] Element is in scoped provider: ${provider.id}`,
1774
+ {
1775
+ elementTag: element.tagName,
1776
+ providerId: provider.id,
1777
+ providerDisabled: provider.disabled
1778
+ }
1779
+ );
1780
+ }
1781
+ return true;
1782
+ }
1783
+ }
1784
+ return false;
1785
+ }
1786
+ }));
1787
+ function dispatchContextMenuEvent(event, element) {
1788
+ const store = useProviderStore.getState();
1789
+ const providers = store.findProvidersForElement(element);
1790
+ const menuEvent = {
1791
+ clientX: event.clientX,
1792
+ clientY: event.clientY,
1793
+ originalEvent: event,
1794
+ isTouch: false
1795
+ };
1796
+ for (const provider of providers) {
1797
+ if (store.isDisabledByAncestor(provider.id)) {
1798
+ continue;
1799
+ }
1800
+ if (provider.onContextMenu) {
1801
+ provider.onContextMenu(menuEvent, element);
1802
+ break;
1803
+ }
1804
+ }
1805
+ }
1806
+
1807
+ // src/AnyclickProvider.tsx
1274
1808
  var import_jsx_runtime3 = require("react/jsx-runtime");
1275
1809
  var defaultMenuItems = [
1276
1810
  { type: "issue", label: "Report an issue", showComment: true },
1277
1811
  { type: "feature", label: "Request a feature", showComment: true },
1278
1812
  { type: "like", label: "I like this!", showComment: false }
1279
1813
  ];
1280
- function FeedbackProvider({
1814
+ function AnyclickProvider({
1281
1815
  adapter,
1282
1816
  children,
1283
1817
  targetFilter,
@@ -1294,7 +1828,11 @@ function FeedbackProvider({
1294
1828
  menuClassName,
1295
1829
  disabled = false,
1296
1830
  highlightConfig,
1297
- screenshotConfig
1831
+ screenshotConfig,
1832
+ scoped = false,
1833
+ theme,
1834
+ touchHoldDurationMs,
1835
+ touchMoveThreshold
1298
1836
  }) {
1299
1837
  const [isSubmitting, setIsSubmitting] = (0, import_react6.useState)(false);
1300
1838
  const [menuVisible, setMenuVisible] = (0, import_react6.useState)(false);
@@ -1303,8 +1841,203 @@ function FeedbackProvider({
1303
1841
  const [containerElement, setContainerElement] = (0, import_react6.useState)(
1304
1842
  null
1305
1843
  );
1844
+ const providerIdRef = (0, import_react6.useRef)(generateProviderId());
1845
+ const providerId = providerIdRef.current;
1846
+ const containerRef = (0, import_react6.useRef)(null);
1847
+ const [containerReady, setContainerReady] = (0, import_react6.useState)(!scoped);
1306
1848
  const clientRef = (0, import_react6.useRef)(null);
1849
+ const setContainerRef = (0, import_react6.useCallback)(
1850
+ (node) => {
1851
+ if (process.env.NODE_ENV === "development") {
1852
+ console.log(`[AnyclickProvider:${providerId}] Container ref callback`, {
1853
+ scoped,
1854
+ nodeReceived: !!node,
1855
+ hadPreviousNode: !!containerRef.current,
1856
+ clientExists: !!clientRef.current
1857
+ });
1858
+ }
1859
+ containerRef.current = node;
1860
+ if (scoped && node) {
1861
+ setContainerReady(true);
1862
+ if (clientRef.current) {
1863
+ clientRef.current.setContainer(node);
1864
+ }
1865
+ }
1866
+ },
1867
+ [scoped, providerId]
1868
+ );
1869
+ let parentContext = null;
1870
+ try {
1871
+ parentContext = useAnyclick();
1872
+ } catch {
1873
+ parentContext = null;
1874
+ }
1875
+ const {
1876
+ registerProvider,
1877
+ unregisterProvider,
1878
+ updateProvider,
1879
+ getMergedTheme,
1880
+ isDisabledByAncestor,
1881
+ findParentProvider,
1882
+ isElementInDisabledScope,
1883
+ isElementInAnyScopedProvider
1884
+ } = useProviderStore();
1885
+ const parentId = parentContext?.providerId ?? null;
1886
+ const depth = parentContext ? parentContext.scoped ? 1 : 0 : 0;
1887
+ const actualDepth = (0, import_react6.useMemo)(() => {
1888
+ if (!parentContext) return 0;
1889
+ let d = 0;
1890
+ let currentId = parentId;
1891
+ const providers = useProviderStore.getState().providers;
1892
+ while (currentId) {
1893
+ d++;
1894
+ const parent = providers.get(currentId);
1895
+ currentId = parent?.parentId ?? null;
1896
+ }
1897
+ return d;
1898
+ }, [parentId]);
1899
+ const isDisabledByTheme = theme === null || theme?.disabled === true;
1900
+ const effectiveDisabled = disabled || isDisabledByTheme;
1901
+ const localTheme = (0, import_react6.useMemo)(() => {
1902
+ if (theme === null) {
1903
+ return { disabled: true };
1904
+ }
1905
+ const explicitThemeProps = {};
1906
+ if (menuStyle) explicitThemeProps.menuStyle = menuStyle;
1907
+ if (menuClassName) explicitThemeProps.menuClassName = menuClassName;
1908
+ if (highlightConfig) explicitThemeProps.highlightConfig = highlightConfig;
1909
+ if (screenshotConfig)
1910
+ explicitThemeProps.screenshotConfig = screenshotConfig;
1911
+ return {
1912
+ ...explicitThemeProps,
1913
+ ...theme
1914
+ };
1915
+ }, [theme, menuStyle, menuClassName, highlightConfig, screenshotConfig]);
1916
+ const handleContextMenu = (0, import_react6.useCallback)(
1917
+ (event, element) => {
1918
+ if (!scoped && isElementInDisabledScope(element)) {
1919
+ if (process.env.NODE_ENV === "development") {
1920
+ console.log(
1921
+ `[AnyclickProvider:${providerId}] Allowing native menu - element is in disabled scope`,
1922
+ {
1923
+ targetTag: element.tagName
1924
+ }
1925
+ );
1926
+ }
1927
+ return false;
1928
+ }
1929
+ if (!scoped && event.isTouch && isElementInAnyScopedProvider(element)) {
1930
+ if (process.env.NODE_ENV === "development") {
1931
+ console.log(
1932
+ `[AnyclickProvider:${providerId}] Deferring to scoped provider for touch event`,
1933
+ {
1934
+ targetTag: element.tagName
1935
+ }
1936
+ );
1937
+ }
1938
+ return false;
1939
+ }
1940
+ const mergedTheme2 = getMergedTheme(providerId);
1941
+ if (process.env.NODE_ENV === "development") {
1942
+ console.log(
1943
+ `[AnyclickProvider:${providerId}] handleContextMenu called`,
1944
+ {
1945
+ scoped,
1946
+ targetTag: element.tagName,
1947
+ mergedThemeColors: mergedTheme2.highlightConfig?.colors,
1948
+ position: { x: event.clientX, y: event.clientY },
1949
+ isTouch: event.isTouch
1950
+ }
1951
+ );
1952
+ }
1953
+ setTargetElement(element);
1954
+ const container = findContainerParent(
1955
+ element,
1956
+ mergedTheme2.highlightConfig ?? highlightConfig
1957
+ );
1958
+ setContainerElement(container);
1959
+ setMenuPosition({ x: event.clientX, y: event.clientY });
1960
+ setMenuVisible(true);
1961
+ return true;
1962
+ },
1963
+ [
1964
+ providerId,
1965
+ getMergedTheme,
1966
+ highlightConfig,
1967
+ scoped,
1968
+ isElementInDisabledScope,
1969
+ isElementInAnyScopedProvider
1970
+ ]
1971
+ );
1972
+ (0, import_react6.useLayoutEffect)(() => {
1973
+ const providerInstance = {
1974
+ id: providerId,
1975
+ containerRef,
1976
+ scoped,
1977
+ theme: localTheme,
1978
+ disabled: effectiveDisabled,
1979
+ parentId,
1980
+ depth: actualDepth,
1981
+ onContextMenu: handleContextMenu
1982
+ };
1983
+ registerProvider(providerInstance);
1984
+ return () => {
1985
+ unregisterProvider(providerId);
1986
+ };
1987
+ }, [
1988
+ providerId,
1989
+ scoped,
1990
+ localTheme,
1991
+ effectiveDisabled,
1992
+ parentId,
1993
+ actualDepth,
1994
+ handleContextMenu,
1995
+ registerProvider,
1996
+ unregisterProvider
1997
+ ]);
1307
1998
  (0, import_react6.useEffect)(() => {
1999
+ updateProvider(providerId, {
2000
+ theme: localTheme,
2001
+ disabled: effectiveDisabled,
2002
+ onContextMenu: handleContextMenu
2003
+ });
2004
+ }, [
2005
+ providerId,
2006
+ localTheme,
2007
+ effectiveDisabled,
2008
+ handleContextMenu,
2009
+ updateProvider
2010
+ ]);
2011
+ (0, import_react6.useEffect)(() => {
2012
+ if (isDisabledByAncestor(providerId)) {
2013
+ if (process.env.NODE_ENV === "development") {
2014
+ console.log(
2015
+ `[AnyclickProvider:${providerId}] Skipping - disabled by ancestor`
2016
+ );
2017
+ }
2018
+ return;
2019
+ }
2020
+ if (scoped && !containerReady) {
2021
+ if (process.env.NODE_ENV === "development") {
2022
+ console.log(
2023
+ `[AnyclickProvider:${providerId}] Waiting for container to be ready`,
2024
+ {
2025
+ scoped,
2026
+ containerReady
2027
+ }
2028
+ );
2029
+ }
2030
+ return;
2031
+ }
2032
+ if (process.env.NODE_ENV === "development") {
2033
+ console.log(`[AnyclickProvider:${providerId}] Creating client`, {
2034
+ scoped,
2035
+ containerReady,
2036
+ hasContainer: !!containerRef.current,
2037
+ effectiveDisabled,
2038
+ localThemeColors: localTheme.highlightConfig?.colors
2039
+ });
2040
+ }
1308
2041
  const client = (0, import_anyclick_core3.createFeedbackClient)({
1309
2042
  adapter,
1310
2043
  targetFilter,
@@ -1312,19 +2045,17 @@ function FeedbackProvider({
1312
2045
  maxOuterHTMLLength,
1313
2046
  maxAncestors,
1314
2047
  cooldownMs,
1315
- stripAttributes
2048
+ stripAttributes,
2049
+ // For scoped providers, pass the container
2050
+ container: scoped ? containerRef.current : null,
2051
+ touchHoldDurationMs,
2052
+ touchMoveThreshold
1316
2053
  });
1317
2054
  client.onSubmitSuccess = onSubmitSuccess;
1318
2055
  client.onSubmitError = onSubmitError;
1319
- client.onContextMenu = (event, element) => {
1320
- setTargetElement(element);
1321
- const container = findContainerParent(element, highlightConfig);
1322
- setContainerElement(container);
1323
- setMenuPosition({ x: event.clientX, y: event.clientY });
1324
- setMenuVisible(true);
1325
- };
2056
+ client.onContextMenu = handleContextMenu;
1326
2057
  clientRef.current = client;
1327
- if (!disabled) {
2058
+ if (!effectiveDisabled) {
1328
2059
  client.attach();
1329
2060
  }
1330
2061
  return () => {
@@ -1340,8 +2071,14 @@ function FeedbackProvider({
1340
2071
  stripAttributes,
1341
2072
  onSubmitSuccess,
1342
2073
  onSubmitError,
1343
- disabled,
1344
- highlightConfig
2074
+ effectiveDisabled,
2075
+ scoped,
2076
+ containerReady,
2077
+ providerId,
2078
+ isDisabledByAncestor,
2079
+ handleContextMenu,
2080
+ touchHoldDurationMs,
2081
+ touchMoveThreshold
1345
2082
  ]);
1346
2083
  const submitFeedback = (0, import_react6.useCallback)(
1347
2084
  async (element, type, comment, screenshots) => {
@@ -1366,12 +2103,16 @@ function FeedbackProvider({
1366
2103
  const openMenu = (0, import_react6.useCallback)(
1367
2104
  (element, position) => {
1368
2105
  setTargetElement(element);
1369
- const container = findContainerParent(element, highlightConfig);
2106
+ const mergedTheme2 = getMergedTheme(providerId);
2107
+ const container = findContainerParent(
2108
+ element,
2109
+ mergedTheme2.highlightConfig ?? highlightConfig
2110
+ );
1370
2111
  setContainerElement(container);
1371
2112
  setMenuPosition(position);
1372
2113
  setMenuVisible(true);
1373
2114
  },
1374
- [highlightConfig]
2115
+ [providerId, getMergedTheme, highlightConfig]
1375
2116
  );
1376
2117
  const closeMenu = (0, import_react6.useCallback)(() => {
1377
2118
  setMenuVisible(false);
@@ -1386,22 +2127,73 @@ function FeedbackProvider({
1386
2127
  },
1387
2128
  [targetElement, submitFeedback]
1388
2129
  );
2130
+ const inheritedTheme = getMergedTheme(providerId);
2131
+ const mergedTheme = (0, import_react6.useMemo)(
2132
+ () => ({
2133
+ ...inheritedTheme,
2134
+ ...localTheme,
2135
+ // Deep merge for nested configs
2136
+ highlightConfig: {
2137
+ ...inheritedTheme.highlightConfig,
2138
+ ...localTheme.highlightConfig,
2139
+ colors: {
2140
+ ...inheritedTheme.highlightConfig?.colors,
2141
+ ...localTheme.highlightConfig?.colors
2142
+ }
2143
+ },
2144
+ screenshotConfig: {
2145
+ ...inheritedTheme.screenshotConfig,
2146
+ ...localTheme.screenshotConfig
2147
+ }
2148
+ }),
2149
+ [inheritedTheme, localTheme]
2150
+ );
2151
+ const effectiveMenuStyle = mergedTheme.menuStyle ?? menuStyle;
2152
+ const effectiveMenuClassName = mergedTheme.menuClassName ?? menuClassName;
2153
+ if (process.env.NODE_ENV === "development") {
2154
+ console.log(`[AnyclickProvider:${providerId}] Theme Debug`, {
2155
+ scoped,
2156
+ localThemeHasMenuStyle: !!localTheme.menuStyle,
2157
+ localThemeMenuStyleKeys: localTheme.menuStyle ? Object.keys(localTheme.menuStyle) : [],
2158
+ mergedThemeHasMenuStyle: !!mergedTheme.menuStyle,
2159
+ mergedThemeMenuStyleKeys: mergedTheme.menuStyle ? Object.keys(mergedTheme.menuStyle) : [],
2160
+ effectiveMenuStyleExists: !!effectiveMenuStyle,
2161
+ effectiveMenuStyleKeys: effectiveMenuStyle ? Object.keys(effectiveMenuStyle) : [],
2162
+ menuStyleProp: !!menuStyle
2163
+ });
2164
+ }
2165
+ const effectiveHighlightConfig = mergedTheme.highlightConfig ?? highlightConfig;
2166
+ const effectiveScreenshotConfig = mergedTheme.screenshotConfig ?? screenshotConfig;
1389
2167
  const contextValue = (0, import_react6.useMemo)(
1390
2168
  () => ({
1391
- isEnabled: !disabled,
2169
+ isEnabled: !effectiveDisabled && !isDisabledByAncestor(providerId),
1392
2170
  isSubmitting,
1393
2171
  submitFeedback,
1394
2172
  openMenu,
1395
- closeMenu
2173
+ closeMenu,
2174
+ theme: mergedTheme,
2175
+ scoped,
2176
+ providerId
1396
2177
  }),
1397
- [disabled, isSubmitting, submitFeedback, openMenu, closeMenu]
2178
+ [
2179
+ effectiveDisabled,
2180
+ providerId,
2181
+ isDisabledByAncestor,
2182
+ isSubmitting,
2183
+ submitFeedback,
2184
+ openMenu,
2185
+ closeMenu,
2186
+ mergedTheme,
2187
+ scoped
2188
+ ]
1398
2189
  );
1399
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(FeedbackContext.Provider, { value: contextValue, children: [
1400
- children,
2190
+ const content = scoped ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { ref: setContainerRef, style: { display: "contents" }, children }) : children;
2191
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(AnyclickContext.Provider, { value: contextValue, children: [
2192
+ content,
1401
2193
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1402
2194
  ContextMenu,
1403
2195
  {
1404
- visible: menuVisible && !disabled,
2196
+ visible: menuVisible && !effectiveDisabled,
1405
2197
  position: menuPosition,
1406
2198
  targetElement,
1407
2199
  containerElement,
@@ -1409,14 +2201,15 @@ function FeedbackProvider({
1409
2201
  onSelect: handleMenuSelect,
1410
2202
  onClose: closeMenu,
1411
2203
  isSubmitting,
1412
- style: menuStyle,
1413
- className: menuClassName,
1414
- highlightConfig,
1415
- screenshotConfig
2204
+ style: effectiveMenuStyle,
2205
+ className: effectiveMenuClassName,
2206
+ highlightConfig: effectiveHighlightConfig,
2207
+ screenshotConfig: effectiveScreenshotConfig
1416
2208
  }
1417
2209
  )
1418
2210
  ] });
1419
2211
  }
2212
+ var FeedbackProvider = AnyclickProvider;
1420
2213
 
1421
2214
  // src/types.ts
1422
2215
  function filterMenuItemsByRole(items, userContext) {
@@ -1438,6 +2231,8 @@ function filterMenuItemsByRole(items, userContext) {
1438
2231
  var import_anyclick_core4 = require("@ewjdev/anyclick-core");
1439
2232
  // Annotate the CommonJS export names for ESM import in node:
1440
2233
  0 && (module.exports = {
2234
+ AnyclickContext,
2235
+ AnyclickProvider,
1441
2236
  ContextMenu,
1442
2237
  DEFAULT_SCREENSHOT_CONFIG,
1443
2238
  DEFAULT_SENSITIVE_SELECTORS,
@@ -1451,15 +2246,20 @@ var import_anyclick_core4 = require("@ewjdev/anyclick-core");
1451
2246
  darkMenuStyles,
1452
2247
  defaultContainerSelectors,
1453
2248
  defaultHighlightColors,
2249
+ dispatchContextMenuEvent,
1454
2250
  estimateTotalSize,
1455
2251
  filterMenuItemsByRole,
1456
2252
  findContainerParent,
1457
2253
  formatBytes,
2254
+ generateProviderId,
1458
2255
  highlightContainer,
1459
2256
  highlightTarget,
1460
2257
  isScreenshotSupported,
2258
+ menuCSSVariables,
1461
2259
  menuStyles,
1462
- useFeedback
2260
+ useAnyclick,
2261
+ useFeedback,
2262
+ useProviderStore
1463
2263
  });
1464
2264
  /*! Bundled license information:
1465
2265
 
@@ -1471,8 +2271,10 @@ lucide-react/dist/esm/icons/camera.js:
1471
2271
  lucide-react/dist/esm/icons/check.js:
1472
2272
  lucide-react/dist/esm/icons/chevron-left.js:
1473
2273
  lucide-react/dist/esm/icons/chevron-right.js:
2274
+ lucide-react/dist/esm/icons/circle-alert.js:
1474
2275
  lucide-react/dist/esm/icons/expand.js:
1475
2276
  lucide-react/dist/esm/icons/flag.js:
2277
+ lucide-react/dist/esm/icons/grip-vertical.js:
1476
2278
  lucide-react/dist/esm/icons/image.js:
1477
2279
  lucide-react/dist/esm/icons/loader-circle.js:
1478
2280
  lucide-react/dist/esm/icons/plus.js: