@ewjdev/anyclick-react 2.0.0 → 4.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 useMemo4,
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,1884 @@ 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 as useMemo2,
38
+ useRef as useRef2,
39
+ useState as useState2
40
+ } from "react";
41
+ import { buildAnyclickPayload } from "@ewjdev/anyclick-core";
42
+ import {
43
+ AlertCircle,
44
+ ChevronDown,
45
+ ChevronLeft,
46
+ ChevronUp,
47
+ Copy,
48
+ ExternalLink,
49
+ Pin,
50
+ PinOff,
51
+ RefreshCw,
52
+ Send,
53
+ X
54
+ } from "lucide-react";
55
+
56
+ // src/QuickChat/styles.ts
57
+ var quickChatStyles = {
58
+ // Inline container - inside the context menu
59
+ container: {
60
+ display: "flex",
61
+ flexDirection: "column",
62
+ width: "100%",
63
+ maxHeight: "320px",
64
+ overflow: "hidden"
65
+ },
66
+ // Pinned drawer - anchored to right side of screen
67
+ pinnedContainer: {
68
+ position: "fixed",
69
+ top: 0,
70
+ right: 0,
71
+ bottom: 0,
72
+ width: "340px",
73
+ display: "flex",
74
+ flexDirection: "column",
75
+ backgroundColor: "var(--anyclick-menu-bg, #ffffff)",
76
+ borderLeft: "1px solid var(--anyclick-menu-border, #e5e5e5)",
77
+ boxShadow: "-4px 0 24px rgba(0, 0, 0, 0.15)",
78
+ fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
79
+ fontSize: "14px",
80
+ overflow: "hidden",
81
+ zIndex: 9998
82
+ },
83
+ // Pinned messages area (taller)
84
+ pinnedMessagesArea: {
85
+ flex: 1,
86
+ overflowY: "auto",
87
+ padding: "12px",
88
+ display: "flex",
89
+ flexDirection: "column",
90
+ gap: "8px",
91
+ minHeight: "200px"
92
+ },
93
+ // Header - minimal design
94
+ header: {
95
+ display: "flex",
96
+ alignItems: "center",
97
+ justifyContent: "space-between",
98
+ padding: "6px 8px",
99
+ gap: "8px"
100
+ },
101
+ headerTitle: {
102
+ fontSize: "11px",
103
+ fontWeight: 600,
104
+ textTransform: "uppercase",
105
+ letterSpacing: "0.5px",
106
+ color: "var(--anyclick-menu-text-muted, #666)"
107
+ },
108
+ headerActions: {
109
+ display: "flex",
110
+ alignItems: "center",
111
+ gap: "2px"
112
+ },
113
+ iconButton: {
114
+ display: "flex",
115
+ alignItems: "center",
116
+ justifyContent: "center",
117
+ width: "24px",
118
+ height: "24px",
119
+ border: "none",
120
+ borderRadius: "4px",
121
+ backgroundColor: "transparent",
122
+ color: "var(--anyclick-menu-text-muted, #666)",
123
+ cursor: "pointer",
124
+ transition: "all 0.15s ease"
125
+ },
126
+ iconButtonActive: {
127
+ backgroundColor: "var(--anyclick-menu-accent, #0066cc)",
128
+ color: "#fff"
129
+ },
130
+ // Context badge in header
131
+ contextBadge: {
132
+ display: "inline-flex",
133
+ alignItems: "center",
134
+ gap: "4px",
135
+ padding: "3px 8px",
136
+ fontSize: "10px",
137
+ fontWeight: 500,
138
+ border: "1px solid var(--anyclick-menu-border, #e5e5e5)",
139
+ borderRadius: "10px",
140
+ backgroundColor: "transparent",
141
+ color: "var(--anyclick-menu-text-muted, #666)",
142
+ cursor: "pointer",
143
+ transition: "all 0.15s ease"
144
+ },
145
+ contextBadgeActive: {
146
+ borderColor: "var(--anyclick-menu-accent, #0066cc)",
147
+ backgroundColor: "rgba(0, 102, 204, 0.08)"
148
+ },
149
+ // Context dropdown (in normal flow, not absolute)
150
+ contextDropdown: {
151
+ display: "flex",
152
+ flexDirection: "column",
153
+ gap: "2px",
154
+ padding: "6px 8px",
155
+ margin: "0 8px 6px 8px",
156
+ backgroundColor: "var(--anyclick-menu-hover, #f5f5f5)",
157
+ borderRadius: "6px",
158
+ maxHeight: "80px",
159
+ overflowY: "auto"
160
+ },
161
+ // Messages area
162
+ messagesArea: {
163
+ flex: 1,
164
+ overflowY: "auto",
165
+ padding: "8px 12px",
166
+ display: "flex",
167
+ flexDirection: "column",
168
+ gap: "8px",
169
+ minHeight: "40px",
170
+ maxHeight: "140px"
171
+ },
172
+ emptyState: {
173
+ display: "none"
174
+ // Hide empty state, placeholder in input is enough
175
+ },
176
+ // Message bubble
177
+ message: {
178
+ display: "flex",
179
+ flexDirection: "column",
180
+ gap: "4px",
181
+ maxWidth: "100%"
182
+ },
183
+ messageUser: {
184
+ alignSelf: "flex-end",
185
+ backgroundColor: "var(--anyclick-menu-accent, #0066cc)",
186
+ color: "#fff",
187
+ padding: "6px 10px",
188
+ borderRadius: "12px 12px 4px 12px",
189
+ fontSize: "13px",
190
+ maxWidth: "85%",
191
+ wordBreak: "break-word"
192
+ },
193
+ messageAssistant: {
194
+ alignSelf: "flex-start",
195
+ backgroundColor: "var(--anyclick-menu-hover, #f5f5f5)",
196
+ color: "var(--anyclick-menu-text, #333)",
197
+ padding: "8px 10px",
198
+ borderRadius: "12px 12px 12px 4px",
199
+ fontSize: "13px",
200
+ maxWidth: "100%",
201
+ wordBreak: "break-word",
202
+ lineHeight: 1.4
203
+ },
204
+ messageActions: {
205
+ display: "flex",
206
+ gap: "4px",
207
+ marginTop: "4px",
208
+ flexWrap: "wrap"
209
+ },
210
+ actionButton: {
211
+ display: "inline-flex",
212
+ alignItems: "center",
213
+ gap: "4px",
214
+ padding: "3px 8px",
215
+ fontSize: "11px",
216
+ fontWeight: 500,
217
+ border: "1px solid var(--anyclick-menu-border, #e5e5e5)",
218
+ borderRadius: "4px",
219
+ backgroundColor: "transparent",
220
+ color: "var(--anyclick-menu-text-muted, #666)",
221
+ cursor: "pointer",
222
+ transition: "all 0.15s ease"
223
+ },
224
+ streamingIndicator: {
225
+ display: "inline-block",
226
+ width: "4px",
227
+ height: "14px",
228
+ backgroundColor: "var(--anyclick-menu-accent, #0066cc)",
229
+ borderRadius: "1px",
230
+ animation: "blink 1s infinite"
231
+ },
232
+ // Suggestions - compact horizontal scroll
233
+ suggestionsContainer: {
234
+ display: "flex",
235
+ flexDirection: "column",
236
+ gap: "6px",
237
+ padding: "6px 8px",
238
+ overflowX: "auto",
239
+ overflowY: "hidden",
240
+ scrollbarWidth: "none",
241
+ msOverflowStyle: "none"
242
+ },
243
+ suggestionChip: {
244
+ display: "inline-flex",
245
+ alignItems: "center",
246
+ padding: "4px 10px",
247
+ fontSize: "11px",
248
+ border: "1px solid var(--anyclick-menu-border, #e5e5e5)",
249
+ borderRadius: "12px",
250
+ backgroundColor: "var(--anyclick-menu-hover, #f5f5f5)",
251
+ color: "var(--anyclick-menu-text, #333)",
252
+ cursor: "pointer",
253
+ transition: "all 0.15s ease",
254
+ whiteSpace: "nowrap",
255
+ flexShrink: 0
256
+ },
257
+ suggestionChipHover: {
258
+ backgroundColor: "var(--anyclick-menu-bg, #fff)",
259
+ borderColor: "var(--anyclick-menu-accent, #0066cc)"
260
+ },
261
+ // Context redaction - now in dropdown
262
+ contextContainer: {
263
+ display: "flex",
264
+ flexDirection: "column",
265
+ gap: "4px"
266
+ },
267
+ contextHeader: {
268
+ display: "flex",
269
+ alignItems: "center",
270
+ justifyContent: "space-between",
271
+ marginBottom: "4px"
272
+ },
273
+ contextTitle: {
274
+ fontSize: "10px",
275
+ fontWeight: 600,
276
+ textTransform: "uppercase",
277
+ letterSpacing: "0.5px",
278
+ color: "var(--anyclick-menu-text-muted, #666)"
279
+ },
280
+ contextToggle: {
281
+ fontSize: "10px",
282
+ color: "var(--anyclick-menu-accent, #0066cc)",
283
+ background: "none",
284
+ border: "none",
285
+ cursor: "pointer",
286
+ padding: 0
287
+ },
288
+ contextToggleSmall: {
289
+ fontSize: "9px",
290
+ fontWeight: 500,
291
+ color: "var(--anyclick-menu-text-muted, #666)",
292
+ background: "none",
293
+ border: "none",
294
+ cursor: "pointer",
295
+ padding: "2px 4px",
296
+ borderRadius: "3px",
297
+ transition: "all 0.15s ease"
298
+ },
299
+ contextChunks: {
300
+ display: "flex",
301
+ flexDirection: "column",
302
+ gap: "4px"
303
+ },
304
+ contextChunk: {
305
+ display: "flex",
306
+ alignItems: "center",
307
+ gap: "6px",
308
+ padding: "4px 6px",
309
+ borderRadius: "4px",
310
+ fontSize: "11px",
311
+ backgroundColor: "var(--anyclick-menu-hover, #f5f5f5)",
312
+ cursor: "pointer",
313
+ transition: "opacity 0.15s ease"
314
+ },
315
+ contextChunkCompact: {
316
+ display: "flex",
317
+ alignItems: "center",
318
+ gap: "6px",
319
+ padding: "2px 4px",
320
+ borderRadius: "3px",
321
+ fontSize: "10px",
322
+ cursor: "pointer",
323
+ transition: "opacity 0.15s ease"
324
+ },
325
+ contextChunkExcluded: {
326
+ opacity: 0.5,
327
+ textDecoration: "line-through"
328
+ },
329
+ contextCheckbox: {
330
+ width: "12px",
331
+ height: "12px",
332
+ accentColor: "var(--anyclick-menu-accent, #0066cc)"
333
+ },
334
+ contextLabel: {
335
+ flex: 1,
336
+ overflow: "hidden",
337
+ textOverflow: "ellipsis",
338
+ whiteSpace: "nowrap",
339
+ color: "var(--anyclick-menu-text, #333)"
340
+ },
341
+ contextSize: {
342
+ fontSize: "10px",
343
+ color: "var(--anyclick-menu-text-muted, #666)"
344
+ },
345
+ // Input area
346
+ inputContainer: {
347
+ display: "flex",
348
+ alignItems: "flex-end",
349
+ gap: "8px",
350
+ padding: "8px"
351
+ },
352
+ input: {
353
+ flex: 1,
354
+ padding: "8px 12px",
355
+ fontSize: "13px",
356
+ border: "1px solid var(--anyclick-menu-input-border, #ddd)",
357
+ borderRadius: "18px",
358
+ backgroundColor: "var(--anyclick-menu-input-bg, #fff)",
359
+ color: "var(--anyclick-menu-text, #333)",
360
+ outline: "none",
361
+ resize: "none",
362
+ fontFamily: "inherit",
363
+ lineHeight: 1.4,
364
+ maxHeight: "80px",
365
+ minHeight: "36px"
366
+ },
367
+ inputFocused: {
368
+ borderColor: "var(--anyclick-menu-accent, #0066cc)",
369
+ boxShadow: "0 0 0 2px rgba(0, 102, 204, 0.1)"
370
+ },
371
+ sendButton: {
372
+ display: "flex",
373
+ alignItems: "center",
374
+ justifyContent: "center",
375
+ width: "32px",
376
+ height: "32px",
377
+ border: "none",
378
+ borderRadius: "50%",
379
+ backgroundColor: "var(--anyclick-menu-accent, #0066cc)",
380
+ color: "#fff",
381
+ cursor: "pointer",
382
+ transition: "all 0.15s ease",
383
+ flexShrink: 0
384
+ },
385
+ sendButtonDisabled: {
386
+ backgroundColor: "var(--anyclick-menu-border, #e5e5e5)",
387
+ cursor: "not-allowed"
388
+ },
389
+ // Loading states
390
+ loadingDots: {
391
+ display: "flex",
392
+ gap: "4px",
393
+ padding: "8px"
394
+ },
395
+ loadingDot: {
396
+ width: "6px",
397
+ height: "6px",
398
+ borderRadius: "50%",
399
+ backgroundColor: "var(--anyclick-menu-text-muted, #666)",
400
+ animation: "bounce 1.4s infinite ease-in-out both"
401
+ },
402
+ // Error states
403
+ errorContainer: {
404
+ display: "flex",
405
+ flexDirection: "column",
406
+ alignItems: "center",
407
+ gap: "8px",
408
+ padding: "16px",
409
+ textAlign: "center"
410
+ },
411
+ errorIcon: {
412
+ color: "#ef4444"
413
+ },
414
+ errorText: {
415
+ fontSize: "12px",
416
+ color: "#ef4444",
417
+ maxWidth: "200px"
418
+ },
419
+ errorRetry: {
420
+ display: "inline-flex",
421
+ alignItems: "center",
422
+ gap: "4px",
423
+ padding: "4px 10px",
424
+ fontSize: "11px",
425
+ fontWeight: 500,
426
+ border: "1px solid #ef4444",
427
+ borderRadius: "4px",
428
+ backgroundColor: "transparent",
429
+ color: "#ef4444",
430
+ cursor: "pointer",
431
+ transition: "all 0.15s ease"
432
+ },
433
+ // Truncation indicator
434
+ truncated: {
435
+ fontSize: "10px",
436
+ color: "var(--anyclick-menu-text-muted, #666)",
437
+ fontStyle: "italic",
438
+ marginTop: "4px"
439
+ }
440
+ };
441
+ var quickChatKeyframes = `
442
+ @keyframes blink {
443
+ 0%, 50% { opacity: 1; }
444
+ 51%, 100% { opacity: 0; }
445
+ }
446
+
447
+ @keyframes bounce {
448
+ 0%, 80%, 100% { transform: scale(0); }
449
+ 40% { transform: scale(1); }
450
+ }
451
+
452
+ @keyframes slideIn {
453
+ from {
454
+ opacity: 0;
455
+ transform: translateY(-8px);
456
+ }
457
+ to {
458
+ opacity: 1;
459
+ transform: translateY(0);
460
+ }
461
+ }
462
+
463
+ @keyframes slideInFromRight {
464
+ from {
465
+ transform: translateX(100%);
466
+ }
467
+ to {
468
+ transform: translateX(0);
469
+ }
470
+ }
471
+
472
+ @keyframes slideOutToRight {
473
+ from {
474
+ transform: translateX(0);
475
+ }
476
+ to {
477
+ transform: translateX(100%);
478
+ }
479
+ }
480
+
481
+ @keyframes fadeIn {
482
+ from { opacity: 0; }
483
+ to { opacity: 1; }
484
+ }
485
+
486
+ @keyframes spin {
487
+ from { transform: rotate(0deg); }
488
+ to { transform: rotate(360deg); }
489
+ }
490
+
491
+ @keyframes pulse {
492
+ 0%, 100% { opacity: 1; }
493
+ 50% { opacity: 0.5; }
494
+ }
495
+ `;
496
+
497
+ // src/QuickChat/useQuickChat.ts
498
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
499
+ import { useChat } from "@ai-sdk/react";
500
+ import { DefaultChatTransport } from "ai";
501
+
502
+ // src/QuickChat/store.ts
503
+ import { create } from "zustand";
504
+ import { createJSONStorage, persist } from "zustand/middleware";
505
+ var STORE_NAME = "anyclick-quick-chat-store";
506
+ var TWENTY_FOUR_HOURS_MS = 24 * 60 * 60 * 1e3;
507
+ function generateMessageId() {
508
+ return `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
509
+ }
510
+ var storage = createJSONStorage(() => localStorage);
511
+ function filterExpiredMessages(messages) {
512
+ const now = Date.now();
513
+ return messages.filter((msg) => msg.expiresAt > now);
514
+ }
515
+ var useQuickChatStore = create()(
516
+ persist(
517
+ (set, get) => ({
518
+ messages: [],
519
+ isPinned: false,
520
+ input: "",
521
+ contextChunks: [],
522
+ suggestedPrompts: [],
523
+ isLoadingSuggestions: false,
524
+ isSending: false,
525
+ isStreaming: false,
526
+ error: null,
527
+ lastSyncedAt: null,
528
+ setInput: (input) => set({ input }),
529
+ addMessage: (message) => {
530
+ const id = generateMessageId();
531
+ const now = Date.now();
532
+ const storedMessage = {
533
+ ...message,
534
+ id,
535
+ timestamp: now,
536
+ expiresAt: now + TWENTY_FOUR_HOURS_MS
537
+ };
538
+ set((state) => {
539
+ const newMessages = [...state.messages, storedMessage];
540
+ return {
541
+ messages: newMessages
542
+ };
543
+ });
544
+ return id;
545
+ },
546
+ updateMessage: (id, updates) => {
547
+ set((state) => ({
548
+ messages: state.messages.map(
549
+ (msg) => msg.id === id ? { ...msg, ...updates } : msg
550
+ )
551
+ }));
552
+ },
553
+ clearMessages: () => set({ messages: [], error: null }),
554
+ setIsPinned: (pinned) => set({ isPinned: pinned }),
555
+ setContextChunks: (chunks) => set({ contextChunks: chunks }),
556
+ toggleChunk: (chunkId) => {
557
+ set((state) => ({
558
+ contextChunks: state.contextChunks.map(
559
+ (chunk) => chunk.id === chunkId ? { ...chunk, included: !chunk.included } : chunk
560
+ )
561
+ }));
562
+ },
563
+ toggleAllChunks: (included) => {
564
+ set((state) => ({
565
+ contextChunks: state.contextChunks.map((chunk) => ({
566
+ ...chunk,
567
+ included
568
+ }))
569
+ }));
570
+ },
571
+ setSuggestedPrompts: (prompts) => set({ suggestedPrompts: prompts }),
572
+ setIsLoadingSuggestions: (loading) => set({ isLoadingSuggestions: loading }),
573
+ setIsSending: (sending) => set({ isSending: sending }),
574
+ setIsStreaming: (streaming) => set({ isStreaming: streaming }),
575
+ setError: (error) => set({ error }),
576
+ setLastSyncedAt: (timestamp) => set({ lastSyncedAt: timestamp }),
577
+ pruneExpiredMessages: () => {
578
+ set((state) => ({
579
+ messages: filterExpiredMessages(state.messages)
580
+ }));
581
+ },
582
+ getActiveMessages: () => {
583
+ const state = get();
584
+ const now = Date.now();
585
+ const active = state.messages.filter((msg) => msg.expiresAt > now).map(({ expiresAt, ...msg }) => msg);
586
+ console.log("[store] getActiveMessages", {
587
+ totalMessages: state.messages.length,
588
+ activeMessages: active.length,
589
+ activeIds: active.map((m) => m.id)
590
+ });
591
+ return active;
592
+ },
593
+ hydrate: (messages) => {
594
+ const now = Date.now();
595
+ const storedMessages = messages.map((msg) => ({
596
+ ...msg,
597
+ expiresAt: now + TWENTY_FOUR_HOURS_MS
598
+ }));
599
+ set({ messages: storedMessages, lastSyncedAt: now });
600
+ }
601
+ }),
602
+ {
603
+ name: STORE_NAME,
604
+ storage,
605
+ partialize: (state) => ({
606
+ messages: state.messages,
607
+ isPinned: state.isPinned,
608
+ lastSyncedAt: state.lastSyncedAt
609
+ }),
610
+ onRehydrateStorage: () => (state) => {
611
+ if (state) {
612
+ state.pruneExpiredMessages();
613
+ }
614
+ }
615
+ }
616
+ )
617
+ );
618
+ function useActiveMessages() {
619
+ const messages = useQuickChatStore((state) => state.messages);
620
+ const now = Date.now();
621
+ return messages.filter((msg) => msg.expiresAt > now).map(({ expiresAt, ...msg }) => msg);
622
+ }
623
+
624
+ // src/QuickChat/types.ts
625
+ var DEFAULT_T3CHAT_CONFIG = {
626
+ enabled: true,
627
+ baseUrl: "https://t3.chat",
628
+ label: "Ask t3.chat"
629
+ };
630
+ var DEFAULT_QUICK_CHAT_CONFIG = {
631
+ endpoint: "/api/anyclick/chat",
632
+ model: "gpt-5-nano",
633
+ prePassModel: "gpt-5-nano",
634
+ maxResponseLength: 1e4,
635
+ showRedactionUI: true,
636
+ showSuggestions: true,
637
+ systemPrompt: "You are a helpful assistant that provides quick, concise answers about web elements and UI. Keep responses brief and actionable.",
638
+ placeholder: "Ask about this element...",
639
+ title: "Quick Ask",
640
+ t3chat: DEFAULT_T3CHAT_CONFIG
641
+ };
642
+
643
+ // src/QuickChat/useQuickChat.context.ts
644
+ import { getElementInspectInfo } from "@ewjdev/anyclick-core";
645
+ function extractContextChunks(targetElement, _containerElement) {
646
+ const chunks = [];
647
+ if (!targetElement) return chunks;
648
+ try {
649
+ const info = getElementInspectInfo(targetElement);
650
+ const tagContent = `<${info.tagName.toLowerCase()}${info.id ? ` id="${info.id}"` : ""}${info.classNames.length > 0 ? ` class="${info.classNames.join(" ")}"` : ""}>`;
651
+ chunks.push({
652
+ id: "element-tag",
653
+ label: "Element Tag",
654
+ content: tagContent,
655
+ type: "element",
656
+ included: true,
657
+ size: tagContent.length
658
+ });
659
+ if (info.innerText && info.innerText.length > 0) {
660
+ const textContent = info.innerText.slice(0, 200);
661
+ chunks.push({
662
+ id: "element-text",
663
+ label: "Text Content",
664
+ content: textContent,
665
+ type: "text",
666
+ included: true,
667
+ size: textContent.length
668
+ });
669
+ }
670
+ if (info.computedStyles) {
671
+ const styleEntries = [];
672
+ for (const [, styles] of Object.entries(info.computedStyles)) {
673
+ if (styles && typeof styles === "object") {
674
+ const entries = Object.entries(styles).slice(0, 2);
675
+ for (const [k, v] of entries) {
676
+ if (v) styleEntries.push(`${k}: ${v}`);
677
+ }
678
+ }
679
+ if (styleEntries.length >= 8) break;
680
+ }
681
+ const stylesContent = styleEntries.join("; ");
682
+ if (stylesContent) {
683
+ chunks.push({
684
+ id: "element-styles",
685
+ label: "Key Styles",
686
+ content: stylesContent,
687
+ type: "element",
688
+ included: true,
689
+ size: stylesContent.length
690
+ });
691
+ }
692
+ }
693
+ if (info.accessibility) {
694
+ const a11yContent = [
695
+ info.accessibility.role && `role="${info.accessibility.role}"`,
696
+ info.accessibility.accessibleName && `aria-label="${info.accessibility.accessibleName}"`
697
+ ].filter(Boolean).join(", ");
698
+ if (a11yContent) {
699
+ chunks.push({
700
+ id: "element-a11y",
701
+ label: "Accessibility",
702
+ content: a11yContent,
703
+ type: "element",
704
+ included: true,
705
+ size: a11yContent.length
706
+ });
707
+ }
708
+ }
709
+ if (info.boxModel) {
710
+ const boxContent = `${Math.round(info.boxModel.content.width)}x${Math.round(
711
+ info.boxModel.content.height
712
+ )}px`;
713
+ chunks.push({
714
+ id: "element-dimensions",
715
+ label: "Dimensions",
716
+ content: boxContent,
717
+ type: "element",
718
+ included: true,
719
+ size: boxContent.length
720
+ });
721
+ }
722
+ } catch (error) {
723
+ console.error("[useQuickChat] Failed to extract context:", error);
724
+ }
725
+ return chunks;
726
+ }
727
+ function buildContextString(chunks) {
728
+ const included = chunks.filter((c) => c.included);
729
+ if (included.length === 0) return "";
730
+ return included.map((c) => `[${c.label}]: ${c.content}`).join("\n");
731
+ }
732
+
733
+ // src/QuickChat/useQuickChat.debug.ts
734
+ function createDebugInfo(args) {
735
+ return {
736
+ status: args.status,
737
+ ok: args.ok,
738
+ contentType: args.contentType ?? null,
739
+ rawTextPreview: args.rawText ?? "",
740
+ timestamp: Date.now(),
741
+ error: args.error
742
+ };
743
+ }
744
+
745
+ // src/QuickChat/useQuickChat.messages.ts
746
+ function getUIMessageText(msg) {
747
+ const partsText = msg.parts?.map((p) => {
748
+ const part = p;
749
+ if (typeof part.text === "string") return part.text;
750
+ if (typeof part.content === "string") return part.content;
751
+ return "";
752
+ }).join("") ?? "";
753
+ if (partsText) return partsText;
754
+ const maybeContent = msg.content;
755
+ return typeof maybeContent === "string" ? maybeContent : "";
756
+ }
757
+ function chatMessagesToUiMessages(messages) {
758
+ return messages.map((msg) => ({
759
+ id: msg.id,
760
+ role: msg.role,
761
+ parts: [{ type: "text", text: msg.content }]
762
+ }));
763
+ }
764
+ function safeCopyToClipboard(text) {
765
+ try {
766
+ if (typeof navigator === "undefined") return;
767
+ void navigator.clipboard.writeText(text);
768
+ } catch {
769
+ }
770
+ }
771
+ function buildAssistantActions(messageText, setInput) {
772
+ return [
773
+ {
774
+ id: "copy",
775
+ label: "Copy",
776
+ onClick: () => safeCopyToClipboard(messageText)
777
+ },
778
+ {
779
+ id: "research",
780
+ label: "Research more",
781
+ onClick: () => setInput(`Tell me more about: ${messageText.slice(0, 50)}`)
782
+ }
783
+ ];
784
+ }
785
+ function uiMessagesToChatMessages(args) {
786
+ const { uiMessages, status, setInput } = args;
787
+ const last = uiMessages[uiMessages.length - 1];
788
+ return uiMessages.map((msg) => {
789
+ const text = getUIMessageText(msg);
790
+ const isStreaming = status === "streaming" && msg.role === "assistant" && msg === last;
791
+ const actions = msg.role === "assistant" && status === "ready" ? buildAssistantActions(text, setInput) : void 0;
792
+ return {
793
+ id: msg.id,
794
+ role: msg.role,
795
+ content: text,
796
+ timestamp: Date.now(),
797
+ isStreaming,
798
+ actions
799
+ };
800
+ });
801
+ }
802
+
803
+ // src/QuickChat/useQuickChat.rateLimit.ts
804
+ function safeJsonParse(text) {
805
+ try {
806
+ return JSON.parse(text);
807
+ } catch {
808
+ return null;
809
+ }
810
+ }
811
+ function formatRetryAt(retryAtMs) {
812
+ try {
813
+ const d = new Date(retryAtMs);
814
+ return new Intl.DateTimeFormat(void 0, {
815
+ hour: "numeric",
816
+ minute: "2-digit"
817
+ }).format(d);
818
+ } catch {
819
+ return new Date(retryAtMs).toLocaleTimeString();
820
+ }
821
+ }
822
+ function getRequestIdFromHeaders(res) {
823
+ return res?.headers?.get?.("X-Anyclick-Request-Id") ?? res?.headers?.get?.("x-anyclick-request-id") ?? void 0;
824
+ }
825
+ function parseRetryAfterSeconds(args) {
826
+ const { payload, res } = args;
827
+ if (typeof payload?.retryAfterSeconds === "number")
828
+ return payload.retryAfterSeconds;
829
+ const headerRetryAfter = res?.headers?.get?.("Retry-After");
830
+ if (!headerRetryAfter) return void 0;
831
+ const n = Number(headerRetryAfter);
832
+ return Number.isFinite(n) ? n : void 0;
833
+ }
834
+ function parseRetryAt(args) {
835
+ const { payload, retryAfterSeconds, nowMs } = args;
836
+ if (typeof payload?.retryAt === "number" && Number.isFinite(payload.retryAt)) {
837
+ return payload.retryAt;
838
+ }
839
+ if (typeof retryAfterSeconds === "number" && Number.isFinite(retryAfterSeconds)) {
840
+ return nowMs + Math.max(0, retryAfterSeconds) * 1e3;
841
+ }
842
+ return void 0;
843
+ }
844
+ function buildNotice(args) {
845
+ const { rawText, endpoint, res, nowMs } = args;
846
+ const parsed = safeJsonParse(rawText);
847
+ const payload = parsed && typeof parsed === "object" ? parsed : null;
848
+ const retryAfterSeconds = parseRetryAfterSeconds({ payload, res });
849
+ const retryAt = parseRetryAt({ payload, retryAfterSeconds, nowMs });
850
+ const timePart = retryAt ? `Try again at ${formatRetryAt(retryAt)}.` : "";
851
+ const message = timePart ? `Rate limited. ${timePart}` : "Rate limited.";
852
+ const requestId = payload?.requestId ?? getRequestIdFromHeaders(res);
853
+ return {
854
+ status: 429,
855
+ message,
856
+ retryAt,
857
+ retryAfterSeconds,
858
+ requestId,
859
+ endpoint,
860
+ raw: rawText
861
+ };
862
+ }
863
+ async function rateLimitNoticeFromResponse(res, endpoint) {
864
+ if (res.status !== 429) return null;
865
+ const raw = await res.text().catch(() => "");
866
+ return buildNotice({ rawText: raw, endpoint, res, nowMs: Date.now() });
867
+ }
868
+ function rateLimitNoticeFromError(args) {
869
+ const { statusCode, response, responseText, endpoint } = args;
870
+ if (statusCode !== 429) return null;
871
+ const raw = responseText ?? "";
872
+ return buildNotice({
873
+ rawText: raw,
874
+ endpoint,
875
+ res: response,
876
+ nowMs: Date.now()
877
+ });
878
+ }
879
+
880
+ // src/QuickChat/useQuickChat.ts
881
+ function useQuickChat(targetElement, containerElement, config = {}) {
882
+ console.count("useQuickChat");
883
+ const mergedConfig = { ...DEFAULT_QUICK_CHAT_CONFIG, ...config };
884
+ const initializedRef = useRef(false);
885
+ const syncTimeoutRef = useRef(null);
886
+ const [manualSending, setManualSending] = useState(false);
887
+ const [debugInfo, setDebugInfo] = useState(null);
888
+ const [rateLimitNotice, setRateLimitNotice] = useState(null);
889
+ const {
890
+ input,
891
+ setInput,
892
+ isPinned,
893
+ setIsPinned,
894
+ contextChunks,
895
+ setContextChunks,
896
+ toggleChunk,
897
+ toggleAllChunks,
898
+ suggestedPrompts,
899
+ setSuggestedPrompts,
900
+ isLoadingSuggestions,
901
+ setIsLoadingSuggestions,
902
+ error,
903
+ setError,
904
+ addMessage,
905
+ clearMessages: storeClearMessages,
906
+ setLastSyncedAt,
907
+ lastSyncedAt,
908
+ setIsSending: setStoreIsSending,
909
+ setIsStreaming: setStoreIsStreaming
910
+ } = useQuickChatStore();
911
+ const storedMessages = useActiveMessages();
912
+ const contextString = useMemo(
913
+ () => buildContextString(contextChunks),
914
+ [contextChunks]
915
+ );
916
+ const chatBody = useMemo(
917
+ () => ({
918
+ action: "chat",
919
+ context: contextString,
920
+ model: mergedConfig.model,
921
+ systemPrompt: mergedConfig.systemPrompt,
922
+ maxLength: mergedConfig.maxResponseLength
923
+ }),
924
+ [
925
+ contextString,
926
+ mergedConfig.model,
927
+ mergedConfig.systemPrompt,
928
+ mergedConfig.maxResponseLength
929
+ ]
930
+ );
931
+ const transport = useMemo(
932
+ () => new DefaultChatTransport({
933
+ api: mergedConfig.endpoint,
934
+ body: chatBody
935
+ }),
936
+ [mergedConfig.endpoint, chatBody]
937
+ );
938
+ const handleRateLimitResponse = useCallback(
939
+ async (res, endpoint) => {
940
+ const notice = await rateLimitNoticeFromResponse(res, endpoint);
941
+ if (!notice) return false;
942
+ setRateLimitNotice(notice);
943
+ setError(null);
944
+ return true;
945
+ },
946
+ [setError]
947
+ );
948
+ const scheduleBackendSync = useCallback(() => {
949
+ if (syncTimeoutRef.current) clearTimeout(syncTimeoutRef.current);
950
+ syncTimeoutRef.current = setTimeout(async () => {
951
+ try {
952
+ const currentMessages = useQuickChatStore.getState().getActiveMessages();
953
+ if (currentMessages.length === 0) return;
954
+ const endpoint = `${mergedConfig.endpoint}/history`;
955
+ const res = await fetch(endpoint, {
956
+ method: "POST",
957
+ headers: { "Content-Type": "application/json" },
958
+ body: JSON.stringify({
959
+ action: "save",
960
+ messages: currentMessages
961
+ })
962
+ });
963
+ if (await handleRateLimitResponse(res, endpoint)) return;
964
+ if (res.ok) setLastSyncedAt(Date.now());
965
+ } catch (err) {
966
+ console.error("[useQuickChat] Failed to sync chat history:", err);
967
+ }
968
+ }, 1e3);
969
+ }, [handleRateLimitResponse, mergedConfig.endpoint, setLastSyncedAt]);
970
+ const {
971
+ messages: aiMessages,
972
+ sendMessage: aiSendMessage,
973
+ status,
974
+ stop,
975
+ setMessages: setAiMessages
976
+ } = useChat({
977
+ transport,
978
+ onError: (err) => {
979
+ const response = err.response ?? void 0;
980
+ const statusCode = err.status ?? (response ? response.status : void 0) ?? -1;
981
+ const responseText = err.responseText ?? err.message ?? "";
982
+ setDebugInfo(
983
+ createDebugInfo({
984
+ status: statusCode,
985
+ ok: false,
986
+ contentType: response?.headers?.get?.("content-type") ?? null,
987
+ rawText: responseText,
988
+ error: err.message
989
+ })
990
+ );
991
+ setStoreIsStreaming(false);
992
+ setStoreIsSending(false);
993
+ const notice = rateLimitNoticeFromError({
994
+ statusCode,
995
+ response,
996
+ responseText,
997
+ endpoint: mergedConfig.endpoint
998
+ });
999
+ if (notice) {
1000
+ setRateLimitNotice(notice);
1001
+ setError(null);
1002
+ return;
1003
+ }
1004
+ setRateLimitNotice(null);
1005
+ setError(err.message);
1006
+ },
1007
+ onFinish: ({ message }) => {
1008
+ const messageText = getUIMessageText(message);
1009
+ const current = useQuickChatStore.getState().getActiveMessages();
1010
+ const last = current[current.length - 1];
1011
+ const alreadyHaveSameTail = last?.role === "assistant" && last.content === messageText;
1012
+ if (!alreadyHaveSameTail && messageText) {
1013
+ addMessage({
1014
+ role: message.role,
1015
+ content: messageText,
1016
+ isStreaming: false
1017
+ });
1018
+ }
1019
+ scheduleBackendSync();
1020
+ setStoreIsStreaming(false);
1021
+ setStoreIsSending(false);
1022
+ }
1023
+ });
1024
+ const loadFromBackend = useCallback(async () => {
1025
+ try {
1026
+ const endpoint = `${mergedConfig.endpoint}/history`;
1027
+ const response = await fetch(endpoint, {
1028
+ method: "POST",
1029
+ headers: { "Content-Type": "application/json" },
1030
+ body: JSON.stringify({ action: "load" })
1031
+ });
1032
+ if (await handleRateLimitResponse(response, endpoint)) return;
1033
+ if (!response.ok) return;
1034
+ const data = await response.json().catch(() => null);
1035
+ if (!data?.messages || !Array.isArray(data.messages)) return;
1036
+ if (data.messages.length === 0) return;
1037
+ useQuickChatStore.getState().hydrate(data.messages);
1038
+ setAiMessages(chatMessagesToUiMessages(data.messages));
1039
+ } catch (err) {
1040
+ console.error("[useQuickChat] Failed to load chat history:", err);
1041
+ }
1042
+ }, [handleRateLimitResponse, mergedConfig.endpoint, setAiMessages]);
1043
+ const messages = useMemo(
1044
+ () => uiMessagesToChatMessages({
1045
+ uiMessages: aiMessages,
1046
+ status,
1047
+ setInput
1048
+ }),
1049
+ [aiMessages, setInput, status]
1050
+ );
1051
+ const isStreaming = status === "streaming";
1052
+ const isSending = status === "submitted" || status === "streaming" || manualSending;
1053
+ useEffect(() => {
1054
+ if (initializedRef.current) return;
1055
+ initializedRef.current = true;
1056
+ if (storedMessages.length > 0) {
1057
+ setAiMessages(chatMessagesToUiMessages(storedMessages));
1058
+ } else {
1059
+ void loadFromBackend();
1060
+ }
1061
+ }, [loadFromBackend, setAiMessages, storedMessages]);
1062
+ useEffect(() => {
1063
+ if (!targetElement) return;
1064
+ const chunks = extractContextChunks(targetElement, containerElement);
1065
+ setContextChunks(chunks);
1066
+ }, [targetElement, containerElement, setContextChunks]);
1067
+ useEffect(() => {
1068
+ if (!mergedConfig.showSuggestions) return;
1069
+ if (!contextString) return;
1070
+ if (suggestedPrompts.length > 0) return;
1071
+ let cancelled = false;
1072
+ const fetchSuggestions = async () => {
1073
+ setIsLoadingSuggestions(true);
1074
+ try {
1075
+ const response = await fetch(mergedConfig.endpoint, {
1076
+ method: "POST",
1077
+ headers: { "Content-Type": "application/json" },
1078
+ body: JSON.stringify({
1079
+ action: "suggest",
1080
+ context: contextString,
1081
+ model: mergedConfig.prePassModel
1082
+ })
1083
+ });
1084
+ if (await handleRateLimitResponse(response, mergedConfig.endpoint)) {
1085
+ return;
1086
+ }
1087
+ if (response.ok) {
1088
+ const data = await response.json().catch(() => null);
1089
+ if (data?.suggestions && Array.isArray(data.suggestions)) {
1090
+ if (!cancelled) {
1091
+ setSuggestedPrompts(
1092
+ data.suggestions.map((text, i) => ({
1093
+ id: `suggestion-${i}`,
1094
+ text
1095
+ }))
1096
+ );
1097
+ setIsLoadingSuggestions(false);
1098
+ }
1099
+ return;
1100
+ }
1101
+ }
1102
+ } catch (err) {
1103
+ console.error("[useQuickChat] Failed to fetch suggestions:", err);
1104
+ }
1105
+ if (!cancelled) {
1106
+ setSuggestedPrompts([
1107
+ { id: "s1", text: "What is this element?" },
1108
+ { id: "s2", text: "How can I style this?" },
1109
+ { id: "s3", text: "Is this accessible?" }
1110
+ ]);
1111
+ setIsLoadingSuggestions(false);
1112
+ }
1113
+ };
1114
+ void fetchSuggestions();
1115
+ return () => {
1116
+ cancelled = true;
1117
+ };
1118
+ }, [
1119
+ contextString,
1120
+ handleRateLimitResponse,
1121
+ mergedConfig.endpoint,
1122
+ mergedConfig.prePassModel,
1123
+ mergedConfig.showSuggestions,
1124
+ setIsLoadingSuggestions,
1125
+ setSuggestedPrompts,
1126
+ suggestedPrompts.length
1127
+ ]);
1128
+ const selectSuggestion = useCallback(
1129
+ (prompt) => {
1130
+ setInput(prompt.text);
1131
+ },
1132
+ [setInput]
1133
+ );
1134
+ const sendMessage = useCallback(
1135
+ async (messageText) => {
1136
+ const text = (messageText || input).trim();
1137
+ if (!text) return;
1138
+ setInput("");
1139
+ setError(null);
1140
+ setManualSending(true);
1141
+ setStoreIsSending(true);
1142
+ setStoreIsStreaming(true);
1143
+ setDebugInfo(null);
1144
+ try {
1145
+ addMessage({
1146
+ role: "user",
1147
+ content: text
1148
+ });
1149
+ await aiSendMessage({ text });
1150
+ } catch (err) {
1151
+ const msg = err instanceof Error ? err.message : String(err);
1152
+ setError(msg);
1153
+ } finally {
1154
+ setManualSending(false);
1155
+ setStoreIsSending(false);
1156
+ setStoreIsStreaming(false);
1157
+ }
1158
+ },
1159
+ [
1160
+ addMessage,
1161
+ aiSendMessage,
1162
+ input,
1163
+ setError,
1164
+ setInput,
1165
+ setStoreIsSending,
1166
+ setStoreIsStreaming
1167
+ ]
1168
+ );
1169
+ const clearMessages = useCallback(() => {
1170
+ stop();
1171
+ storeClearMessages();
1172
+ setAiMessages([]);
1173
+ const endpoint = `${mergedConfig.endpoint}/history`;
1174
+ fetch(endpoint, {
1175
+ method: "POST",
1176
+ headers: { "Content-Type": "application/json" },
1177
+ body: JSON.stringify({ action: "clear" })
1178
+ }).then((res) => handleRateLimitResponse(res, endpoint)).catch(
1179
+ (err) => console.error("[useQuickChat] Failed to clear backend history:", err)
1180
+ );
1181
+ }, [
1182
+ handleRateLimitResponse,
1183
+ mergedConfig.endpoint,
1184
+ setAiMessages,
1185
+ stop,
1186
+ storeClearMessages
1187
+ ]);
1188
+ useEffect(() => {
1189
+ return () => {
1190
+ if (syncTimeoutRef.current) clearTimeout(syncTimeoutRef.current);
1191
+ };
1192
+ }, []);
1193
+ return {
1194
+ input,
1195
+ messages,
1196
+ isLoadingSuggestions,
1197
+ isSending,
1198
+ isStreaming,
1199
+ suggestedPrompts,
1200
+ contextChunks,
1201
+ error,
1202
+ debugInfo,
1203
+ rateLimitNotice,
1204
+ isPinned,
1205
+ lastSyncedAt,
1206
+ config: mergedConfig,
1207
+ setInput,
1208
+ toggleChunk,
1209
+ toggleAllChunks,
1210
+ selectSuggestion,
1211
+ sendMessage,
1212
+ clearMessages,
1213
+ setIsPinned,
1214
+ clearRateLimitNotice: () => setRateLimitNotice(null)
1215
+ };
1216
+ }
1217
+
1218
+ // src/QuickChat/QuickChat.tsx
1219
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
1220
+ var stylesInjected = false;
1221
+ function injectStyles() {
1222
+ if (stylesInjected || typeof document === "undefined") return;
1223
+ const style = document.createElement("style");
1224
+ style.textContent = quickChatKeyframes;
1225
+ document.head.appendChild(style);
1226
+ stylesInjected = true;
1227
+ }
1228
+ var LoadingDots = React.memo(function LoadingDots2() {
1229
+ return /* @__PURE__ */ jsx("div", { style: quickChatStyles.loadingDots, children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx(
1230
+ "div",
1231
+ {
1232
+ style: {
1233
+ ...quickChatStyles.loadingDot,
1234
+ animationDelay: `${i * 0.16}s`
1235
+ }
1236
+ },
1237
+ i
1238
+ )) });
1239
+ });
1240
+ function QuickChat({
1241
+ visible,
1242
+ targetElement,
1243
+ containerElement,
1244
+ onClose,
1245
+ onPin,
1246
+ isPinned: isPinnedProp = false,
1247
+ config,
1248
+ style,
1249
+ className,
1250
+ initialInput,
1251
+ onInitialInputConsumed
1252
+ }) {
1253
+ const inputRef = useRef2(null);
1254
+ const messagesEndRef = useRef2(null);
1255
+ const [inputFocused, setInputFocused] = useState2(false);
1256
+ const [showContext, setShowContext] = useState2(false);
1257
+ const [hoveredSuggestion, setHoveredSuggestion] = useState2(
1258
+ null
1259
+ );
1260
+ const {
1261
+ input,
1262
+ messages,
1263
+ isLoadingSuggestions,
1264
+ isSending,
1265
+ isStreaming,
1266
+ debugInfo,
1267
+ rateLimitNotice,
1268
+ suggestedPrompts,
1269
+ contextChunks,
1270
+ error,
1271
+ isPinned: storePinned,
1272
+ setInput,
1273
+ toggleChunk,
1274
+ toggleAllChunks,
1275
+ selectSuggestion,
1276
+ sendMessage,
1277
+ clearMessages,
1278
+ setIsPinned,
1279
+ clearRateLimitNotice,
1280
+ config: mergedConfig
1281
+ } = useQuickChat(targetElement, containerElement, config);
1282
+ const isPinned = isPinnedProp || storePinned;
1283
+ const handlePinToggle = useCallback2(() => {
1284
+ const newPinned = !isPinned;
1285
+ setIsPinned(newPinned);
1286
+ onPin?.(newPinned);
1287
+ }, [isPinned, setIsPinned, onPin]);
1288
+ const handleClose = useCallback2(() => {
1289
+ if (isPinned) {
1290
+ setIsPinned(false);
1291
+ onPin?.(false);
1292
+ }
1293
+ onClose();
1294
+ }, [isPinned, setIsPinned, onPin, onClose]);
1295
+ useEffect2(() => {
1296
+ injectStyles();
1297
+ }, []);
1298
+ useEffect2(() => {
1299
+ if (visible && inputRef.current) {
1300
+ const timer = setTimeout(() => {
1301
+ inputRef.current?.focus();
1302
+ }, 100);
1303
+ return () => clearTimeout(timer);
1304
+ }
1305
+ }, [visible]);
1306
+ useEffect2(() => {
1307
+ if (initialInput && visible) {
1308
+ setInput(initialInput);
1309
+ onInitialInputConsumed?.();
1310
+ if (inputRef.current) {
1311
+ inputRef.current.focus();
1312
+ inputRef.current.setSelectionRange(
1313
+ initialInput.length,
1314
+ initialInput.length
1315
+ );
1316
+ }
1317
+ }
1318
+ }, [initialInput, visible, setInput, onInitialInputConsumed]);
1319
+ useEffect2(() => {
1320
+ if (messagesEndRef.current) {
1321
+ messagesEndRef.current.scrollIntoView({ behavior: "smooth" });
1322
+ }
1323
+ }, [messages]);
1324
+ const handleInputChange = useCallback2(
1325
+ (e) => {
1326
+ setInput(e.target.value);
1327
+ const target = e.target;
1328
+ target.style.height = "auto";
1329
+ target.style.height = `${Math.min(target.scrollHeight, 80)}px`;
1330
+ },
1331
+ [setInput]
1332
+ );
1333
+ const handleKeyDown = useCallback2(
1334
+ (e) => {
1335
+ if (e.key === "Enter" && !e.shiftKey) {
1336
+ e.preventDefault();
1337
+ sendMessage();
1338
+ } else if (e.key === "Escape") {
1339
+ onClose();
1340
+ }
1341
+ },
1342
+ [sendMessage, onClose]
1343
+ );
1344
+ const handleSend = useCallback2(() => {
1345
+ sendMessage();
1346
+ }, [sendMessage]);
1347
+ const handleSendToT3Chat = useCallback2(() => {
1348
+ if (typeof window === "undefined") return;
1349
+ const query = input.trim();
1350
+ const baseUrl = mergedConfig.t3chat?.baseUrl ?? "https://t3.chat";
1351
+ const url = query ? `${baseUrl}/?q=${encodeURIComponent(query)}` : baseUrl;
1352
+ window.open(url, "_blank", "noopener,noreferrer");
1353
+ }, [input, mergedConfig.t3chat?.baseUrl]);
1354
+ const handleCopy = useCallback2((text) => {
1355
+ navigator.clipboard.writeText(text);
1356
+ }, []);
1357
+ const includedCount = useMemo2(
1358
+ () => contextChunks.filter((c) => c.included).length,
1359
+ [contextChunks]
1360
+ );
1361
+ const [rateLimitExpanded, setRateLimitExpanded] = useState2(false);
1362
+ const [reportStatus, setReportStatus] = useState2("idle");
1363
+ const [reportUrl, setReportUrl] = useState2(null);
1364
+ const [reportError, setReportError] = useState2(null);
1365
+ useEffect2(() => {
1366
+ if (rateLimitNotice) {
1367
+ setReportStatus("idle");
1368
+ setReportUrl(null);
1369
+ setReportError(null);
1370
+ setRateLimitExpanded(false);
1371
+ }
1372
+ }, [rateLimitNotice]);
1373
+ const handleReportIssue = useCallback2(async () => {
1374
+ if (!rateLimitNotice) return;
1375
+ if (!targetElement) {
1376
+ setReportStatus("error");
1377
+ setReportError("No target element available to report.");
1378
+ return;
1379
+ }
1380
+ setReportStatus("sending");
1381
+ setReportError(null);
1382
+ try {
1383
+ const payload = buildAnyclickPayload(targetElement, "issue", {
1384
+ comment: `QuickChat: ${rateLimitNotice.message}`,
1385
+ metadata: {
1386
+ source: "quickchat",
1387
+ kind: "rate_limit",
1388
+ endpoint: rateLimitNotice.endpoint ?? mergedConfig.endpoint,
1389
+ retryAt: rateLimitNotice.retryAt,
1390
+ retryAfterSeconds: rateLimitNotice.retryAfterSeconds,
1391
+ requestId: rateLimitNotice.requestId,
1392
+ debugInfo: debugInfo ?? void 0,
1393
+ raw: rateLimitNotice.raw ?? void 0
1394
+ }
1395
+ });
1396
+ const res = await fetch("/api/feedback", {
1397
+ method: "POST",
1398
+ headers: { "Content-Type": "application/json" },
1399
+ body: JSON.stringify(payload)
1400
+ });
1401
+ const json = await res.json().catch(() => null);
1402
+ if (!res.ok || !json?.success) {
1403
+ const msg = json?.error || (res.status ? `Failed to create issue (${res.status}).` : "Failed to create issue.");
1404
+ throw new Error(msg);
1405
+ }
1406
+ const firstUrl = json.results?.find(
1407
+ (r) => typeof r.url === "string"
1408
+ )?.url;
1409
+ setReportUrl(firstUrl ?? null);
1410
+ setReportStatus("sent");
1411
+ } catch (e) {
1412
+ setReportStatus("error");
1413
+ setReportError(e instanceof Error ? e.message : String(e));
1414
+ }
1415
+ }, [rateLimitNotice, targetElement, mergedConfig.endpoint, debugInfo]);
1416
+ if (!visible) return null;
1417
+ const containerStyles = isPinned ? {
1418
+ ...quickChatStyles.pinnedContainer,
1419
+ animation: "slideInFromRight 0.25s ease-out",
1420
+ ...style
1421
+ } : {
1422
+ ...quickChatStyles.container,
1423
+ animation: "fadeIn 0.15s ease-out",
1424
+ ...style
1425
+ };
1426
+ return /* @__PURE__ */ jsxs("div", { className, style: containerStyles, children: [
1427
+ /* @__PURE__ */ jsxs(
1428
+ "div",
1429
+ {
1430
+ style: {
1431
+ ...quickChatStyles.header,
1432
+ padding: isPinned ? "12px 12px 8px 12px" : "6px 8px"
1433
+ },
1434
+ children: [
1435
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "4px" }, children: [
1436
+ !isPinned && /* @__PURE__ */ jsx(
1437
+ "button",
1438
+ {
1439
+ type: "button",
1440
+ onClick: onClose,
1441
+ style: {
1442
+ ...quickChatStyles.iconButton,
1443
+ marginLeft: "-4px"
1444
+ },
1445
+ title: "Back to menu",
1446
+ children: /* @__PURE__ */ jsx(ChevronLeft, { size: 16 })
1447
+ }
1448
+ ),
1449
+ mergedConfig.showRedactionUI && contextChunks.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
1450
+ /* @__PURE__ */ jsxs(
1451
+ "button",
1452
+ {
1453
+ type: "button",
1454
+ onClick: () => setShowContext(!showContext),
1455
+ style: {
1456
+ ...quickChatStyles.contextBadge,
1457
+ ...showContext ? quickChatStyles.contextBadgeActive : {}
1458
+ },
1459
+ title: "Edit context",
1460
+ children: [
1461
+ /* @__PURE__ */ jsxs("span", { children: [
1462
+ includedCount,
1463
+ "/",
1464
+ contextChunks.length
1465
+ ] }),
1466
+ showContext ? /* @__PURE__ */ jsx(ChevronUp, { size: 10 }) : /* @__PURE__ */ jsx(ChevronDown, { size: 10 })
1467
+ ]
1468
+ }
1469
+ ),
1470
+ showContext && /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "2px" }, children: [
1471
+ /* @__PURE__ */ jsx(
1472
+ "button",
1473
+ {
1474
+ type: "button",
1475
+ onClick: () => toggleAllChunks(true),
1476
+ style: quickChatStyles.contextToggleSmall,
1477
+ title: "Include all",
1478
+ children: "All"
1479
+ }
1480
+ ),
1481
+ /* @__PURE__ */ jsx(
1482
+ "button",
1483
+ {
1484
+ type: "button",
1485
+ onClick: () => toggleAllChunks(false),
1486
+ style: quickChatStyles.contextToggleSmall,
1487
+ title: "Exclude all",
1488
+ children: "None"
1489
+ }
1490
+ )
1491
+ ] })
1492
+ ] })
1493
+ ] }),
1494
+ /* @__PURE__ */ jsxs("div", { style: quickChatStyles.headerActions, children: [
1495
+ messages.length > 0 && /* @__PURE__ */ jsx(
1496
+ "button",
1497
+ {
1498
+ type: "button",
1499
+ onClick: clearMessages,
1500
+ style: quickChatStyles.iconButton,
1501
+ title: "Clear chat",
1502
+ children: /* @__PURE__ */ jsx(RefreshCw, { size: 14 })
1503
+ }
1504
+ ),
1505
+ /* @__PURE__ */ jsx(
1506
+ "button",
1507
+ {
1508
+ type: "button",
1509
+ onClick: handlePinToggle,
1510
+ style: {
1511
+ ...quickChatStyles.iconButton,
1512
+ ...isPinned ? quickChatStyles.iconButtonActive : {}
1513
+ },
1514
+ title: isPinned ? "Unpin (closes with menu)" : "Pin (stays open)",
1515
+ children: isPinned ? /* @__PURE__ */ jsx(PinOff, { size: 14 }) : /* @__PURE__ */ jsx(Pin, { size: 14 })
1516
+ }
1517
+ ),
1518
+ /* @__PURE__ */ jsx(
1519
+ "button",
1520
+ {
1521
+ type: "button",
1522
+ onClick: handleClose,
1523
+ style: quickChatStyles.iconButton,
1524
+ title: "Close",
1525
+ children: /* @__PURE__ */ jsx(X, { size: 14 })
1526
+ }
1527
+ )
1528
+ ] })
1529
+ ]
1530
+ }
1531
+ ),
1532
+ showContext && contextChunks.length > 0 && /* @__PURE__ */ jsx("div", { style: quickChatStyles.contextDropdown, children: contextChunks.map((chunk) => /* @__PURE__ */ jsxs(
1533
+ "label",
1534
+ {
1535
+ style: {
1536
+ ...quickChatStyles.contextChunkCompact,
1537
+ ...chunk.included ? {} : quickChatStyles.contextChunkExcluded
1538
+ },
1539
+ children: [
1540
+ /* @__PURE__ */ jsx(
1541
+ "input",
1542
+ {
1543
+ type: "checkbox",
1544
+ checked: chunk.included,
1545
+ onChange: () => toggleChunk(chunk.id),
1546
+ style: quickChatStyles.contextCheckbox
1547
+ }
1548
+ ),
1549
+ /* @__PURE__ */ jsx("span", { style: quickChatStyles.contextLabel, children: chunk.label })
1550
+ ]
1551
+ },
1552
+ chunk.id
1553
+ )) }),
1554
+ mergedConfig.showSuggestions && messages.length === 0 && suggestedPrompts.length > 0 && /* @__PURE__ */ jsx("div", { style: quickChatStyles.suggestionsContainer, children: isLoadingSuggestions ? /* @__PURE__ */ jsx(LoadingDots, {}) : suggestedPrompts.map((prompt) => /* @__PURE__ */ jsx(
1555
+ "button",
1556
+ {
1557
+ type: "button",
1558
+ onClick: () => selectSuggestion(prompt),
1559
+ onMouseEnter: () => setHoveredSuggestion(prompt.id),
1560
+ onMouseLeave: () => setHoveredSuggestion(null),
1561
+ style: {
1562
+ ...quickChatStyles.suggestionChip,
1563
+ ...hoveredSuggestion === prompt.id ? quickChatStyles.suggestionChipHover : {}
1564
+ },
1565
+ children: prompt.text
1566
+ },
1567
+ prompt.id
1568
+ )) }),
1569
+ /* @__PURE__ */ jsxs(
1570
+ "div",
1571
+ {
1572
+ style: isPinned ? quickChatStyles.pinnedMessagesArea : quickChatStyles.messagesArea,
1573
+ children: [
1574
+ error && !rateLimitNotice && /* @__PURE__ */ jsxs("div", { style: quickChatStyles.errorContainer, children: [
1575
+ /* @__PURE__ */ jsx(AlertCircle, { size: 20, style: quickChatStyles.errorIcon }),
1576
+ /* @__PURE__ */ jsx("span", { style: quickChatStyles.errorText, children: error }),
1577
+ /* @__PURE__ */ jsxs(
1578
+ "button",
1579
+ {
1580
+ type: "button",
1581
+ onClick: () => sendMessage(),
1582
+ style: quickChatStyles.errorRetry,
1583
+ children: [
1584
+ /* @__PURE__ */ jsx(RefreshCw, { size: 10 }),
1585
+ "Retry"
1586
+ ]
1587
+ }
1588
+ )
1589
+ ] }),
1590
+ debugInfo && /* @__PURE__ */ jsxs(
1591
+ "div",
1592
+ {
1593
+ style: {
1594
+ backgroundColor: "#0f172a",
1595
+ color: "#e2e8f0",
1596
+ border: "1px solid #334155",
1597
+ borderRadius: "8px",
1598
+ padding: "8px",
1599
+ margin: "0 0 8px",
1600
+ fontSize: "12px",
1601
+ lineHeight: 1.4,
1602
+ wordBreak: "break-word"
1603
+ },
1604
+ children: [
1605
+ /* @__PURE__ */ jsxs(
1606
+ "div",
1607
+ {
1608
+ style: {
1609
+ display: "flex",
1610
+ justifyContent: "space-between",
1611
+ gap: "8px"
1612
+ },
1613
+ children: [
1614
+ /* @__PURE__ */ jsxs("span", { children: [
1615
+ "Last request: ",
1616
+ debugInfo.status,
1617
+ " ",
1618
+ debugInfo.ok ? "(ok)" : "(error)"
1619
+ ] }),
1620
+ /* @__PURE__ */ jsx("span", { style: { opacity: 0.7 }, children: new Date(debugInfo.timestamp).toLocaleTimeString() })
1621
+ ]
1622
+ }
1623
+ ),
1624
+ debugInfo.error && /* @__PURE__ */ jsxs("div", { style: { color: "#f87171", marginTop: "4px" }, children: [
1625
+ "Error: ",
1626
+ debugInfo.error
1627
+ ] }),
1628
+ debugInfo.contentPreview && /* @__PURE__ */ jsxs("div", { style: { marginTop: "4px" }, children: [
1629
+ "Content: ",
1630
+ debugInfo.contentPreview
1631
+ ] }),
1632
+ /* @__PURE__ */ jsxs("div", { style: { marginTop: "4px", opacity: 0.8 }, children: [
1633
+ "Raw: ",
1634
+ debugInfo.rawTextPreview || "(empty)"
1635
+ ] })
1636
+ ]
1637
+ }
1638
+ ),
1639
+ messages.length > 0 && messages.map((message) => /* @__PURE__ */ jsxs(
1640
+ "div",
1641
+ {
1642
+ style: {
1643
+ ...quickChatStyles.message,
1644
+ animation: "fadeIn 0.2s ease-out"
1645
+ },
1646
+ children: [
1647
+ /* @__PURE__ */ jsxs(
1648
+ "div",
1649
+ {
1650
+ style: message.role === "user" ? quickChatStyles.messageUser : quickChatStyles.messageAssistant,
1651
+ children: [
1652
+ message.content,
1653
+ message.isStreaming && /* @__PURE__ */ jsx("span", { style: quickChatStyles.streamingIndicator }),
1654
+ message.role === "assistant" && !message.isStreaming && message.content.endsWith("...") && /* @__PURE__ */ jsx("span", { style: quickChatStyles.truncated, children: "(truncated)" })
1655
+ ]
1656
+ }
1657
+ ),
1658
+ message.role === "assistant" && !message.isStreaming && message.content && /* @__PURE__ */ jsxs("div", { style: quickChatStyles.messageActions, children: [
1659
+ /* @__PURE__ */ jsxs(
1660
+ "button",
1661
+ {
1662
+ type: "button",
1663
+ onClick: () => handleCopy(message.content),
1664
+ style: quickChatStyles.actionButton,
1665
+ children: [
1666
+ /* @__PURE__ */ jsx(Copy, { size: 10 }),
1667
+ "Copy"
1668
+ ]
1669
+ }
1670
+ ),
1671
+ message.actions?.map((action) => /* @__PURE__ */ jsxs(
1672
+ "button",
1673
+ {
1674
+ type: "button",
1675
+ onClick: action.onClick,
1676
+ style: quickChatStyles.actionButton,
1677
+ children: [
1678
+ action.icon,
1679
+ action.label
1680
+ ]
1681
+ },
1682
+ action.id
1683
+ ))
1684
+ ] })
1685
+ ]
1686
+ },
1687
+ message.id
1688
+ )),
1689
+ /* @__PURE__ */ jsx("div", { ref: messagesEndRef })
1690
+ ]
1691
+ }
1692
+ ),
1693
+ rateLimitNotice && /* @__PURE__ */ jsxs(
1694
+ "div",
1695
+ {
1696
+ style: {
1697
+ borderTop: "1px solid rgba(148, 163, 184, 0.25)",
1698
+ background: "linear-gradient(180deg, rgba(15, 23, 42, 0.92), rgba(15, 23, 42, 0.96))",
1699
+ color: "#e2e8f0",
1700
+ padding: "8px 10px"
1701
+ },
1702
+ children: [
1703
+ /* @__PURE__ */ jsxs(
1704
+ "div",
1705
+ {
1706
+ style: {
1707
+ display: "flex",
1708
+ alignItems: "center",
1709
+ justifyContent: "space-between",
1710
+ gap: "8px"
1711
+ },
1712
+ children: [
1713
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
1714
+ /* @__PURE__ */ jsx(AlertCircle, { size: 16, style: { color: "#fbbf24" } }),
1715
+ /* @__PURE__ */ jsx("span", { style: { fontSize: "13px", lineHeight: 1.2 }, children: rateLimitNotice.message })
1716
+ ] }),
1717
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "6px" }, children: [
1718
+ /* @__PURE__ */ jsx(
1719
+ "button",
1720
+ {
1721
+ type: "button",
1722
+ onClick: () => setRateLimitExpanded((v) => !v),
1723
+ style: {
1724
+ border: "1px solid rgba(148, 163, 184, 0.25)",
1725
+ background: "rgba(30, 41, 59, 0.6)",
1726
+ color: "#e2e8f0",
1727
+ borderRadius: "6px",
1728
+ padding: "4px 8px",
1729
+ fontSize: "12px",
1730
+ cursor: "pointer"
1731
+ },
1732
+ children: rateLimitExpanded ? "Hide" : "Details"
1733
+ }
1734
+ ),
1735
+ /* @__PURE__ */ jsx(
1736
+ "button",
1737
+ {
1738
+ type: "button",
1739
+ onClick: handleReportIssue,
1740
+ disabled: reportStatus === "sending" || reportStatus === "sent",
1741
+ style: {
1742
+ border: "1px solid rgba(148, 163, 184, 0.25)",
1743
+ background: reportStatus === "sent" ? "rgba(34, 197, 94, 0.22)" : "rgba(30, 41, 59, 0.6)",
1744
+ color: "#e2e8f0",
1745
+ borderRadius: "6px",
1746
+ padding: "4px 8px",
1747
+ fontSize: "12px",
1748
+ cursor: reportStatus === "sending" || reportStatus === "sent" ? "not-allowed" : "pointer",
1749
+ opacity: reportStatus === "sending" ? 0.7 : 1
1750
+ },
1751
+ title: "Create a GitHub issue via /api/feedback",
1752
+ children: reportStatus === "sending" ? "Reporting..." : reportStatus === "sent" ? "Reported" : "Report"
1753
+ }
1754
+ ),
1755
+ /* @__PURE__ */ jsx(
1756
+ "button",
1757
+ {
1758
+ type: "button",
1759
+ onClick: () => {
1760
+ clearRateLimitNotice();
1761
+ setRateLimitExpanded(false);
1762
+ },
1763
+ style: {
1764
+ border: "none",
1765
+ background: "transparent",
1766
+ color: "rgba(226, 232, 240, 0.8)",
1767
+ padding: "4px",
1768
+ cursor: "pointer"
1769
+ },
1770
+ title: "Dismiss",
1771
+ children: /* @__PURE__ */ jsx(X, { size: 14 })
1772
+ }
1773
+ )
1774
+ ] })
1775
+ ]
1776
+ }
1777
+ ),
1778
+ reportUrl && /* @__PURE__ */ jsxs("div", { style: { marginTop: "6px", fontSize: "12px" }, children: [
1779
+ "Created:",
1780
+ " ",
1781
+ /* @__PURE__ */ jsx(
1782
+ "a",
1783
+ {
1784
+ href: reportUrl,
1785
+ target: "_blank",
1786
+ rel: "noopener noreferrer",
1787
+ style: { color: "#93c5fd" },
1788
+ children: "Open issue"
1789
+ }
1790
+ )
1791
+ ] }),
1792
+ reportError && /* @__PURE__ */ jsx(
1793
+ "div",
1794
+ {
1795
+ style: { marginTop: "6px", fontSize: "12px", color: "#fca5a5" },
1796
+ children: reportError
1797
+ }
1798
+ ),
1799
+ rateLimitExpanded && /* @__PURE__ */ jsxs(
1800
+ "div",
1801
+ {
1802
+ style: {
1803
+ marginTop: "8px",
1804
+ fontSize: "12px",
1805
+ lineHeight: 1.4,
1806
+ backgroundColor: "rgba(2, 6, 23, 0.55)",
1807
+ border: "1px solid rgba(148, 163, 184, 0.25)",
1808
+ borderRadius: "8px",
1809
+ padding: "8px",
1810
+ wordBreak: "break-word"
1811
+ },
1812
+ children: [
1813
+ /* @__PURE__ */ jsxs("div", { style: { opacity: 0.85 }, children: [
1814
+ "Status: ",
1815
+ rateLimitNotice.status,
1816
+ rateLimitNotice.requestId ? ` \u2022 Request: ${rateLimitNotice.requestId}` : ""
1817
+ ] }),
1818
+ rateLimitNotice.endpoint && /* @__PURE__ */ jsxs("div", { style: { opacity: 0.75, marginTop: "4px" }, children: [
1819
+ "Endpoint: ",
1820
+ rateLimitNotice.endpoint
1821
+ ] }),
1822
+ rateLimitNotice.retryAt && /* @__PURE__ */ jsxs("div", { style: { opacity: 0.75, marginTop: "4px" }, children: [
1823
+ "RetryAt: ",
1824
+ new Date(rateLimitNotice.retryAt).toLocaleString()
1825
+ ] }),
1826
+ rateLimitNotice.raw && /* @__PURE__ */ jsxs("div", { style: { marginTop: "6px", opacity: 0.85 }, children: [
1827
+ "Raw: ",
1828
+ rateLimitNotice.raw
1829
+ ] }),
1830
+ debugInfo && /* @__PURE__ */ jsxs("div", { style: { marginTop: "6px", opacity: 0.85 }, children: [
1831
+ "Debug: ",
1832
+ debugInfo.rawTextPreview || "(empty)"
1833
+ ] })
1834
+ ]
1835
+ }
1836
+ )
1837
+ ]
1838
+ }
1839
+ ),
1840
+ /* @__PURE__ */ jsxs("div", { style: quickChatStyles.inputContainer, children: [
1841
+ /* @__PURE__ */ jsx(
1842
+ "textarea",
1843
+ {
1844
+ ref: inputRef,
1845
+ value: input,
1846
+ onChange: handleInputChange,
1847
+ onKeyDown: handleKeyDown,
1848
+ onFocus: () => setInputFocused(true),
1849
+ onBlur: () => setInputFocused(false),
1850
+ placeholder: mergedConfig.placeholder,
1851
+ disabled: isSending,
1852
+ rows: 1,
1853
+ style: {
1854
+ ...quickChatStyles.input,
1855
+ ...inputFocused ? quickChatStyles.inputFocused : {}
1856
+ }
1857
+ }
1858
+ ),
1859
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "4px" }, children: [
1860
+ mergedConfig.t3chat?.enabled !== false && /* @__PURE__ */ jsx(
1861
+ "button",
1862
+ {
1863
+ type: "button",
1864
+ onClick: handleSendToT3Chat,
1865
+ disabled: !input.trim(),
1866
+ title: mergedConfig.t3chat?.label ?? "Ask t3.chat",
1867
+ style: {
1868
+ ...quickChatStyles.sendButton,
1869
+ backgroundColor: "#7c3aed",
1870
+ ...!input.trim() ? quickChatStyles.sendButtonDisabled : {}
1871
+ },
1872
+ children: /* @__PURE__ */ jsx(ExternalLink, { size: 14 })
1873
+ }
1874
+ ),
1875
+ /* @__PURE__ */ jsx(
1876
+ "button",
1877
+ {
1878
+ type: "button",
1879
+ onClick: handleSend,
1880
+ disabled: isSending || !input.trim(),
1881
+ style: {
1882
+ ...quickChatStyles.sendButton,
1883
+ ...isSending || !input.trim() ? quickChatStyles.sendButtonDisabled : {}
1884
+ },
1885
+ children: isSending ? /* @__PURE__ */ jsx(
1886
+ "div",
1887
+ {
1888
+ style: {
1889
+ width: "14px",
1890
+ height: "14px",
1891
+ border: "2px solid transparent",
1892
+ borderTopColor: "#fff",
1893
+ borderRadius: "50%",
1894
+ animation: "spin 0.8s linear infinite"
1895
+ }
1896
+ }
1897
+ ) : /* @__PURE__ */ jsx(Send, { size: 14 })
1898
+ }
1899
+ )
1900
+ ] })
1901
+ ] })
1902
+ ] });
1903
+ }
1904
+
32
1905
  // src/ScreenshotPreview.tsx
33
- import React, { useMemo, useState } from "react";
1906
+ import React2, { useMemo as useMemo3, useState as useState3 } from "react";
34
1907
  import { estimateTotalSize, formatBytes } from "@ewjdev/anyclick-core";
35
1908
  import {
36
1909
  AlertCircleIcon,
@@ -516,8 +2389,8 @@ var screenshotPreviewStyles = {
516
2389
  };
517
2390
 
518
2391
  // src/ScreenshotPreview.tsx
519
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
520
- var ScreenshotPreview = React.memo(function ScreenshotPreview2({
2392
+ import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
2393
+ var ScreenshotPreview = React2.memo(function ScreenshotPreview2({
521
2394
  isLoading,
522
2395
  isSubmitting,
523
2396
  onCancel,
@@ -525,12 +2398,12 @@ var ScreenshotPreview = React.memo(function ScreenshotPreview2({
525
2398
  onRetake,
526
2399
  screenshots
527
2400
  }) {
528
- const [activeTab, setActiveTab] = useState("element");
529
- const [isExpanded, setIsExpanded] = useState(false);
2401
+ const [activeTab, setActiveTab] = useState3("element");
2402
+ const [isExpanded, setIsExpanded] = useState3(false);
530
2403
  const getError = (key) => {
531
2404
  return screenshots?.errors?.[key];
532
2405
  };
533
- const tabs = useMemo(() => {
2406
+ const tabs = useMemo3(() => {
534
2407
  if (!screenshots) return [];
535
2408
  const allTabs = [
536
2409
  {
@@ -554,29 +2427,29 @@ var ScreenshotPreview = React.memo(function ScreenshotPreview2({
554
2427
  ];
555
2428
  return allTabs.filter((tab) => tab.data || tab.error);
556
2429
  }, [screenshots]);
557
- const totalSize = useMemo(
2430
+ const totalSize = useMemo3(
558
2431
  () => screenshots ? estimateTotalSize(screenshots) : 0,
559
2432
  [screenshots]
560
2433
  );
561
2434
  if (isLoading) {
562
- return /* @__PURE__ */ jsx("div", { style: screenshotPreviewStyles.container, children: /* @__PURE__ */ jsxs("div", { style: screenshotPreviewStyles.loadingContainer, children: [
563
- /* @__PURE__ */ jsx(
2435
+ return /* @__PURE__ */ jsx2("div", { style: screenshotPreviewStyles.container, children: /* @__PURE__ */ jsxs2("div", { style: screenshotPreviewStyles.loadingContainer, children: [
2436
+ /* @__PURE__ */ jsx2(
564
2437
  Loader2Icon,
565
2438
  {
566
2439
  className: "w-6 h-6 animate-spin",
567
2440
  style: { color: "#3b82f6" }
568
2441
  }
569
2442
  ),
570
- /* @__PURE__ */ jsx("span", { style: screenshotPreviewStyles.loadingText, children: "Capturing screenshots..." })
2443
+ /* @__PURE__ */ jsx2("span", { style: screenshotPreviewStyles.loadingText, children: "Capturing screenshots..." })
571
2444
  ] }) });
572
2445
  }
573
2446
  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(
2447
+ return /* @__PURE__ */ jsx2("div", { style: screenshotPreviewStyles.container, children: /* @__PURE__ */ jsxs2("div", { style: screenshotPreviewStyles.emptyContainer, children: [
2448
+ /* @__PURE__ */ jsx2(ImageIcon, { className: "w-8 h-8", style: { color: "#9ca3af" } }),
2449
+ /* @__PURE__ */ jsx2("span", { style: screenshotPreviewStyles.emptyText, children: "Screenshots unavailable" }),
2450
+ /* @__PURE__ */ jsx2("span", { style: screenshotPreviewStyles.emptySubtext, children: "Some elements can't be captured (e.g., gradient text)" }),
2451
+ /* @__PURE__ */ jsxs2("div", { style: screenshotPreviewStyles.emptyActions, children: [
2452
+ /* @__PURE__ */ jsxs2(
580
2453
  "button",
581
2454
  {
582
2455
  type: "button",
@@ -584,12 +2457,12 @@ var ScreenshotPreview = React.memo(function ScreenshotPreview2({
584
2457
  onClick: onRetake,
585
2458
  style: screenshotPreviewStyles.retakeButtonOutline,
586
2459
  children: [
587
- /* @__PURE__ */ jsx(RefreshCwIcon, { className: "w-4 h-4" }),
2460
+ /* @__PURE__ */ jsx2(RefreshCwIcon, { className: "w-4 h-4" }),
588
2461
  "Try Again"
589
2462
  ]
590
2463
  }
591
2464
  ),
592
- /* @__PURE__ */ jsxs(
2465
+ /* @__PURE__ */ jsxs2(
593
2466
  "button",
594
2467
  {
595
2468
  type: "button",
@@ -597,7 +2470,7 @@ var ScreenshotPreview = React.memo(function ScreenshotPreview2({
597
2470
  onClick: () => onConfirm({ capturedAt: (/* @__PURE__ */ new Date()).toISOString() }),
598
2471
  style: screenshotPreviewStyles.continueButton,
599
2472
  children: [
600
- /* @__PURE__ */ jsx(CheckIcon, { className: "w-4 h-4" }),
2473
+ /* @__PURE__ */ jsx2(CheckIcon, { className: "w-4 h-4" }),
601
2474
  "Continue Without"
602
2475
  ]
603
2476
  }
@@ -607,7 +2480,7 @@ var ScreenshotPreview = React.memo(function ScreenshotPreview2({
607
2480
  }
608
2481
  const activeScreenshot = activeTab === "element" ? screenshots.element : activeTab === "container" ? screenshots.container : screenshots.viewport;
609
2482
  const activeError = getError(activeTab);
610
- return /* @__PURE__ */ jsxs(
2483
+ return /* @__PURE__ */ jsxs2(
611
2484
  "div",
612
2485
  {
613
2486
  style: {
@@ -616,23 +2489,23 @@ var ScreenshotPreview = React.memo(function ScreenshotPreview2({
616
2489
  padding: "8px"
617
2490
  },
618
2491
  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(
2492
+ /* @__PURE__ */ jsxs2("div", { style: screenshotPreviewStyles.header, children: [
2493
+ /* @__PURE__ */ jsx2("span", { style: screenshotPreviewStyles.headerTitle, children: "Review Screenshots" }),
2494
+ /* @__PURE__ */ jsxs2("div", { style: screenshotPreviewStyles.headerActions, children: [
2495
+ /* @__PURE__ */ jsx2("span", { style: screenshotPreviewStyles.sizeLabel, children: formatBytes(totalSize) }),
2496
+ /* @__PURE__ */ jsx2(
624
2497
  "button",
625
2498
  {
626
2499
  type: "button",
627
2500
  onClick: () => setIsExpanded(!isExpanded),
628
2501
  style: screenshotPreviewStyles.iconButton,
629
2502
  title: isExpanded ? "Collapse" : "Expand",
630
- children: isExpanded ? /* @__PURE__ */ jsx(ShrinkIcon, { className: "w-4 h-4" }) : /* @__PURE__ */ jsx(ExpandIcon, { className: "w-4 h-4" })
2503
+ children: isExpanded ? /* @__PURE__ */ jsx2(ShrinkIcon, { className: "w-4 h-4" }) : /* @__PURE__ */ jsx2(ExpandIcon, { className: "w-4 h-4" })
631
2504
  }
632
2505
  )
633
2506
  ] })
634
2507
  ] }),
635
- /* @__PURE__ */ jsx("div", { style: screenshotPreviewStyles.tabContainer, children: tabs.map((tab) => /* @__PURE__ */ jsxs(
2508
+ /* @__PURE__ */ jsx2("div", { style: screenshotPreviewStyles.tabContainer, children: tabs.map((tab) => /* @__PURE__ */ jsxs2(
636
2509
  "button",
637
2510
  {
638
2511
  type: "button",
@@ -643,7 +2516,7 @@ var ScreenshotPreview = React.memo(function ScreenshotPreview2({
643
2516
  ...tab.error && !tab.data ? screenshotPreviewStyles.tabError : {}
644
2517
  },
645
2518
  children: [
646
- tab.error && !tab.data && /* @__PURE__ */ jsx(
2519
+ tab.error && !tab.data && /* @__PURE__ */ jsx2(
647
2520
  AlertCircleIcon,
648
2521
  {
649
2522
  className: "w-3 h-3",
@@ -651,32 +2524,32 @@ var ScreenshotPreview = React.memo(function ScreenshotPreview2({
651
2524
  }
652
2525
  ),
653
2526
  tab.label,
654
- tab.data && /* @__PURE__ */ jsx("span", { style: screenshotPreviewStyles.tabSize, children: formatBytes(tab.data.sizeBytes) })
2527
+ tab.data && /* @__PURE__ */ jsx2("span", { style: screenshotPreviewStyles.tabSize, children: formatBytes(tab.data.sizeBytes) })
655
2528
  ]
656
2529
  },
657
2530
  tab.key
658
2531
  )) }),
659
- /* @__PURE__ */ jsx(
2532
+ /* @__PURE__ */ jsx2(
660
2533
  "div",
661
2534
  {
662
2535
  style: {
663
2536
  ...screenshotPreviewStyles.previewContainer,
664
2537
  ...isExpanded ? screenshotPreviewStyles.previewContainerExpanded : {}
665
2538
  },
666
- children: activeScreenshot ? /* @__PURE__ */ jsx(
2539
+ children: activeScreenshot ? /* @__PURE__ */ jsx2(
667
2540
  "img",
668
2541
  {
669
2542
  alt: `${activeTab} screenshot`,
670
2543
  src: activeScreenshot.dataUrl,
671
2544
  style: screenshotPreviewStyles.previewImage
672
2545
  }
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: [
2546
+ ) : activeError ? /* @__PURE__ */ jsxs2("div", { style: screenshotPreviewStyles.errorPreview, children: [
2547
+ /* @__PURE__ */ jsx2(AlertCircleIcon, { className: "w-8 h-8", style: { color: "#ef4444" } }),
2548
+ /* @__PURE__ */ jsx2("span", { style: screenshotPreviewStyles.errorTitle, children: "Capture Failed" }),
2549
+ /* @__PURE__ */ jsx2("span", { style: screenshotPreviewStyles.errorMessage, children: activeError.message })
2550
+ ] }) : /* @__PURE__ */ jsxs2("div", { style: screenshotPreviewStyles.noPreview, children: [
2551
+ /* @__PURE__ */ jsx2(ImageIcon, { className: "w-6 h-6", style: { color: "#9ca3af" } }),
2552
+ /* @__PURE__ */ jsxs2("span", { children: [
680
2553
  "No ",
681
2554
  activeTab,
682
2555
  " screenshot"
@@ -684,14 +2557,14 @@ var ScreenshotPreview = React.memo(function ScreenshotPreview2({
684
2557
  ] })
685
2558
  }
686
2559
  ),
687
- activeScreenshot && /* @__PURE__ */ jsxs("div", { style: screenshotPreviewStyles.dimensionsInfo, children: [
2560
+ activeScreenshot && /* @__PURE__ */ jsxs2("div", { style: screenshotPreviewStyles.dimensionsInfo, children: [
688
2561
  activeScreenshot.width,
689
2562
  " \xD7 ",
690
2563
  activeScreenshot.height,
691
2564
  "px"
692
2565
  ] }),
693
- /* @__PURE__ */ jsxs("div", { style: screenshotPreviewStyles.actions, children: [
694
- /* @__PURE__ */ jsxs(
2566
+ /* @__PURE__ */ jsxs2("div", { style: screenshotPreviewStyles.actions, children: [
2567
+ /* @__PURE__ */ jsxs2(
695
2568
  "button",
696
2569
  {
697
2570
  type: "button",
@@ -699,13 +2572,13 @@ var ScreenshotPreview = React.memo(function ScreenshotPreview2({
699
2572
  onClick: onRetake,
700
2573
  style: screenshotPreviewStyles.retakeButtonSmall,
701
2574
  children: [
702
- /* @__PURE__ */ jsx(RefreshCwIcon, { className: "w-3 h-3" }),
2575
+ /* @__PURE__ */ jsx2(RefreshCwIcon, { className: "w-3 h-3" }),
703
2576
  "Retake"
704
2577
  ]
705
2578
  }
706
2579
  ),
707
- /* @__PURE__ */ jsxs("div", { style: screenshotPreviewStyles.actionsRight, children: [
708
- /* @__PURE__ */ jsxs(
2580
+ /* @__PURE__ */ jsxs2("div", { style: screenshotPreviewStyles.actionsRight, children: [
2581
+ /* @__PURE__ */ jsxs2(
709
2582
  "button",
710
2583
  {
711
2584
  type: "button",
@@ -714,12 +2587,12 @@ var ScreenshotPreview = React.memo(function ScreenshotPreview2({
714
2587
  onClick: onCancel,
715
2588
  style: { ...menuStyles.button, ...menuStyles.cancelButton },
716
2589
  children: [
717
- /* @__PURE__ */ jsx(XIcon, { className: "w-3 h-3" }),
2590
+ /* @__PURE__ */ jsx2(XIcon, { className: "w-3 h-3" }),
718
2591
  "Cancel"
719
2592
  ]
720
2593
  }
721
2594
  ),
722
- /* @__PURE__ */ jsx(
2595
+ /* @__PURE__ */ jsx2(
723
2596
  "button",
724
2597
  {
725
2598
  type: "button",
@@ -730,11 +2603,11 @@ var ScreenshotPreview = React.memo(function ScreenshotPreview2({
730
2603
  ...menuStyles.submitButton,
731
2604
  ...isSubmitting ? menuStyles.submitButtonDisabled : {}
732
2605
  },
733
- children: isSubmitting ? /* @__PURE__ */ jsxs(Fragment, { children: [
734
- /* @__PURE__ */ jsx(Loader2Icon, { className: "w-3 h-3 animate-spin" }),
2606
+ children: isSubmitting ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
2607
+ /* @__PURE__ */ jsx2(Loader2Icon, { className: "w-3 h-3 animate-spin" }),
735
2608
  "Sending..."
736
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
737
- /* @__PURE__ */ jsx(CheckIcon, { className: "w-3 h-3" }),
2609
+ ] }) : /* @__PURE__ */ jsxs2(Fragment2, { children: [
2610
+ /* @__PURE__ */ jsx2(CheckIcon, { className: "w-3 h-3" }),
738
2611
  "Send"
739
2612
  ] })
740
2613
  }
@@ -831,7 +2704,7 @@ svg.${HIGHLIGHT_CONTAINER_CLASS},
831
2704
  }
832
2705
  `;
833
2706
  }
834
- function injectStyles(colors) {
2707
+ function injectStyles2(colors) {
835
2708
  if (typeof document === "undefined") return;
836
2709
  const existingStyle = document.getElementById(STYLE_ID);
837
2710
  if (existingStyle) {
@@ -879,12 +2752,12 @@ function findContainerParent(element, config) {
879
2752
  }
880
2753
  function highlightTarget(element, colors) {
881
2754
  const mergedColors = { ...defaultHighlightColors, ...colors };
882
- injectStyles(mergedColors);
2755
+ injectStyles2(mergedColors);
883
2756
  element.classList.add(HIGHLIGHT_TARGET_CLASS);
884
2757
  }
885
2758
  function highlightContainer(element, colors) {
886
2759
  const mergedColors = { ...defaultHighlightColors, ...colors };
887
- injectStyles(mergedColors);
2760
+ injectStyles2(mergedColors);
888
2761
  element.classList.add(HIGHLIGHT_CONTAINER_CLASS);
889
2762
  }
890
2763
  function clearHighlights() {
@@ -911,12 +2784,12 @@ function applyHighlights(targetElement, config) {
911
2784
  }
912
2785
 
913
2786
  // src/ContextMenu.tsx
914
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
2787
+ import { Fragment as Fragment3, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
915
2788
  var VIEWPORT_PADDING = 10;
916
2789
  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" })
2790
+ feature: /* @__PURE__ */ jsx3(PlusIcon, { className: "w-4 h-4" }),
2791
+ issue: /* @__PURE__ */ jsx3(FlagIcon, { className: "w-4 h-4" }),
2792
+ like: /* @__PURE__ */ jsx3(ThumbsUpIcon, { className: "w-4 h-4" })
920
2793
  };
921
2794
  var OFFSCREEN_POSITION = { x: -9999, y: -9999 };
922
2795
  var DefaultHeader = ({
@@ -925,25 +2798,25 @@ var DefaultHeader = ({
925
2798
  styles,
926
2799
  title = "Send Feedback"
927
2800
  }) => {
928
- return /* @__PURE__ */ jsxs2("div", { style: styles, className, children: [
929
- /* @__PURE__ */ jsx2("span", { children: title }),
2801
+ return /* @__PURE__ */ jsxs3("div", { style: styles, className, children: [
2802
+ /* @__PURE__ */ jsx3("span", { children: title }),
930
2803
  children
931
2804
  ] });
932
2805
  };
933
- var MenuItem = React2.memo(function MenuItem2({
2806
+ var MenuItem = React3.memo(function MenuItem2({
934
2807
  disabled,
935
2808
  hasChildren,
936
2809
  item,
937
2810
  onClick
938
2811
  }) {
939
- const [isHovered, setIsHovered] = useState2(false);
940
- const [isPressed, setIsPressed] = useState2(false);
2812
+ const [isHovered, setIsHovered] = useState4(false);
2813
+ const [isPressed, setIsPressed] = useState4(false);
941
2814
  const isComingSoon = item.status === "comingSoon";
942
2815
  const badgeLabel = item.badge?.label ?? (isComingSoon ? "Coming soon" : null);
943
2816
  const badgeTone = item.badge?.tone ?? (isComingSoon ? "neutral" : "neutral");
944
2817
  const badgeStyle = badgeLabel ? getBadgeStyle(badgeTone) : void 0;
945
2818
  const iconNode = item.icon ?? defaultIcons[item.type];
946
- return /* @__PURE__ */ jsxs2(
2819
+ return /* @__PURE__ */ jsxs3(
947
2820
  "button",
948
2821
  {
949
2822
  type: "button",
@@ -964,22 +2837,22 @@ var MenuItem = React2.memo(function MenuItem2({
964
2837
  ...disabled ? menuStyles.itemDisabled : {}
965
2838
  },
966
2839
  children: [
967
- iconNode ? /* @__PURE__ */ jsx2("span", { style: menuStyles.itemIcon, children: iconNode }) : null,
968
- /* @__PURE__ */ jsxs2("span", { style: menuStyles.itemLabel, children: [
2840
+ iconNode ? /* @__PURE__ */ jsx3("span", { style: menuStyles.itemIcon, children: iconNode }) : null,
2841
+ /* @__PURE__ */ jsxs3("span", { style: menuStyles.itemLabel, children: [
969
2842
  item.label,
970
- badgeLabel && /* @__PURE__ */ jsx2("span", { style: badgeStyle, children: badgeLabel })
2843
+ badgeLabel && /* @__PURE__ */ jsx3("span", { style: badgeStyle, children: badgeLabel })
971
2844
  ] }),
972
- hasChildren && /* @__PURE__ */ jsx2(ChevronRightIcon, { className: "w-4 h-4", style: menuStyles.submenuIcon })
2845
+ hasChildren && /* @__PURE__ */ jsx3(ChevronRightIcon, { className: "w-4 h-4", style: menuStyles.submenuIcon })
973
2846
  ]
974
2847
  }
975
2848
  );
976
2849
  });
977
- var BackButton = React2.memo(function BackButton2({
2850
+ var BackButton = React3.memo(function BackButton2({
978
2851
  onClick
979
2852
  }) {
980
- const [isHovered, setIsHovered] = useState2(false);
981
- const [isPressed, setIsPressed] = useState2(false);
982
- return /* @__PURE__ */ jsxs2(
2853
+ const [isHovered, setIsHovered] = useState4(false);
2854
+ const [isPressed, setIsPressed] = useState4(false);
2855
+ return /* @__PURE__ */ jsxs3(
983
2856
  "button",
984
2857
  {
985
2858
  type: "button",
@@ -999,26 +2872,26 @@ var BackButton = React2.memo(function BackButton2({
999
2872
  ...isHovered || isPressed ? menuStyles.itemHover : {}
1000
2873
  },
1001
2874
  children: [
1002
- /* @__PURE__ */ jsx2(ChevronLeftIcon, { className: "w-4 h-4", style: { opacity: 0.5 } }),
1003
- /* @__PURE__ */ jsx2("span", { style: { opacity: 0.7 }, children: "Back" })
2875
+ /* @__PURE__ */ jsx3(ChevronLeftIcon, { className: "w-4 h-4", style: { opacity: 0.5 } }),
2876
+ /* @__PURE__ */ jsx3("span", { style: { opacity: 0.7 }, children: "Back" })
1004
2877
  ]
1005
2878
  }
1006
2879
  );
1007
2880
  });
1008
- var CommentForm = React2.memo(function CommentForm2({
2881
+ var CommentForm = React3.memo(function CommentForm2({
1009
2882
  isSubmitting,
1010
2883
  onCancel,
1011
2884
  onSubmit
1012
2885
  }) {
1013
- const [comment, setComment] = useState2("");
1014
- const inputRef = useRef(null);
1015
- useEffect(() => {
2886
+ const [comment, setComment] = useState4("");
2887
+ const inputRef = useRef3(null);
2888
+ useEffect3(() => {
1016
2889
  inputRef.current?.focus();
1017
2890
  }, []);
1018
- const handleSubmit = useCallback(() => {
2891
+ const handleSubmit = useCallback3(() => {
1019
2892
  onSubmit(comment);
1020
2893
  }, [comment, onSubmit]);
1021
- const handleKeyDown = useCallback(
2894
+ const handleKeyDown = useCallback3(
1022
2895
  (e) => {
1023
2896
  if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
1024
2897
  handleSubmit();
@@ -1028,8 +2901,8 @@ var CommentForm = React2.memo(function CommentForm2({
1028
2901
  },
1029
2902
  [handleSubmit, onCancel]
1030
2903
  );
1031
- return /* @__PURE__ */ jsxs2("div", { style: menuStyles.commentSection, children: [
1032
- /* @__PURE__ */ jsx2(
2904
+ return /* @__PURE__ */ jsxs3("div", { style: menuStyles.commentSection, children: [
2905
+ /* @__PURE__ */ jsx3(
1033
2906
  "textarea",
1034
2907
  {
1035
2908
  ref: inputRef,
@@ -1041,8 +2914,8 @@ var CommentForm = React2.memo(function CommentForm2({
1041
2914
  value: comment
1042
2915
  }
1043
2916
  ),
1044
- /* @__PURE__ */ jsxs2("div", { style: menuStyles.buttonRow, children: [
1045
- /* @__PURE__ */ jsx2(
2917
+ /* @__PURE__ */ jsxs3("div", { style: menuStyles.buttonRow, children: [
2918
+ /* @__PURE__ */ jsx3(
1046
2919
  "button",
1047
2920
  {
1048
2921
  type: "button",
@@ -1052,7 +2925,7 @@ var CommentForm = React2.memo(function CommentForm2({
1052
2925
  children: "Cancel"
1053
2926
  }
1054
2927
  ),
1055
- /* @__PURE__ */ jsx2(
2928
+ /* @__PURE__ */ jsx3(
1056
2929
  "button",
1057
2930
  {
1058
2931
  type: "button",
@@ -1100,26 +2973,35 @@ function ContextMenu({
1100
2973
  onSelect,
1101
2974
  position,
1102
2975
  positionMode = "inView",
2976
+ quickChatConfig,
1103
2977
  screenshotConfig,
1104
2978
  style,
1105
2979
  targetElement,
1106
2980
  visible
1107
2981
  }) {
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({
2982
+ const [selectedType, setSelectedType] = useState4(null);
2983
+ const [currentView, setCurrentView] = useState4("menu");
2984
+ const [pendingComment, setPendingComment] = useState4();
2985
+ const [submenuStack, setSubmenuStack] = useState4([]);
2986
+ const [screenshots, setScreenshots] = useState4(null);
2987
+ const [isCapturing, setIsCapturing] = useState4(false);
2988
+ const [isQuickChatPinned, setIsQuickChatPinned] = useState4(() => {
2989
+ if (typeof window === "undefined") return false;
2990
+ try {
2991
+ return sessionStorage.getItem("anyclick-quick-chat-pinned") === "true";
2992
+ } catch {
2993
+ return false;
2994
+ }
2995
+ });
2996
+ const menuRef = useRef3(null);
2997
+ const [adjustedPosition, setAdjustedPosition] = useState4(OFFSCREEN_POSITION);
2998
+ const [isDragging, setIsDragging] = useState4(false);
2999
+ const [dragOffset, setDragOffset] = useState4({
1118
3000
  x: 0,
1119
3001
  y: 0
1120
3002
  });
1121
- const dragStartRef = useRef(null);
1122
- const mergedScreenshotConfig = React2.useMemo(
3003
+ const dragStartRef = useRef3(null);
3004
+ const mergedScreenshotConfig = React3.useMemo(
1123
3005
  () => ({
1124
3006
  ...DEFAULT_SCREENSHOT_CONFIG,
1125
3007
  ...screenshotConfig
@@ -1128,7 +3010,7 @@ function ContextMenu({
1128
3010
  );
1129
3011
  const showPreview = mergedScreenshotConfig.showPreview && isScreenshotSupported();
1130
3012
  const currentItems = submenuStack.length > 0 ? submenuStack[submenuStack.length - 1] : items;
1131
- const captureScreenshots = useCallback(async () => {
3013
+ const captureScreenshots = useCallback3(async () => {
1132
3014
  if (!targetElement || !showPreview) return;
1133
3015
  setIsCapturing(true);
1134
3016
  try {
@@ -1145,7 +3027,7 @@ function ContextMenu({
1145
3027
  setIsCapturing(false);
1146
3028
  }
1147
3029
  }, [containerElement, mergedScreenshotConfig, showPreview, targetElement]);
1148
- useEffect(() => {
3030
+ useEffect3(() => {
1149
3031
  if (!visible) {
1150
3032
  setSelectedType(null);
1151
3033
  setCurrentView("menu");
@@ -1159,7 +3041,7 @@ function ContextMenu({
1159
3041
  dragStartRef.current = null;
1160
3042
  }
1161
3043
  }, [visible]);
1162
- useEffect(() => {
3044
+ useEffect3(() => {
1163
3045
  if (visible && targetElement) {
1164
3046
  clearHighlights();
1165
3047
  applyHighlights(targetElement, highlightConfig);
@@ -1170,7 +3052,7 @@ function ContextMenu({
1170
3052
  clearHighlights();
1171
3053
  };
1172
3054
  }, [highlightConfig, targetElement, visible]);
1173
- useEffect(() => {
3055
+ useEffect3(() => {
1174
3056
  if (!visible) {
1175
3057
  return;
1176
3058
  }
@@ -1189,13 +3071,13 @@ function ContextMenu({
1189
3071
  document.removeEventListener("pointerdown", handlePointerDown);
1190
3072
  };
1191
3073
  }, [onClose, visible]);
1192
- useEffect(() => {
3074
+ useEffect3(() => {
1193
3075
  if (visible) {
1194
3076
  setAdjustedPosition(position);
1195
3077
  setDragOffset({ x: 0, y: 0 });
1196
3078
  }
1197
3079
  }, [position.x, position.y, visible]);
1198
- useEffect(() => {
3080
+ useEffect3(() => {
1199
3081
  if (!visible || !menuRef.current) return;
1200
3082
  const updatePosition = () => {
1201
3083
  const menuElement = menuRef.current;
@@ -1219,7 +3101,7 @@ function ContextMenu({
1219
3101
  window.addEventListener("resize", updatePosition);
1220
3102
  return () => window.removeEventListener("resize", updatePosition);
1221
3103
  }, [currentView, dragOffset, position, positionMode, visible]);
1222
- useEffect(() => {
3104
+ useEffect3(() => {
1223
3105
  if (!visible || positionMode !== "dynamic") return;
1224
3106
  const handlePointerMove = (event) => {
1225
3107
  if (!isDragging || !dragStartRef.current) return;
@@ -1246,7 +3128,7 @@ function ContextMenu({
1246
3128
  };
1247
3129
  }
1248
3130
  }, [isDragging, positionMode, visible]);
1249
- const handleDragStart = useCallback(
3131
+ const handleDragStart = useCallback3(
1250
3132
  (event) => {
1251
3133
  if (positionMode !== "dynamic") return;
1252
3134
  event.preventDefault();
@@ -1255,7 +3137,8 @@ function ContextMenu({
1255
3137
  },
1256
3138
  [positionMode]
1257
3139
  );
1258
- useEffect(() => {
3140
+ const [initialChatInput, setInitialChatInput] = useState4("");
3141
+ useEffect3(() => {
1259
3142
  const handleKeyDown = (e) => {
1260
3143
  if (e.key === "Escape") {
1261
3144
  if (currentView === "screenshot-preview") {
@@ -1264,19 +3147,38 @@ function ContextMenu({
1264
3147
  setCurrentView("menu");
1265
3148
  setSelectedType(null);
1266
3149
  setPendingComment(void 0);
3150
+ } else if (currentView === "quick-chat") {
3151
+ if (!isQuickChatPinned) {
3152
+ setCurrentView("menu");
3153
+ }
1267
3154
  } else if (submenuStack.length > 0) {
1268
3155
  setSubmenuStack((prev) => prev.slice(0, -1));
1269
3156
  } else {
1270
3157
  onClose();
1271
3158
  }
3159
+ return;
3160
+ }
3161
+ if (quickChatConfig && currentView === "menu" && !isQuickChatPinned && !e.ctrlKey && !e.metaKey && !e.altKey) {
3162
+ if (e.key.length === 1 && e.key.match(/[a-zA-Z0-9\s.,!?'"]/)) {
3163
+ e.preventDefault();
3164
+ setInitialChatInput(e.key);
3165
+ setCurrentView("quick-chat");
3166
+ }
1272
3167
  }
1273
3168
  };
1274
3169
  if (visible) {
1275
3170
  document.addEventListener("keydown", handleKeyDown);
1276
3171
  return () => document.removeEventListener("keydown", handleKeyDown);
1277
3172
  }
1278
- }, [currentView, onClose, submenuStack.length, visible]);
1279
- useEffect(() => {
3173
+ }, [
3174
+ currentView,
3175
+ isQuickChatPinned,
3176
+ onClose,
3177
+ quickChatConfig,
3178
+ submenuStack.length,
3179
+ visible
3180
+ ]);
3181
+ useEffect3(() => {
1280
3182
  const menuElement = menuRef.current;
1281
3183
  if (!visible || !menuElement) return;
1282
3184
  const preventTouchDefault = (e) => {
@@ -1293,9 +3195,6 @@ function ContextMenu({
1293
3195
  menuElement.removeEventListener("touchmove", preventTouchDefault);
1294
3196
  };
1295
3197
  }, [visible]);
1296
- if (!visible || !targetElement) {
1297
- return null;
1298
- }
1299
3198
  const handleItemClick = (item) => {
1300
3199
  if (item.status === "comingSoon") {
1301
3200
  return;
@@ -1364,85 +3263,166 @@ function ContextMenu({
1364
3263
  const handleRetakeScreenshots = () => {
1365
3264
  captureScreenshots();
1366
3265
  };
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"
3266
+ const handleQuickChatToggle = () => {
3267
+ if (currentView === "quick-chat" && !isQuickChatPinned) {
3268
+ setCurrentView("menu");
3269
+ } else {
3270
+ setCurrentView("quick-chat");
3271
+ }
3272
+ };
3273
+ const handleQuickChatPin = (pinned) => {
3274
+ setIsQuickChatPinned(pinned);
3275
+ try {
3276
+ if (pinned) {
3277
+ sessionStorage.setItem("anyclick-quick-chat-pinned", "true");
3278
+ } else {
3279
+ sessionStorage.removeItem("anyclick-quick-chat-pinned");
3280
+ }
3281
+ } catch {
3282
+ }
3283
+ if (pinned) {
3284
+ setCurrentView("menu");
3285
+ }
3286
+ };
3287
+ const handleQuickChatClose = () => {
3288
+ setIsQuickChatPinned(false);
3289
+ try {
3290
+ sessionStorage.removeItem("anyclick-quick-chat-pinned");
3291
+ } catch {
3292
+ }
3293
+ setCurrentView("menu");
3294
+ };
3295
+ const containerWidth = currentView === "screenshot-preview" ? 360 : currentView === "quick-chat" && !isQuickChatPinned ? 320 : void 0;
3296
+ const showPinnedDrawer = isQuickChatPinned && quickChatConfig;
3297
+ const showMenu = visible && targetElement;
3298
+ return /* @__PURE__ */ jsxs3(Fragment3, { children: [
3299
+ showPinnedDrawer && /* @__PURE__ */ jsx3(
3300
+ QuickChat,
3301
+ {
3302
+ visible: true,
3303
+ targetElement,
3304
+ containerElement,
3305
+ onClose: handleQuickChatClose,
3306
+ onPin: handleQuickChatPin,
3307
+ isPinned: true,
3308
+ config: quickChatConfig
3309
+ }
3310
+ ),
3311
+ showMenu && /* @__PURE__ */ jsxs3(
3312
+ "div",
3313
+ {
3314
+ ref: menuRef,
3315
+ "aria-label": "Feedback options",
3316
+ className,
3317
+ role: "menu",
3318
+ style: {
3319
+ ...menuStyles.container,
3320
+ left: adjustedPosition.x,
3321
+ top: adjustedPosition.y,
3322
+ ...containerWidth ? { minWidth: containerWidth, width: containerWidth } : {},
3323
+ touchAction: "none",
3324
+ userSelect: "none",
3325
+ WebkitTouchCallout: "none",
3326
+ WebkitUserSelect: "none",
3327
+ ...isDragging ? { cursor: "grabbing" } : {},
3328
+ ...style
3329
+ },
3330
+ children: [
3331
+ !header && currentView !== "screenshot-preview" && currentView !== "quick-chat" && /* @__PURE__ */ jsxs3(DefaultHeader, { styles: menuStyles.header, title: "Send Feedback", children: [
3332
+ positionMode === "dynamic" && /* @__PURE__ */ jsx3(
3333
+ "div",
3334
+ {
3335
+ "data-drag-handle": true,
3336
+ onMouseEnter: (e) => {
3337
+ e.currentTarget.style.opacity = "1";
3338
+ },
3339
+ onMouseLeave: (e) => {
3340
+ e.currentTarget.style.opacity = "0.5";
3341
+ },
3342
+ onPointerDown: handleDragStart,
3343
+ style: {
3344
+ ...menuStyles.dragHandle,
3345
+ cursor: isDragging ? "grabbing" : "grab"
3346
+ },
3347
+ title: "Drag to move",
3348
+ children: /* @__PURE__ */ jsx3(GripVertical, { className: "w-4 h-4" })
3349
+ }
3350
+ ),
3351
+ showPreview && /* @__PURE__ */ jsx3("div", { style: menuStyles.screenshotIndicator, children: /* @__PURE__ */ jsx3(CameraIcon, { className: "w-3 h-3" }) }),
3352
+ quickChatConfig && /* @__PURE__ */ jsx3(
3353
+ "button",
3354
+ {
3355
+ type: "button",
3356
+ onClick: handleQuickChatToggle,
3357
+ style: {
3358
+ display: "flex",
3359
+ alignItems: "center",
3360
+ justifyContent: "center",
3361
+ width: "24px",
3362
+ height: "24px",
3363
+ border: "none",
3364
+ borderRadius: "4px",
3365
+ backgroundColor: isQuickChatPinned ? "var(--anyclick-menu-accent, #0066cc)" : "transparent",
3366
+ color: isQuickChatPinned ? "#fff" : "var(--anyclick-menu-accent, #0066cc)",
3367
+ cursor: "pointer",
3368
+ transition: "all 0.15s ease"
3369
+ },
3370
+ title: isQuickChatPinned ? "Quick Chat (pinned)" : "Quick Ask AI",
3371
+ children: /* @__PURE__ */ jsx3(Sparkles, { className: "w-3.5 h-3.5" })
3372
+ }
3373
+ )
3374
+ ] }),
3375
+ !!header && header,
3376
+ currentView === "menu" && /* @__PURE__ */ jsxs3("div", { style: menuStyles.itemList, children: [
3377
+ submenuStack.length > 0 && /* @__PURE__ */ jsx3(BackButton, { onClick: handleBack }),
3378
+ currentItems.map((item) => /* @__PURE__ */ jsx3(
3379
+ MenuItem,
3380
+ {
3381
+ disabled: isSubmitting,
3382
+ hasChildren: item.children && item.children.length > 0,
3383
+ item,
3384
+ onClick: () => handleItemClick(item)
1404
3385
  },
1405
- title: "Drag to move",
1406
- children: /* @__PURE__ */ jsx2(GripVertical, { className: "w-4 h-4" })
3386
+ item.type
3387
+ ))
3388
+ ] }),
3389
+ currentView === "comment" && /* @__PURE__ */ jsx3(
3390
+ CommentForm,
3391
+ {
3392
+ isSubmitting,
3393
+ onCancel: handleCommentCancel,
3394
+ onSubmit: handleCommentSubmit
1407
3395
  }
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,
3396
+ ),
3397
+ currentView === "screenshot-preview" && /* @__PURE__ */ jsx3(
3398
+ ScreenshotPreview,
1415
3399
  {
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
- );
3400
+ isLoading: isCapturing,
3401
+ isSubmitting,
3402
+ onCancel: handleScreenshotCancel,
3403
+ onConfirm: handleScreenshotConfirm,
3404
+ onRetake: handleRetakeScreenshots,
3405
+ screenshots
3406
+ }
3407
+ ),
3408
+ currentView === "quick-chat" && quickChatConfig && !isQuickChatPinned && /* @__PURE__ */ jsx3(
3409
+ QuickChat,
3410
+ {
3411
+ visible: true,
3412
+ targetElement,
3413
+ containerElement,
3414
+ onClose: handleQuickChatClose,
3415
+ onPin: handleQuickChatPin,
3416
+ isPinned: false,
3417
+ config: quickChatConfig,
3418
+ initialInput: initialChatInput,
3419
+ onInitialInputConsumed: () => setInitialChatInput("")
3420
+ }
3421
+ )
3422
+ ]
3423
+ }
3424
+ )
3425
+ ] });
1446
3426
  }
1447
3427
 
1448
3428
  // src/context.ts
@@ -1465,12 +3445,12 @@ function useFeedback() {
1465
3445
  }
1466
3446
 
1467
3447
  // src/store.ts
1468
- import { create } from "zustand";
3448
+ import { create as create2 } from "zustand";
1469
3449
  var providerIdCounter = 0;
1470
3450
  function generateProviderId() {
1471
3451
  return `anyclick-provider-${++providerIdCounter}`;
1472
3452
  }
1473
- var useProviderStore = create((set, get) => ({
3453
+ var useProviderStore = create2((set, get) => ({
1474
3454
  providers: /* @__PURE__ */ new Map(),
1475
3455
  findProvidersForElement: (element) => {
1476
3456
  const { providers } = get();
@@ -1626,7 +3606,7 @@ function dispatchContextMenuEvent(event, element) {
1626
3606
  }
1627
3607
 
1628
3608
  // src/AnyclickProvider.tsx
1629
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
3609
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1630
3610
  var defaultMenuItems = [
1631
3611
  { label: "Report an issue", showComment: true, type: "issue" },
1632
3612
  { label: "Request a feature", showComment: true, type: "feature" },
@@ -1649,6 +3629,7 @@ function AnyclickProvider({
1649
3629
  metadata,
1650
3630
  onSubmitError,
1651
3631
  onSubmitSuccess,
3632
+ quickChatConfig,
1652
3633
  scoped = false,
1653
3634
  screenshotConfig,
1654
3635
  stripAttributes,
@@ -1657,18 +3638,18 @@ function AnyclickProvider({
1657
3638
  touchHoldDurationMs,
1658
3639
  touchMoveThreshold
1659
3640
  }) {
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(
3641
+ const [isSubmitting, setIsSubmitting] = useState5(false);
3642
+ const [menuVisible, setMenuVisible] = useState5(false);
3643
+ const [menuPosition, setMenuPosition] = useState5(OFFSCREEN_POSITION2);
3644
+ const [targetElement, setTargetElement] = useState5(null);
3645
+ const [containerElement, setContainerElement] = useState5(
1665
3646
  null
1666
3647
  );
1667
3648
  const providerId = useId();
1668
- const containerRef = useRef2(null);
1669
- const [containerReady, setContainerReady] = useState3(!scoped);
1670
- const clientRef = useRef2(null);
1671
- const setContainerRef = useCallback2(
3649
+ const containerRef = useRef4(null);
3650
+ const [containerReady, setContainerReady] = useState5(!scoped);
3651
+ const clientRef = useRef4(null);
3652
+ const setContainerRef = useCallback4(
1672
3653
  (node) => {
1673
3654
  containerRef.current = node;
1674
3655
  if (scoped && node) {
@@ -1697,7 +3678,7 @@ function AnyclickProvider({
1697
3678
  updateProvider
1698
3679
  } = useProviderStore();
1699
3680
  const parentId = parentContext?.providerId ?? null;
1700
- const actualDepth = useMemo2(() => {
3681
+ const actualDepth = useMemo4(() => {
1701
3682
  if (!parentContext) return 0;
1702
3683
  let d = 0;
1703
3684
  let currentId = parentId;
@@ -1711,7 +3692,7 @@ function AnyclickProvider({
1711
3692
  }, [parentContext, parentId]);
1712
3693
  const isDisabledByTheme = theme === null || theme?.disabled === true;
1713
3694
  const effectiveDisabled = disabled || isDisabledByTheme;
1714
- const localTheme = useMemo2(() => {
3695
+ const localTheme = useMemo4(() => {
1715
3696
  if (theme === null) {
1716
3697
  return { disabled: true };
1717
3698
  }
@@ -1726,7 +3707,7 @@ function AnyclickProvider({
1726
3707
  ...theme
1727
3708
  };
1728
3709
  }, [highlightConfig, menuClassName, menuStyle, screenshotConfig, theme]);
1729
- const handleContextMenu = useCallback2(
3710
+ const handleContextMenu = useCallback4(
1730
3711
  (event, element) => {
1731
3712
  if (!scoped && isElementInDisabledScope(element)) {
1732
3713
  return false;
@@ -1780,7 +3761,7 @@ function AnyclickProvider({
1780
3761
  scoped,
1781
3762
  unregisterProvider
1782
3763
  ]);
1783
- useEffect2(() => {
3764
+ useEffect4(() => {
1784
3765
  updateProvider(providerId, {
1785
3766
  disabled: effectiveDisabled,
1786
3767
  onContextMenu: handleContextMenu,
@@ -1793,7 +3774,7 @@ function AnyclickProvider({
1793
3774
  providerId,
1794
3775
  updateProvider
1795
3776
  ]);
1796
- useEffect2(() => {
3777
+ useEffect4(() => {
1797
3778
  if (isDisabledByAncestor(providerId)) {
1798
3779
  return;
1799
3780
  }
@@ -1841,7 +3822,7 @@ function AnyclickProvider({
1841
3822
  touchHoldDurationMs,
1842
3823
  touchMoveThreshold
1843
3824
  ]);
1844
- const submitAnyclick = useCallback2(
3825
+ const submitAnyclick = useCallback4(
1845
3826
  async (element, type, comment, screenshots) => {
1846
3827
  const client = clientRef.current;
1847
3828
  if (!client) return;
@@ -1862,7 +3843,7 @@ function AnyclickProvider({
1862
3843
  },
1863
3844
  [metadata]
1864
3845
  );
1865
- const openMenu = useCallback2(
3846
+ const openMenu = useCallback4(
1866
3847
  (element, position) => {
1867
3848
  setTargetElement(element);
1868
3849
  const mergedTheme2 = getMergedTheme(providerId);
@@ -1876,13 +3857,13 @@ function AnyclickProvider({
1876
3857
  },
1877
3858
  [getMergedTheme, highlightConfig, providerId]
1878
3859
  );
1879
- const closeMenu = useCallback2(() => {
3860
+ const closeMenu = useCallback4(() => {
1880
3861
  setMenuVisible(false);
1881
3862
  setMenuPosition(OFFSCREEN_POSITION2);
1882
3863
  setTargetElement(null);
1883
3864
  setContainerElement(null);
1884
3865
  }, []);
1885
- const handleMenuSelect = useCallback2(
3866
+ const handleMenuSelect = useCallback4(
1886
3867
  (type, comment, screenshots) => {
1887
3868
  if (targetElement) {
1888
3869
  submitAnyclick(targetElement, type, comment, screenshots);
@@ -1891,7 +3872,7 @@ function AnyclickProvider({
1891
3872
  [submitAnyclick, targetElement]
1892
3873
  );
1893
3874
  const inheritedTheme = getMergedTheme(providerId);
1894
- const mergedTheme = useMemo2(
3875
+ const mergedTheme = useMemo4(
1895
3876
  () => ({
1896
3877
  ...inheritedTheme,
1897
3878
  ...localTheme,
@@ -1914,7 +3895,7 @@ function AnyclickProvider({
1914
3895
  const effectiveMenuClassName = mergedTheme.menuClassName ?? menuClassName;
1915
3896
  const effectiveHighlightConfig = mergedTheme.highlightConfig ?? highlightConfig;
1916
3897
  const effectiveScreenshotConfig = mergedTheme.screenshotConfig ?? screenshotConfig;
1917
- const contextValue = useMemo2(
3898
+ const contextValue = useMemo4(
1918
3899
  () => ({
1919
3900
  closeMenu,
1920
3901
  isEnabled: !effectiveDisabled && !isDisabledByAncestor(providerId),
@@ -1937,7 +3918,7 @@ function AnyclickProvider({
1937
3918
  submitAnyclick
1938
3919
  ]
1939
3920
  );
1940
- const content = scoped ? /* @__PURE__ */ jsx3(
3921
+ const content = scoped ? /* @__PURE__ */ jsx4(
1941
3922
  "div",
1942
3923
  {
1943
3924
  ref: setContainerRef,
@@ -1946,9 +3927,9 @@ function AnyclickProvider({
1946
3927
  children
1947
3928
  }
1948
3929
  ) : children;
1949
- return /* @__PURE__ */ jsxs3(AnyclickContext.Provider, { value: contextValue, children: [
3930
+ return /* @__PURE__ */ jsx4(AnyclickContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxs4("div", { "data-anyclick-root": true, children: [
1950
3931
  content,
1951
- /* @__PURE__ */ jsx3(
3932
+ /* @__PURE__ */ jsx4(
1952
3933
  ContextMenu,
1953
3934
  {
1954
3935
  className: effectiveMenuClassName,
@@ -1960,18 +3941,19 @@ function AnyclickProvider({
1960
3941
  onClose: closeMenu,
1961
3942
  onSelect: handleMenuSelect,
1962
3943
  position: menuPosition,
3944
+ quickChatConfig,
1963
3945
  screenshotConfig: effectiveScreenshotConfig,
1964
3946
  style: effectiveMenuStyle,
1965
3947
  targetElement,
1966
3948
  visible: menuVisible && !effectiveDisabled
1967
3949
  }
1968
3950
  )
1969
- ] });
3951
+ ] }) });
1970
3952
  }
1971
3953
  var FeedbackProvider = AnyclickProvider;
1972
3954
 
1973
3955
  // src/FunModeBridge.tsx
1974
- import { useEffect as useEffect3, useMemo as useMemo3, useRef as useRef3 } from "react";
3956
+ import { useEffect as useEffect5, useMemo as useMemo5, useRef as useRef5 } from "react";
1975
3957
  import {
1976
3958
  usePointer
1977
3959
  } from "@ewjdev/anyclick-pointer";
@@ -2020,9 +4002,9 @@ function buildFunConfig(theme, container) {
2020
4002
  function FunModeBridge() {
2021
4003
  const { setConfig } = usePointer();
2022
4004
  const providerStore = useProviderStore();
2023
- const activeProviderRef = useRef3(null);
2024
- const cachedConfigs = useRef3({});
2025
- const findActiveFunProvider = useMemo3(() => {
4005
+ const activeProviderRef = useRef5(null);
4006
+ const cachedConfigs = useRef5({});
4007
+ const findActiveFunProvider = useMemo5(() => {
2026
4008
  return (el) => {
2027
4009
  if (!el) return null;
2028
4010
  const providers = providerStore.findProvidersForElement(el);
@@ -2034,7 +4016,7 @@ function FunModeBridge() {
2034
4016
  return null;
2035
4017
  };
2036
4018
  }, [providerStore]);
2037
- useEffect3(() => {
4019
+ useEffect5(() => {
2038
4020
  const handleMove = (event) => {
2039
4021
  const el = document.elementFromPoint(event.clientX, event.clientY);
2040
4022
  const provider = findActiveFunProvider(el);
@@ -2068,16 +4050,76 @@ function FunModeBridge() {
2068
4050
  return null;
2069
4051
  }
2070
4052
 
4053
+ // src/ui/button.tsx
4054
+ import { forwardRef } from "react";
4055
+
4056
+ // src/utils/cn.ts
4057
+ function cn(...classes) {
4058
+ return classes.filter(Boolean).join(" ");
4059
+ }
4060
+
4061
+ // src/ui/button.tsx
4062
+ import { jsx as jsx5 } from "react/jsx-runtime";
4063
+ var variantClasses = {
4064
+ default: "ac-bg-accent ac-text-accent-foreground hover:ac-bg-accent-muted ac-border ac-border-border",
4065
+ ghost: "ac-bg-transparent hover:ac-bg-surface-muted ac-text-text",
4066
+ outline: "ac-bg-transparent ac-text-text ac-border ac-border-border hover:ac-bg-surface-muted",
4067
+ destructive: "ac-bg-destructive ac-text-accent-foreground hover:ac-bg-destructive/80"
4068
+ };
4069
+ var sizeClasses = {
4070
+ sm: "ac-h-8 ac-px-3 ac-text-sm",
4071
+ md: "ac-h-10 ac-px-4 ac-text-sm",
4072
+ lg: "ac-h-11 ac-px-5 ac-text-base"
4073
+ };
4074
+ var Button = forwardRef(
4075
+ ({ className, variant = "default", size = "md", ...props }, ref) => {
4076
+ return /* @__PURE__ */ jsx5(
4077
+ "button",
4078
+ {
4079
+ ref,
4080
+ className: cn(
4081
+ "ac-inline-flex ac-items-center ac-justify-center ac-gap-2 ac-rounded-md ac-font-medium ac-transition-colors focus-visible:ac-outline focus-visible:ac-outline-2 focus-visible:ac-outline-offset-2 focus-visible:ac-outline-accent disabled:ac-opacity-50 disabled:ac-cursor-not-allowed",
4082
+ variantClasses[variant],
4083
+ sizeClasses[size],
4084
+ className
4085
+ ),
4086
+ ...props
4087
+ }
4088
+ );
4089
+ }
4090
+ );
4091
+ Button.displayName = "Button";
4092
+
4093
+ // src/ui/input.tsx
4094
+ import { forwardRef as forwardRef2 } from "react";
4095
+ import { jsx as jsx6 } from "react/jsx-runtime";
4096
+ var Input = forwardRef2(
4097
+ ({ className, ...props }, ref) => {
4098
+ return /* @__PURE__ */ jsx6(
4099
+ "input",
4100
+ {
4101
+ ref,
4102
+ className: cn(
4103
+ "ac-h-10 ac-w-full ac-rounded-md ac-border ac-border-border ac-bg-surface ac-text-text ac-placeholder-text-muted ac-px-3 ac-py-2 ac-text-sm focus-visible:ac-outline focus-visible:ac-outline-2 focus-visible:ac-outline-offset-2 focus-visible:ac-outline-accent disabled:ac-opacity-50 disabled:ac-cursor-not-allowed",
4104
+ className
4105
+ ),
4106
+ ...props
4107
+ }
4108
+ );
4109
+ }
4110
+ );
4111
+ Input.displayName = "Input";
4112
+
2071
4113
  // src/InspectDialog/InspectDialogManager.tsx
2072
- import { useCallback as useCallback3, useEffect as useEffect5, useState as useState5 } from "react";
4114
+ import { useCallback as useCallback5, useEffect as useEffect7, useState as useState7 } from "react";
2073
4115
 
2074
4116
  // src/InspectDialog/InspectSimple.tsx
2075
- import { useEffect as useEffect4, useMemo as useMemo4, useRef as useRef4, useState as useState4 } from "react";
4117
+ import { useEffect as useEffect6, useMemo as useMemo6, useRef as useRef6, useState as useState6 } from "react";
2076
4118
  import {
2077
4119
  captureScreenshot,
2078
- getElementInspectInfo
4120
+ getElementInspectInfo as getElementInspectInfo2
2079
4121
  } from "@ewjdev/anyclick-core";
2080
- import { Camera, Code, Copy, ExternalLink, FileText, X } from "lucide-react";
4122
+ import { Camera, Code, Copy as Copy2, ExternalLink as ExternalLink2, FileText, X as X2 } from "lucide-react";
2081
4123
 
2082
4124
  // src/ide.ts
2083
4125
  var DEFAULT_IDE_CONFIG = {
@@ -2179,7 +4221,7 @@ function formatSourceLocation(location) {
2179
4221
  }
2180
4222
 
2181
4223
  // src/InspectDialog/InspectSimple.tsx
2182
- import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
4224
+ import { Fragment as Fragment4, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
2183
4225
  var DEFAULT_COMPACT_CONFIG = {
2184
4226
  scale: 0.5,
2185
4227
  fonts: {
@@ -2241,8 +4283,8 @@ function downloadDataUrl(dataUrl, filename) {
2241
4283
  link.click();
2242
4284
  }
2243
4285
  function useIsMobile() {
2244
- const [isMobile, setIsMobile] = useState4(false);
2245
- useEffect4(() => {
4286
+ const [isMobile, setIsMobile] = useState6(false);
4287
+ useEffect6(() => {
2246
4288
  const mq = window.matchMedia("(max-width: 640px)");
2247
4289
  setIsMobile(mq.matches);
2248
4290
  const handler = (e) => setIsMobile(e.matches);
@@ -2260,20 +4302,20 @@ function InspectSimple({
2260
4302
  className,
2261
4303
  highlightColors
2262
4304
  }) {
2263
- const [info, setInfo] = useState4(null);
2264
- const [sourceLocation, setSourceLocation] = useState4(
4305
+ const [info, setInfo] = useState6(null);
4306
+ const [sourceLocation, setSourceLocation] = useState6(
2265
4307
  null
2266
4308
  );
2267
- const [status, setStatus] = useState4(null);
2268
- const [saving, setSaving] = useState4(false);
2269
- const dialogRef = useRef4(null);
4309
+ const [status, setStatus] = useState6(null);
4310
+ const [saving, setSaving] = useState6(false);
4311
+ const dialogRef = useRef6(null);
2270
4312
  const isMobile = useIsMobile();
2271
- useEffect4(() => {
4313
+ useEffect6(() => {
2272
4314
  if (!status) return;
2273
4315
  const timer = setTimeout(() => setStatus(null), 5e3);
2274
4316
  return () => clearTimeout(timer);
2275
4317
  }, [status]);
2276
- useEffect4(() => {
4318
+ useEffect6(() => {
2277
4319
  if (!visible || !targetElement) return;
2278
4320
  try {
2279
4321
  clearHighlights();
@@ -2282,7 +4324,7 @@ function InspectSimple({
2282
4324
  if (container) highlightContainer(container, highlightColors);
2283
4325
  } catch {
2284
4326
  }
2285
- const nextInfo = getElementInspectInfo(targetElement);
4327
+ const nextInfo = getElementInspectInfo2(targetElement);
2286
4328
  setInfo(nextInfo);
2287
4329
  setSourceLocation(
2288
4330
  findSourceLocationInAncestors(targetElement) ?? nextInfo.sourceLocation ?? null
@@ -2291,7 +4333,7 @@ function InspectSimple({
2291
4333
  clearHighlights();
2292
4334
  };
2293
4335
  }, [visible, targetElement, highlightColors]);
2294
- useEffect4(() => {
4336
+ useEffect6(() => {
2295
4337
  if (!visible) return;
2296
4338
  const handleClickOutside = (e) => {
2297
4339
  if (dialogRef.current && !dialogRef.current.contains(e.target)) {
@@ -2306,7 +4348,7 @@ function InspectSimple({
2306
4348
  document.removeEventListener("mousedown", handleClickOutside);
2307
4349
  };
2308
4350
  }, [visible, onClose]);
2309
- const identityLabel = useMemo4(() => {
4351
+ const identityLabel = useMemo6(() => {
2310
4352
  if (!info) return "Select an element";
2311
4353
  const classes = info.classNames[0] ? `.${info.classNames[0]}` : "";
2312
4354
  const id = info.id ? `#${info.id}` : "";
@@ -2392,8 +4434,8 @@ function InspectSimple({
2392
4434
  overflow: "hidden",
2393
4435
  ...style
2394
4436
  };
2395
- return /* @__PURE__ */ jsxs4(Fragment2, { children: [
2396
- /* @__PURE__ */ jsx4(
4437
+ return /* @__PURE__ */ jsxs5(Fragment4, { children: [
4438
+ /* @__PURE__ */ jsx7(
2397
4439
  "div",
2398
4440
  {
2399
4441
  style: {
@@ -2408,14 +4450,14 @@ function InspectSimple({
2408
4450
  role: "presentation"
2409
4451
  }
2410
4452
  ),
2411
- /* @__PURE__ */ jsxs4(
4453
+ /* @__PURE__ */ jsxs5(
2412
4454
  "div",
2413
4455
  {
2414
4456
  ref: dialogRef,
2415
4457
  className: `anyclick-tiny-inspect ${className ?? ""}`,
2416
4458
  style: dialogStyles,
2417
4459
  children: [
2418
- /* @__PURE__ */ jsxs4(
4460
+ /* @__PURE__ */ jsxs5(
2419
4461
  "div",
2420
4462
  {
2421
4463
  style: {
@@ -2427,7 +4469,7 @@ function InspectSimple({
2427
4469
  borderBottom: "1px solid #1e293b"
2428
4470
  },
2429
4471
  children: [
2430
- isMobile && /* @__PURE__ */ jsx4(
4472
+ isMobile && /* @__PURE__ */ jsx7(
2431
4473
  "div",
2432
4474
  {
2433
4475
  style: {
@@ -2442,8 +4484,8 @@ function InspectSimple({
2442
4484
  }
2443
4485
  }
2444
4486
  ),
2445
- /* @__PURE__ */ jsxs4("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
2446
- /* @__PURE__ */ jsx4(
4487
+ /* @__PURE__ */ jsxs5("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
4488
+ /* @__PURE__ */ jsx7(
2447
4489
  "span",
2448
4490
  {
2449
4491
  style: {
@@ -2458,31 +4500,31 @@ function InspectSimple({
2458
4500
  children: identityLabel
2459
4501
  }
2460
4502
  ),
2461
- sourceLocation && /* @__PURE__ */ jsx4(
4503
+ sourceLocation && /* @__PURE__ */ jsx7(
2462
4504
  "button",
2463
4505
  {
2464
4506
  type: "button",
2465
4507
  onClick: handleOpenIDE,
2466
4508
  title: formatSourceLocation(sourceLocation),
2467
4509
  style: iconBtnStyle,
2468
- children: /* @__PURE__ */ jsx4(ExternalLink, { size: 14 })
4510
+ children: /* @__PURE__ */ jsx7(ExternalLink2, { size: 14 })
2469
4511
  }
2470
4512
  )
2471
4513
  ] }),
2472
- /* @__PURE__ */ jsx4(
4514
+ /* @__PURE__ */ jsx7(
2473
4515
  "button",
2474
4516
  {
2475
4517
  type: "button",
2476
4518
  onClick: onClose,
2477
4519
  style: iconBtnStyle,
2478
4520
  "aria-label": "Close inspector",
2479
- children: /* @__PURE__ */ jsx4(X, { size: 16 })
4521
+ children: /* @__PURE__ */ jsx7(X2, { size: 16 })
2480
4522
  }
2481
4523
  )
2482
4524
  ]
2483
4525
  }
2484
4526
  ),
2485
- /* @__PURE__ */ jsxs4(
4527
+ /* @__PURE__ */ jsxs5(
2486
4528
  "div",
2487
4529
  {
2488
4530
  style: {
@@ -2492,7 +4534,7 @@ function InspectSimple({
2492
4534
  gap: 8
2493
4535
  },
2494
4536
  children: [
2495
- info?.selector && /* @__PURE__ */ jsx4(
4537
+ info?.selector && /* @__PURE__ */ jsx7(
2496
4538
  "code",
2497
4539
  {
2498
4540
  style: {
@@ -2507,7 +4549,7 @@ function InspectSimple({
2507
4549
  children: info.selector
2508
4550
  }
2509
4551
  ),
2510
- status && /* @__PURE__ */ jsx4(
4552
+ status && /* @__PURE__ */ jsx7(
2511
4553
  "div",
2512
4554
  {
2513
4555
  style: {
@@ -2519,7 +4561,7 @@ function InspectSimple({
2519
4561
  children: status
2520
4562
  }
2521
4563
  ),
2522
- /* @__PURE__ */ jsxs4(
4564
+ /* @__PURE__ */ jsxs5(
2523
4565
  "div",
2524
4566
  {
2525
4567
  style: {
@@ -2528,7 +4570,7 @@ function InspectSimple({
2528
4570
  gap: 6
2529
4571
  },
2530
4572
  children: [
2531
- /* @__PURE__ */ jsx4(
4573
+ /* @__PURE__ */ jsx7(
2532
4574
  "button",
2533
4575
  {
2534
4576
  type: "button",
@@ -2536,10 +4578,10 @@ function InspectSimple({
2536
4578
  style: iconActionStyle,
2537
4579
  title: "Copy CSS selector",
2538
4580
  "aria-label": "Copy CSS selector",
2539
- children: /* @__PURE__ */ jsx4(Copy, { size: 15 })
4581
+ children: /* @__PURE__ */ jsx7(Copy2, { size: 15 })
2540
4582
  }
2541
4583
  ),
2542
- /* @__PURE__ */ jsx4(
4584
+ /* @__PURE__ */ jsx7(
2543
4585
  "button",
2544
4586
  {
2545
4587
  type: "button",
@@ -2547,10 +4589,10 @@ function InspectSimple({
2547
4589
  style: iconActionStyle,
2548
4590
  title: "Copy text content",
2549
4591
  "aria-label": "Copy text content",
2550
- children: /* @__PURE__ */ jsx4(FileText, { size: 15 })
4592
+ children: /* @__PURE__ */ jsx7(FileText, { size: 15 })
2551
4593
  }
2552
4594
  ),
2553
- /* @__PURE__ */ jsx4(
4595
+ /* @__PURE__ */ jsx7(
2554
4596
  "button",
2555
4597
  {
2556
4598
  type: "button",
@@ -2558,10 +4600,10 @@ function InspectSimple({
2558
4600
  style: iconActionStyle,
2559
4601
  title: "Copy HTML markup",
2560
4602
  "aria-label": "Copy HTML markup",
2561
- children: /* @__PURE__ */ jsx4(Code, { size: 15 })
4603
+ children: /* @__PURE__ */ jsx7(Code, { size: 15 })
2562
4604
  }
2563
4605
  ),
2564
- /* @__PURE__ */ jsx4(
4606
+ /* @__PURE__ */ jsx7(
2565
4607
  "button",
2566
4608
  {
2567
4609
  type: "button",
@@ -2573,7 +4615,7 @@ function InspectSimple({
2573
4615
  disabled: saving,
2574
4616
  title: "Save screenshot",
2575
4617
  "aria-label": "Save screenshot",
2576
- children: /* @__PURE__ */ jsx4(Camera, { size: 15 })
4618
+ children: /* @__PURE__ */ jsx7(Camera, { size: 15 })
2577
4619
  }
2578
4620
  )
2579
4621
  ]
@@ -2616,7 +4658,7 @@ var iconActionStyle = {
2616
4658
  };
2617
4659
 
2618
4660
  // src/InspectDialog/InspectDialogManager.tsx
2619
- import { jsx as jsx5 } from "react/jsx-runtime";
4661
+ import { jsx as jsx8 } from "react/jsx-runtime";
2620
4662
  var INSPECT_DIALOG_EVENT = "anyclick:inspect";
2621
4663
  function openInspectDialog(targetElement) {
2622
4664
  if (typeof window === "undefined") return;
@@ -2639,16 +4681,16 @@ function InspectDialogManager({
2639
4681
  initialPinnedPosition = "floating",
2640
4682
  compactConfig
2641
4683
  }) {
2642
- const [visible, setVisible] = useState5(false);
2643
- const [targetElement, setTargetElement] = useState5(null);
2644
- const handleClose = useCallback3(() => {
4684
+ const [visible, setVisible] = useState7(false);
4685
+ const [targetElement, setTargetElement] = useState7(null);
4686
+ const handleClose = useCallback5(() => {
2645
4687
  setVisible(false);
2646
4688
  setTargetElement(null);
2647
4689
  }, []);
2648
- const handleSelectElement = useCallback3((element) => {
4690
+ const handleSelectElement = useCallback5((element) => {
2649
4691
  setTargetElement(element);
2650
4692
  }, []);
2651
- useEffect5(() => {
4693
+ useEffect7(() => {
2652
4694
  const handleInspectEvent = (event) => {
2653
4695
  const customEvent = event;
2654
4696
  if (customEvent.detail?.targetElement) {
@@ -2661,7 +4703,7 @@ function InspectDialogManager({
2661
4703
  window.removeEventListener(INSPECT_DIALOG_EVENT, handleInspectEvent);
2662
4704
  };
2663
4705
  }, []);
2664
- return /* @__PURE__ */ jsx5(
4706
+ return /* @__PURE__ */ jsx8(
2665
4707
  InspectSimple,
2666
4708
  {
2667
4709
  visible,
@@ -2723,6 +4765,20 @@ var presetDefaults = {
2723
4765
  showComment: false,
2724
4766
  type: "search_google"
2725
4767
  },
4768
+ {
4769
+ label: "Ask t3.chat",
4770
+ onClick: ({ closeMenu }) => {
4771
+ closeMenu();
4772
+ if (typeof window === "undefined") return false;
4773
+ const selection = window.getSelection()?.toString().trim();
4774
+ const query = selection && selection.length > 0 ? selection : "";
4775
+ const url = query ? `https://t3.chat/?q=${encodeURIComponent(query)}` : "https://t3.chat";
4776
+ window.open(url, "_blank", "noopener,noreferrer");
4777
+ return false;
4778
+ },
4779
+ showComment: false,
4780
+ type: "ask_t3chat"
4781
+ },
2726
4782
  {
2727
4783
  label: "Share\u2026",
2728
4784
  onClick: async ({ closeMenu }) => {
@@ -2992,6 +5048,172 @@ function listPresets() {
2992
5048
  }))
2993
5049
  }));
2994
5050
  }
5051
+ function createT3ChatMenuItem(options = {}) {
5052
+ const { label = "Ask t3.chat", baseUrl = "https://t3.chat" } = options;
5053
+ return {
5054
+ label,
5055
+ onClick: ({ closeMenu }) => {
5056
+ closeMenu();
5057
+ if (typeof window === "undefined") return false;
5058
+ const selection = window.getSelection()?.toString().trim();
5059
+ const query = selection && selection.length > 0 ? selection : "";
5060
+ const url = query ? `${baseUrl}/?q=${encodeURIComponent(query)}` : baseUrl;
5061
+ window.open(url, "_blank", "noopener,noreferrer");
5062
+ return false;
5063
+ },
5064
+ showComment: false,
5065
+ type: "ask_t3chat"
5066
+ };
5067
+ }
5068
+ function getSelectedText() {
5069
+ if (typeof window === "undefined") return "";
5070
+ return window.getSelection()?.toString().trim() ?? "";
5071
+ }
5072
+ function hasTextSelection() {
5073
+ return getSelectedText().length > 0;
5074
+ }
5075
+ function detectImageElement(element) {
5076
+ if (!element) return { isImage: false };
5077
+ if (element.tagName === "IMG") {
5078
+ const img = element;
5079
+ return {
5080
+ isImage: true,
5081
+ src: img.src || img.currentSrc,
5082
+ type: "img"
5083
+ };
5084
+ }
5085
+ if (element.tagName === "PICTURE") {
5086
+ const img = element.querySelector("img");
5087
+ return {
5088
+ isImage: true,
5089
+ src: img?.src || img?.currentSrc,
5090
+ type: "picture"
5091
+ };
5092
+ }
5093
+ if (element.tagName === "SVG" || element.tagName === "svg") {
5094
+ return {
5095
+ isImage: true,
5096
+ type: "svg"
5097
+ };
5098
+ }
5099
+ if (element.tagName === "CANVAS") {
5100
+ return {
5101
+ isImage: true,
5102
+ type: "canvas"
5103
+ };
5104
+ }
5105
+ if (typeof window !== "undefined") {
5106
+ const computedStyle = window.getComputedStyle(element);
5107
+ const backgroundImage = computedStyle.backgroundImage;
5108
+ if (backgroundImage && backgroundImage !== "none") {
5109
+ const urlMatch = backgroundImage.match(/url\(["']?(.+?)["']?\)/);
5110
+ if (urlMatch) {
5111
+ return {
5112
+ isImage: true,
5113
+ src: urlMatch[1],
5114
+ type: "background"
5115
+ };
5116
+ }
5117
+ }
5118
+ }
5119
+ const imgChild = element.querySelector("img");
5120
+ if (imgChild) {
5121
+ return {
5122
+ isImage: true,
5123
+ src: imgChild.src || imgChild.currentSrc,
5124
+ type: "img"
5125
+ };
5126
+ }
5127
+ return { isImage: false };
5128
+ }
5129
+ function createUploadThingMenuItem(options = {}) {
5130
+ const {
5131
+ label = "Upload to UploadThing",
5132
+ endpoint = "/api/uploadthing",
5133
+ onUploadComplete,
5134
+ onUploadError
5135
+ } = options;
5136
+ return {
5137
+ label,
5138
+ onClick: async ({ closeMenu, targetElement }) => {
5139
+ if (!targetElement) {
5140
+ onUploadError?.(new Error("No target element"));
5141
+ return false;
5142
+ }
5143
+ try {
5144
+ const imageInfo = detectImageElement(targetElement);
5145
+ if (imageInfo.isImage && imageInfo.src) {
5146
+ const response = await fetch(imageInfo.src);
5147
+ const blob = await response.blob();
5148
+ const formData = new FormData();
5149
+ const filename = `image-${Date.now()}.${blob.type.split("/")[1] || "png"}`;
5150
+ formData.append("file", blob, filename);
5151
+ const uploadResponse = await fetch(endpoint, {
5152
+ method: "POST",
5153
+ body: formData
5154
+ });
5155
+ if (!uploadResponse.ok) {
5156
+ throw new Error(`Upload failed: ${uploadResponse.status}`);
5157
+ }
5158
+ const result = await uploadResponse.json();
5159
+ onUploadComplete?.(result);
5160
+ } else if (imageInfo.isImage && imageInfo.type === "canvas") {
5161
+ const canvas = targetElement;
5162
+ const dataUrl = canvas.toDataURL("image/png");
5163
+ const response = await fetch(dataUrl);
5164
+ const blob = await response.blob();
5165
+ const formData = new FormData();
5166
+ formData.append("file", blob, `canvas-${Date.now()}.png`);
5167
+ const uploadResponse = await fetch(endpoint, {
5168
+ method: "POST",
5169
+ body: formData
5170
+ });
5171
+ if (!uploadResponse.ok) {
5172
+ throw new Error(`Upload failed: ${uploadResponse.status}`);
5173
+ }
5174
+ const result = await uploadResponse.json();
5175
+ onUploadComplete?.(result);
5176
+ } else {
5177
+ onUploadError?.(
5178
+ new Error(
5179
+ "Element is not an image. Screenshot upload requires anyclick-core."
5180
+ )
5181
+ );
5182
+ }
5183
+ } catch (error) {
5184
+ onUploadError?.(
5185
+ error instanceof Error ? error : new Error(String(error))
5186
+ );
5187
+ }
5188
+ closeMenu();
5189
+ return false;
5190
+ },
5191
+ showComment: false,
5192
+ type: "upload_image"
5193
+ };
5194
+ }
5195
+ function createUploadScreenshotMenuItem(options = {}) {
5196
+ const {
5197
+ label = "Upload Screenshot",
5198
+ endpoint = "/api/uploadthing",
5199
+ onUploadComplete,
5200
+ onUploadError
5201
+ } = options;
5202
+ return {
5203
+ label,
5204
+ badge: { label: "Coming soon", tone: "info" },
5205
+ status: "comingSoon",
5206
+ onClick: async ({ closeMenu }) => {
5207
+ onUploadError?.(
5208
+ new Error("Screenshot upload will be available in a future release")
5209
+ );
5210
+ closeMenu();
5211
+ return false;
5212
+ },
5213
+ showComment: false,
5214
+ type: "upload_screenshot"
5215
+ };
5216
+ }
2995
5217
 
2996
5218
  // src/types.ts
2997
5219
  function filterMenuItemsByRole(items, userContext) {
@@ -3021,7 +5243,7 @@ import {
3021
5243
  } from "@ewjdev/anyclick-core";
3022
5244
 
3023
5245
  // src/AnyclickLogo.tsx
3024
- import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
5246
+ import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
3025
5247
  function AnyclickLogo({
3026
5248
  size = 64,
3027
5249
  borderWidth = 2,
@@ -3033,7 +5255,7 @@ function AnyclickLogo({
3033
5255
  }) {
3034
5256
  const cursorSize = size * 0.45;
3035
5257
  const cursorStroke = borderWidth;
3036
- return /* @__PURE__ */ jsxs5(
5258
+ return /* @__PURE__ */ jsxs6(
3037
5259
  "svg",
3038
5260
  {
3039
5261
  width: size,
@@ -3047,7 +5269,7 @@ function AnyclickLogo({
3047
5269
  role: onClick ? "button" : "img",
3048
5270
  "aria-label": "Anyclick Logo",
3049
5271
  children: [
3050
- /* @__PURE__ */ jsx6(
5272
+ /* @__PURE__ */ jsx9(
3051
5273
  "circle",
3052
5274
  {
3053
5275
  cx: size / 2,
@@ -3058,11 +5280,11 @@ function AnyclickLogo({
3058
5280
  strokeWidth: borderWidth
3059
5281
  }
3060
5282
  ),
3061
- /* @__PURE__ */ jsx6(
5283
+ /* @__PURE__ */ jsx9(
3062
5284
  "g",
3063
5285
  {
3064
5286
  transform: `translate(${(size - cursorSize) / 2}, ${(size - cursorSize) / 2})`,
3065
- children: /* @__PURE__ */ jsx6(
5287
+ children: /* @__PURE__ */ jsx9(
3066
5288
  "path",
3067
5289
  {
3068
5290
  d: `
@@ -3095,7 +5317,7 @@ import {
3095
5317
  getAccessibilityInfo,
3096
5318
  getBoxModelInfo,
3097
5319
  getAttributes,
3098
- getElementInspectInfo as getElementInspectInfo2,
5320
+ getElementInspectInfo as getElementInspectInfo3,
3099
5321
  formatStylesAsCSS,
3100
5322
  formatBoxModel,
3101
5323
  CURATED_STYLE_PROPERTIES,
@@ -3106,17 +5328,21 @@ export {
3106
5328
  AnyclickContext,
3107
5329
  AnyclickLogo,
3108
5330
  AnyclickProvider,
5331
+ Button,
3109
5332
  CURATED_STYLE_PROPERTIES,
3110
5333
  ContextMenu,
3111
5334
  DEFAULT_COMPACT_CONFIG,
5335
+ DEFAULT_QUICK_CHAT_CONFIG,
3112
5336
  DEFAULT_SCREENSHOT_CONFIG2 as DEFAULT_SCREENSHOT_CONFIG,
3113
5337
  DEFAULT_SENSITIVE_SELECTORS,
3114
5338
  FeedbackContext,
3115
5339
  FeedbackProvider,
3116
5340
  FunModeBridge,
3117
5341
  INSPECT_DIALOG_EVENT,
5342
+ Input,
3118
5343
  InspectDialogManager,
3119
5344
  InspectSimple,
5345
+ QuickChat,
3120
5346
  ScreenshotPreview,
3121
5347
  applyHighlights,
3122
5348
  buildIDEUrl,
@@ -3125,9 +5351,13 @@ export {
3125
5351
  clearHighlights,
3126
5352
  createIDEOpener,
3127
5353
  createPresetMenu,
5354
+ createT3ChatMenuItem,
5355
+ createUploadScreenshotMenuItem,
5356
+ createUploadThingMenuItem,
3128
5357
  darkMenuStyles,
3129
5358
  defaultContainerSelectors,
3130
5359
  defaultHighlightColors,
5360
+ detectImageElement,
3131
5361
  detectPreferredIDE,
3132
5362
  dispatchContextMenuEvent,
3133
5363
  estimateTotalSize2 as estimateTotalSize,
@@ -3144,8 +5374,10 @@ export {
3144
5374
  getBadgeStyle,
3145
5375
  getBoxModelInfo,
3146
5376
  getComputedStyles,
3147
- getElementInspectInfo2 as getElementInspectInfo,
5377
+ getElementInspectInfo3 as getElementInspectInfo,
5378
+ getSelectedText,
3148
5379
  getSourceLocationFromElement,
5380
+ hasTextSelection,
3149
5381
  highlightContainer,
3150
5382
  highlightTarget,
3151
5383
  isIDEProtocolSupported,
@@ -3156,8 +5388,11 @@ export {
3156
5388
  openInIDE,
3157
5389
  openInspectDialog,
3158
5390
  presetDefaults,
5391
+ quickChatKeyframes,
5392
+ quickChatStyles,
3159
5393
  useAnyclick,
3160
5394
  useFeedback,
3161
- useProviderStore
5395
+ useProviderStore,
5396
+ useQuickChat
3162
5397
  };
3163
5398
  //# sourceMappingURL=index.mjs.map