@ewjdev/anyclick-react 2.0.0 → 3.0.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.mjs CHANGED
@@ -2,18 +2,18 @@
2
2
 
3
3
  // src/AnyclickProvider.tsx
4
4
  import {
5
- useCallback as useCallback2,
6
- useEffect as useEffect2,
5
+ useCallback as useCallback4,
6
+ useEffect as useEffect4,
7
7
  useId,
8
8
  useLayoutEffect,
9
- useMemo as useMemo2,
10
- useRef as useRef2,
11
- useState as useState3
9
+ useMemo as useMemo3,
10
+ useRef as useRef4,
11
+ useState as useState5
12
12
  } from "react";
13
13
  import { createAnyclickClient } from "@ewjdev/anyclick-core";
14
14
 
15
15
  // src/ContextMenu.tsx
16
- import React2, { useCallback, useEffect, useRef, useState as useState2 } from "react";
16
+ import React3, { useCallback as useCallback3, useEffect as useEffect3, useRef as useRef3, useState as useState4 } from "react";
17
17
  import {
18
18
  DEFAULT_SCREENSHOT_CONFIG,
19
19
  captureAllScreenshots,
@@ -26,11 +26,1356 @@ import {
26
26
  FlagIcon,
27
27
  GripVertical,
28
28
  PlusIcon,
29
+ Sparkles,
29
30
  ThumbsUpIcon
30
31
  } from "lucide-react";
31
32
 
33
+ // src/QuickChat/QuickChat.tsx
34
+ import React, {
35
+ useCallback as useCallback2,
36
+ useEffect as useEffect2,
37
+ useMemo,
38
+ useRef as useRef2,
39
+ useState as useState2
40
+ } from "react";
41
+ import {
42
+ AlertCircle,
43
+ ChevronDown,
44
+ ChevronLeft,
45
+ ChevronUp,
46
+ Copy,
47
+ ExternalLink,
48
+ Pin,
49
+ PinOff,
50
+ RefreshCw,
51
+ Send,
52
+ X
53
+ } from "lucide-react";
54
+
55
+ // src/QuickChat/styles.ts
56
+ var quickChatStyles = {
57
+ // Inline container - inside the context menu
58
+ container: {
59
+ display: "flex",
60
+ flexDirection: "column",
61
+ width: "100%",
62
+ maxHeight: "320px",
63
+ overflow: "hidden"
64
+ },
65
+ // Pinned drawer - anchored to right side of screen
66
+ pinnedContainer: {
67
+ position: "fixed",
68
+ top: 0,
69
+ right: 0,
70
+ bottom: 0,
71
+ width: "340px",
72
+ display: "flex",
73
+ flexDirection: "column",
74
+ backgroundColor: "var(--anyclick-menu-bg, #ffffff)",
75
+ borderLeft: "1px solid var(--anyclick-menu-border, #e5e5e5)",
76
+ boxShadow: "-4px 0 24px rgba(0, 0, 0, 0.15)",
77
+ fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
78
+ fontSize: "14px",
79
+ overflow: "hidden",
80
+ zIndex: 9998
81
+ },
82
+ // Pinned messages area (taller)
83
+ pinnedMessagesArea: {
84
+ flex: 1,
85
+ overflowY: "auto",
86
+ padding: "12px",
87
+ display: "flex",
88
+ flexDirection: "column",
89
+ gap: "8px",
90
+ minHeight: "200px"
91
+ },
92
+ // Header - minimal design
93
+ header: {
94
+ display: "flex",
95
+ alignItems: "center",
96
+ justifyContent: "space-between",
97
+ padding: "6px 8px",
98
+ gap: "8px"
99
+ },
100
+ headerTitle: {
101
+ fontSize: "11px",
102
+ fontWeight: 600,
103
+ textTransform: "uppercase",
104
+ letterSpacing: "0.5px",
105
+ color: "var(--anyclick-menu-text-muted, #666)"
106
+ },
107
+ headerActions: {
108
+ display: "flex",
109
+ alignItems: "center",
110
+ gap: "2px"
111
+ },
112
+ iconButton: {
113
+ display: "flex",
114
+ alignItems: "center",
115
+ justifyContent: "center",
116
+ width: "24px",
117
+ height: "24px",
118
+ border: "none",
119
+ borderRadius: "4px",
120
+ backgroundColor: "transparent",
121
+ color: "var(--anyclick-menu-text-muted, #666)",
122
+ cursor: "pointer",
123
+ transition: "all 0.15s ease"
124
+ },
125
+ iconButtonActive: {
126
+ backgroundColor: "var(--anyclick-menu-accent, #0066cc)",
127
+ color: "#fff"
128
+ },
129
+ // Context badge in header
130
+ contextBadge: {
131
+ display: "inline-flex",
132
+ alignItems: "center",
133
+ gap: "4px",
134
+ padding: "3px 8px",
135
+ fontSize: "10px",
136
+ fontWeight: 500,
137
+ border: "1px solid var(--anyclick-menu-border, #e5e5e5)",
138
+ borderRadius: "10px",
139
+ backgroundColor: "transparent",
140
+ color: "var(--anyclick-menu-text-muted, #666)",
141
+ cursor: "pointer",
142
+ transition: "all 0.15s ease"
143
+ },
144
+ contextBadgeActive: {
145
+ borderColor: "var(--anyclick-menu-accent, #0066cc)",
146
+ backgroundColor: "rgba(0, 102, 204, 0.08)"
147
+ },
148
+ // Context dropdown (in normal flow, not absolute)
149
+ contextDropdown: {
150
+ display: "flex",
151
+ flexDirection: "column",
152
+ gap: "2px",
153
+ padding: "6px 8px",
154
+ margin: "0 8px 6px 8px",
155
+ backgroundColor: "var(--anyclick-menu-hover, #f5f5f5)",
156
+ borderRadius: "6px",
157
+ maxHeight: "80px",
158
+ overflowY: "auto"
159
+ },
160
+ // Messages area
161
+ messagesArea: {
162
+ flex: 1,
163
+ overflowY: "auto",
164
+ padding: "8px 12px",
165
+ display: "flex",
166
+ flexDirection: "column",
167
+ gap: "8px",
168
+ minHeight: "40px",
169
+ maxHeight: "140px"
170
+ },
171
+ emptyState: {
172
+ display: "none"
173
+ // Hide empty state, placeholder in input is enough
174
+ },
175
+ // Message bubble
176
+ message: {
177
+ display: "flex",
178
+ flexDirection: "column",
179
+ gap: "4px",
180
+ maxWidth: "100%"
181
+ },
182
+ messageUser: {
183
+ alignSelf: "flex-end",
184
+ backgroundColor: "var(--anyclick-menu-accent, #0066cc)",
185
+ color: "#fff",
186
+ padding: "6px 10px",
187
+ borderRadius: "12px 12px 4px 12px",
188
+ fontSize: "13px",
189
+ maxWidth: "85%",
190
+ wordBreak: "break-word"
191
+ },
192
+ messageAssistant: {
193
+ alignSelf: "flex-start",
194
+ backgroundColor: "var(--anyclick-menu-hover, #f5f5f5)",
195
+ color: "var(--anyclick-menu-text, #333)",
196
+ padding: "8px 10px",
197
+ borderRadius: "12px 12px 12px 4px",
198
+ fontSize: "13px",
199
+ maxWidth: "100%",
200
+ wordBreak: "break-word",
201
+ lineHeight: 1.4
202
+ },
203
+ messageActions: {
204
+ display: "flex",
205
+ gap: "4px",
206
+ marginTop: "4px",
207
+ flexWrap: "wrap"
208
+ },
209
+ actionButton: {
210
+ display: "inline-flex",
211
+ alignItems: "center",
212
+ gap: "4px",
213
+ padding: "3px 8px",
214
+ fontSize: "11px",
215
+ fontWeight: 500,
216
+ border: "1px solid var(--anyclick-menu-border, #e5e5e5)",
217
+ borderRadius: "4px",
218
+ backgroundColor: "transparent",
219
+ color: "var(--anyclick-menu-text-muted, #666)",
220
+ cursor: "pointer",
221
+ transition: "all 0.15s ease"
222
+ },
223
+ streamingIndicator: {
224
+ display: "inline-block",
225
+ width: "4px",
226
+ height: "14px",
227
+ backgroundColor: "var(--anyclick-menu-accent, #0066cc)",
228
+ borderRadius: "1px",
229
+ animation: "blink 1s infinite"
230
+ },
231
+ // Suggestions - compact horizontal scroll
232
+ suggestionsContainer: {
233
+ display: "flex",
234
+ gap: "6px",
235
+ padding: "6px 8px",
236
+ overflowX: "auto",
237
+ overflowY: "hidden",
238
+ scrollbarWidth: "none",
239
+ msOverflowStyle: "none"
240
+ },
241
+ suggestionChip: {
242
+ display: "inline-flex",
243
+ alignItems: "center",
244
+ padding: "4px 10px",
245
+ fontSize: "11px",
246
+ border: "1px solid var(--anyclick-menu-border, #e5e5e5)",
247
+ borderRadius: "12px",
248
+ backgroundColor: "var(--anyclick-menu-hover, #f5f5f5)",
249
+ color: "var(--anyclick-menu-text, #333)",
250
+ cursor: "pointer",
251
+ transition: "all 0.15s ease",
252
+ whiteSpace: "nowrap",
253
+ flexShrink: 0
254
+ },
255
+ suggestionChipHover: {
256
+ backgroundColor: "var(--anyclick-menu-bg, #fff)",
257
+ borderColor: "var(--anyclick-menu-accent, #0066cc)"
258
+ },
259
+ // Context redaction - now in dropdown
260
+ contextContainer: {
261
+ display: "flex",
262
+ flexDirection: "column",
263
+ gap: "4px"
264
+ },
265
+ contextHeader: {
266
+ display: "flex",
267
+ alignItems: "center",
268
+ justifyContent: "space-between",
269
+ marginBottom: "4px"
270
+ },
271
+ contextTitle: {
272
+ fontSize: "10px",
273
+ fontWeight: 600,
274
+ textTransform: "uppercase",
275
+ letterSpacing: "0.5px",
276
+ color: "var(--anyclick-menu-text-muted, #666)"
277
+ },
278
+ contextToggle: {
279
+ fontSize: "10px",
280
+ color: "var(--anyclick-menu-accent, #0066cc)",
281
+ background: "none",
282
+ border: "none",
283
+ cursor: "pointer",
284
+ padding: 0
285
+ },
286
+ contextToggleSmall: {
287
+ fontSize: "9px",
288
+ fontWeight: 500,
289
+ color: "var(--anyclick-menu-text-muted, #666)",
290
+ background: "none",
291
+ border: "none",
292
+ cursor: "pointer",
293
+ padding: "2px 4px",
294
+ borderRadius: "3px",
295
+ transition: "all 0.15s ease"
296
+ },
297
+ contextChunks: {
298
+ display: "flex",
299
+ flexDirection: "column",
300
+ gap: "4px"
301
+ },
302
+ contextChunk: {
303
+ display: "flex",
304
+ alignItems: "center",
305
+ gap: "6px",
306
+ padding: "4px 6px",
307
+ borderRadius: "4px",
308
+ fontSize: "11px",
309
+ backgroundColor: "var(--anyclick-menu-hover, #f5f5f5)",
310
+ cursor: "pointer",
311
+ transition: "opacity 0.15s ease"
312
+ },
313
+ contextChunkCompact: {
314
+ display: "flex",
315
+ alignItems: "center",
316
+ gap: "6px",
317
+ padding: "2px 4px",
318
+ borderRadius: "3px",
319
+ fontSize: "10px",
320
+ cursor: "pointer",
321
+ transition: "opacity 0.15s ease"
322
+ },
323
+ contextChunkExcluded: {
324
+ opacity: 0.5,
325
+ textDecoration: "line-through"
326
+ },
327
+ contextCheckbox: {
328
+ width: "12px",
329
+ height: "12px",
330
+ accentColor: "var(--anyclick-menu-accent, #0066cc)"
331
+ },
332
+ contextLabel: {
333
+ flex: 1,
334
+ overflow: "hidden",
335
+ textOverflow: "ellipsis",
336
+ whiteSpace: "nowrap",
337
+ color: "var(--anyclick-menu-text, #333)"
338
+ },
339
+ contextSize: {
340
+ fontSize: "10px",
341
+ color: "var(--anyclick-menu-text-muted, #666)"
342
+ },
343
+ // Input area
344
+ inputContainer: {
345
+ display: "flex",
346
+ alignItems: "flex-end",
347
+ gap: "8px",
348
+ padding: "8px"
349
+ },
350
+ input: {
351
+ flex: 1,
352
+ padding: "8px 12px",
353
+ fontSize: "13px",
354
+ border: "1px solid var(--anyclick-menu-input-border, #ddd)",
355
+ borderRadius: "18px",
356
+ backgroundColor: "var(--anyclick-menu-input-bg, #fff)",
357
+ color: "var(--anyclick-menu-text, #333)",
358
+ outline: "none",
359
+ resize: "none",
360
+ fontFamily: "inherit",
361
+ lineHeight: 1.4,
362
+ maxHeight: "80px",
363
+ minHeight: "36px"
364
+ },
365
+ inputFocused: {
366
+ borderColor: "var(--anyclick-menu-accent, #0066cc)",
367
+ boxShadow: "0 0 0 2px rgba(0, 102, 204, 0.1)"
368
+ },
369
+ sendButton: {
370
+ display: "flex",
371
+ alignItems: "center",
372
+ justifyContent: "center",
373
+ width: "32px",
374
+ height: "32px",
375
+ border: "none",
376
+ borderRadius: "50%",
377
+ backgroundColor: "var(--anyclick-menu-accent, #0066cc)",
378
+ color: "#fff",
379
+ cursor: "pointer",
380
+ transition: "all 0.15s ease",
381
+ flexShrink: 0
382
+ },
383
+ sendButtonDisabled: {
384
+ backgroundColor: "var(--anyclick-menu-border, #e5e5e5)",
385
+ cursor: "not-allowed"
386
+ },
387
+ // Loading states
388
+ loadingDots: {
389
+ display: "flex",
390
+ gap: "4px",
391
+ padding: "8px"
392
+ },
393
+ loadingDot: {
394
+ width: "6px",
395
+ height: "6px",
396
+ borderRadius: "50%",
397
+ backgroundColor: "var(--anyclick-menu-text-muted, #666)",
398
+ animation: "bounce 1.4s infinite ease-in-out both"
399
+ },
400
+ // Error states
401
+ errorContainer: {
402
+ display: "flex",
403
+ flexDirection: "column",
404
+ alignItems: "center",
405
+ gap: "8px",
406
+ padding: "16px",
407
+ textAlign: "center"
408
+ },
409
+ errorIcon: {
410
+ color: "#ef4444"
411
+ },
412
+ errorText: {
413
+ fontSize: "12px",
414
+ color: "#ef4444",
415
+ maxWidth: "200px"
416
+ },
417
+ errorRetry: {
418
+ display: "inline-flex",
419
+ alignItems: "center",
420
+ gap: "4px",
421
+ padding: "4px 10px",
422
+ fontSize: "11px",
423
+ fontWeight: 500,
424
+ border: "1px solid #ef4444",
425
+ borderRadius: "4px",
426
+ backgroundColor: "transparent",
427
+ color: "#ef4444",
428
+ cursor: "pointer",
429
+ transition: "all 0.15s ease"
430
+ },
431
+ // Truncation indicator
432
+ truncated: {
433
+ fontSize: "10px",
434
+ color: "var(--anyclick-menu-text-muted, #666)",
435
+ fontStyle: "italic",
436
+ marginTop: "4px"
437
+ }
438
+ };
439
+ var quickChatKeyframes = `
440
+ @keyframes blink {
441
+ 0%, 50% { opacity: 1; }
442
+ 51%, 100% { opacity: 0; }
443
+ }
444
+
445
+ @keyframes bounce {
446
+ 0%, 80%, 100% { transform: scale(0); }
447
+ 40% { transform: scale(1); }
448
+ }
449
+
450
+ @keyframes slideIn {
451
+ from {
452
+ opacity: 0;
453
+ transform: translateY(-8px);
454
+ }
455
+ to {
456
+ opacity: 1;
457
+ transform: translateY(0);
458
+ }
459
+ }
460
+
461
+ @keyframes slideInFromRight {
462
+ from {
463
+ transform: translateX(100%);
464
+ }
465
+ to {
466
+ transform: translateX(0);
467
+ }
468
+ }
469
+
470
+ @keyframes slideOutToRight {
471
+ from {
472
+ transform: translateX(0);
473
+ }
474
+ to {
475
+ transform: translateX(100%);
476
+ }
477
+ }
478
+
479
+ @keyframes fadeIn {
480
+ from { opacity: 0; }
481
+ to { opacity: 1; }
482
+ }
483
+
484
+ @keyframes spin {
485
+ from { transform: rotate(0deg); }
486
+ to { transform: rotate(360deg); }
487
+ }
488
+
489
+ @keyframes pulse {
490
+ 0%, 100% { opacity: 1; }
491
+ 50% { opacity: 0.5; }
492
+ }
493
+ `;
494
+
495
+ // src/QuickChat/useQuickChat.ts
496
+ import { useCallback, useEffect, useRef, useState } from "react";
497
+ import { getElementInspectInfo } from "@ewjdev/anyclick-core";
498
+
499
+ // src/QuickChat/types.ts
500
+ var DEFAULT_T3CHAT_CONFIG = {
501
+ enabled: true,
502
+ baseUrl: "https://t3.chat",
503
+ label: "Ask t3.chat"
504
+ };
505
+ var DEFAULT_QUICK_CHAT_CONFIG = {
506
+ endpoint: "/api/anyclick/chat",
507
+ model: "gpt-5-mini",
508
+ prePassModel: "gpt-5-nano",
509
+ maxResponseLength: 500,
510
+ showRedactionUI: true,
511
+ showSuggestions: true,
512
+ systemPrompt: "You are a helpful assistant that provides quick, concise answers about web elements and UI. Keep responses brief and actionable.",
513
+ placeholder: "Ask about this element...",
514
+ title: "Quick Ask",
515
+ t3chat: DEFAULT_T3CHAT_CONFIG
516
+ };
517
+
518
+ // src/QuickChat/useQuickChat.ts
519
+ var PINNED_STATE_KEY = "anyclick-quick-chat-pinned";
520
+ var CHAT_HISTORY_KEY = "anyclick-quick-chat-history";
521
+ function generateId() {
522
+ return `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
523
+ }
524
+ function getPinnedState() {
525
+ if (typeof window === "undefined") return false;
526
+ try {
527
+ return sessionStorage.getItem(PINNED_STATE_KEY) === "true";
528
+ } catch {
529
+ return false;
530
+ }
531
+ }
532
+ function getChatHistory() {
533
+ if (typeof window === "undefined") return [];
534
+ try {
535
+ const stored = sessionStorage.getItem(CHAT_HISTORY_KEY);
536
+ if (stored) {
537
+ const parsed = JSON.parse(stored);
538
+ return parsed.map((msg) => ({
539
+ ...msg,
540
+ isStreaming: false,
541
+ actions: void 0
542
+ }));
543
+ }
544
+ } catch {
545
+ }
546
+ return [];
547
+ }
548
+ function saveChatHistory(messages) {
549
+ if (typeof window === "undefined") return;
550
+ try {
551
+ const toStore = messages.slice(-10).map((msg) => ({
552
+ id: msg.id,
553
+ role: msg.role,
554
+ content: msg.content,
555
+ timestamp: msg.timestamp
556
+ }));
557
+ sessionStorage.setItem(CHAT_HISTORY_KEY, JSON.stringify(toStore));
558
+ } catch {
559
+ }
560
+ }
561
+ function clearChatHistory() {
562
+ if (typeof window === "undefined") return;
563
+ try {
564
+ sessionStorage.removeItem(CHAT_HISTORY_KEY);
565
+ } catch {
566
+ }
567
+ }
568
+ function extractContextChunks(targetElement, containerElement) {
569
+ const chunks = [];
570
+ if (!targetElement) return chunks;
571
+ try {
572
+ const info = getElementInspectInfo(targetElement);
573
+ const tagContent = `<${info.tagName.toLowerCase()}${info.id ? ` id="${info.id}"` : ""}${info.classNames.length > 0 ? ` class="${info.classNames.join(" ")}"` : ""}>`;
574
+ chunks.push({
575
+ id: "element-tag",
576
+ label: "Element Tag",
577
+ content: tagContent,
578
+ type: "element",
579
+ included: true,
580
+ size: tagContent.length
581
+ });
582
+ if (info.innerText && info.innerText.length > 0) {
583
+ const textContent = info.innerText.slice(0, 200);
584
+ chunks.push({
585
+ id: "element-text",
586
+ label: "Text Content",
587
+ content: textContent,
588
+ type: "text",
589
+ included: true,
590
+ size: textContent.length
591
+ });
592
+ }
593
+ if (info.computedStyles) {
594
+ const styleEntries = [];
595
+ for (const [category, styles] of Object.entries(info.computedStyles)) {
596
+ if (styles && typeof styles === "object") {
597
+ const entries = Object.entries(styles).slice(0, 2);
598
+ for (const [k, v] of entries) {
599
+ if (v) styleEntries.push(`${k}: ${v}`);
600
+ }
601
+ }
602
+ if (styleEntries.length >= 8) break;
603
+ }
604
+ const stylesContent = styleEntries.join("; ");
605
+ if (stylesContent) {
606
+ chunks.push({
607
+ id: "element-styles",
608
+ label: "Key Styles",
609
+ content: stylesContent,
610
+ type: "element",
611
+ included: true,
612
+ size: stylesContent.length
613
+ });
614
+ }
615
+ }
616
+ if (info.accessibility) {
617
+ const a11yContent = [
618
+ info.accessibility.role && `role="${info.accessibility.role}"`,
619
+ info.accessibility.accessibleName && `aria-label="${info.accessibility.accessibleName}"`
620
+ ].filter(Boolean).join(", ");
621
+ if (a11yContent) {
622
+ chunks.push({
623
+ id: "element-a11y",
624
+ label: "Accessibility",
625
+ content: a11yContent,
626
+ type: "element",
627
+ included: true,
628
+ size: a11yContent.length
629
+ });
630
+ }
631
+ }
632
+ if (info.boxModel) {
633
+ const boxContent = `${Math.round(info.boxModel.content.width)}x${Math.round(info.boxModel.content.height)}px`;
634
+ chunks.push({
635
+ id: "element-dimensions",
636
+ label: "Dimensions",
637
+ content: boxContent,
638
+ type: "element",
639
+ included: true,
640
+ size: boxContent.length
641
+ });
642
+ }
643
+ } catch (error) {
644
+ console.error("Failed to extract context:", error);
645
+ }
646
+ return chunks;
647
+ }
648
+ function buildContextString(chunks) {
649
+ const includedChunks = chunks.filter((c) => c.included);
650
+ if (includedChunks.length === 0) return "";
651
+ return includedChunks.map((c) => `[${c.label}]: ${c.content}`).join("\n");
652
+ }
653
+ function useQuickChat(targetElement, containerElement, config = {}) {
654
+ const mergedConfig = { ...DEFAULT_QUICK_CHAT_CONFIG, ...config };
655
+ const abortControllerRef = useRef(null);
656
+ const initializedRef = useRef(false);
657
+ const [state, setState] = useState({
658
+ input: "",
659
+ messages: [],
660
+ isLoadingSuggestions: false,
661
+ isSending: false,
662
+ isStreaming: false,
663
+ suggestedPrompts: [],
664
+ contextChunks: [],
665
+ error: null
666
+ });
667
+ useEffect(() => {
668
+ if (initializedRef.current) return;
669
+ initializedRef.current = true;
670
+ const isPinned = getPinnedState();
671
+ if (isPinned) {
672
+ const history = getChatHistory();
673
+ if (history.length > 0) {
674
+ setState((prev) => ({ ...prev, messages: history }));
675
+ }
676
+ }
677
+ }, []);
678
+ useEffect(() => {
679
+ if (targetElement) {
680
+ const chunks = extractContextChunks(targetElement, containerElement);
681
+ setState((prev) => ({ ...prev, contextChunks: chunks }));
682
+ }
683
+ }, [targetElement, containerElement]);
684
+ useEffect(() => {
685
+ if (!mergedConfig.showSuggestions || state.contextChunks.length === 0 || state.suggestedPrompts.length > 0) {
686
+ return;
687
+ }
688
+ const fetchSuggestions = async () => {
689
+ setState((prev) => ({ ...prev, isLoadingSuggestions: true }));
690
+ try {
691
+ const contextString = buildContextString(state.contextChunks);
692
+ const response = await fetch(mergedConfig.endpoint, {
693
+ method: "POST",
694
+ headers: { "Content-Type": "application/json" },
695
+ body: JSON.stringify({
696
+ action: "suggest",
697
+ context: contextString,
698
+ model: mergedConfig.prePassModel
699
+ })
700
+ });
701
+ if (response.ok) {
702
+ const data = await response.json();
703
+ if (data.suggestions && Array.isArray(data.suggestions)) {
704
+ setState((prev) => ({
705
+ ...prev,
706
+ suggestedPrompts: data.suggestions.map(
707
+ (text, i) => ({
708
+ id: `suggestion-${i}`,
709
+ text
710
+ })
711
+ ),
712
+ isLoadingSuggestions: false
713
+ }));
714
+ return;
715
+ }
716
+ }
717
+ } catch (error) {
718
+ console.error("Failed to fetch suggestions:", error);
719
+ }
720
+ setState((prev) => ({
721
+ ...prev,
722
+ suggestedPrompts: [
723
+ { id: "s1", text: "What is this element?" },
724
+ { id: "s2", text: "How can I style this?" },
725
+ { id: "s3", text: "Is this accessible?" }
726
+ ],
727
+ isLoadingSuggestions: false
728
+ }));
729
+ };
730
+ fetchSuggestions();
731
+ }, [
732
+ state.contextChunks,
733
+ state.suggestedPrompts.length,
734
+ mergedConfig.showSuggestions,
735
+ mergedConfig.endpoint,
736
+ mergedConfig.prePassModel
737
+ ]);
738
+ const setInput = useCallback((value) => {
739
+ setState((prev) => ({ ...prev, input: value }));
740
+ }, []);
741
+ const toggleChunk = useCallback((chunkId) => {
742
+ setState((prev) => ({
743
+ ...prev,
744
+ contextChunks: prev.contextChunks.map(
745
+ (chunk) => chunk.id === chunkId ? { ...chunk, included: !chunk.included } : chunk
746
+ )
747
+ }));
748
+ }, []);
749
+ const toggleAllChunks = useCallback((included) => {
750
+ setState((prev) => ({
751
+ ...prev,
752
+ contextChunks: prev.contextChunks.map((chunk) => ({
753
+ ...chunk,
754
+ included
755
+ }))
756
+ }));
757
+ }, []);
758
+ const selectSuggestion = useCallback((prompt) => {
759
+ setState((prev) => ({ ...prev, input: prompt.text }));
760
+ }, []);
761
+ const sendMessage = useCallback(
762
+ async (messageText) => {
763
+ const text = (messageText || state.input).trim();
764
+ if (!text) return;
765
+ if (abortControllerRef.current) {
766
+ abortControllerRef.current.abort();
767
+ }
768
+ abortControllerRef.current = new AbortController();
769
+ const userMessage = {
770
+ id: generateId(),
771
+ role: "user",
772
+ content: text,
773
+ timestamp: Date.now()
774
+ };
775
+ const assistantMessageId = generateId();
776
+ const assistantMessage = {
777
+ id: assistantMessageId,
778
+ role: "assistant",
779
+ content: "",
780
+ timestamp: Date.now(),
781
+ isStreaming: true
782
+ };
783
+ setState((prev) => ({
784
+ ...prev,
785
+ input: "",
786
+ messages: [...prev.messages, userMessage, assistantMessage],
787
+ isSending: true,
788
+ isStreaming: true,
789
+ error: null
790
+ }));
791
+ try {
792
+ const contextString = buildContextString(state.contextChunks);
793
+ const response = await fetch(mergedConfig.endpoint, {
794
+ method: "POST",
795
+ headers: { "Content-Type": "application/json" },
796
+ body: JSON.stringify({
797
+ action: "chat",
798
+ message: text,
799
+ context: contextString,
800
+ model: mergedConfig.model,
801
+ systemPrompt: mergedConfig.systemPrompt,
802
+ maxLength: mergedConfig.maxResponseLength
803
+ }),
804
+ signal: abortControllerRef.current.signal
805
+ });
806
+ if (!response.ok) {
807
+ throw new Error(`Request failed: ${response.status}`);
808
+ }
809
+ const reader = response.body?.getReader();
810
+ if (!reader) {
811
+ throw new Error("No response body");
812
+ }
813
+ const decoder = new TextDecoder();
814
+ let fullContent = "";
815
+ while (true) {
816
+ const { done, value } = await reader.read();
817
+ if (done) break;
818
+ const chunk = decoder.decode(value, { stream: true });
819
+ fullContent += chunk;
820
+ if (fullContent.length > mergedConfig.maxResponseLength) {
821
+ fullContent = fullContent.slice(0, mergedConfig.maxResponseLength) + "...";
822
+ }
823
+ setState((prev) => ({
824
+ ...prev,
825
+ messages: prev.messages.map(
826
+ (msg) => msg.id === assistantMessageId ? { ...msg, content: fullContent } : msg
827
+ )
828
+ }));
829
+ }
830
+ setState((prev) => ({
831
+ ...prev,
832
+ messages: prev.messages.map(
833
+ (msg) => msg.id === assistantMessageId ? {
834
+ ...msg,
835
+ content: fullContent,
836
+ isStreaming: false,
837
+ actions: [
838
+ {
839
+ id: "copy",
840
+ label: "Copy",
841
+ onClick: () => {
842
+ navigator.clipboard.writeText(fullContent);
843
+ }
844
+ },
845
+ {
846
+ id: "research",
847
+ label: "Research more",
848
+ onClick: () => {
849
+ setState((p) => ({
850
+ ...p,
851
+ input: `Tell me more about: ${text}`
852
+ }));
853
+ }
854
+ }
855
+ ]
856
+ } : msg
857
+ ),
858
+ isSending: false,
859
+ isStreaming: false
860
+ }));
861
+ } catch (error) {
862
+ if (error.name === "AbortError") {
863
+ return;
864
+ }
865
+ console.error("Chat error:", error);
866
+ setState((prev) => ({
867
+ ...prev,
868
+ messages: prev.messages.map(
869
+ (msg) => msg.id === assistantMessageId ? {
870
+ ...msg,
871
+ content: "Sorry, I couldn't process your request. Please try again.",
872
+ isStreaming: false
873
+ } : msg
874
+ ),
875
+ isSending: false,
876
+ isStreaming: false,
877
+ error: error.message
878
+ }));
879
+ }
880
+ },
881
+ [state.input, state.contextChunks, mergedConfig]
882
+ );
883
+ const clearMessages = useCallback(() => {
884
+ if (abortControllerRef.current) {
885
+ abortControllerRef.current.abort();
886
+ }
887
+ clearChatHistory();
888
+ setState((prev) => ({
889
+ ...prev,
890
+ messages: [],
891
+ error: null
892
+ }));
893
+ }, []);
894
+ useEffect(() => {
895
+ if (state.messages.length > 0 && getPinnedState()) {
896
+ const completedMessages = state.messages.filter(
897
+ (msg) => !msg.isStreaming
898
+ );
899
+ if (completedMessages.length > 0) {
900
+ saveChatHistory(completedMessages);
901
+ }
902
+ }
903
+ }, [state.messages]);
904
+ useEffect(() => {
905
+ return () => {
906
+ if (abortControllerRef.current) {
907
+ abortControllerRef.current.abort();
908
+ }
909
+ };
910
+ }, []);
911
+ return {
912
+ ...state,
913
+ config: mergedConfig,
914
+ setInput,
915
+ toggleChunk,
916
+ toggleAllChunks,
917
+ selectSuggestion,
918
+ sendMessage,
919
+ clearMessages
920
+ };
921
+ }
922
+
923
+ // src/QuickChat/QuickChat.tsx
924
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
925
+ var PINNED_STATE_KEY2 = "anyclick-quick-chat-pinned";
926
+ function getStoredPinnedState() {
927
+ if (typeof window === "undefined") return false;
928
+ try {
929
+ return sessionStorage.getItem(PINNED_STATE_KEY2) === "true";
930
+ } catch {
931
+ return false;
932
+ }
933
+ }
934
+ function setStoredPinnedState(pinned) {
935
+ if (typeof window === "undefined") return;
936
+ try {
937
+ if (pinned) {
938
+ sessionStorage.setItem(PINNED_STATE_KEY2, "true");
939
+ } else {
940
+ sessionStorage.removeItem(PINNED_STATE_KEY2);
941
+ }
942
+ } catch {
943
+ }
944
+ }
945
+ var stylesInjected = false;
946
+ function injectStyles() {
947
+ if (stylesInjected || typeof document === "undefined") return;
948
+ const style = document.createElement("style");
949
+ style.textContent = quickChatKeyframes;
950
+ document.head.appendChild(style);
951
+ stylesInjected = true;
952
+ }
953
+ var LoadingDots = React.memo(function LoadingDots2() {
954
+ return /* @__PURE__ */ jsx("div", { style: quickChatStyles.loadingDots, children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx(
955
+ "div",
956
+ {
957
+ style: {
958
+ ...quickChatStyles.loadingDot,
959
+ animationDelay: `${i * 0.16}s`
960
+ }
961
+ },
962
+ i
963
+ )) });
964
+ });
965
+ function QuickChat({
966
+ visible,
967
+ targetElement,
968
+ containerElement,
969
+ onClose,
970
+ onPin,
971
+ isPinned: isPinnedProp = false,
972
+ config,
973
+ style,
974
+ className,
975
+ initialInput,
976
+ onInitialInputConsumed
977
+ }) {
978
+ const inputRef = useRef2(null);
979
+ const messagesEndRef = useRef2(null);
980
+ const [inputFocused, setInputFocused] = useState2(false);
981
+ const [showContext, setShowContext] = useState2(false);
982
+ const [hoveredSuggestion, setHoveredSuggestion] = useState2(
983
+ null
984
+ );
985
+ const [localPinned, setLocalPinned] = useState2(() => getStoredPinnedState());
986
+ const isPinned = isPinnedProp || localPinned;
987
+ const handlePinToggle = useCallback2(() => {
988
+ const newPinned = !isPinned;
989
+ setLocalPinned(newPinned);
990
+ setStoredPinnedState(newPinned);
991
+ onPin?.(newPinned);
992
+ }, [isPinned, onPin]);
993
+ const handleClose = useCallback2(() => {
994
+ if (isPinned) {
995
+ setLocalPinned(false);
996
+ setStoredPinnedState(false);
997
+ onPin?.(false);
998
+ }
999
+ onClose();
1000
+ }, [isPinned, onPin, onClose]);
1001
+ const {
1002
+ input,
1003
+ messages,
1004
+ isLoadingSuggestions,
1005
+ isSending,
1006
+ isStreaming,
1007
+ suggestedPrompts,
1008
+ contextChunks,
1009
+ error,
1010
+ setInput,
1011
+ toggleChunk,
1012
+ toggleAllChunks,
1013
+ selectSuggestion,
1014
+ sendMessage,
1015
+ clearMessages,
1016
+ config: mergedConfig
1017
+ } = useQuickChat(targetElement, containerElement, config);
1018
+ useEffect2(() => {
1019
+ injectStyles();
1020
+ }, []);
1021
+ useEffect2(() => {
1022
+ if (visible && inputRef.current) {
1023
+ const timer = setTimeout(() => {
1024
+ inputRef.current?.focus();
1025
+ }, 100);
1026
+ return () => clearTimeout(timer);
1027
+ }
1028
+ }, [visible]);
1029
+ useEffect2(() => {
1030
+ if (initialInput && visible) {
1031
+ setInput(initialInput);
1032
+ onInitialInputConsumed?.();
1033
+ if (inputRef.current) {
1034
+ inputRef.current.focus();
1035
+ inputRef.current.setSelectionRange(
1036
+ initialInput.length,
1037
+ initialInput.length
1038
+ );
1039
+ }
1040
+ }
1041
+ }, [initialInput, visible, setInput, onInitialInputConsumed]);
1042
+ useEffect2(() => {
1043
+ if (messagesEndRef.current) {
1044
+ messagesEndRef.current.scrollIntoView({ behavior: "smooth" });
1045
+ }
1046
+ }, [messages]);
1047
+ const handleInputChange = useCallback2(
1048
+ (e) => {
1049
+ setInput(e.target.value);
1050
+ const target = e.target;
1051
+ target.style.height = "auto";
1052
+ target.style.height = `${Math.min(target.scrollHeight, 80)}px`;
1053
+ },
1054
+ [setInput]
1055
+ );
1056
+ const handleKeyDown = useCallback2(
1057
+ (e) => {
1058
+ if (e.key === "Enter" && !e.shiftKey) {
1059
+ e.preventDefault();
1060
+ sendMessage();
1061
+ } else if (e.key === "Escape") {
1062
+ onClose();
1063
+ }
1064
+ },
1065
+ [sendMessage, onClose]
1066
+ );
1067
+ const handleSend = useCallback2(() => {
1068
+ sendMessage();
1069
+ }, [sendMessage]);
1070
+ const handleSendToT3Chat = useCallback2(() => {
1071
+ if (typeof window === "undefined") return;
1072
+ const query = input.trim();
1073
+ const baseUrl = mergedConfig.t3chat?.baseUrl ?? "https://t3.chat";
1074
+ const url = query ? `${baseUrl}/?q=${encodeURIComponent(query)}` : baseUrl;
1075
+ window.open(url, "_blank", "noopener,noreferrer");
1076
+ }, [input, mergedConfig.t3chat?.baseUrl]);
1077
+ const handleCopy = useCallback2((text) => {
1078
+ navigator.clipboard.writeText(text);
1079
+ }, []);
1080
+ const includedCount = useMemo(
1081
+ () => contextChunks.filter((c) => c.included).length,
1082
+ [contextChunks]
1083
+ );
1084
+ if (!visible) return null;
1085
+ const containerStyles = isPinned ? {
1086
+ ...quickChatStyles.pinnedContainer,
1087
+ animation: "slideInFromRight 0.25s ease-out",
1088
+ ...style
1089
+ } : {
1090
+ ...quickChatStyles.container,
1091
+ animation: "fadeIn 0.15s ease-out",
1092
+ ...style
1093
+ };
1094
+ return /* @__PURE__ */ jsxs("div", { className, style: containerStyles, children: [
1095
+ /* @__PURE__ */ jsxs(
1096
+ "div",
1097
+ {
1098
+ style: {
1099
+ ...quickChatStyles.header,
1100
+ padding: isPinned ? "12px 12px 8px 12px" : "6px 8px"
1101
+ },
1102
+ children: [
1103
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "4px" }, children: [
1104
+ !isPinned && /* @__PURE__ */ jsx(
1105
+ "button",
1106
+ {
1107
+ type: "button",
1108
+ onClick: onClose,
1109
+ style: {
1110
+ ...quickChatStyles.iconButton,
1111
+ marginLeft: "-4px"
1112
+ },
1113
+ title: "Back to menu",
1114
+ children: /* @__PURE__ */ jsx(ChevronLeft, { size: 16 })
1115
+ }
1116
+ ),
1117
+ mergedConfig.showRedactionUI && contextChunks.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
1118
+ /* @__PURE__ */ jsxs(
1119
+ "button",
1120
+ {
1121
+ type: "button",
1122
+ onClick: () => setShowContext(!showContext),
1123
+ style: {
1124
+ ...quickChatStyles.contextBadge,
1125
+ ...showContext ? quickChatStyles.contextBadgeActive : {}
1126
+ },
1127
+ title: "Edit context",
1128
+ children: [
1129
+ /* @__PURE__ */ jsxs("span", { children: [
1130
+ includedCount,
1131
+ "/",
1132
+ contextChunks.length
1133
+ ] }),
1134
+ showContext ? /* @__PURE__ */ jsx(ChevronUp, { size: 10 }) : /* @__PURE__ */ jsx(ChevronDown, { size: 10 })
1135
+ ]
1136
+ }
1137
+ ),
1138
+ showContext && /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "2px" }, children: [
1139
+ /* @__PURE__ */ jsx(
1140
+ "button",
1141
+ {
1142
+ type: "button",
1143
+ onClick: () => toggleAllChunks(true),
1144
+ style: quickChatStyles.contextToggleSmall,
1145
+ title: "Include all",
1146
+ children: "All"
1147
+ }
1148
+ ),
1149
+ /* @__PURE__ */ jsx(
1150
+ "button",
1151
+ {
1152
+ type: "button",
1153
+ onClick: () => toggleAllChunks(false),
1154
+ style: quickChatStyles.contextToggleSmall,
1155
+ title: "Exclude all",
1156
+ children: "None"
1157
+ }
1158
+ )
1159
+ ] })
1160
+ ] })
1161
+ ] }),
1162
+ /* @__PURE__ */ jsxs("div", { style: quickChatStyles.headerActions, children: [
1163
+ messages.length > 0 && /* @__PURE__ */ jsx(
1164
+ "button",
1165
+ {
1166
+ type: "button",
1167
+ onClick: clearMessages,
1168
+ style: quickChatStyles.iconButton,
1169
+ title: "Clear chat",
1170
+ children: /* @__PURE__ */ jsx(RefreshCw, { size: 14 })
1171
+ }
1172
+ ),
1173
+ /* @__PURE__ */ jsx(
1174
+ "button",
1175
+ {
1176
+ type: "button",
1177
+ onClick: handlePinToggle,
1178
+ style: {
1179
+ ...quickChatStyles.iconButton,
1180
+ ...isPinned ? quickChatStyles.iconButtonActive : {}
1181
+ },
1182
+ title: isPinned ? "Unpin (closes with menu)" : "Pin (stays open)",
1183
+ children: isPinned ? /* @__PURE__ */ jsx(PinOff, { size: 14 }) : /* @__PURE__ */ jsx(Pin, { size: 14 })
1184
+ }
1185
+ ),
1186
+ /* @__PURE__ */ jsx(
1187
+ "button",
1188
+ {
1189
+ type: "button",
1190
+ onClick: handleClose,
1191
+ style: quickChatStyles.iconButton,
1192
+ title: "Close",
1193
+ children: /* @__PURE__ */ jsx(X, { size: 14 })
1194
+ }
1195
+ )
1196
+ ] })
1197
+ ]
1198
+ }
1199
+ ),
1200
+ showContext && contextChunks.length > 0 && /* @__PURE__ */ jsx("div", { style: quickChatStyles.contextDropdown, children: contextChunks.map((chunk) => /* @__PURE__ */ jsxs(
1201
+ "label",
1202
+ {
1203
+ style: {
1204
+ ...quickChatStyles.contextChunkCompact,
1205
+ ...chunk.included ? {} : quickChatStyles.contextChunkExcluded
1206
+ },
1207
+ children: [
1208
+ /* @__PURE__ */ jsx(
1209
+ "input",
1210
+ {
1211
+ type: "checkbox",
1212
+ checked: chunk.included,
1213
+ onChange: () => toggleChunk(chunk.id),
1214
+ style: quickChatStyles.contextCheckbox
1215
+ }
1216
+ ),
1217
+ /* @__PURE__ */ jsx("span", { style: quickChatStyles.contextLabel, children: chunk.label })
1218
+ ]
1219
+ },
1220
+ chunk.id
1221
+ )) }),
1222
+ mergedConfig.showSuggestions && messages.length === 0 && suggestedPrompts.length > 0 && /* @__PURE__ */ jsx("div", { style: quickChatStyles.suggestionsContainer, children: isLoadingSuggestions ? /* @__PURE__ */ jsx(LoadingDots, {}) : suggestedPrompts.map((prompt) => /* @__PURE__ */ jsx(
1223
+ "button",
1224
+ {
1225
+ type: "button",
1226
+ onClick: () => selectSuggestion(prompt),
1227
+ onMouseEnter: () => setHoveredSuggestion(prompt.id),
1228
+ onMouseLeave: () => setHoveredSuggestion(null),
1229
+ style: {
1230
+ ...quickChatStyles.suggestionChip,
1231
+ ...hoveredSuggestion === prompt.id ? quickChatStyles.suggestionChipHover : {}
1232
+ },
1233
+ children: prompt.text
1234
+ },
1235
+ prompt.id
1236
+ )) }),
1237
+ /* @__PURE__ */ jsxs(
1238
+ "div",
1239
+ {
1240
+ style: isPinned ? quickChatStyles.pinnedMessagesArea : quickChatStyles.messagesArea,
1241
+ children: [
1242
+ error && /* @__PURE__ */ jsxs("div", { style: quickChatStyles.errorContainer, children: [
1243
+ /* @__PURE__ */ jsx(AlertCircle, { size: 20, style: quickChatStyles.errorIcon }),
1244
+ /* @__PURE__ */ jsx("span", { style: quickChatStyles.errorText, children: error }),
1245
+ /* @__PURE__ */ jsxs(
1246
+ "button",
1247
+ {
1248
+ type: "button",
1249
+ onClick: () => sendMessage(),
1250
+ style: quickChatStyles.errorRetry,
1251
+ children: [
1252
+ /* @__PURE__ */ jsx(RefreshCw, { size: 10 }),
1253
+ "Retry"
1254
+ ]
1255
+ }
1256
+ )
1257
+ ] }),
1258
+ messages.length > 0 && messages.map((message) => /* @__PURE__ */ jsxs(
1259
+ "div",
1260
+ {
1261
+ style: {
1262
+ ...quickChatStyles.message,
1263
+ animation: "fadeIn 0.2s ease-out"
1264
+ },
1265
+ children: [
1266
+ /* @__PURE__ */ jsxs(
1267
+ "div",
1268
+ {
1269
+ style: message.role === "user" ? quickChatStyles.messageUser : quickChatStyles.messageAssistant,
1270
+ children: [
1271
+ message.content,
1272
+ message.isStreaming && /* @__PURE__ */ jsx("span", { style: quickChatStyles.streamingIndicator }),
1273
+ message.role === "assistant" && !message.isStreaming && message.content.endsWith("...") && /* @__PURE__ */ jsx("span", { style: quickChatStyles.truncated, children: "(truncated)" })
1274
+ ]
1275
+ }
1276
+ ),
1277
+ message.role === "assistant" && !message.isStreaming && message.content && /* @__PURE__ */ jsxs("div", { style: quickChatStyles.messageActions, children: [
1278
+ /* @__PURE__ */ jsxs(
1279
+ "button",
1280
+ {
1281
+ type: "button",
1282
+ onClick: () => handleCopy(message.content),
1283
+ style: quickChatStyles.actionButton,
1284
+ children: [
1285
+ /* @__PURE__ */ jsx(Copy, { size: 10 }),
1286
+ "Copy"
1287
+ ]
1288
+ }
1289
+ ),
1290
+ message.actions?.map((action) => /* @__PURE__ */ jsxs(
1291
+ "button",
1292
+ {
1293
+ type: "button",
1294
+ onClick: action.onClick,
1295
+ style: quickChatStyles.actionButton,
1296
+ children: [
1297
+ action.icon,
1298
+ action.label
1299
+ ]
1300
+ },
1301
+ action.id
1302
+ ))
1303
+ ] })
1304
+ ]
1305
+ },
1306
+ message.id
1307
+ )),
1308
+ /* @__PURE__ */ jsx("div", { ref: messagesEndRef })
1309
+ ]
1310
+ }
1311
+ ),
1312
+ /* @__PURE__ */ jsxs("div", { style: quickChatStyles.inputContainer, children: [
1313
+ /* @__PURE__ */ jsx(
1314
+ "textarea",
1315
+ {
1316
+ ref: inputRef,
1317
+ value: input,
1318
+ onChange: handleInputChange,
1319
+ onKeyDown: handleKeyDown,
1320
+ onFocus: () => setInputFocused(true),
1321
+ onBlur: () => setInputFocused(false),
1322
+ placeholder: mergedConfig.placeholder,
1323
+ disabled: isSending,
1324
+ rows: 1,
1325
+ style: {
1326
+ ...quickChatStyles.input,
1327
+ ...inputFocused ? quickChatStyles.inputFocused : {}
1328
+ }
1329
+ }
1330
+ ),
1331
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "4px" }, children: [
1332
+ mergedConfig.t3chat?.enabled !== false && /* @__PURE__ */ jsx(
1333
+ "button",
1334
+ {
1335
+ type: "button",
1336
+ onClick: handleSendToT3Chat,
1337
+ disabled: !input.trim(),
1338
+ title: mergedConfig.t3chat?.label ?? "Ask t3.chat",
1339
+ style: {
1340
+ ...quickChatStyles.sendButton,
1341
+ backgroundColor: "#7c3aed",
1342
+ ...!input.trim() ? quickChatStyles.sendButtonDisabled : {}
1343
+ },
1344
+ children: /* @__PURE__ */ jsx(ExternalLink, { size: 14 })
1345
+ }
1346
+ ),
1347
+ /* @__PURE__ */ jsx(
1348
+ "button",
1349
+ {
1350
+ type: "button",
1351
+ onClick: handleSend,
1352
+ disabled: isSending || !input.trim(),
1353
+ style: {
1354
+ ...quickChatStyles.sendButton,
1355
+ ...isSending || !input.trim() ? quickChatStyles.sendButtonDisabled : {}
1356
+ },
1357
+ children: isSending ? /* @__PURE__ */ jsx(
1358
+ "div",
1359
+ {
1360
+ style: {
1361
+ width: "14px",
1362
+ height: "14px",
1363
+ border: "2px solid transparent",
1364
+ borderTopColor: "#fff",
1365
+ borderRadius: "50%",
1366
+ animation: "spin 0.8s linear infinite"
1367
+ }
1368
+ }
1369
+ ) : /* @__PURE__ */ jsx(Send, { size: 14 })
1370
+ }
1371
+ )
1372
+ ] })
1373
+ ] })
1374
+ ] });
1375
+ }
1376
+
32
1377
  // src/ScreenshotPreview.tsx
33
- import React, { useMemo, useState } from "react";
1378
+ import React2, { useMemo as useMemo2, useState as useState3 } from "react";
34
1379
  import { estimateTotalSize, formatBytes } from "@ewjdev/anyclick-core";
35
1380
  import {
36
1381
  AlertCircleIcon,
@@ -516,8 +1861,8 @@ var screenshotPreviewStyles = {
516
1861
  };
517
1862
 
518
1863
  // src/ScreenshotPreview.tsx
519
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
520
- var ScreenshotPreview = React.memo(function ScreenshotPreview2({
1864
+ import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
1865
+ var ScreenshotPreview = React2.memo(function ScreenshotPreview2({
521
1866
  isLoading,
522
1867
  isSubmitting,
523
1868
  onCancel,
@@ -525,12 +1870,12 @@ var ScreenshotPreview = React.memo(function ScreenshotPreview2({
525
1870
  onRetake,
526
1871
  screenshots
527
1872
  }) {
528
- const [activeTab, setActiveTab] = useState("element");
529
- const [isExpanded, setIsExpanded] = useState(false);
1873
+ const [activeTab, setActiveTab] = useState3("element");
1874
+ const [isExpanded, setIsExpanded] = useState3(false);
530
1875
  const getError = (key) => {
531
1876
  return screenshots?.errors?.[key];
532
1877
  };
533
- const tabs = useMemo(() => {
1878
+ const tabs = useMemo2(() => {
534
1879
  if (!screenshots) return [];
535
1880
  const allTabs = [
536
1881
  {
@@ -554,29 +1899,29 @@ var ScreenshotPreview = React.memo(function ScreenshotPreview2({
554
1899
  ];
555
1900
  return allTabs.filter((tab) => tab.data || tab.error);
556
1901
  }, [screenshots]);
557
- const totalSize = useMemo(
1902
+ const totalSize = useMemo2(
558
1903
  () => screenshots ? estimateTotalSize(screenshots) : 0,
559
1904
  [screenshots]
560
1905
  );
561
1906
  if (isLoading) {
562
- return /* @__PURE__ */ jsx("div", { style: screenshotPreviewStyles.container, children: /* @__PURE__ */ jsxs("div", { style: screenshotPreviewStyles.loadingContainer, children: [
563
- /* @__PURE__ */ jsx(
1907
+ return /* @__PURE__ */ jsx2("div", { style: screenshotPreviewStyles.container, children: /* @__PURE__ */ jsxs2("div", { style: screenshotPreviewStyles.loadingContainer, children: [
1908
+ /* @__PURE__ */ jsx2(
564
1909
  Loader2Icon,
565
1910
  {
566
1911
  className: "w-6 h-6 animate-spin",
567
1912
  style: { color: "#3b82f6" }
568
1913
  }
569
1914
  ),
570
- /* @__PURE__ */ jsx("span", { style: screenshotPreviewStyles.loadingText, children: "Capturing screenshots..." })
1915
+ /* @__PURE__ */ jsx2("span", { style: screenshotPreviewStyles.loadingText, children: "Capturing screenshots..." })
571
1916
  ] }) });
572
1917
  }
573
1918
  if (!screenshots) {
574
- return /* @__PURE__ */ jsx("div", { style: screenshotPreviewStyles.container, children: /* @__PURE__ */ jsxs("div", { style: screenshotPreviewStyles.emptyContainer, children: [
575
- /* @__PURE__ */ jsx(ImageIcon, { className: "w-8 h-8", style: { color: "#9ca3af" } }),
576
- /* @__PURE__ */ jsx("span", { style: screenshotPreviewStyles.emptyText, children: "Screenshots unavailable" }),
577
- /* @__PURE__ */ jsx("span", { style: screenshotPreviewStyles.emptySubtext, children: "Some elements can't be captured (e.g., gradient text)" }),
578
- /* @__PURE__ */ jsxs("div", { style: screenshotPreviewStyles.emptyActions, children: [
579
- /* @__PURE__ */ jsxs(
1919
+ return /* @__PURE__ */ jsx2("div", { style: screenshotPreviewStyles.container, children: /* @__PURE__ */ jsxs2("div", { style: screenshotPreviewStyles.emptyContainer, children: [
1920
+ /* @__PURE__ */ jsx2(ImageIcon, { className: "w-8 h-8", style: { color: "#9ca3af" } }),
1921
+ /* @__PURE__ */ jsx2("span", { style: screenshotPreviewStyles.emptyText, children: "Screenshots unavailable" }),
1922
+ /* @__PURE__ */ jsx2("span", { style: screenshotPreviewStyles.emptySubtext, children: "Some elements can't be captured (e.g., gradient text)" }),
1923
+ /* @__PURE__ */ jsxs2("div", { style: screenshotPreviewStyles.emptyActions, children: [
1924
+ /* @__PURE__ */ jsxs2(
580
1925
  "button",
581
1926
  {
582
1927
  type: "button",
@@ -584,12 +1929,12 @@ var ScreenshotPreview = React.memo(function ScreenshotPreview2({
584
1929
  onClick: onRetake,
585
1930
  style: screenshotPreviewStyles.retakeButtonOutline,
586
1931
  children: [
587
- /* @__PURE__ */ jsx(RefreshCwIcon, { className: "w-4 h-4" }),
1932
+ /* @__PURE__ */ jsx2(RefreshCwIcon, { className: "w-4 h-4" }),
588
1933
  "Try Again"
589
1934
  ]
590
1935
  }
591
1936
  ),
592
- /* @__PURE__ */ jsxs(
1937
+ /* @__PURE__ */ jsxs2(
593
1938
  "button",
594
1939
  {
595
1940
  type: "button",
@@ -597,7 +1942,7 @@ var ScreenshotPreview = React.memo(function ScreenshotPreview2({
597
1942
  onClick: () => onConfirm({ capturedAt: (/* @__PURE__ */ new Date()).toISOString() }),
598
1943
  style: screenshotPreviewStyles.continueButton,
599
1944
  children: [
600
- /* @__PURE__ */ jsx(CheckIcon, { className: "w-4 h-4" }),
1945
+ /* @__PURE__ */ jsx2(CheckIcon, { className: "w-4 h-4" }),
601
1946
  "Continue Without"
602
1947
  ]
603
1948
  }
@@ -607,7 +1952,7 @@ var ScreenshotPreview = React.memo(function ScreenshotPreview2({
607
1952
  }
608
1953
  const activeScreenshot = activeTab === "element" ? screenshots.element : activeTab === "container" ? screenshots.container : screenshots.viewport;
609
1954
  const activeError = getError(activeTab);
610
- return /* @__PURE__ */ jsxs(
1955
+ return /* @__PURE__ */ jsxs2(
611
1956
  "div",
612
1957
  {
613
1958
  style: {
@@ -616,23 +1961,23 @@ var ScreenshotPreview = React.memo(function ScreenshotPreview2({
616
1961
  padding: "8px"
617
1962
  },
618
1963
  children: [
619
- /* @__PURE__ */ jsxs("div", { style: screenshotPreviewStyles.header, children: [
620
- /* @__PURE__ */ jsx("span", { style: screenshotPreviewStyles.headerTitle, children: "Review Screenshots" }),
621
- /* @__PURE__ */ jsxs("div", { style: screenshotPreviewStyles.headerActions, children: [
622
- /* @__PURE__ */ jsx("span", { style: screenshotPreviewStyles.sizeLabel, children: formatBytes(totalSize) }),
623
- /* @__PURE__ */ jsx(
1964
+ /* @__PURE__ */ jsxs2("div", { style: screenshotPreviewStyles.header, children: [
1965
+ /* @__PURE__ */ jsx2("span", { style: screenshotPreviewStyles.headerTitle, children: "Review Screenshots" }),
1966
+ /* @__PURE__ */ jsxs2("div", { style: screenshotPreviewStyles.headerActions, children: [
1967
+ /* @__PURE__ */ jsx2("span", { style: screenshotPreviewStyles.sizeLabel, children: formatBytes(totalSize) }),
1968
+ /* @__PURE__ */ jsx2(
624
1969
  "button",
625
1970
  {
626
1971
  type: "button",
627
1972
  onClick: () => setIsExpanded(!isExpanded),
628
1973
  style: screenshotPreviewStyles.iconButton,
629
1974
  title: isExpanded ? "Collapse" : "Expand",
630
- children: isExpanded ? /* @__PURE__ */ jsx(ShrinkIcon, { className: "w-4 h-4" }) : /* @__PURE__ */ jsx(ExpandIcon, { className: "w-4 h-4" })
1975
+ children: isExpanded ? /* @__PURE__ */ jsx2(ShrinkIcon, { className: "w-4 h-4" }) : /* @__PURE__ */ jsx2(ExpandIcon, { className: "w-4 h-4" })
631
1976
  }
632
1977
  )
633
1978
  ] })
634
1979
  ] }),
635
- /* @__PURE__ */ jsx("div", { style: screenshotPreviewStyles.tabContainer, children: tabs.map((tab) => /* @__PURE__ */ jsxs(
1980
+ /* @__PURE__ */ jsx2("div", { style: screenshotPreviewStyles.tabContainer, children: tabs.map((tab) => /* @__PURE__ */ jsxs2(
636
1981
  "button",
637
1982
  {
638
1983
  type: "button",
@@ -643,7 +1988,7 @@ var ScreenshotPreview = React.memo(function ScreenshotPreview2({
643
1988
  ...tab.error && !tab.data ? screenshotPreviewStyles.tabError : {}
644
1989
  },
645
1990
  children: [
646
- tab.error && !tab.data && /* @__PURE__ */ jsx(
1991
+ tab.error && !tab.data && /* @__PURE__ */ jsx2(
647
1992
  AlertCircleIcon,
648
1993
  {
649
1994
  className: "w-3 h-3",
@@ -651,32 +1996,32 @@ var ScreenshotPreview = React.memo(function ScreenshotPreview2({
651
1996
  }
652
1997
  ),
653
1998
  tab.label,
654
- tab.data && /* @__PURE__ */ jsx("span", { style: screenshotPreviewStyles.tabSize, children: formatBytes(tab.data.sizeBytes) })
1999
+ tab.data && /* @__PURE__ */ jsx2("span", { style: screenshotPreviewStyles.tabSize, children: formatBytes(tab.data.sizeBytes) })
655
2000
  ]
656
2001
  },
657
2002
  tab.key
658
2003
  )) }),
659
- /* @__PURE__ */ jsx(
2004
+ /* @__PURE__ */ jsx2(
660
2005
  "div",
661
2006
  {
662
2007
  style: {
663
2008
  ...screenshotPreviewStyles.previewContainer,
664
2009
  ...isExpanded ? screenshotPreviewStyles.previewContainerExpanded : {}
665
2010
  },
666
- children: activeScreenshot ? /* @__PURE__ */ jsx(
2011
+ children: activeScreenshot ? /* @__PURE__ */ jsx2(
667
2012
  "img",
668
2013
  {
669
2014
  alt: `${activeTab} screenshot`,
670
2015
  src: activeScreenshot.dataUrl,
671
2016
  style: screenshotPreviewStyles.previewImage
672
2017
  }
673
- ) : activeError ? /* @__PURE__ */ jsxs("div", { style: screenshotPreviewStyles.errorPreview, children: [
674
- /* @__PURE__ */ jsx(AlertCircleIcon, { className: "w-8 h-8", style: { color: "#ef4444" } }),
675
- /* @__PURE__ */ jsx("span", { style: screenshotPreviewStyles.errorTitle, children: "Capture Failed" }),
676
- /* @__PURE__ */ jsx("span", { style: screenshotPreviewStyles.errorMessage, children: activeError.message })
677
- ] }) : /* @__PURE__ */ jsxs("div", { style: screenshotPreviewStyles.noPreview, children: [
678
- /* @__PURE__ */ jsx(ImageIcon, { className: "w-6 h-6", style: { color: "#9ca3af" } }),
679
- /* @__PURE__ */ jsxs("span", { children: [
2018
+ ) : activeError ? /* @__PURE__ */ jsxs2("div", { style: screenshotPreviewStyles.errorPreview, children: [
2019
+ /* @__PURE__ */ jsx2(AlertCircleIcon, { className: "w-8 h-8", style: { color: "#ef4444" } }),
2020
+ /* @__PURE__ */ jsx2("span", { style: screenshotPreviewStyles.errorTitle, children: "Capture Failed" }),
2021
+ /* @__PURE__ */ jsx2("span", { style: screenshotPreviewStyles.errorMessage, children: activeError.message })
2022
+ ] }) : /* @__PURE__ */ jsxs2("div", { style: screenshotPreviewStyles.noPreview, children: [
2023
+ /* @__PURE__ */ jsx2(ImageIcon, { className: "w-6 h-6", style: { color: "#9ca3af" } }),
2024
+ /* @__PURE__ */ jsxs2("span", { children: [
680
2025
  "No ",
681
2026
  activeTab,
682
2027
  " screenshot"
@@ -684,14 +2029,14 @@ var ScreenshotPreview = React.memo(function ScreenshotPreview2({
684
2029
  ] })
685
2030
  }
686
2031
  ),
687
- activeScreenshot && /* @__PURE__ */ jsxs("div", { style: screenshotPreviewStyles.dimensionsInfo, children: [
2032
+ activeScreenshot && /* @__PURE__ */ jsxs2("div", { style: screenshotPreviewStyles.dimensionsInfo, children: [
688
2033
  activeScreenshot.width,
689
2034
  " \xD7 ",
690
2035
  activeScreenshot.height,
691
2036
  "px"
692
2037
  ] }),
693
- /* @__PURE__ */ jsxs("div", { style: screenshotPreviewStyles.actions, children: [
694
- /* @__PURE__ */ jsxs(
2038
+ /* @__PURE__ */ jsxs2("div", { style: screenshotPreviewStyles.actions, children: [
2039
+ /* @__PURE__ */ jsxs2(
695
2040
  "button",
696
2041
  {
697
2042
  type: "button",
@@ -699,13 +2044,13 @@ var ScreenshotPreview = React.memo(function ScreenshotPreview2({
699
2044
  onClick: onRetake,
700
2045
  style: screenshotPreviewStyles.retakeButtonSmall,
701
2046
  children: [
702
- /* @__PURE__ */ jsx(RefreshCwIcon, { className: "w-3 h-3" }),
2047
+ /* @__PURE__ */ jsx2(RefreshCwIcon, { className: "w-3 h-3" }),
703
2048
  "Retake"
704
2049
  ]
705
2050
  }
706
2051
  ),
707
- /* @__PURE__ */ jsxs("div", { style: screenshotPreviewStyles.actionsRight, children: [
708
- /* @__PURE__ */ jsxs(
2052
+ /* @__PURE__ */ jsxs2("div", { style: screenshotPreviewStyles.actionsRight, children: [
2053
+ /* @__PURE__ */ jsxs2(
709
2054
  "button",
710
2055
  {
711
2056
  type: "button",
@@ -714,12 +2059,12 @@ var ScreenshotPreview = React.memo(function ScreenshotPreview2({
714
2059
  onClick: onCancel,
715
2060
  style: { ...menuStyles.button, ...menuStyles.cancelButton },
716
2061
  children: [
717
- /* @__PURE__ */ jsx(XIcon, { className: "w-3 h-3" }),
2062
+ /* @__PURE__ */ jsx2(XIcon, { className: "w-3 h-3" }),
718
2063
  "Cancel"
719
2064
  ]
720
2065
  }
721
2066
  ),
722
- /* @__PURE__ */ jsx(
2067
+ /* @__PURE__ */ jsx2(
723
2068
  "button",
724
2069
  {
725
2070
  type: "button",
@@ -730,11 +2075,11 @@ var ScreenshotPreview = React.memo(function ScreenshotPreview2({
730
2075
  ...menuStyles.submitButton,
731
2076
  ...isSubmitting ? menuStyles.submitButtonDisabled : {}
732
2077
  },
733
- children: isSubmitting ? /* @__PURE__ */ jsxs(Fragment, { children: [
734
- /* @__PURE__ */ jsx(Loader2Icon, { className: "w-3 h-3 animate-spin" }),
2078
+ children: isSubmitting ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
2079
+ /* @__PURE__ */ jsx2(Loader2Icon, { className: "w-3 h-3 animate-spin" }),
735
2080
  "Sending..."
736
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
737
- /* @__PURE__ */ jsx(CheckIcon, { className: "w-3 h-3" }),
2081
+ ] }) : /* @__PURE__ */ jsxs2(Fragment2, { children: [
2082
+ /* @__PURE__ */ jsx2(CheckIcon, { className: "w-3 h-3" }),
738
2083
  "Send"
739
2084
  ] })
740
2085
  }
@@ -831,7 +2176,7 @@ svg.${HIGHLIGHT_CONTAINER_CLASS},
831
2176
  }
832
2177
  `;
833
2178
  }
834
- function injectStyles(colors) {
2179
+ function injectStyles2(colors) {
835
2180
  if (typeof document === "undefined") return;
836
2181
  const existingStyle = document.getElementById(STYLE_ID);
837
2182
  if (existingStyle) {
@@ -879,12 +2224,12 @@ function findContainerParent(element, config) {
879
2224
  }
880
2225
  function highlightTarget(element, colors) {
881
2226
  const mergedColors = { ...defaultHighlightColors, ...colors };
882
- injectStyles(mergedColors);
2227
+ injectStyles2(mergedColors);
883
2228
  element.classList.add(HIGHLIGHT_TARGET_CLASS);
884
2229
  }
885
2230
  function highlightContainer(element, colors) {
886
2231
  const mergedColors = { ...defaultHighlightColors, ...colors };
887
- injectStyles(mergedColors);
2232
+ injectStyles2(mergedColors);
888
2233
  element.classList.add(HIGHLIGHT_CONTAINER_CLASS);
889
2234
  }
890
2235
  function clearHighlights() {
@@ -911,12 +2256,12 @@ function applyHighlights(targetElement, config) {
911
2256
  }
912
2257
 
913
2258
  // src/ContextMenu.tsx
914
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
2259
+ import { Fragment as Fragment3, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
915
2260
  var VIEWPORT_PADDING = 10;
916
2261
  var defaultIcons = {
917
- feature: /* @__PURE__ */ jsx2(PlusIcon, { className: "w-4 h-4" }),
918
- issue: /* @__PURE__ */ jsx2(FlagIcon, { className: "w-4 h-4" }),
919
- like: /* @__PURE__ */ jsx2(ThumbsUpIcon, { className: "w-4 h-4" })
2262
+ feature: /* @__PURE__ */ jsx3(PlusIcon, { className: "w-4 h-4" }),
2263
+ issue: /* @__PURE__ */ jsx3(FlagIcon, { className: "w-4 h-4" }),
2264
+ like: /* @__PURE__ */ jsx3(ThumbsUpIcon, { className: "w-4 h-4" })
920
2265
  };
921
2266
  var OFFSCREEN_POSITION = { x: -9999, y: -9999 };
922
2267
  var DefaultHeader = ({
@@ -925,25 +2270,25 @@ var DefaultHeader = ({
925
2270
  styles,
926
2271
  title = "Send Feedback"
927
2272
  }) => {
928
- return /* @__PURE__ */ jsxs2("div", { style: styles, className, children: [
929
- /* @__PURE__ */ jsx2("span", { children: title }),
2273
+ return /* @__PURE__ */ jsxs3("div", { style: styles, className, children: [
2274
+ /* @__PURE__ */ jsx3("span", { children: title }),
930
2275
  children
931
2276
  ] });
932
2277
  };
933
- var MenuItem = React2.memo(function MenuItem2({
2278
+ var MenuItem = React3.memo(function MenuItem2({
934
2279
  disabled,
935
2280
  hasChildren,
936
2281
  item,
937
2282
  onClick
938
2283
  }) {
939
- const [isHovered, setIsHovered] = useState2(false);
940
- const [isPressed, setIsPressed] = useState2(false);
2284
+ const [isHovered, setIsHovered] = useState4(false);
2285
+ const [isPressed, setIsPressed] = useState4(false);
941
2286
  const isComingSoon = item.status === "comingSoon";
942
2287
  const badgeLabel = item.badge?.label ?? (isComingSoon ? "Coming soon" : null);
943
2288
  const badgeTone = item.badge?.tone ?? (isComingSoon ? "neutral" : "neutral");
944
2289
  const badgeStyle = badgeLabel ? getBadgeStyle(badgeTone) : void 0;
945
2290
  const iconNode = item.icon ?? defaultIcons[item.type];
946
- return /* @__PURE__ */ jsxs2(
2291
+ return /* @__PURE__ */ jsxs3(
947
2292
  "button",
948
2293
  {
949
2294
  type: "button",
@@ -964,22 +2309,22 @@ var MenuItem = React2.memo(function MenuItem2({
964
2309
  ...disabled ? menuStyles.itemDisabled : {}
965
2310
  },
966
2311
  children: [
967
- iconNode ? /* @__PURE__ */ jsx2("span", { style: menuStyles.itemIcon, children: iconNode }) : null,
968
- /* @__PURE__ */ jsxs2("span", { style: menuStyles.itemLabel, children: [
2312
+ iconNode ? /* @__PURE__ */ jsx3("span", { style: menuStyles.itemIcon, children: iconNode }) : null,
2313
+ /* @__PURE__ */ jsxs3("span", { style: menuStyles.itemLabel, children: [
969
2314
  item.label,
970
- badgeLabel && /* @__PURE__ */ jsx2("span", { style: badgeStyle, children: badgeLabel })
2315
+ badgeLabel && /* @__PURE__ */ jsx3("span", { style: badgeStyle, children: badgeLabel })
971
2316
  ] }),
972
- hasChildren && /* @__PURE__ */ jsx2(ChevronRightIcon, { className: "w-4 h-4", style: menuStyles.submenuIcon })
2317
+ hasChildren && /* @__PURE__ */ jsx3(ChevronRightIcon, { className: "w-4 h-4", style: menuStyles.submenuIcon })
973
2318
  ]
974
2319
  }
975
2320
  );
976
2321
  });
977
- var BackButton = React2.memo(function BackButton2({
2322
+ var BackButton = React3.memo(function BackButton2({
978
2323
  onClick
979
2324
  }) {
980
- const [isHovered, setIsHovered] = useState2(false);
981
- const [isPressed, setIsPressed] = useState2(false);
982
- return /* @__PURE__ */ jsxs2(
2325
+ const [isHovered, setIsHovered] = useState4(false);
2326
+ const [isPressed, setIsPressed] = useState4(false);
2327
+ return /* @__PURE__ */ jsxs3(
983
2328
  "button",
984
2329
  {
985
2330
  type: "button",
@@ -999,26 +2344,26 @@ var BackButton = React2.memo(function BackButton2({
999
2344
  ...isHovered || isPressed ? menuStyles.itemHover : {}
1000
2345
  },
1001
2346
  children: [
1002
- /* @__PURE__ */ jsx2(ChevronLeftIcon, { className: "w-4 h-4", style: { opacity: 0.5 } }),
1003
- /* @__PURE__ */ jsx2("span", { style: { opacity: 0.7 }, children: "Back" })
2347
+ /* @__PURE__ */ jsx3(ChevronLeftIcon, { className: "w-4 h-4", style: { opacity: 0.5 } }),
2348
+ /* @__PURE__ */ jsx3("span", { style: { opacity: 0.7 }, children: "Back" })
1004
2349
  ]
1005
2350
  }
1006
2351
  );
1007
2352
  });
1008
- var CommentForm = React2.memo(function CommentForm2({
2353
+ var CommentForm = React3.memo(function CommentForm2({
1009
2354
  isSubmitting,
1010
2355
  onCancel,
1011
2356
  onSubmit
1012
2357
  }) {
1013
- const [comment, setComment] = useState2("");
1014
- const inputRef = useRef(null);
1015
- useEffect(() => {
2358
+ const [comment, setComment] = useState4("");
2359
+ const inputRef = useRef3(null);
2360
+ useEffect3(() => {
1016
2361
  inputRef.current?.focus();
1017
2362
  }, []);
1018
- const handleSubmit = useCallback(() => {
2363
+ const handleSubmit = useCallback3(() => {
1019
2364
  onSubmit(comment);
1020
2365
  }, [comment, onSubmit]);
1021
- const handleKeyDown = useCallback(
2366
+ const handleKeyDown = useCallback3(
1022
2367
  (e) => {
1023
2368
  if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
1024
2369
  handleSubmit();
@@ -1028,8 +2373,8 @@ var CommentForm = React2.memo(function CommentForm2({
1028
2373
  },
1029
2374
  [handleSubmit, onCancel]
1030
2375
  );
1031
- return /* @__PURE__ */ jsxs2("div", { style: menuStyles.commentSection, children: [
1032
- /* @__PURE__ */ jsx2(
2376
+ return /* @__PURE__ */ jsxs3("div", { style: menuStyles.commentSection, children: [
2377
+ /* @__PURE__ */ jsx3(
1033
2378
  "textarea",
1034
2379
  {
1035
2380
  ref: inputRef,
@@ -1041,8 +2386,8 @@ var CommentForm = React2.memo(function CommentForm2({
1041
2386
  value: comment
1042
2387
  }
1043
2388
  ),
1044
- /* @__PURE__ */ jsxs2("div", { style: menuStyles.buttonRow, children: [
1045
- /* @__PURE__ */ jsx2(
2389
+ /* @__PURE__ */ jsxs3("div", { style: menuStyles.buttonRow, children: [
2390
+ /* @__PURE__ */ jsx3(
1046
2391
  "button",
1047
2392
  {
1048
2393
  type: "button",
@@ -1052,7 +2397,7 @@ var CommentForm = React2.memo(function CommentForm2({
1052
2397
  children: "Cancel"
1053
2398
  }
1054
2399
  ),
1055
- /* @__PURE__ */ jsx2(
2400
+ /* @__PURE__ */ jsx3(
1056
2401
  "button",
1057
2402
  {
1058
2403
  type: "button",
@@ -1100,26 +2445,35 @@ function ContextMenu({
1100
2445
  onSelect,
1101
2446
  position,
1102
2447
  positionMode = "inView",
2448
+ quickChatConfig,
1103
2449
  screenshotConfig,
1104
2450
  style,
1105
2451
  targetElement,
1106
2452
  visible
1107
2453
  }) {
1108
- const [selectedType, setSelectedType] = useState2(null);
1109
- const [currentView, setCurrentView] = useState2("menu");
1110
- const [pendingComment, setPendingComment] = useState2();
1111
- const [submenuStack, setSubmenuStack] = useState2([]);
1112
- const [screenshots, setScreenshots] = useState2(null);
1113
- const [isCapturing, setIsCapturing] = useState2(false);
1114
- const menuRef = useRef(null);
1115
- const [adjustedPosition, setAdjustedPosition] = useState2(OFFSCREEN_POSITION);
1116
- const [isDragging, setIsDragging] = useState2(false);
1117
- const [dragOffset, setDragOffset] = useState2({
2454
+ const [selectedType, setSelectedType] = useState4(null);
2455
+ const [currentView, setCurrentView] = useState4("menu");
2456
+ const [pendingComment, setPendingComment] = useState4();
2457
+ const [submenuStack, setSubmenuStack] = useState4([]);
2458
+ const [screenshots, setScreenshots] = useState4(null);
2459
+ const [isCapturing, setIsCapturing] = useState4(false);
2460
+ const [isQuickChatPinned, setIsQuickChatPinned] = useState4(() => {
2461
+ if (typeof window === "undefined") return false;
2462
+ try {
2463
+ return sessionStorage.getItem("anyclick-quick-chat-pinned") === "true";
2464
+ } catch {
2465
+ return false;
2466
+ }
2467
+ });
2468
+ const menuRef = useRef3(null);
2469
+ const [adjustedPosition, setAdjustedPosition] = useState4(OFFSCREEN_POSITION);
2470
+ const [isDragging, setIsDragging] = useState4(false);
2471
+ const [dragOffset, setDragOffset] = useState4({
1118
2472
  x: 0,
1119
2473
  y: 0
1120
2474
  });
1121
- const dragStartRef = useRef(null);
1122
- const mergedScreenshotConfig = React2.useMemo(
2475
+ const dragStartRef = useRef3(null);
2476
+ const mergedScreenshotConfig = React3.useMemo(
1123
2477
  () => ({
1124
2478
  ...DEFAULT_SCREENSHOT_CONFIG,
1125
2479
  ...screenshotConfig
@@ -1128,7 +2482,7 @@ function ContextMenu({
1128
2482
  );
1129
2483
  const showPreview = mergedScreenshotConfig.showPreview && isScreenshotSupported();
1130
2484
  const currentItems = submenuStack.length > 0 ? submenuStack[submenuStack.length - 1] : items;
1131
- const captureScreenshots = useCallback(async () => {
2485
+ const captureScreenshots = useCallback3(async () => {
1132
2486
  if (!targetElement || !showPreview) return;
1133
2487
  setIsCapturing(true);
1134
2488
  try {
@@ -1145,7 +2499,7 @@ function ContextMenu({
1145
2499
  setIsCapturing(false);
1146
2500
  }
1147
2501
  }, [containerElement, mergedScreenshotConfig, showPreview, targetElement]);
1148
- useEffect(() => {
2502
+ useEffect3(() => {
1149
2503
  if (!visible) {
1150
2504
  setSelectedType(null);
1151
2505
  setCurrentView("menu");
@@ -1159,7 +2513,7 @@ function ContextMenu({
1159
2513
  dragStartRef.current = null;
1160
2514
  }
1161
2515
  }, [visible]);
1162
- useEffect(() => {
2516
+ useEffect3(() => {
1163
2517
  if (visible && targetElement) {
1164
2518
  clearHighlights();
1165
2519
  applyHighlights(targetElement, highlightConfig);
@@ -1170,7 +2524,7 @@ function ContextMenu({
1170
2524
  clearHighlights();
1171
2525
  };
1172
2526
  }, [highlightConfig, targetElement, visible]);
1173
- useEffect(() => {
2527
+ useEffect3(() => {
1174
2528
  if (!visible) {
1175
2529
  return;
1176
2530
  }
@@ -1189,13 +2543,13 @@ function ContextMenu({
1189
2543
  document.removeEventListener("pointerdown", handlePointerDown);
1190
2544
  };
1191
2545
  }, [onClose, visible]);
1192
- useEffect(() => {
2546
+ useEffect3(() => {
1193
2547
  if (visible) {
1194
2548
  setAdjustedPosition(position);
1195
2549
  setDragOffset({ x: 0, y: 0 });
1196
2550
  }
1197
2551
  }, [position.x, position.y, visible]);
1198
- useEffect(() => {
2552
+ useEffect3(() => {
1199
2553
  if (!visible || !menuRef.current) return;
1200
2554
  const updatePosition = () => {
1201
2555
  const menuElement = menuRef.current;
@@ -1219,7 +2573,7 @@ function ContextMenu({
1219
2573
  window.addEventListener("resize", updatePosition);
1220
2574
  return () => window.removeEventListener("resize", updatePosition);
1221
2575
  }, [currentView, dragOffset, position, positionMode, visible]);
1222
- useEffect(() => {
2576
+ useEffect3(() => {
1223
2577
  if (!visible || positionMode !== "dynamic") return;
1224
2578
  const handlePointerMove = (event) => {
1225
2579
  if (!isDragging || !dragStartRef.current) return;
@@ -1246,7 +2600,7 @@ function ContextMenu({
1246
2600
  };
1247
2601
  }
1248
2602
  }, [isDragging, positionMode, visible]);
1249
- const handleDragStart = useCallback(
2603
+ const handleDragStart = useCallback3(
1250
2604
  (event) => {
1251
2605
  if (positionMode !== "dynamic") return;
1252
2606
  event.preventDefault();
@@ -1255,7 +2609,8 @@ function ContextMenu({
1255
2609
  },
1256
2610
  [positionMode]
1257
2611
  );
1258
- useEffect(() => {
2612
+ const [initialChatInput, setInitialChatInput] = useState4("");
2613
+ useEffect3(() => {
1259
2614
  const handleKeyDown = (e) => {
1260
2615
  if (e.key === "Escape") {
1261
2616
  if (currentView === "screenshot-preview") {
@@ -1264,19 +2619,38 @@ function ContextMenu({
1264
2619
  setCurrentView("menu");
1265
2620
  setSelectedType(null);
1266
2621
  setPendingComment(void 0);
2622
+ } else if (currentView === "quick-chat") {
2623
+ if (!isQuickChatPinned) {
2624
+ setCurrentView("menu");
2625
+ }
1267
2626
  } else if (submenuStack.length > 0) {
1268
2627
  setSubmenuStack((prev) => prev.slice(0, -1));
1269
2628
  } else {
1270
2629
  onClose();
1271
2630
  }
2631
+ return;
2632
+ }
2633
+ if (quickChatConfig && currentView === "menu" && !isQuickChatPinned && !e.ctrlKey && !e.metaKey && !e.altKey) {
2634
+ if (e.key.length === 1 && e.key.match(/[a-zA-Z0-9\s.,!?'"]/)) {
2635
+ e.preventDefault();
2636
+ setInitialChatInput(e.key);
2637
+ setCurrentView("quick-chat");
2638
+ }
1272
2639
  }
1273
2640
  };
1274
2641
  if (visible) {
1275
2642
  document.addEventListener("keydown", handleKeyDown);
1276
2643
  return () => document.removeEventListener("keydown", handleKeyDown);
1277
2644
  }
1278
- }, [currentView, onClose, submenuStack.length, visible]);
1279
- useEffect(() => {
2645
+ }, [
2646
+ currentView,
2647
+ isQuickChatPinned,
2648
+ onClose,
2649
+ quickChatConfig,
2650
+ submenuStack.length,
2651
+ visible
2652
+ ]);
2653
+ useEffect3(() => {
1280
2654
  const menuElement = menuRef.current;
1281
2655
  if (!visible || !menuElement) return;
1282
2656
  const preventTouchDefault = (e) => {
@@ -1293,9 +2667,6 @@ function ContextMenu({
1293
2667
  menuElement.removeEventListener("touchmove", preventTouchDefault);
1294
2668
  };
1295
2669
  }, [visible]);
1296
- if (!visible || !targetElement) {
1297
- return null;
1298
- }
1299
2670
  const handleItemClick = (item) => {
1300
2671
  if (item.status === "comingSoon") {
1301
2672
  return;
@@ -1364,85 +2735,166 @@ function ContextMenu({
1364
2735
  const handleRetakeScreenshots = () => {
1365
2736
  captureScreenshots();
1366
2737
  };
1367
- const containerWidth = currentView === "screenshot-preview" ? 360 : void 0;
1368
- return /* @__PURE__ */ jsxs2(
1369
- "div",
1370
- {
1371
- ref: menuRef,
1372
- "aria-label": "Feedback options",
1373
- className,
1374
- role: "menu",
1375
- style: {
1376
- ...menuStyles.container,
1377
- left: adjustedPosition.x,
1378
- top: adjustedPosition.y,
1379
- ...containerWidth ? { minWidth: containerWidth, width: containerWidth } : {},
1380
- touchAction: "none",
1381
- userSelect: "none",
1382
- WebkitTouchCallout: "none",
1383
- WebkitUserSelect: "none",
1384
- ...isDragging ? { cursor: "grabbing" } : {},
1385
- ...style
1386
- },
1387
- children: [
1388
- !header && currentView !== "screenshot-preview" && /* @__PURE__ */ jsxs2(DefaultHeader, { styles: menuStyles.header, title: "Send Feedback", children: [
1389
- showPreview && /* @__PURE__ */ jsx2("div", { style: menuStyles.screenshotIndicator, children: /* @__PURE__ */ jsx2(CameraIcon, { className: "w-3 h-3" }) }),
1390
- positionMode === "dynamic" && /* @__PURE__ */ jsx2(
1391
- "div",
1392
- {
1393
- "data-drag-handle": true,
1394
- onMouseEnter: (e) => {
1395
- e.currentTarget.style.opacity = "1";
1396
- },
1397
- onMouseLeave: (e) => {
1398
- e.currentTarget.style.opacity = "0.5";
1399
- },
1400
- onPointerDown: handleDragStart,
1401
- style: {
1402
- ...menuStyles.dragHandle,
1403
- cursor: isDragging ? "grabbing" : "grab"
2738
+ const handleQuickChatToggle = () => {
2739
+ if (currentView === "quick-chat" && !isQuickChatPinned) {
2740
+ setCurrentView("menu");
2741
+ } else {
2742
+ setCurrentView("quick-chat");
2743
+ }
2744
+ };
2745
+ const handleQuickChatPin = (pinned) => {
2746
+ setIsQuickChatPinned(pinned);
2747
+ try {
2748
+ if (pinned) {
2749
+ sessionStorage.setItem("anyclick-quick-chat-pinned", "true");
2750
+ } else {
2751
+ sessionStorage.removeItem("anyclick-quick-chat-pinned");
2752
+ }
2753
+ } catch {
2754
+ }
2755
+ if (pinned) {
2756
+ setCurrentView("menu");
2757
+ }
2758
+ };
2759
+ const handleQuickChatClose = () => {
2760
+ setIsQuickChatPinned(false);
2761
+ try {
2762
+ sessionStorage.removeItem("anyclick-quick-chat-pinned");
2763
+ } catch {
2764
+ }
2765
+ setCurrentView("menu");
2766
+ };
2767
+ const containerWidth = currentView === "screenshot-preview" ? 360 : currentView === "quick-chat" && !isQuickChatPinned ? 320 : void 0;
2768
+ const showPinnedDrawer = isQuickChatPinned && quickChatConfig;
2769
+ const showMenu = visible && targetElement;
2770
+ return /* @__PURE__ */ jsxs3(Fragment3, { children: [
2771
+ showPinnedDrawer && /* @__PURE__ */ jsx3(
2772
+ QuickChat,
2773
+ {
2774
+ visible: true,
2775
+ targetElement,
2776
+ containerElement,
2777
+ onClose: handleQuickChatClose,
2778
+ onPin: handleQuickChatPin,
2779
+ isPinned: true,
2780
+ config: quickChatConfig
2781
+ }
2782
+ ),
2783
+ showMenu && /* @__PURE__ */ jsxs3(
2784
+ "div",
2785
+ {
2786
+ ref: menuRef,
2787
+ "aria-label": "Feedback options",
2788
+ className,
2789
+ role: "menu",
2790
+ style: {
2791
+ ...menuStyles.container,
2792
+ left: adjustedPosition.x,
2793
+ top: adjustedPosition.y,
2794
+ ...containerWidth ? { minWidth: containerWidth, width: containerWidth } : {},
2795
+ touchAction: "none",
2796
+ userSelect: "none",
2797
+ WebkitTouchCallout: "none",
2798
+ WebkitUserSelect: "none",
2799
+ ...isDragging ? { cursor: "grabbing" } : {},
2800
+ ...style
2801
+ },
2802
+ children: [
2803
+ !header && currentView !== "screenshot-preview" && currentView !== "quick-chat" && /* @__PURE__ */ jsxs3(DefaultHeader, { styles: menuStyles.header, title: "Send Feedback", children: [
2804
+ positionMode === "dynamic" && /* @__PURE__ */ jsx3(
2805
+ "div",
2806
+ {
2807
+ "data-drag-handle": true,
2808
+ onMouseEnter: (e) => {
2809
+ e.currentTarget.style.opacity = "1";
2810
+ },
2811
+ onMouseLeave: (e) => {
2812
+ e.currentTarget.style.opacity = "0.5";
2813
+ },
2814
+ onPointerDown: handleDragStart,
2815
+ style: {
2816
+ ...menuStyles.dragHandle,
2817
+ cursor: isDragging ? "grabbing" : "grab"
2818
+ },
2819
+ title: "Drag to move",
2820
+ children: /* @__PURE__ */ jsx3(GripVertical, { className: "w-4 h-4" })
2821
+ }
2822
+ ),
2823
+ showPreview && /* @__PURE__ */ jsx3("div", { style: menuStyles.screenshotIndicator, children: /* @__PURE__ */ jsx3(CameraIcon, { className: "w-3 h-3" }) }),
2824
+ quickChatConfig && /* @__PURE__ */ jsx3(
2825
+ "button",
2826
+ {
2827
+ type: "button",
2828
+ onClick: handleQuickChatToggle,
2829
+ style: {
2830
+ display: "flex",
2831
+ alignItems: "center",
2832
+ justifyContent: "center",
2833
+ width: "24px",
2834
+ height: "24px",
2835
+ border: "none",
2836
+ borderRadius: "4px",
2837
+ backgroundColor: isQuickChatPinned ? "var(--anyclick-menu-accent, #0066cc)" : "transparent",
2838
+ color: isQuickChatPinned ? "#fff" : "var(--anyclick-menu-accent, #0066cc)",
2839
+ cursor: "pointer",
2840
+ transition: "all 0.15s ease"
2841
+ },
2842
+ title: isQuickChatPinned ? "Quick Chat (pinned)" : "Quick Ask AI",
2843
+ children: /* @__PURE__ */ jsx3(Sparkles, { className: "w-3.5 h-3.5" })
2844
+ }
2845
+ )
2846
+ ] }),
2847
+ !!header && header,
2848
+ currentView === "menu" && /* @__PURE__ */ jsxs3("div", { style: menuStyles.itemList, children: [
2849
+ submenuStack.length > 0 && /* @__PURE__ */ jsx3(BackButton, { onClick: handleBack }),
2850
+ currentItems.map((item) => /* @__PURE__ */ jsx3(
2851
+ MenuItem,
2852
+ {
2853
+ disabled: isSubmitting,
2854
+ hasChildren: item.children && item.children.length > 0,
2855
+ item,
2856
+ onClick: () => handleItemClick(item)
1404
2857
  },
1405
- title: "Drag to move",
1406
- children: /* @__PURE__ */ jsx2(GripVertical, { className: "w-4 h-4" })
2858
+ item.type
2859
+ ))
2860
+ ] }),
2861
+ currentView === "comment" && /* @__PURE__ */ jsx3(
2862
+ CommentForm,
2863
+ {
2864
+ isSubmitting,
2865
+ onCancel: handleCommentCancel,
2866
+ onSubmit: handleCommentSubmit
1407
2867
  }
1408
- )
1409
- ] }),
1410
- !!header && header,
1411
- currentView === "menu" && /* @__PURE__ */ jsxs2("div", { style: menuStyles.itemList, children: [
1412
- submenuStack.length > 0 && /* @__PURE__ */ jsx2(BackButton, { onClick: handleBack }),
1413
- currentItems.map((item) => /* @__PURE__ */ jsx2(
1414
- MenuItem,
2868
+ ),
2869
+ currentView === "screenshot-preview" && /* @__PURE__ */ jsx3(
2870
+ ScreenshotPreview,
1415
2871
  {
1416
- disabled: isSubmitting,
1417
- hasChildren: item.children && item.children.length > 0,
1418
- item,
1419
- onClick: () => handleItemClick(item)
1420
- },
1421
- item.type
1422
- ))
1423
- ] }),
1424
- currentView === "comment" && /* @__PURE__ */ jsx2(
1425
- CommentForm,
1426
- {
1427
- isSubmitting,
1428
- onCancel: handleCommentCancel,
1429
- onSubmit: handleCommentSubmit
1430
- }
1431
- ),
1432
- currentView === "screenshot-preview" && /* @__PURE__ */ jsx2(
1433
- ScreenshotPreview,
1434
- {
1435
- isLoading: isCapturing,
1436
- isSubmitting,
1437
- onCancel: handleScreenshotCancel,
1438
- onConfirm: handleScreenshotConfirm,
1439
- onRetake: handleRetakeScreenshots,
1440
- screenshots
1441
- }
1442
- )
1443
- ]
1444
- }
1445
- );
2872
+ isLoading: isCapturing,
2873
+ isSubmitting,
2874
+ onCancel: handleScreenshotCancel,
2875
+ onConfirm: handleScreenshotConfirm,
2876
+ onRetake: handleRetakeScreenshots,
2877
+ screenshots
2878
+ }
2879
+ ),
2880
+ currentView === "quick-chat" && quickChatConfig && !isQuickChatPinned && /* @__PURE__ */ jsx3(
2881
+ QuickChat,
2882
+ {
2883
+ visible: true,
2884
+ targetElement,
2885
+ containerElement,
2886
+ onClose: handleQuickChatClose,
2887
+ onPin: handleQuickChatPin,
2888
+ isPinned: false,
2889
+ config: quickChatConfig,
2890
+ initialInput: initialChatInput,
2891
+ onInitialInputConsumed: () => setInitialChatInput("")
2892
+ }
2893
+ )
2894
+ ]
2895
+ }
2896
+ )
2897
+ ] });
1446
2898
  }
1447
2899
 
1448
2900
  // src/context.ts
@@ -1626,7 +3078,7 @@ function dispatchContextMenuEvent(event, element) {
1626
3078
  }
1627
3079
 
1628
3080
  // src/AnyclickProvider.tsx
1629
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
3081
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1630
3082
  var defaultMenuItems = [
1631
3083
  { label: "Report an issue", showComment: true, type: "issue" },
1632
3084
  { label: "Request a feature", showComment: true, type: "feature" },
@@ -1649,6 +3101,7 @@ function AnyclickProvider({
1649
3101
  metadata,
1650
3102
  onSubmitError,
1651
3103
  onSubmitSuccess,
3104
+ quickChatConfig,
1652
3105
  scoped = false,
1653
3106
  screenshotConfig,
1654
3107
  stripAttributes,
@@ -1657,18 +3110,18 @@ function AnyclickProvider({
1657
3110
  touchHoldDurationMs,
1658
3111
  touchMoveThreshold
1659
3112
  }) {
1660
- const [isSubmitting, setIsSubmitting] = useState3(false);
1661
- const [menuVisible, setMenuVisible] = useState3(false);
1662
- const [menuPosition, setMenuPosition] = useState3(OFFSCREEN_POSITION2);
1663
- const [targetElement, setTargetElement] = useState3(null);
1664
- const [containerElement, setContainerElement] = useState3(
3113
+ const [isSubmitting, setIsSubmitting] = useState5(false);
3114
+ const [menuVisible, setMenuVisible] = useState5(false);
3115
+ const [menuPosition, setMenuPosition] = useState5(OFFSCREEN_POSITION2);
3116
+ const [targetElement, setTargetElement] = useState5(null);
3117
+ const [containerElement, setContainerElement] = useState5(
1665
3118
  null
1666
3119
  );
1667
3120
  const providerId = useId();
1668
- const containerRef = useRef2(null);
1669
- const [containerReady, setContainerReady] = useState3(!scoped);
1670
- const clientRef = useRef2(null);
1671
- const setContainerRef = useCallback2(
3121
+ const containerRef = useRef4(null);
3122
+ const [containerReady, setContainerReady] = useState5(!scoped);
3123
+ const clientRef = useRef4(null);
3124
+ const setContainerRef = useCallback4(
1672
3125
  (node) => {
1673
3126
  containerRef.current = node;
1674
3127
  if (scoped && node) {
@@ -1697,7 +3150,7 @@ function AnyclickProvider({
1697
3150
  updateProvider
1698
3151
  } = useProviderStore();
1699
3152
  const parentId = parentContext?.providerId ?? null;
1700
- const actualDepth = useMemo2(() => {
3153
+ const actualDepth = useMemo3(() => {
1701
3154
  if (!parentContext) return 0;
1702
3155
  let d = 0;
1703
3156
  let currentId = parentId;
@@ -1711,7 +3164,7 @@ function AnyclickProvider({
1711
3164
  }, [parentContext, parentId]);
1712
3165
  const isDisabledByTheme = theme === null || theme?.disabled === true;
1713
3166
  const effectiveDisabled = disabled || isDisabledByTheme;
1714
- const localTheme = useMemo2(() => {
3167
+ const localTheme = useMemo3(() => {
1715
3168
  if (theme === null) {
1716
3169
  return { disabled: true };
1717
3170
  }
@@ -1726,7 +3179,7 @@ function AnyclickProvider({
1726
3179
  ...theme
1727
3180
  };
1728
3181
  }, [highlightConfig, menuClassName, menuStyle, screenshotConfig, theme]);
1729
- const handleContextMenu = useCallback2(
3182
+ const handleContextMenu = useCallback4(
1730
3183
  (event, element) => {
1731
3184
  if (!scoped && isElementInDisabledScope(element)) {
1732
3185
  return false;
@@ -1780,7 +3233,7 @@ function AnyclickProvider({
1780
3233
  scoped,
1781
3234
  unregisterProvider
1782
3235
  ]);
1783
- useEffect2(() => {
3236
+ useEffect4(() => {
1784
3237
  updateProvider(providerId, {
1785
3238
  disabled: effectiveDisabled,
1786
3239
  onContextMenu: handleContextMenu,
@@ -1793,7 +3246,7 @@ function AnyclickProvider({
1793
3246
  providerId,
1794
3247
  updateProvider
1795
3248
  ]);
1796
- useEffect2(() => {
3249
+ useEffect4(() => {
1797
3250
  if (isDisabledByAncestor(providerId)) {
1798
3251
  return;
1799
3252
  }
@@ -1841,7 +3294,7 @@ function AnyclickProvider({
1841
3294
  touchHoldDurationMs,
1842
3295
  touchMoveThreshold
1843
3296
  ]);
1844
- const submitAnyclick = useCallback2(
3297
+ const submitAnyclick = useCallback4(
1845
3298
  async (element, type, comment, screenshots) => {
1846
3299
  const client = clientRef.current;
1847
3300
  if (!client) return;
@@ -1862,7 +3315,7 @@ function AnyclickProvider({
1862
3315
  },
1863
3316
  [metadata]
1864
3317
  );
1865
- const openMenu = useCallback2(
3318
+ const openMenu = useCallback4(
1866
3319
  (element, position) => {
1867
3320
  setTargetElement(element);
1868
3321
  const mergedTheme2 = getMergedTheme(providerId);
@@ -1876,13 +3329,13 @@ function AnyclickProvider({
1876
3329
  },
1877
3330
  [getMergedTheme, highlightConfig, providerId]
1878
3331
  );
1879
- const closeMenu = useCallback2(() => {
3332
+ const closeMenu = useCallback4(() => {
1880
3333
  setMenuVisible(false);
1881
3334
  setMenuPosition(OFFSCREEN_POSITION2);
1882
3335
  setTargetElement(null);
1883
3336
  setContainerElement(null);
1884
3337
  }, []);
1885
- const handleMenuSelect = useCallback2(
3338
+ const handleMenuSelect = useCallback4(
1886
3339
  (type, comment, screenshots) => {
1887
3340
  if (targetElement) {
1888
3341
  submitAnyclick(targetElement, type, comment, screenshots);
@@ -1891,7 +3344,7 @@ function AnyclickProvider({
1891
3344
  [submitAnyclick, targetElement]
1892
3345
  );
1893
3346
  const inheritedTheme = getMergedTheme(providerId);
1894
- const mergedTheme = useMemo2(
3347
+ const mergedTheme = useMemo3(
1895
3348
  () => ({
1896
3349
  ...inheritedTheme,
1897
3350
  ...localTheme,
@@ -1914,7 +3367,7 @@ function AnyclickProvider({
1914
3367
  const effectiveMenuClassName = mergedTheme.menuClassName ?? menuClassName;
1915
3368
  const effectiveHighlightConfig = mergedTheme.highlightConfig ?? highlightConfig;
1916
3369
  const effectiveScreenshotConfig = mergedTheme.screenshotConfig ?? screenshotConfig;
1917
- const contextValue = useMemo2(
3370
+ const contextValue = useMemo3(
1918
3371
  () => ({
1919
3372
  closeMenu,
1920
3373
  isEnabled: !effectiveDisabled && !isDisabledByAncestor(providerId),
@@ -1937,7 +3390,7 @@ function AnyclickProvider({
1937
3390
  submitAnyclick
1938
3391
  ]
1939
3392
  );
1940
- const content = scoped ? /* @__PURE__ */ jsx3(
3393
+ const content = scoped ? /* @__PURE__ */ jsx4(
1941
3394
  "div",
1942
3395
  {
1943
3396
  ref: setContainerRef,
@@ -1946,9 +3399,9 @@ function AnyclickProvider({
1946
3399
  children
1947
3400
  }
1948
3401
  ) : children;
1949
- return /* @__PURE__ */ jsxs3(AnyclickContext.Provider, { value: contextValue, children: [
3402
+ return /* @__PURE__ */ jsxs4(AnyclickContext.Provider, { value: contextValue, children: [
1950
3403
  content,
1951
- /* @__PURE__ */ jsx3(
3404
+ /* @__PURE__ */ jsx4(
1952
3405
  ContextMenu,
1953
3406
  {
1954
3407
  className: effectiveMenuClassName,
@@ -1960,6 +3413,7 @@ function AnyclickProvider({
1960
3413
  onClose: closeMenu,
1961
3414
  onSelect: handleMenuSelect,
1962
3415
  position: menuPosition,
3416
+ quickChatConfig,
1963
3417
  screenshotConfig: effectiveScreenshotConfig,
1964
3418
  style: effectiveMenuStyle,
1965
3419
  targetElement,
@@ -1971,7 +3425,7 @@ function AnyclickProvider({
1971
3425
  var FeedbackProvider = AnyclickProvider;
1972
3426
 
1973
3427
  // src/FunModeBridge.tsx
1974
- import { useEffect as useEffect3, useMemo as useMemo3, useRef as useRef3 } from "react";
3428
+ import { useEffect as useEffect5, useMemo as useMemo4, useRef as useRef5 } from "react";
1975
3429
  import {
1976
3430
  usePointer
1977
3431
  } from "@ewjdev/anyclick-pointer";
@@ -2020,9 +3474,9 @@ function buildFunConfig(theme, container) {
2020
3474
  function FunModeBridge() {
2021
3475
  const { setConfig } = usePointer();
2022
3476
  const providerStore = useProviderStore();
2023
- const activeProviderRef = useRef3(null);
2024
- const cachedConfigs = useRef3({});
2025
- const findActiveFunProvider = useMemo3(() => {
3477
+ const activeProviderRef = useRef5(null);
3478
+ const cachedConfigs = useRef5({});
3479
+ const findActiveFunProvider = useMemo4(() => {
2026
3480
  return (el) => {
2027
3481
  if (!el) return null;
2028
3482
  const providers = providerStore.findProvidersForElement(el);
@@ -2034,7 +3488,7 @@ function FunModeBridge() {
2034
3488
  return null;
2035
3489
  };
2036
3490
  }, [providerStore]);
2037
- useEffect3(() => {
3491
+ useEffect5(() => {
2038
3492
  const handleMove = (event) => {
2039
3493
  const el = document.elementFromPoint(event.clientX, event.clientY);
2040
3494
  const provider = findActiveFunProvider(el);
@@ -2069,15 +3523,15 @@ function FunModeBridge() {
2069
3523
  }
2070
3524
 
2071
3525
  // src/InspectDialog/InspectDialogManager.tsx
2072
- import { useCallback as useCallback3, useEffect as useEffect5, useState as useState5 } from "react";
3526
+ import { useCallback as useCallback5, useEffect as useEffect7, useState as useState7 } from "react";
2073
3527
 
2074
3528
  // src/InspectDialog/InspectSimple.tsx
2075
- import { useEffect as useEffect4, useMemo as useMemo4, useRef as useRef4, useState as useState4 } from "react";
3529
+ import { useEffect as useEffect6, useMemo as useMemo5, useRef as useRef6, useState as useState6 } from "react";
2076
3530
  import {
2077
3531
  captureScreenshot,
2078
- getElementInspectInfo
3532
+ getElementInspectInfo as getElementInspectInfo2
2079
3533
  } from "@ewjdev/anyclick-core";
2080
- import { Camera, Code, Copy, ExternalLink, FileText, X } from "lucide-react";
3534
+ import { Camera, Code, Copy as Copy2, ExternalLink as ExternalLink2, FileText, X as X2 } from "lucide-react";
2081
3535
 
2082
3536
  // src/ide.ts
2083
3537
  var DEFAULT_IDE_CONFIG = {
@@ -2179,7 +3633,7 @@ function formatSourceLocation(location) {
2179
3633
  }
2180
3634
 
2181
3635
  // src/InspectDialog/InspectSimple.tsx
2182
- import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
3636
+ import { Fragment as Fragment4, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
2183
3637
  var DEFAULT_COMPACT_CONFIG = {
2184
3638
  scale: 0.5,
2185
3639
  fonts: {
@@ -2241,8 +3695,8 @@ function downloadDataUrl(dataUrl, filename) {
2241
3695
  link.click();
2242
3696
  }
2243
3697
  function useIsMobile() {
2244
- const [isMobile, setIsMobile] = useState4(false);
2245
- useEffect4(() => {
3698
+ const [isMobile, setIsMobile] = useState6(false);
3699
+ useEffect6(() => {
2246
3700
  const mq = window.matchMedia("(max-width: 640px)");
2247
3701
  setIsMobile(mq.matches);
2248
3702
  const handler = (e) => setIsMobile(e.matches);
@@ -2260,20 +3714,20 @@ function InspectSimple({
2260
3714
  className,
2261
3715
  highlightColors
2262
3716
  }) {
2263
- const [info, setInfo] = useState4(null);
2264
- const [sourceLocation, setSourceLocation] = useState4(
3717
+ const [info, setInfo] = useState6(null);
3718
+ const [sourceLocation, setSourceLocation] = useState6(
2265
3719
  null
2266
3720
  );
2267
- const [status, setStatus] = useState4(null);
2268
- const [saving, setSaving] = useState4(false);
2269
- const dialogRef = useRef4(null);
3721
+ const [status, setStatus] = useState6(null);
3722
+ const [saving, setSaving] = useState6(false);
3723
+ const dialogRef = useRef6(null);
2270
3724
  const isMobile = useIsMobile();
2271
- useEffect4(() => {
3725
+ useEffect6(() => {
2272
3726
  if (!status) return;
2273
3727
  const timer = setTimeout(() => setStatus(null), 5e3);
2274
3728
  return () => clearTimeout(timer);
2275
3729
  }, [status]);
2276
- useEffect4(() => {
3730
+ useEffect6(() => {
2277
3731
  if (!visible || !targetElement) return;
2278
3732
  try {
2279
3733
  clearHighlights();
@@ -2282,7 +3736,7 @@ function InspectSimple({
2282
3736
  if (container) highlightContainer(container, highlightColors);
2283
3737
  } catch {
2284
3738
  }
2285
- const nextInfo = getElementInspectInfo(targetElement);
3739
+ const nextInfo = getElementInspectInfo2(targetElement);
2286
3740
  setInfo(nextInfo);
2287
3741
  setSourceLocation(
2288
3742
  findSourceLocationInAncestors(targetElement) ?? nextInfo.sourceLocation ?? null
@@ -2291,7 +3745,7 @@ function InspectSimple({
2291
3745
  clearHighlights();
2292
3746
  };
2293
3747
  }, [visible, targetElement, highlightColors]);
2294
- useEffect4(() => {
3748
+ useEffect6(() => {
2295
3749
  if (!visible) return;
2296
3750
  const handleClickOutside = (e) => {
2297
3751
  if (dialogRef.current && !dialogRef.current.contains(e.target)) {
@@ -2306,7 +3760,7 @@ function InspectSimple({
2306
3760
  document.removeEventListener("mousedown", handleClickOutside);
2307
3761
  };
2308
3762
  }, [visible, onClose]);
2309
- const identityLabel = useMemo4(() => {
3763
+ const identityLabel = useMemo5(() => {
2310
3764
  if (!info) return "Select an element";
2311
3765
  const classes = info.classNames[0] ? `.${info.classNames[0]}` : "";
2312
3766
  const id = info.id ? `#${info.id}` : "";
@@ -2392,8 +3846,8 @@ function InspectSimple({
2392
3846
  overflow: "hidden",
2393
3847
  ...style
2394
3848
  };
2395
- return /* @__PURE__ */ jsxs4(Fragment2, { children: [
2396
- /* @__PURE__ */ jsx4(
3849
+ return /* @__PURE__ */ jsxs5(Fragment4, { children: [
3850
+ /* @__PURE__ */ jsx5(
2397
3851
  "div",
2398
3852
  {
2399
3853
  style: {
@@ -2408,14 +3862,14 @@ function InspectSimple({
2408
3862
  role: "presentation"
2409
3863
  }
2410
3864
  ),
2411
- /* @__PURE__ */ jsxs4(
3865
+ /* @__PURE__ */ jsxs5(
2412
3866
  "div",
2413
3867
  {
2414
3868
  ref: dialogRef,
2415
3869
  className: `anyclick-tiny-inspect ${className ?? ""}`,
2416
3870
  style: dialogStyles,
2417
3871
  children: [
2418
- /* @__PURE__ */ jsxs4(
3872
+ /* @__PURE__ */ jsxs5(
2419
3873
  "div",
2420
3874
  {
2421
3875
  style: {
@@ -2427,7 +3881,7 @@ function InspectSimple({
2427
3881
  borderBottom: "1px solid #1e293b"
2428
3882
  },
2429
3883
  children: [
2430
- isMobile && /* @__PURE__ */ jsx4(
3884
+ isMobile && /* @__PURE__ */ jsx5(
2431
3885
  "div",
2432
3886
  {
2433
3887
  style: {
@@ -2442,8 +3896,8 @@ function InspectSimple({
2442
3896
  }
2443
3897
  }
2444
3898
  ),
2445
- /* @__PURE__ */ jsxs4("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
2446
- /* @__PURE__ */ jsx4(
3899
+ /* @__PURE__ */ jsxs5("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
3900
+ /* @__PURE__ */ jsx5(
2447
3901
  "span",
2448
3902
  {
2449
3903
  style: {
@@ -2458,31 +3912,31 @@ function InspectSimple({
2458
3912
  children: identityLabel
2459
3913
  }
2460
3914
  ),
2461
- sourceLocation && /* @__PURE__ */ jsx4(
3915
+ sourceLocation && /* @__PURE__ */ jsx5(
2462
3916
  "button",
2463
3917
  {
2464
3918
  type: "button",
2465
3919
  onClick: handleOpenIDE,
2466
3920
  title: formatSourceLocation(sourceLocation),
2467
3921
  style: iconBtnStyle,
2468
- children: /* @__PURE__ */ jsx4(ExternalLink, { size: 14 })
3922
+ children: /* @__PURE__ */ jsx5(ExternalLink2, { size: 14 })
2469
3923
  }
2470
3924
  )
2471
3925
  ] }),
2472
- /* @__PURE__ */ jsx4(
3926
+ /* @__PURE__ */ jsx5(
2473
3927
  "button",
2474
3928
  {
2475
3929
  type: "button",
2476
3930
  onClick: onClose,
2477
3931
  style: iconBtnStyle,
2478
3932
  "aria-label": "Close inspector",
2479
- children: /* @__PURE__ */ jsx4(X, { size: 16 })
3933
+ children: /* @__PURE__ */ jsx5(X2, { size: 16 })
2480
3934
  }
2481
3935
  )
2482
3936
  ]
2483
3937
  }
2484
3938
  ),
2485
- /* @__PURE__ */ jsxs4(
3939
+ /* @__PURE__ */ jsxs5(
2486
3940
  "div",
2487
3941
  {
2488
3942
  style: {
@@ -2492,7 +3946,7 @@ function InspectSimple({
2492
3946
  gap: 8
2493
3947
  },
2494
3948
  children: [
2495
- info?.selector && /* @__PURE__ */ jsx4(
3949
+ info?.selector && /* @__PURE__ */ jsx5(
2496
3950
  "code",
2497
3951
  {
2498
3952
  style: {
@@ -2507,7 +3961,7 @@ function InspectSimple({
2507
3961
  children: info.selector
2508
3962
  }
2509
3963
  ),
2510
- status && /* @__PURE__ */ jsx4(
3964
+ status && /* @__PURE__ */ jsx5(
2511
3965
  "div",
2512
3966
  {
2513
3967
  style: {
@@ -2519,7 +3973,7 @@ function InspectSimple({
2519
3973
  children: status
2520
3974
  }
2521
3975
  ),
2522
- /* @__PURE__ */ jsxs4(
3976
+ /* @__PURE__ */ jsxs5(
2523
3977
  "div",
2524
3978
  {
2525
3979
  style: {
@@ -2528,7 +3982,7 @@ function InspectSimple({
2528
3982
  gap: 6
2529
3983
  },
2530
3984
  children: [
2531
- /* @__PURE__ */ jsx4(
3985
+ /* @__PURE__ */ jsx5(
2532
3986
  "button",
2533
3987
  {
2534
3988
  type: "button",
@@ -2536,10 +3990,10 @@ function InspectSimple({
2536
3990
  style: iconActionStyle,
2537
3991
  title: "Copy CSS selector",
2538
3992
  "aria-label": "Copy CSS selector",
2539
- children: /* @__PURE__ */ jsx4(Copy, { size: 15 })
3993
+ children: /* @__PURE__ */ jsx5(Copy2, { size: 15 })
2540
3994
  }
2541
3995
  ),
2542
- /* @__PURE__ */ jsx4(
3996
+ /* @__PURE__ */ jsx5(
2543
3997
  "button",
2544
3998
  {
2545
3999
  type: "button",
@@ -2547,10 +4001,10 @@ function InspectSimple({
2547
4001
  style: iconActionStyle,
2548
4002
  title: "Copy text content",
2549
4003
  "aria-label": "Copy text content",
2550
- children: /* @__PURE__ */ jsx4(FileText, { size: 15 })
4004
+ children: /* @__PURE__ */ jsx5(FileText, { size: 15 })
2551
4005
  }
2552
4006
  ),
2553
- /* @__PURE__ */ jsx4(
4007
+ /* @__PURE__ */ jsx5(
2554
4008
  "button",
2555
4009
  {
2556
4010
  type: "button",
@@ -2558,10 +4012,10 @@ function InspectSimple({
2558
4012
  style: iconActionStyle,
2559
4013
  title: "Copy HTML markup",
2560
4014
  "aria-label": "Copy HTML markup",
2561
- children: /* @__PURE__ */ jsx4(Code, { size: 15 })
4015
+ children: /* @__PURE__ */ jsx5(Code, { size: 15 })
2562
4016
  }
2563
4017
  ),
2564
- /* @__PURE__ */ jsx4(
4018
+ /* @__PURE__ */ jsx5(
2565
4019
  "button",
2566
4020
  {
2567
4021
  type: "button",
@@ -2573,7 +4027,7 @@ function InspectSimple({
2573
4027
  disabled: saving,
2574
4028
  title: "Save screenshot",
2575
4029
  "aria-label": "Save screenshot",
2576
- children: /* @__PURE__ */ jsx4(Camera, { size: 15 })
4030
+ children: /* @__PURE__ */ jsx5(Camera, { size: 15 })
2577
4031
  }
2578
4032
  )
2579
4033
  ]
@@ -2616,7 +4070,7 @@ var iconActionStyle = {
2616
4070
  };
2617
4071
 
2618
4072
  // src/InspectDialog/InspectDialogManager.tsx
2619
- import { jsx as jsx5 } from "react/jsx-runtime";
4073
+ import { jsx as jsx6 } from "react/jsx-runtime";
2620
4074
  var INSPECT_DIALOG_EVENT = "anyclick:inspect";
2621
4075
  function openInspectDialog(targetElement) {
2622
4076
  if (typeof window === "undefined") return;
@@ -2639,16 +4093,16 @@ function InspectDialogManager({
2639
4093
  initialPinnedPosition = "floating",
2640
4094
  compactConfig
2641
4095
  }) {
2642
- const [visible, setVisible] = useState5(false);
2643
- const [targetElement, setTargetElement] = useState5(null);
2644
- const handleClose = useCallback3(() => {
4096
+ const [visible, setVisible] = useState7(false);
4097
+ const [targetElement, setTargetElement] = useState7(null);
4098
+ const handleClose = useCallback5(() => {
2645
4099
  setVisible(false);
2646
4100
  setTargetElement(null);
2647
4101
  }, []);
2648
- const handleSelectElement = useCallback3((element) => {
4102
+ const handleSelectElement = useCallback5((element) => {
2649
4103
  setTargetElement(element);
2650
4104
  }, []);
2651
- useEffect5(() => {
4105
+ useEffect7(() => {
2652
4106
  const handleInspectEvent = (event) => {
2653
4107
  const customEvent = event;
2654
4108
  if (customEvent.detail?.targetElement) {
@@ -2661,7 +4115,7 @@ function InspectDialogManager({
2661
4115
  window.removeEventListener(INSPECT_DIALOG_EVENT, handleInspectEvent);
2662
4116
  };
2663
4117
  }, []);
2664
- return /* @__PURE__ */ jsx5(
4118
+ return /* @__PURE__ */ jsx6(
2665
4119
  InspectSimple,
2666
4120
  {
2667
4121
  visible,
@@ -2723,6 +4177,20 @@ var presetDefaults = {
2723
4177
  showComment: false,
2724
4178
  type: "search_google"
2725
4179
  },
4180
+ {
4181
+ label: "Ask t3.chat",
4182
+ onClick: ({ closeMenu }) => {
4183
+ closeMenu();
4184
+ if (typeof window === "undefined") return false;
4185
+ const selection = window.getSelection()?.toString().trim();
4186
+ const query = selection && selection.length > 0 ? selection : "";
4187
+ const url = query ? `https://t3.chat/?q=${encodeURIComponent(query)}` : "https://t3.chat";
4188
+ window.open(url, "_blank", "noopener,noreferrer");
4189
+ return false;
4190
+ },
4191
+ showComment: false,
4192
+ type: "ask_t3chat"
4193
+ },
2726
4194
  {
2727
4195
  label: "Share\u2026",
2728
4196
  onClick: async ({ closeMenu }) => {
@@ -2992,6 +4460,172 @@ function listPresets() {
2992
4460
  }))
2993
4461
  }));
2994
4462
  }
4463
+ function createT3ChatMenuItem(options = {}) {
4464
+ const { label = "Ask t3.chat", baseUrl = "https://t3.chat" } = options;
4465
+ return {
4466
+ label,
4467
+ onClick: ({ closeMenu }) => {
4468
+ closeMenu();
4469
+ if (typeof window === "undefined") return false;
4470
+ const selection = window.getSelection()?.toString().trim();
4471
+ const query = selection && selection.length > 0 ? selection : "";
4472
+ const url = query ? `${baseUrl}/?q=${encodeURIComponent(query)}` : baseUrl;
4473
+ window.open(url, "_blank", "noopener,noreferrer");
4474
+ return false;
4475
+ },
4476
+ showComment: false,
4477
+ type: "ask_t3chat"
4478
+ };
4479
+ }
4480
+ function getSelectedText() {
4481
+ if (typeof window === "undefined") return "";
4482
+ return window.getSelection()?.toString().trim() ?? "";
4483
+ }
4484
+ function hasTextSelection() {
4485
+ return getSelectedText().length > 0;
4486
+ }
4487
+ function detectImageElement(element) {
4488
+ if (!element) return { isImage: false };
4489
+ if (element.tagName === "IMG") {
4490
+ const img = element;
4491
+ return {
4492
+ isImage: true,
4493
+ src: img.src || img.currentSrc,
4494
+ type: "img"
4495
+ };
4496
+ }
4497
+ if (element.tagName === "PICTURE") {
4498
+ const img = element.querySelector("img");
4499
+ return {
4500
+ isImage: true,
4501
+ src: img?.src || img?.currentSrc,
4502
+ type: "picture"
4503
+ };
4504
+ }
4505
+ if (element.tagName === "SVG" || element.tagName === "svg") {
4506
+ return {
4507
+ isImage: true,
4508
+ type: "svg"
4509
+ };
4510
+ }
4511
+ if (element.tagName === "CANVAS") {
4512
+ return {
4513
+ isImage: true,
4514
+ type: "canvas"
4515
+ };
4516
+ }
4517
+ if (typeof window !== "undefined") {
4518
+ const computedStyle = window.getComputedStyle(element);
4519
+ const backgroundImage = computedStyle.backgroundImage;
4520
+ if (backgroundImage && backgroundImage !== "none") {
4521
+ const urlMatch = backgroundImage.match(/url\(["']?(.+?)["']?\)/);
4522
+ if (urlMatch) {
4523
+ return {
4524
+ isImage: true,
4525
+ src: urlMatch[1],
4526
+ type: "background"
4527
+ };
4528
+ }
4529
+ }
4530
+ }
4531
+ const imgChild = element.querySelector("img");
4532
+ if (imgChild) {
4533
+ return {
4534
+ isImage: true,
4535
+ src: imgChild.src || imgChild.currentSrc,
4536
+ type: "img"
4537
+ };
4538
+ }
4539
+ return { isImage: false };
4540
+ }
4541
+ function createUploadThingMenuItem(options = {}) {
4542
+ const {
4543
+ label = "Upload to UploadThing",
4544
+ endpoint = "/api/uploadthing",
4545
+ onUploadComplete,
4546
+ onUploadError
4547
+ } = options;
4548
+ return {
4549
+ label,
4550
+ onClick: async ({ closeMenu, targetElement }) => {
4551
+ if (!targetElement) {
4552
+ onUploadError?.(new Error("No target element"));
4553
+ return false;
4554
+ }
4555
+ try {
4556
+ const imageInfo = detectImageElement(targetElement);
4557
+ if (imageInfo.isImage && imageInfo.src) {
4558
+ const response = await fetch(imageInfo.src);
4559
+ const blob = await response.blob();
4560
+ const formData = new FormData();
4561
+ const filename = `image-${Date.now()}.${blob.type.split("/")[1] || "png"}`;
4562
+ formData.append("file", blob, filename);
4563
+ const uploadResponse = await fetch(endpoint, {
4564
+ method: "POST",
4565
+ body: formData
4566
+ });
4567
+ if (!uploadResponse.ok) {
4568
+ throw new Error(`Upload failed: ${uploadResponse.status}`);
4569
+ }
4570
+ const result = await uploadResponse.json();
4571
+ onUploadComplete?.(result);
4572
+ } else if (imageInfo.isImage && imageInfo.type === "canvas") {
4573
+ const canvas = targetElement;
4574
+ const dataUrl = canvas.toDataURL("image/png");
4575
+ const response = await fetch(dataUrl);
4576
+ const blob = await response.blob();
4577
+ const formData = new FormData();
4578
+ formData.append("file", blob, `canvas-${Date.now()}.png`);
4579
+ const uploadResponse = await fetch(endpoint, {
4580
+ method: "POST",
4581
+ body: formData
4582
+ });
4583
+ if (!uploadResponse.ok) {
4584
+ throw new Error(`Upload failed: ${uploadResponse.status}`);
4585
+ }
4586
+ const result = await uploadResponse.json();
4587
+ onUploadComplete?.(result);
4588
+ } else {
4589
+ onUploadError?.(
4590
+ new Error(
4591
+ "Element is not an image. Screenshot upload requires anyclick-core."
4592
+ )
4593
+ );
4594
+ }
4595
+ } catch (error) {
4596
+ onUploadError?.(
4597
+ error instanceof Error ? error : new Error(String(error))
4598
+ );
4599
+ }
4600
+ closeMenu();
4601
+ return false;
4602
+ },
4603
+ showComment: false,
4604
+ type: "upload_image"
4605
+ };
4606
+ }
4607
+ function createUploadScreenshotMenuItem(options = {}) {
4608
+ const {
4609
+ label = "Upload Screenshot",
4610
+ endpoint = "/api/uploadthing",
4611
+ onUploadComplete,
4612
+ onUploadError
4613
+ } = options;
4614
+ return {
4615
+ label,
4616
+ badge: { label: "Coming soon", tone: "info" },
4617
+ status: "comingSoon",
4618
+ onClick: async ({ closeMenu }) => {
4619
+ onUploadError?.(
4620
+ new Error("Screenshot upload will be available in a future release")
4621
+ );
4622
+ closeMenu();
4623
+ return false;
4624
+ },
4625
+ showComment: false,
4626
+ type: "upload_screenshot"
4627
+ };
4628
+ }
2995
4629
 
2996
4630
  // src/types.ts
2997
4631
  function filterMenuItemsByRole(items, userContext) {
@@ -3021,7 +4655,7 @@ import {
3021
4655
  } from "@ewjdev/anyclick-core";
3022
4656
 
3023
4657
  // src/AnyclickLogo.tsx
3024
- import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
4658
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
3025
4659
  function AnyclickLogo({
3026
4660
  size = 64,
3027
4661
  borderWidth = 2,
@@ -3033,7 +4667,7 @@ function AnyclickLogo({
3033
4667
  }) {
3034
4668
  const cursorSize = size * 0.45;
3035
4669
  const cursorStroke = borderWidth;
3036
- return /* @__PURE__ */ jsxs5(
4670
+ return /* @__PURE__ */ jsxs6(
3037
4671
  "svg",
3038
4672
  {
3039
4673
  width: size,
@@ -3047,7 +4681,7 @@ function AnyclickLogo({
3047
4681
  role: onClick ? "button" : "img",
3048
4682
  "aria-label": "Anyclick Logo",
3049
4683
  children: [
3050
- /* @__PURE__ */ jsx6(
4684
+ /* @__PURE__ */ jsx7(
3051
4685
  "circle",
3052
4686
  {
3053
4687
  cx: size / 2,
@@ -3058,11 +4692,11 @@ function AnyclickLogo({
3058
4692
  strokeWidth: borderWidth
3059
4693
  }
3060
4694
  ),
3061
- /* @__PURE__ */ jsx6(
4695
+ /* @__PURE__ */ jsx7(
3062
4696
  "g",
3063
4697
  {
3064
4698
  transform: `translate(${(size - cursorSize) / 2}, ${(size - cursorSize) / 2})`,
3065
- children: /* @__PURE__ */ jsx6(
4699
+ children: /* @__PURE__ */ jsx7(
3066
4700
  "path",
3067
4701
  {
3068
4702
  d: `
@@ -3095,7 +4729,7 @@ import {
3095
4729
  getAccessibilityInfo,
3096
4730
  getBoxModelInfo,
3097
4731
  getAttributes,
3098
- getElementInspectInfo as getElementInspectInfo2,
4732
+ getElementInspectInfo as getElementInspectInfo3,
3099
4733
  formatStylesAsCSS,
3100
4734
  formatBoxModel,
3101
4735
  CURATED_STYLE_PROPERTIES,
@@ -3109,6 +4743,7 @@ export {
3109
4743
  CURATED_STYLE_PROPERTIES,
3110
4744
  ContextMenu,
3111
4745
  DEFAULT_COMPACT_CONFIG,
4746
+ DEFAULT_QUICK_CHAT_CONFIG,
3112
4747
  DEFAULT_SCREENSHOT_CONFIG2 as DEFAULT_SCREENSHOT_CONFIG,
3113
4748
  DEFAULT_SENSITIVE_SELECTORS,
3114
4749
  FeedbackContext,
@@ -3117,6 +4752,7 @@ export {
3117
4752
  INSPECT_DIALOG_EVENT,
3118
4753
  InspectDialogManager,
3119
4754
  InspectSimple,
4755
+ QuickChat,
3120
4756
  ScreenshotPreview,
3121
4757
  applyHighlights,
3122
4758
  buildIDEUrl,
@@ -3125,9 +4761,13 @@ export {
3125
4761
  clearHighlights,
3126
4762
  createIDEOpener,
3127
4763
  createPresetMenu,
4764
+ createT3ChatMenuItem,
4765
+ createUploadScreenshotMenuItem,
4766
+ createUploadThingMenuItem,
3128
4767
  darkMenuStyles,
3129
4768
  defaultContainerSelectors,
3130
4769
  defaultHighlightColors,
4770
+ detectImageElement,
3131
4771
  detectPreferredIDE,
3132
4772
  dispatchContextMenuEvent,
3133
4773
  estimateTotalSize2 as estimateTotalSize,
@@ -3144,8 +4784,10 @@ export {
3144
4784
  getBadgeStyle,
3145
4785
  getBoxModelInfo,
3146
4786
  getComputedStyles,
3147
- getElementInspectInfo2 as getElementInspectInfo,
4787
+ getElementInspectInfo3 as getElementInspectInfo,
4788
+ getSelectedText,
3148
4789
  getSourceLocationFromElement,
4790
+ hasTextSelection,
3149
4791
  highlightContainer,
3150
4792
  highlightTarget,
3151
4793
  isIDEProtocolSupported,
@@ -3156,8 +4798,11 @@ export {
3156
4798
  openInIDE,
3157
4799
  openInspectDialog,
3158
4800
  presetDefaults,
4801
+ quickChatKeyframes,
4802
+ quickChatStyles,
3159
4803
  useAnyclick,
3160
4804
  useFeedback,
3161
- useProviderStore
4805
+ useProviderStore,
4806
+ useQuickChat
3162
4807
  };
3163
4808
  //# sourceMappingURL=index.mjs.map