@ewjdev/anyclick-react 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,1491 @@
1
+ "use strict";
2
+ "use client";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/index.ts
22
+ var index_exports = {};
23
+ __export(index_exports, {
24
+ ContextMenu: () => ContextMenu,
25
+ DEFAULT_SCREENSHOT_CONFIG: () => import_anyclick_core4.DEFAULT_SCREENSHOT_CONFIG,
26
+ DEFAULT_SENSITIVE_SELECTORS: () => import_anyclick_core4.DEFAULT_SENSITIVE_SELECTORS,
27
+ FeedbackContext: () => FeedbackContext,
28
+ FeedbackProvider: () => FeedbackProvider,
29
+ ScreenshotPreview: () => ScreenshotPreview,
30
+ applyHighlights: () => applyHighlights,
31
+ captureAllScreenshots: () => import_anyclick_core4.captureAllScreenshots,
32
+ captureScreenshot: () => import_anyclick_core4.captureScreenshot,
33
+ clearHighlights: () => clearHighlights,
34
+ darkMenuStyles: () => darkMenuStyles,
35
+ defaultContainerSelectors: () => defaultContainerSelectors,
36
+ defaultHighlightColors: () => defaultHighlightColors,
37
+ estimateTotalSize: () => import_anyclick_core4.estimateTotalSize,
38
+ filterMenuItemsByRole: () => filterMenuItemsByRole,
39
+ findContainerParent: () => findContainerParent,
40
+ formatBytes: () => import_anyclick_core4.formatBytes,
41
+ highlightContainer: () => highlightContainer,
42
+ highlightTarget: () => highlightTarget,
43
+ isScreenshotSupported: () => import_anyclick_core4.isScreenshotSupported,
44
+ menuStyles: () => menuStyles,
45
+ useFeedback: () => useFeedback
46
+ });
47
+ module.exports = __toCommonJS(index_exports);
48
+
49
+ // src/FeedbackProvider.tsx
50
+ var import_react6 = require("react");
51
+ var import_anyclick_core3 = require("@ewjdev/anyclick-core");
52
+
53
+ // src/context.ts
54
+ var import_react = require("react");
55
+ var FeedbackContext = (0, import_react.createContext)(null);
56
+ function useFeedback() {
57
+ const context = (0, import_react.useContext)(FeedbackContext);
58
+ if (!context) {
59
+ throw new Error("useFeedback must be used within a FeedbackProvider");
60
+ }
61
+ return context;
62
+ }
63
+
64
+ // src/ContextMenu.tsx
65
+ var import_react5 = require("react");
66
+ var import_anyclick_core2 = require("@ewjdev/anyclick-core");
67
+
68
+ // src/styles.ts
69
+ var menuStyles = {
70
+ overlay: {
71
+ position: "fixed",
72
+ inset: 0,
73
+ zIndex: 9998
74
+ },
75
+ container: {
76
+ position: "fixed",
77
+ zIndex: 9999,
78
+ minWidth: "200px",
79
+ backgroundColor: "#ffffff",
80
+ borderRadius: "8px",
81
+ boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15), 0 0 0 1px rgba(0, 0, 0, 0.05)",
82
+ overflow: "hidden",
83
+ fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
84
+ fontSize: "14px"
85
+ },
86
+ header: {
87
+ padding: "12px 16px",
88
+ borderBottom: "1px solid #e5e5e5",
89
+ color: "#666",
90
+ fontSize: "12px",
91
+ fontWeight: 500,
92
+ textTransform: "uppercase",
93
+ letterSpacing: "0.5px"
94
+ },
95
+ itemList: {
96
+ padding: "4px 0"
97
+ },
98
+ item: {
99
+ display: "flex",
100
+ alignItems: "center",
101
+ gap: "10px",
102
+ padding: "10px 16px",
103
+ cursor: "pointer",
104
+ transition: "background-color 0.15s",
105
+ color: "#333",
106
+ border: "none",
107
+ background: "none",
108
+ width: "100%",
109
+ textAlign: "left",
110
+ fontSize: "14px"
111
+ },
112
+ itemHover: {
113
+ backgroundColor: "#f5f5f5"
114
+ },
115
+ itemIcon: {
116
+ display: "flex",
117
+ alignItems: "center",
118
+ justifyContent: "center",
119
+ width: "20px",
120
+ height: "20px",
121
+ fontSize: "16px"
122
+ },
123
+ commentSection: {
124
+ padding: "12px 16px",
125
+ borderTop: "1px solid #e5e5e5"
126
+ },
127
+ commentInput: {
128
+ width: "100%",
129
+ minHeight: "60px",
130
+ padding: "8px 12px",
131
+ border: "1px solid #ddd",
132
+ borderRadius: "6px",
133
+ fontSize: "14px",
134
+ fontFamily: "inherit",
135
+ resize: "vertical",
136
+ outline: "none",
137
+ boxSizing: "border-box"
138
+ },
139
+ buttonRow: {
140
+ display: "flex",
141
+ justifyContent: "flex-end",
142
+ gap: "8px",
143
+ marginTop: "8px"
144
+ },
145
+ button: {
146
+ padding: "6px 12px",
147
+ borderRadius: "6px",
148
+ fontSize: "13px",
149
+ fontWeight: 500,
150
+ cursor: "pointer",
151
+ transition: "background-color 0.15s, opacity 0.15s",
152
+ border: "none"
153
+ },
154
+ cancelButton: {
155
+ backgroundColor: "#f0f0f0",
156
+ color: "#666",
157
+ display: "flex",
158
+ alignItems: "center",
159
+ gap: "2px"
160
+ },
161
+ submitButton: {
162
+ backgroundColor: "#0066cc",
163
+ color: "#ffffff",
164
+ display: "flex",
165
+ alignItems: "center",
166
+ gap: "2px"
167
+ },
168
+ submitButtonDisabled: {
169
+ opacity: 0.6,
170
+ cursor: "not-allowed"
171
+ }
172
+ };
173
+ var darkMenuStyles = {
174
+ container: {
175
+ ...menuStyles.container,
176
+ backgroundColor: "#1a1a1a",
177
+ boxShadow: "0 4px 12px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(255, 255, 255, 0.1)"
178
+ },
179
+ header: {
180
+ ...menuStyles.header,
181
+ borderBottom: "1px solid #333",
182
+ color: "#888"
183
+ },
184
+ item: {
185
+ ...menuStyles.item,
186
+ color: "#e0e0e0"
187
+ },
188
+ itemHover: {
189
+ backgroundColor: "#2a2a2a"
190
+ },
191
+ commentSection: {
192
+ ...menuStyles.commentSection,
193
+ borderTop: "1px solid #333"
194
+ },
195
+ commentInput: {
196
+ ...menuStyles.commentInput,
197
+ backgroundColor: "#2a2a2a",
198
+ border: "1px solid #444",
199
+ color: "#e0e0e0"
200
+ },
201
+ cancelButton: {
202
+ ...menuStyles.cancelButton,
203
+ backgroundColor: "#333",
204
+ color: "#ccc"
205
+ }
206
+ };
207
+
208
+ // src/highlight.ts
209
+ var HIGHLIGHT_TARGET_CLASS = "uifeedback-highlight-target";
210
+ var HIGHLIGHT_CONTAINER_CLASS = "uifeedback-highlight-container";
211
+ var STYLE_ID = "uifeedback-highlight-styles";
212
+ var defaultHighlightColors = {
213
+ targetColor: "#3b82f6",
214
+ containerColor: "#8b5cf6",
215
+ targetShadowOpacity: 0.25,
216
+ containerShadowOpacity: 0.1
217
+ };
218
+ var defaultContainerSelectors = [
219
+ ".container",
220
+ ".card",
221
+ ".panel",
222
+ ".section",
223
+ ".wrapper",
224
+ ".box",
225
+ ".modal",
226
+ ".dialog",
227
+ ".drawer",
228
+ '[role="dialog"]',
229
+ '[role="region"]',
230
+ '[role="article"]',
231
+ '[role="main"]',
232
+ "article",
233
+ "section",
234
+ "main",
235
+ "aside",
236
+ "nav",
237
+ "header",
238
+ "footer"
239
+ ];
240
+ function generateHighlightCSS(colors) {
241
+ const { targetColor, containerColor, targetShadowOpacity, containerShadowOpacity } = colors;
242
+ const hexToRgba = (hex, alpha) => {
243
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
244
+ if (!result) return `rgba(0, 0, 0, ${alpha})`;
245
+ return `rgba(${parseInt(result[1], 16)}, ${parseInt(result[2], 16)}, ${parseInt(result[3], 16)}, ${alpha})`;
246
+ };
247
+ return `
248
+ .${HIGHLIGHT_TARGET_CLASS} {
249
+ outline: 2px solid ${targetColor} !important;
250
+ outline-offset: 2px !important;
251
+ box-shadow: 0 0 0 4px ${hexToRgba(targetColor, targetShadowOpacity)}, 0 4px 12px ${hexToRgba(targetColor, targetShadowOpacity * 0.6)} !important;
252
+ border-radius: 4px !important;
253
+ position: relative;
254
+ z-index: 9997;
255
+ }
256
+
257
+ .${HIGHLIGHT_CONTAINER_CLASS} {
258
+ outline: 2px dashed ${hexToRgba(containerColor, 0.6)} !important;
259
+ outline-offset: 4px !important;
260
+ box-shadow: 0 0 0 6px ${hexToRgba(containerColor, containerShadowOpacity)} !important;
261
+ border-radius: 6px !important;
262
+ position: relative;
263
+ z-index: 9996;
264
+ }
265
+ `;
266
+ }
267
+ function injectStyles(colors) {
268
+ if (typeof document === "undefined") return;
269
+ const existingStyle = document.getElementById(STYLE_ID);
270
+ if (existingStyle) {
271
+ existingStyle.remove();
272
+ }
273
+ const style = document.createElement("style");
274
+ style.id = STYLE_ID;
275
+ style.textContent = generateHighlightCSS(colors);
276
+ document.head.appendChild(style);
277
+ }
278
+ function findContainerParent(element, config) {
279
+ const containerSelectors = config?.containerSelectors ?? defaultContainerSelectors;
280
+ const minChildren = config?.minChildrenForContainer ?? 2;
281
+ let current = element.parentElement;
282
+ let fallbackContainer = null;
283
+ while (current && current !== document.body) {
284
+ for (const selector of containerSelectors) {
285
+ try {
286
+ if (current.matches(selector)) {
287
+ return current;
288
+ }
289
+ } catch {
290
+ }
291
+ }
292
+ const meaningfulChildren = Array.from(current.children).filter((child) => {
293
+ const tag = child.tagName.toLowerCase();
294
+ if (tag === "script" || tag === "style" || tag === "noscript") {
295
+ return false;
296
+ }
297
+ const style = window.getComputedStyle(child);
298
+ if (style.display === "none" || style.visibility === "hidden") {
299
+ return false;
300
+ }
301
+ return true;
302
+ });
303
+ if (meaningfulChildren.length >= minChildren && !fallbackContainer) {
304
+ fallbackContainer = current;
305
+ }
306
+ if (meaningfulChildren.length >= 3) {
307
+ return current;
308
+ }
309
+ current = current.parentElement;
310
+ }
311
+ return fallbackContainer;
312
+ }
313
+ function highlightTarget(element, colors) {
314
+ const mergedColors = { ...defaultHighlightColors, ...colors };
315
+ injectStyles(mergedColors);
316
+ element.classList.add(HIGHLIGHT_TARGET_CLASS);
317
+ }
318
+ function highlightContainer(element, colors) {
319
+ const mergedColors = { ...defaultHighlightColors, ...colors };
320
+ injectStyles(mergedColors);
321
+ element.classList.add(HIGHLIGHT_CONTAINER_CLASS);
322
+ }
323
+ function clearHighlights() {
324
+ if (typeof document === "undefined") return;
325
+ document.querySelectorAll(`.${HIGHLIGHT_TARGET_CLASS}`).forEach((el) => {
326
+ el.classList.remove(HIGHLIGHT_TARGET_CLASS);
327
+ });
328
+ document.querySelectorAll(`.${HIGHLIGHT_CONTAINER_CLASS}`).forEach((el) => {
329
+ el.classList.remove(HIGHLIGHT_CONTAINER_CLASS);
330
+ });
331
+ }
332
+ function applyHighlights(targetElement, config) {
333
+ if (config?.enabled === false) {
334
+ return { target: targetElement, container: null };
335
+ }
336
+ clearHighlights();
337
+ const colors = { ...defaultHighlightColors, ...config?.colors };
338
+ highlightTarget(targetElement, colors);
339
+ const container = findContainerParent(targetElement, config);
340
+ if (container && container !== targetElement) {
341
+ highlightContainer(container, colors);
342
+ }
343
+ return { target: targetElement, container };
344
+ }
345
+
346
+ // src/ScreenshotPreview.tsx
347
+ var import_react4 = require("react");
348
+ var import_anyclick_core = require("@ewjdev/anyclick-core");
349
+
350
+ // ../../node_modules/lucide-react/dist/esm/createLucideIcon.js
351
+ var import_react3 = require("react");
352
+
353
+ // ../../node_modules/lucide-react/dist/esm/shared/src/utils.js
354
+ var toKebabCase = (string) => string.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
355
+ var toCamelCase = (string) => string.replace(
356
+ /^([A-Z])|[\s-_]+(\w)/g,
357
+ (match, p1, p2) => p2 ? p2.toUpperCase() : p1.toLowerCase()
358
+ );
359
+ var toPascalCase = (string) => {
360
+ const camelCase = toCamelCase(string);
361
+ return camelCase.charAt(0).toUpperCase() + camelCase.slice(1);
362
+ };
363
+ var mergeClasses = (...classes) => classes.filter((className, index, array) => {
364
+ return Boolean(className) && className.trim() !== "" && array.indexOf(className) === index;
365
+ }).join(" ").trim();
366
+ var hasA11yProp = (props) => {
367
+ for (const prop in props) {
368
+ if (prop.startsWith("aria-") || prop === "role" || prop === "title") {
369
+ return true;
370
+ }
371
+ }
372
+ };
373
+
374
+ // ../../node_modules/lucide-react/dist/esm/Icon.js
375
+ var import_react2 = require("react");
376
+
377
+ // ../../node_modules/lucide-react/dist/esm/defaultAttributes.js
378
+ var defaultAttributes = {
379
+ xmlns: "http://www.w3.org/2000/svg",
380
+ width: 24,
381
+ height: 24,
382
+ viewBox: "0 0 24 24",
383
+ fill: "none",
384
+ stroke: "currentColor",
385
+ strokeWidth: 2,
386
+ strokeLinecap: "round",
387
+ strokeLinejoin: "round"
388
+ };
389
+
390
+ // ../../node_modules/lucide-react/dist/esm/Icon.js
391
+ var Icon = (0, import_react2.forwardRef)(
392
+ ({
393
+ color = "currentColor",
394
+ size = 24,
395
+ strokeWidth = 2,
396
+ absoluteStrokeWidth,
397
+ className = "",
398
+ children,
399
+ iconNode,
400
+ ...rest
401
+ }, ref) => (0, import_react2.createElement)(
402
+ "svg",
403
+ {
404
+ ref,
405
+ ...defaultAttributes,
406
+ width: size,
407
+ height: size,
408
+ stroke: color,
409
+ strokeWidth: absoluteStrokeWidth ? Number(strokeWidth) * 24 / Number(size) : strokeWidth,
410
+ className: mergeClasses("lucide", className),
411
+ ...!children && !hasA11yProp(rest) && { "aria-hidden": "true" },
412
+ ...rest
413
+ },
414
+ [
415
+ ...iconNode.map(([tag, attrs]) => (0, import_react2.createElement)(tag, attrs)),
416
+ ...Array.isArray(children) ? children : [children]
417
+ ]
418
+ )
419
+ );
420
+
421
+ // ../../node_modules/lucide-react/dist/esm/createLucideIcon.js
422
+ var createLucideIcon = (iconName, iconNode) => {
423
+ const Component = (0, import_react3.forwardRef)(
424
+ ({ className, ...props }, ref) => (0, import_react3.createElement)(Icon, {
425
+ ref,
426
+ iconNode,
427
+ className: mergeClasses(
428
+ `lucide-${toKebabCase(toPascalCase(iconName))}`,
429
+ `lucide-${iconName}`,
430
+ className
431
+ ),
432
+ ...props
433
+ })
434
+ );
435
+ Component.displayName = toPascalCase(iconName);
436
+ return Component;
437
+ };
438
+
439
+ // ../../node_modules/lucide-react/dist/esm/icons/camera.js
440
+ var __iconNode = [
441
+ [
442
+ "path",
443
+ {
444
+ d: "M13.997 4a2 2 0 0 1 1.76 1.05l.486.9A2 2 0 0 0 18.003 7H20a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V9a2 2 0 0 1 2-2h1.997a2 2 0 0 0 1.759-1.048l.489-.904A2 2 0 0 1 10.004 4z",
445
+ key: "18u6gg"
446
+ }
447
+ ],
448
+ ["circle", { cx: "12", cy: "13", r: "3", key: "1vg3eu" }]
449
+ ];
450
+ var Camera = createLucideIcon("camera", __iconNode);
451
+
452
+ // ../../node_modules/lucide-react/dist/esm/icons/check.js
453
+ var __iconNode2 = [["path", { d: "M20 6 9 17l-5-5", key: "1gmf2c" }]];
454
+ var Check = createLucideIcon("check", __iconNode2);
455
+
456
+ // ../../node_modules/lucide-react/dist/esm/icons/chevron-left.js
457
+ var __iconNode3 = [["path", { d: "m15 18-6-6 6-6", key: "1wnfg3" }]];
458
+ var ChevronLeft = createLucideIcon("chevron-left", __iconNode3);
459
+
460
+ // ../../node_modules/lucide-react/dist/esm/icons/chevron-right.js
461
+ var __iconNode4 = [["path", { d: "m9 18 6-6-6-6", key: "mthhwq" }]];
462
+ var ChevronRight = createLucideIcon("chevron-right", __iconNode4);
463
+
464
+ // ../../node_modules/lucide-react/dist/esm/icons/expand.js
465
+ var __iconNode5 = [
466
+ ["path", { d: "m15 15 6 6", key: "1s409w" }],
467
+ ["path", { d: "m15 9 6-6", key: "ko1vev" }],
468
+ ["path", { d: "M21 16v5h-5", key: "1ck2sf" }],
469
+ ["path", { d: "M21 8V3h-5", key: "1qoq8a" }],
470
+ ["path", { d: "M3 16v5h5", key: "1t08am" }],
471
+ ["path", { d: "m3 21 6-6", key: "wwnumi" }],
472
+ ["path", { d: "M3 8V3h5", key: "1ln10m" }],
473
+ ["path", { d: "M9 9 3 3", key: "v551iv" }]
474
+ ];
475
+ var Expand = createLucideIcon("expand", __iconNode5);
476
+
477
+ // ../../node_modules/lucide-react/dist/esm/icons/flag.js
478
+ var __iconNode6 = [
479
+ [
480
+ "path",
481
+ {
482
+ d: "M4 22V4a1 1 0 0 1 .4-.8A6 6 0 0 1 8 2c3 0 5 2 7.333 2q2 0 3.067-.8A1 1 0 0 1 20 4v10a1 1 0 0 1-.4.8A6 6 0 0 1 16 16c-3 0-5-2-8-2a6 6 0 0 0-4 1.528",
483
+ key: "1jaruq"
484
+ }
485
+ ]
486
+ ];
487
+ var Flag = createLucideIcon("flag", __iconNode6);
488
+
489
+ // ../../node_modules/lucide-react/dist/esm/icons/image.js
490
+ var __iconNode7 = [
491
+ ["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", ry: "2", key: "1m3agn" }],
492
+ ["circle", { cx: "9", cy: "9", r: "2", key: "af1f0g" }],
493
+ ["path", { d: "m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21", key: "1xmnt7" }]
494
+ ];
495
+ var Image = createLucideIcon("image", __iconNode7);
496
+
497
+ // ../../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);
500
+
501
+ // ../../node_modules/lucide-react/dist/esm/icons/plus.js
502
+ var __iconNode9 = [
503
+ ["path", { d: "M5 12h14", key: "1ays0h" }],
504
+ ["path", { d: "M12 5v14", key: "s699le" }]
505
+ ];
506
+ var Plus = createLucideIcon("plus", __iconNode9);
507
+
508
+ // ../../node_modules/lucide-react/dist/esm/icons/refresh-cw.js
509
+ var __iconNode10 = [
510
+ ["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
+ ["path", { d: "M21 3v5h-5", key: "1q7to0" }],
512
+ ["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
+ ["path", { d: "M8 16H3v5", key: "1cv678" }]
514
+ ];
515
+ var RefreshCw = createLucideIcon("refresh-cw", __iconNode10);
516
+
517
+ // ../../node_modules/lucide-react/dist/esm/icons/shrink.js
518
+ var __iconNode11 = [
519
+ ["path", { d: "m15 15 6 6m-6-6v4.8m0-4.8h4.8", key: "17vawe" }],
520
+ ["path", { d: "M9 19.8V15m0 0H4.2M9 15l-6 6", key: "chjx8e" }],
521
+ ["path", { d: "M15 4.2V9m0 0h4.8M15 9l6-6", key: "lav6yq" }],
522
+ ["path", { d: "M9 4.2V9m0 0H4.2M9 9 3 3", key: "1pxi2q" }]
523
+ ];
524
+ var Shrink = createLucideIcon("shrink", __iconNode11);
525
+
526
+ // ../../node_modules/lucide-react/dist/esm/icons/thumbs-up.js
527
+ var __iconNode12 = [
528
+ ["path", { d: "M7 10v12", key: "1qc93n" }],
529
+ [
530
+ "path",
531
+ {
532
+ d: "M15 5.88 14 10h5.83a2 2 0 0 1 1.92 2.56l-2.33 8A2 2 0 0 1 17.5 22H4a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h2.76a2 2 0 0 0 1.79-1.11L12 2a3.13 3.13 0 0 1 3 3.88Z",
533
+ key: "emmmcr"
534
+ }
535
+ ]
536
+ ];
537
+ var ThumbsUp = createLucideIcon("thumbs-up", __iconNode12);
538
+
539
+ // ../../node_modules/lucide-react/dist/esm/icons/x.js
540
+ var __iconNode13 = [
541
+ ["path", { d: "M18 6 6 18", key: "1bl5f8" }],
542
+ ["path", { d: "m6 6 12 12", key: "d8bk6v" }]
543
+ ];
544
+ var X = createLucideIcon("x", __iconNode13);
545
+
546
+ // src/ScreenshotPreview.tsx
547
+ var import_jsx_runtime = require("react/jsx-runtime");
548
+ function ScreenshotPreview({
549
+ screenshots,
550
+ isLoading,
551
+ onConfirm,
552
+ onCancel,
553
+ onRetake,
554
+ isSubmitting
555
+ }) {
556
+ const [activeTab, setActiveTab] = (0, import_react4.useState)("element");
557
+ const [isExpanded, setIsExpanded] = (0, import_react4.useState)(false);
558
+ 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: [
560
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
561
+ LoaderCircle,
562
+ {
563
+ className: "w-6 h-6 animate-spin",
564
+ style: { color: "#3b82f6" }
565
+ }
566
+ ),
567
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.loadingText, children: "Capturing screenshots..." })
568
+ ] }) });
569
+ }
570
+ 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: [
572
+ /* @__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
+ }
589
+ const allTabs = [
590
+ { key: "element", label: "Element", data: screenshots.element },
591
+ {
592
+ key: "container",
593
+ label: "Container",
594
+ data: screenshots.container
595
+ },
596
+ { key: "viewport", label: "Viewport", data: screenshots.viewport }
597
+ ];
598
+ const tabs = allTabs.filter((tab) => tab.data);
599
+ const activeScreenshot = activeTab === "element" ? screenshots.element : activeTab === "container" ? screenshots.container : screenshots.viewport;
600
+ const totalSize = (0, import_anyclick_core.estimateTotalSize)(screenshots);
601
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
602
+ "div",
603
+ {
604
+ style: {
605
+ ...styles.container,
606
+ ...isExpanded ? styles.containerExpanded : {}
607
+ },
608
+ 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) }),
613
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
614
+ "button",
615
+ {
616
+ type: "button",
617
+ onClick: () => setIsExpanded(!isExpanded),
618
+ style: styles.iconButton,
619
+ title: isExpanded ? "Collapse" : "Expand",
620
+ 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
+ }
622
+ )
623
+ ] })
624
+ ] }),
625
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.tabContainer, children: tabs.map((tab) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
626
+ "button",
627
+ {
628
+ type: "button",
629
+ onClick: () => setActiveTab(tab.key),
630
+ style: {
631
+ ...styles.tab,
632
+ ...activeTab === tab.key ? styles.tabActive : {}
633
+ },
634
+ children: [
635
+ tab.label,
636
+ tab.data && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.tabSize, children: (0, import_anyclick_core.formatBytes)(tab.data.sizeBytes) })
637
+ ]
638
+ },
639
+ tab.key
640
+ )) }),
641
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
642
+ "div",
643
+ {
644
+ style: {
645
+ ...styles.previewContainer,
646
+ ...isExpanded ? styles.previewContainerExpanded : {}
647
+ },
648
+ children: activeScreenshot ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
649
+ "img",
650
+ {
651
+ src: activeScreenshot.dataUrl,
652
+ alt: `${activeTab} screenshot`,
653
+ style: styles.previewImage
654
+ }
655
+ ) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.noPreview, children: [
656
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Image, { className: "w-6 h-6", style: { color: "#9ca3af" } }),
657
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
658
+ "No ",
659
+ activeTab,
660
+ " screenshot"
661
+ ] })
662
+ ] })
663
+ }
664
+ ),
665
+ activeScreenshot && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.dimensionsInfo, children: [
666
+ activeScreenshot.width,
667
+ " \xD7 ",
668
+ activeScreenshot.height,
669
+ "px"
670
+ ] }),
671
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.actions, children: [
672
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
673
+ "button",
674
+ {
675
+ type: "button",
676
+ onClick: onRetake,
677
+ disabled: isSubmitting,
678
+ style: styles.retakeButtonSmall,
679
+ children: [
680
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RefreshCw, { className: "w-3 h-3" }),
681
+ "Retake"
682
+ ]
683
+ }
684
+ ),
685
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.actionsRight, children: [
686
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
687
+ "button",
688
+ {
689
+ type: "button",
690
+ onClick: onCancel,
691
+ disabled: isSubmitting,
692
+ className: "flex items-center gap-1",
693
+ style: { ...menuStyles.button, ...menuStyles.cancelButton },
694
+ children: [
695
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(X, { className: "w-3 h-3" }),
696
+ "Cancel"
697
+ ]
698
+ }
699
+ ),
700
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
701
+ "button",
702
+ {
703
+ type: "button",
704
+ onClick: () => onConfirm(screenshots),
705
+ disabled: isSubmitting,
706
+ style: {
707
+ ...menuStyles.button,
708
+ ...menuStyles.submitButton,
709
+ ...isSubmitting ? menuStyles.submitButtonDisabled : {}
710
+ },
711
+ children: isSubmitting ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
712
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LoaderCircle, { className: "w-3 h-3 animate-spin" }),
713
+ "Sending..."
714
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
715
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Check, { className: "w-3 h-3" }),
716
+ "Send"
717
+ ] })
718
+ }
719
+ )
720
+ ] })
721
+ ] })
722
+ ]
723
+ }
724
+ );
725
+ }
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
+
904
+ // src/ContextMenu.tsx
905
+ var import_jsx_runtime2 = require("react/jsx-runtime");
906
+ var defaultIcons = {
907
+ issue: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Flag, { className: "w-4 h-4" }),
908
+ feature: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Plus, { className: "w-4 h-4" }),
909
+ like: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ThumbsUp, { className: "w-4 h-4" })
910
+ };
911
+ function MenuItem({
912
+ item,
913
+ onClick,
914
+ disabled,
915
+ hasChildren
916
+ }) {
917
+ const [isHovered, setIsHovered] = (0, import_react5.useState)(false);
918
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
919
+ "button",
920
+ {
921
+ type: "button",
922
+ onClick,
923
+ disabled,
924
+ onMouseEnter: () => setIsHovered(true),
925
+ onMouseLeave: () => setIsHovered(false),
926
+ style: {
927
+ ...menuStyles.item,
928
+ ...isHovered ? menuStyles.itemHover : {},
929
+ ...disabled ? { opacity: 0.5, cursor: "not-allowed" } : {}
930
+ },
931
+ children: [
932
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: menuStyles.itemIcon, children: item.icon ?? defaultIcons[item.type] }),
933
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { flex: 1 }, children: item.label }),
934
+ hasChildren && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
935
+ ChevronRight,
936
+ {
937
+ className: "w-4 h-4",
938
+ style: { marginLeft: "auto", opacity: 0.5 }
939
+ }
940
+ )
941
+ ]
942
+ }
943
+ );
944
+ }
945
+ function BackButton({ onClick }) {
946
+ const [isHovered, setIsHovered] = (0, import_react5.useState)(false);
947
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
948
+ "button",
949
+ {
950
+ type: "button",
951
+ onClick,
952
+ onMouseEnter: () => setIsHovered(true),
953
+ onMouseLeave: () => setIsHovered(false),
954
+ style: {
955
+ ...menuStyles.item,
956
+ ...isHovered ? menuStyles.itemHover : {},
957
+ borderBottom: "1px solid #e5e5e5",
958
+ marginBottom: "4px"
959
+ },
960
+ children: [
961
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ChevronLeft, { className: "w-4 h-4", style: { opacity: 0.5 } }),
962
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { opacity: 0.7 }, children: "Back" })
963
+ ]
964
+ }
965
+ );
966
+ }
967
+ function CommentForm({
968
+ onSubmit,
969
+ onCancel,
970
+ isSubmitting
971
+ }) {
972
+ const [comment, setComment] = (0, import_react5.useState)("");
973
+ const inputRef = (0, import_react5.useRef)(null);
974
+ (0, import_react5.useEffect)(() => {
975
+ inputRef.current?.focus();
976
+ }, []);
977
+ const handleSubmit = () => {
978
+ onSubmit(comment);
979
+ };
980
+ const handleKeyDown = (e) => {
981
+ if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
982
+ handleSubmit();
983
+ } else if (e.key === "Escape") {
984
+ onCancel();
985
+ }
986
+ };
987
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: menuStyles.commentSection, children: [
988
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
989
+ "textarea",
990
+ {
991
+ ref: inputRef,
992
+ value: comment,
993
+ onChange: (e) => setComment(e.target.value),
994
+ onKeyDown: handleKeyDown,
995
+ placeholder: "Add a comment (optional)...",
996
+ style: menuStyles.commentInput,
997
+ disabled: isSubmitting
998
+ }
999
+ ),
1000
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: menuStyles.buttonRow, children: [
1001
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1002
+ "button",
1003
+ {
1004
+ type: "button",
1005
+ onClick: onCancel,
1006
+ disabled: isSubmitting,
1007
+ style: { ...menuStyles.button, ...menuStyles.cancelButton },
1008
+ children: "Cancel"
1009
+ }
1010
+ ),
1011
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1012
+ "button",
1013
+ {
1014
+ type: "button",
1015
+ onClick: handleSubmit,
1016
+ disabled: isSubmitting,
1017
+ style: {
1018
+ ...menuStyles.button,
1019
+ ...menuStyles.submitButton,
1020
+ ...isSubmitting ? menuStyles.submitButtonDisabled : {}
1021
+ },
1022
+ children: isSubmitting ? "Sending..." : "Send"
1023
+ }
1024
+ )
1025
+ ] })
1026
+ ] });
1027
+ }
1028
+ function ContextMenu({
1029
+ visible,
1030
+ position,
1031
+ targetElement,
1032
+ containerElement,
1033
+ items,
1034
+ onSelect,
1035
+ onClose,
1036
+ isSubmitting,
1037
+ style,
1038
+ className,
1039
+ highlightConfig,
1040
+ screenshotConfig
1041
+ }) {
1042
+ const [selectedType, setSelectedType] = (0, import_react5.useState)(null);
1043
+ const [currentView, setCurrentView] = (0, import_react5.useState)("menu");
1044
+ const [pendingComment, setPendingComment] = (0, import_react5.useState)();
1045
+ const [submenuStack, setSubmenuStack] = (0, import_react5.useState)([]);
1046
+ const [screenshots, setScreenshots] = (0, import_react5.useState)(null);
1047
+ const [isCapturing, setIsCapturing] = (0, import_react5.useState)(false);
1048
+ const menuRef = (0, import_react5.useRef)(null);
1049
+ const mergedScreenshotConfig = {
1050
+ ...import_anyclick_core2.DEFAULT_SCREENSHOT_CONFIG,
1051
+ ...screenshotConfig
1052
+ };
1053
+ const showPreview = mergedScreenshotConfig.showPreview && (0, import_anyclick_core2.isScreenshotSupported)();
1054
+ const currentItems = submenuStack.length > 0 ? submenuStack[submenuStack.length - 1] : items;
1055
+ const captureScreenshots = (0, import_react5.useCallback)(async () => {
1056
+ if (!targetElement || !showPreview) return;
1057
+ setIsCapturing(true);
1058
+ try {
1059
+ const captured = await (0, import_anyclick_core2.captureAllScreenshots)(
1060
+ targetElement,
1061
+ containerElement,
1062
+ mergedScreenshotConfig
1063
+ );
1064
+ setScreenshots(captured);
1065
+ } catch (error) {
1066
+ console.error("Failed to capture screenshots:", error);
1067
+ setScreenshots(null);
1068
+ } finally {
1069
+ setIsCapturing(false);
1070
+ }
1071
+ }, [targetElement, containerElement, mergedScreenshotConfig, showPreview]);
1072
+ (0, import_react5.useEffect)(() => {
1073
+ if (!visible) {
1074
+ setSelectedType(null);
1075
+ setCurrentView("menu");
1076
+ setPendingComment(void 0);
1077
+ setSubmenuStack([]);
1078
+ setScreenshots(null);
1079
+ setIsCapturing(false);
1080
+ }
1081
+ }, [visible]);
1082
+ (0, import_react5.useEffect)(() => {
1083
+ if (visible && targetElement) {
1084
+ clearHighlights();
1085
+ applyHighlights(targetElement, highlightConfig);
1086
+ } else {
1087
+ clearHighlights();
1088
+ }
1089
+ return () => {
1090
+ clearHighlights();
1091
+ };
1092
+ }, [visible, targetElement, highlightConfig]);
1093
+ (0, import_react5.useEffect)(() => {
1094
+ if (!visible) {
1095
+ return;
1096
+ }
1097
+ const handlePointerDown = (event) => {
1098
+ if (!menuRef.current) return;
1099
+ const target = event.target;
1100
+ if (!menuRef.current.contains(target)) {
1101
+ onClose();
1102
+ }
1103
+ };
1104
+ document.addEventListener("pointerdown", handlePointerDown);
1105
+ return () => {
1106
+ document.removeEventListener("pointerdown", handlePointerDown);
1107
+ };
1108
+ }, [visible, onClose]);
1109
+ (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`;
1125
+ }
1126
+ }
1127
+ }, [visible, position]);
1128
+ (0, import_react5.useEffect)(() => {
1129
+ const handleKeyDown = (e) => {
1130
+ if (e.key === "Escape") {
1131
+ if (currentView === "screenshot-preview") {
1132
+ setCurrentView("comment");
1133
+ } else if (currentView === "comment") {
1134
+ setCurrentView("menu");
1135
+ setSelectedType(null);
1136
+ setPendingComment(void 0);
1137
+ } else if (submenuStack.length > 0) {
1138
+ setSubmenuStack((prev) => prev.slice(0, -1));
1139
+ } else {
1140
+ onClose();
1141
+ }
1142
+ }
1143
+ };
1144
+ if (visible) {
1145
+ document.addEventListener("keydown", handleKeyDown);
1146
+ return () => document.removeEventListener("keydown", handleKeyDown);
1147
+ }
1148
+ }, [visible, currentView, submenuStack.length, onClose]);
1149
+ if (!visible || !targetElement) {
1150
+ return null;
1151
+ }
1152
+ const handleItemClick = (item) => {
1153
+ if (item.children && item.children.length > 0) {
1154
+ setSubmenuStack((prev) => [...prev, item.children]);
1155
+ return;
1156
+ }
1157
+ if (item.showComment) {
1158
+ setSelectedType(item.type);
1159
+ setCurrentView("comment");
1160
+ } else {
1161
+ if (showPreview) {
1162
+ setSelectedType(item.type);
1163
+ setCurrentView("screenshot-preview");
1164
+ captureScreenshots();
1165
+ } else {
1166
+ onSelect(item.type);
1167
+ }
1168
+ }
1169
+ };
1170
+ const handleBack = () => {
1171
+ setSubmenuStack((prev) => prev.slice(0, -1));
1172
+ };
1173
+ const handleCommentSubmit = (comment) => {
1174
+ if (!selectedType) return;
1175
+ if (showPreview) {
1176
+ setPendingComment(comment || void 0);
1177
+ setCurrentView("screenshot-preview");
1178
+ captureScreenshots();
1179
+ } else {
1180
+ onSelect(selectedType, comment || void 0);
1181
+ }
1182
+ };
1183
+ const handleCommentCancel = () => {
1184
+ setCurrentView("menu");
1185
+ setSelectedType(null);
1186
+ setPendingComment(void 0);
1187
+ };
1188
+ const handleScreenshotConfirm = (confirmedScreenshots) => {
1189
+ if (selectedType) {
1190
+ onSelect(selectedType, pendingComment, confirmedScreenshots);
1191
+ }
1192
+ };
1193
+ const handleScreenshotCancel = () => {
1194
+ if (pendingComment !== void 0) {
1195
+ setCurrentView("comment");
1196
+ } else {
1197
+ setCurrentView("menu");
1198
+ setSelectedType(null);
1199
+ }
1200
+ setScreenshots(null);
1201
+ };
1202
+ const handleRetakeScreenshots = () => {
1203
+ captureScreenshots();
1204
+ };
1205
+ const containerWidth = currentView === "screenshot-preview" ? 360 : void 0;
1206
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1207
+ "div",
1208
+ {
1209
+ ref: menuRef,
1210
+ className,
1211
+ style: {
1212
+ ...menuStyles.container,
1213
+ left: position.x,
1214
+ top: position.y,
1215
+ ...containerWidth ? { width: containerWidth, minWidth: containerWidth } : {},
1216
+ ...style
1217
+ },
1218
+ role: "menu",
1219
+ "aria-label": "Feedback options",
1220
+ children: [
1221
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: menuStyles.header, children: currentView === "screenshot-preview" ? "Review Screenshots" : "Send Feedback" }),
1222
+ currentView === "menu" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: menuStyles.itemList, children: [
1223
+ submenuStack.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(BackButton, { onClick: handleBack }),
1224
+ currentItems.map((item) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1225
+ MenuItem,
1226
+ {
1227
+ item,
1228
+ onClick: () => handleItemClick(item),
1229
+ disabled: isSubmitting,
1230
+ hasChildren: item.children && item.children.length > 0
1231
+ },
1232
+ item.type
1233
+ )),
1234
+ showPreview && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: screenshotIndicatorStyle, children: [
1235
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Camera, { className: "w-3 h-3" }),
1236
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: "Screenshots will be captured" })
1237
+ ] })
1238
+ ] }),
1239
+ currentView === "comment" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1240
+ CommentForm,
1241
+ {
1242
+ onSubmit: handleCommentSubmit,
1243
+ onCancel: handleCommentCancel,
1244
+ isSubmitting
1245
+ }
1246
+ ),
1247
+ currentView === "screenshot-preview" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1248
+ ScreenshotPreview,
1249
+ {
1250
+ screenshots,
1251
+ isLoading: isCapturing,
1252
+ onConfirm: handleScreenshotConfirm,
1253
+ onCancel: handleScreenshotCancel,
1254
+ onRetake: handleRetakeScreenshots,
1255
+ isSubmitting
1256
+ }
1257
+ )
1258
+ ]
1259
+ }
1260
+ );
1261
+ }
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
+
1273
+ // src/FeedbackProvider.tsx
1274
+ var import_jsx_runtime3 = require("react/jsx-runtime");
1275
+ var defaultMenuItems = [
1276
+ { type: "issue", label: "Report an issue", showComment: true },
1277
+ { type: "feature", label: "Request a feature", showComment: true },
1278
+ { type: "like", label: "I like this!", showComment: false }
1279
+ ];
1280
+ function FeedbackProvider({
1281
+ adapter,
1282
+ children,
1283
+ targetFilter,
1284
+ menuItems = defaultMenuItems,
1285
+ maxInnerTextLength,
1286
+ maxOuterHTMLLength,
1287
+ maxAncestors,
1288
+ cooldownMs,
1289
+ stripAttributes,
1290
+ metadata,
1291
+ onSubmitSuccess,
1292
+ onSubmitError,
1293
+ menuStyle,
1294
+ menuClassName,
1295
+ disabled = false,
1296
+ highlightConfig,
1297
+ screenshotConfig
1298
+ }) {
1299
+ const [isSubmitting, setIsSubmitting] = (0, import_react6.useState)(false);
1300
+ const [menuVisible, setMenuVisible] = (0, import_react6.useState)(false);
1301
+ const [menuPosition, setMenuPosition] = (0, import_react6.useState)({ x: 0, y: 0 });
1302
+ const [targetElement, setTargetElement] = (0, import_react6.useState)(null);
1303
+ const [containerElement, setContainerElement] = (0, import_react6.useState)(
1304
+ null
1305
+ );
1306
+ const clientRef = (0, import_react6.useRef)(null);
1307
+ (0, import_react6.useEffect)(() => {
1308
+ const client = (0, import_anyclick_core3.createFeedbackClient)({
1309
+ adapter,
1310
+ targetFilter,
1311
+ maxInnerTextLength,
1312
+ maxOuterHTMLLength,
1313
+ maxAncestors,
1314
+ cooldownMs,
1315
+ stripAttributes
1316
+ });
1317
+ client.onSubmitSuccess = onSubmitSuccess;
1318
+ 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
+ };
1326
+ clientRef.current = client;
1327
+ if (!disabled) {
1328
+ client.attach();
1329
+ }
1330
+ return () => {
1331
+ client.detach();
1332
+ };
1333
+ }, [
1334
+ adapter,
1335
+ targetFilter,
1336
+ maxInnerTextLength,
1337
+ maxOuterHTMLLength,
1338
+ maxAncestors,
1339
+ cooldownMs,
1340
+ stripAttributes,
1341
+ onSubmitSuccess,
1342
+ onSubmitError,
1343
+ disabled,
1344
+ highlightConfig
1345
+ ]);
1346
+ const submitFeedback = (0, import_react6.useCallback)(
1347
+ async (element, type, comment, screenshots) => {
1348
+ const client = clientRef.current;
1349
+ if (!client) return;
1350
+ setIsSubmitting(true);
1351
+ try {
1352
+ await client.submitFeedback(element, type, {
1353
+ comment,
1354
+ metadata,
1355
+ screenshots
1356
+ });
1357
+ } finally {
1358
+ setIsSubmitting(false);
1359
+ setMenuVisible(false);
1360
+ setTargetElement(null);
1361
+ setContainerElement(null);
1362
+ }
1363
+ },
1364
+ [metadata]
1365
+ );
1366
+ const openMenu = (0, import_react6.useCallback)(
1367
+ (element, position) => {
1368
+ setTargetElement(element);
1369
+ const container = findContainerParent(element, highlightConfig);
1370
+ setContainerElement(container);
1371
+ setMenuPosition(position);
1372
+ setMenuVisible(true);
1373
+ },
1374
+ [highlightConfig]
1375
+ );
1376
+ const closeMenu = (0, import_react6.useCallback)(() => {
1377
+ setMenuVisible(false);
1378
+ setTargetElement(null);
1379
+ setContainerElement(null);
1380
+ }, []);
1381
+ const handleMenuSelect = (0, import_react6.useCallback)(
1382
+ (type, comment, screenshots) => {
1383
+ if (targetElement) {
1384
+ submitFeedback(targetElement, type, comment, screenshots);
1385
+ }
1386
+ },
1387
+ [targetElement, submitFeedback]
1388
+ );
1389
+ const contextValue = (0, import_react6.useMemo)(
1390
+ () => ({
1391
+ isEnabled: !disabled,
1392
+ isSubmitting,
1393
+ submitFeedback,
1394
+ openMenu,
1395
+ closeMenu
1396
+ }),
1397
+ [disabled, isSubmitting, submitFeedback, openMenu, closeMenu]
1398
+ );
1399
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(FeedbackContext.Provider, { value: contextValue, children: [
1400
+ children,
1401
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1402
+ ContextMenu,
1403
+ {
1404
+ visible: menuVisible && !disabled,
1405
+ position: menuPosition,
1406
+ targetElement,
1407
+ containerElement,
1408
+ items: menuItems,
1409
+ onSelect: handleMenuSelect,
1410
+ onClose: closeMenu,
1411
+ isSubmitting,
1412
+ style: menuStyle,
1413
+ className: menuClassName,
1414
+ highlightConfig,
1415
+ screenshotConfig
1416
+ }
1417
+ )
1418
+ ] });
1419
+ }
1420
+
1421
+ // src/types.ts
1422
+ function filterMenuItemsByRole(items, userContext) {
1423
+ if (!userContext) {
1424
+ return items.filter(
1425
+ (item) => !item.requiredRoles || item.requiredRoles.length === 0
1426
+ );
1427
+ }
1428
+ const userRoles = userContext.roles ?? [];
1429
+ return items.filter((item) => {
1430
+ if (!item.requiredRoles || item.requiredRoles.length === 0) {
1431
+ return true;
1432
+ }
1433
+ return item.requiredRoles.some((role) => userRoles.includes(role));
1434
+ });
1435
+ }
1436
+
1437
+ // src/index.ts
1438
+ var import_anyclick_core4 = require("@ewjdev/anyclick-core");
1439
+ // Annotate the CommonJS export names for ESM import in node:
1440
+ 0 && (module.exports = {
1441
+ ContextMenu,
1442
+ DEFAULT_SCREENSHOT_CONFIG,
1443
+ DEFAULT_SENSITIVE_SELECTORS,
1444
+ FeedbackContext,
1445
+ FeedbackProvider,
1446
+ ScreenshotPreview,
1447
+ applyHighlights,
1448
+ captureAllScreenshots,
1449
+ captureScreenshot,
1450
+ clearHighlights,
1451
+ darkMenuStyles,
1452
+ defaultContainerSelectors,
1453
+ defaultHighlightColors,
1454
+ estimateTotalSize,
1455
+ filterMenuItemsByRole,
1456
+ findContainerParent,
1457
+ formatBytes,
1458
+ highlightContainer,
1459
+ highlightTarget,
1460
+ isScreenshotSupported,
1461
+ menuStyles,
1462
+ useFeedback
1463
+ });
1464
+ /*! Bundled license information:
1465
+
1466
+ lucide-react/dist/esm/shared/src/utils.js:
1467
+ lucide-react/dist/esm/defaultAttributes.js:
1468
+ lucide-react/dist/esm/Icon.js:
1469
+ lucide-react/dist/esm/createLucideIcon.js:
1470
+ lucide-react/dist/esm/icons/camera.js:
1471
+ lucide-react/dist/esm/icons/check.js:
1472
+ lucide-react/dist/esm/icons/chevron-left.js:
1473
+ lucide-react/dist/esm/icons/chevron-right.js:
1474
+ lucide-react/dist/esm/icons/expand.js:
1475
+ lucide-react/dist/esm/icons/flag.js:
1476
+ lucide-react/dist/esm/icons/image.js:
1477
+ lucide-react/dist/esm/icons/loader-circle.js:
1478
+ lucide-react/dist/esm/icons/plus.js:
1479
+ lucide-react/dist/esm/icons/refresh-cw.js:
1480
+ lucide-react/dist/esm/icons/shrink.js:
1481
+ lucide-react/dist/esm/icons/thumbs-up.js:
1482
+ lucide-react/dist/esm/icons/x.js:
1483
+ lucide-react/dist/esm/lucide-react.js:
1484
+ (**
1485
+ * @license lucide-react v0.544.0 - ISC
1486
+ *
1487
+ * This source code is licensed under the ISC license.
1488
+ * See the LICENSE file in the root directory of this source tree.
1489
+ *)
1490
+ */
1491
+ //# sourceMappingURL=index.js.map