@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.js CHANGED
@@ -31,79 +31,1946 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  // src/index.ts
32
32
  var index_exports = {};
33
33
  __export(index_exports, {
34
- ALL_CURATED_PROPERTIES: () => import_anyclick_core6.ALL_CURATED_PROPERTIES,
34
+ ALL_CURATED_PROPERTIES: () => import_anyclick_core8.ALL_CURATED_PROPERTIES,
35
35
  AnyclickContext: () => AnyclickContext,
36
36
  AnyclickLogo: () => AnyclickLogo,
37
37
  AnyclickProvider: () => AnyclickProvider,
38
- CURATED_STYLE_PROPERTIES: () => import_anyclick_core6.CURATED_STYLE_PROPERTIES,
38
+ Button: () => Button,
39
+ CURATED_STYLE_PROPERTIES: () => import_anyclick_core8.CURATED_STYLE_PROPERTIES,
39
40
  ContextMenu: () => ContextMenu,
40
41
  DEFAULT_COMPACT_CONFIG: () => DEFAULT_COMPACT_CONFIG,
41
- DEFAULT_SCREENSHOT_CONFIG: () => import_anyclick_core5.DEFAULT_SCREENSHOT_CONFIG,
42
- DEFAULT_SENSITIVE_SELECTORS: () => import_anyclick_core5.DEFAULT_SENSITIVE_SELECTORS,
42
+ DEFAULT_QUICK_CHAT_CONFIG: () => DEFAULT_QUICK_CHAT_CONFIG,
43
+ DEFAULT_SCREENSHOT_CONFIG: () => import_anyclick_core7.DEFAULT_SCREENSHOT_CONFIG,
44
+ DEFAULT_SENSITIVE_SELECTORS: () => import_anyclick_core7.DEFAULT_SENSITIVE_SELECTORS,
43
45
  FeedbackContext: () => FeedbackContext,
44
46
  FeedbackProvider: () => FeedbackProvider,
45
47
  FunModeBridge: () => FunModeBridge,
46
48
  INSPECT_DIALOG_EVENT: () => INSPECT_DIALOG_EVENT,
49
+ Input: () => Input,
47
50
  InspectDialogManager: () => InspectDialogManager,
48
51
  InspectSimple: () => InspectSimple,
52
+ QuickChat: () => QuickChat,
49
53
  ScreenshotPreview: () => ScreenshotPreview,
50
54
  applyHighlights: () => applyHighlights,
51
55
  buildIDEUrl: () => buildIDEUrl,
52
- captureAllScreenshots: () => import_anyclick_core5.captureAllScreenshots,
53
- captureScreenshot: () => import_anyclick_core5.captureScreenshot,
56
+ captureAllScreenshots: () => import_anyclick_core7.captureAllScreenshots,
57
+ captureScreenshot: () => import_anyclick_core7.captureScreenshot,
54
58
  clearHighlights: () => clearHighlights,
55
59
  createIDEOpener: () => createIDEOpener,
56
60
  createPresetMenu: () => createPresetMenu,
61
+ createT3ChatMenuItem: () => createT3ChatMenuItem,
62
+ createUploadScreenshotMenuItem: () => createUploadScreenshotMenuItem,
63
+ createUploadThingMenuItem: () => createUploadThingMenuItem,
57
64
  darkMenuStyles: () => darkMenuStyles,
58
65
  defaultContainerSelectors: () => defaultContainerSelectors,
59
66
  defaultHighlightColors: () => defaultHighlightColors,
67
+ detectImageElement: () => detectImageElement,
60
68
  detectPreferredIDE: () => detectPreferredIDE,
61
69
  dispatchContextMenuEvent: () => dispatchContextMenuEvent,
62
- estimateTotalSize: () => import_anyclick_core5.estimateTotalSize,
70
+ estimateTotalSize: () => import_anyclick_core7.estimateTotalSize,
63
71
  filterMenuItemsByRole: () => filterMenuItemsByRole,
64
72
  findContainerParent: () => findContainerParent,
65
73
  findSourceLocationInAncestors: () => findSourceLocationInAncestors,
66
- formatBoxModel: () => import_anyclick_core6.formatBoxModel,
67
- formatBytes: () => import_anyclick_core5.formatBytes,
74
+ formatBoxModel: () => import_anyclick_core8.formatBoxModel,
75
+ formatBytes: () => import_anyclick_core7.formatBytes,
68
76
  formatSourceLocation: () => formatSourceLocation,
69
- formatStylesAsCSS: () => import_anyclick_core6.formatStylesAsCSS,
77
+ formatStylesAsCSS: () => import_anyclick_core8.formatStylesAsCSS,
70
78
  generateProviderId: () => generateProviderId,
71
- getAccessibilityInfo: () => import_anyclick_core6.getAccessibilityInfo,
72
- getAttributes: () => import_anyclick_core6.getAttributes,
79
+ getAccessibilityInfo: () => import_anyclick_core8.getAccessibilityInfo,
80
+ getAttributes: () => import_anyclick_core8.getAttributes,
73
81
  getBadgeStyle: () => getBadgeStyle,
74
- getBoxModelInfo: () => import_anyclick_core6.getBoxModelInfo,
75
- getComputedStyles: () => import_anyclick_core6.getComputedStyles,
76
- getElementInspectInfo: () => import_anyclick_core6.getElementInspectInfo,
82
+ getBoxModelInfo: () => import_anyclick_core8.getBoxModelInfo,
83
+ getComputedStyles: () => import_anyclick_core8.getComputedStyles,
84
+ getElementInspectInfo: () => import_anyclick_core8.getElementInspectInfo,
85
+ getSelectedText: () => getSelectedText,
77
86
  getSourceLocationFromElement: () => getSourceLocationFromElement,
87
+ hasTextSelection: () => hasTextSelection,
78
88
  highlightContainer: () => highlightContainer,
79
89
  highlightTarget: () => highlightTarget,
80
90
  isIDEProtocolSupported: () => isIDEProtocolSupported,
81
- isScreenshotSupported: () => import_anyclick_core5.isScreenshotSupported,
91
+ isScreenshotSupported: () => import_anyclick_core7.isScreenshotSupported,
82
92
  listPresets: () => listPresets,
83
93
  menuCSSVariables: () => menuCSSVariables,
84
94
  menuStyles: () => menuStyles,
85
95
  openInIDE: () => openInIDE,
86
96
  openInspectDialog: () => openInspectDialog,
87
97
  presetDefaults: () => presetDefaults,
98
+ quickChatKeyframes: () => quickChatKeyframes,
99
+ quickChatStyles: () => quickChatStyles,
88
100
  useAnyclick: () => useAnyclick,
89
101
  useFeedback: () => useFeedback,
90
- useProviderStore: () => useProviderStore
102
+ useProviderStore: () => useProviderStore,
103
+ useQuickChat: () => useQuickChat
91
104
  });
92
105
  module.exports = __toCommonJS(index_exports);
93
106
 
94
107
  // src/AnyclickProvider.tsx
95
- var import_react4 = require("react");
96
- var import_anyclick_core3 = require("@ewjdev/anyclick-core");
108
+ var import_react7 = require("react");
109
+ var import_anyclick_core5 = require("@ewjdev/anyclick-core");
97
110
 
98
111
  // src/ContextMenu.tsx
99
- var import_react2 = __toESM(require("react"));
112
+ var import_react5 = __toESM(require("react"));
113
+ var import_anyclick_core4 = require("@ewjdev/anyclick-core");
114
+ var import_lucide_react3 = require("lucide-react");
115
+
116
+ // src/QuickChat/QuickChat.tsx
117
+ var import_react3 = __toESM(require("react"));
100
118
  var import_anyclick_core2 = require("@ewjdev/anyclick-core");
101
- var import_lucide_react2 = require("lucide-react");
119
+ var import_lucide_react = require("lucide-react");
120
+
121
+ // src/QuickChat/styles.ts
122
+ var quickChatStyles = {
123
+ // Inline container - inside the context menu
124
+ container: {
125
+ display: "flex",
126
+ flexDirection: "column",
127
+ width: "100%",
128
+ maxHeight: "320px",
129
+ overflow: "hidden"
130
+ },
131
+ // Pinned drawer - anchored to right side of screen
132
+ pinnedContainer: {
133
+ position: "fixed",
134
+ top: 0,
135
+ right: 0,
136
+ bottom: 0,
137
+ width: "340px",
138
+ display: "flex",
139
+ flexDirection: "column",
140
+ backgroundColor: "var(--anyclick-menu-bg, #ffffff)",
141
+ borderLeft: "1px solid var(--anyclick-menu-border, #e5e5e5)",
142
+ boxShadow: "-4px 0 24px rgba(0, 0, 0, 0.15)",
143
+ fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
144
+ fontSize: "14px",
145
+ overflow: "hidden",
146
+ zIndex: 9998
147
+ },
148
+ // Pinned messages area (taller)
149
+ pinnedMessagesArea: {
150
+ flex: 1,
151
+ overflowY: "auto",
152
+ padding: "12px",
153
+ display: "flex",
154
+ flexDirection: "column",
155
+ gap: "8px",
156
+ minHeight: "200px"
157
+ },
158
+ // Header - minimal design
159
+ header: {
160
+ display: "flex",
161
+ alignItems: "center",
162
+ justifyContent: "space-between",
163
+ padding: "6px 8px",
164
+ gap: "8px"
165
+ },
166
+ headerTitle: {
167
+ fontSize: "11px",
168
+ fontWeight: 600,
169
+ textTransform: "uppercase",
170
+ letterSpacing: "0.5px",
171
+ color: "var(--anyclick-menu-text-muted, #666)"
172
+ },
173
+ headerActions: {
174
+ display: "flex",
175
+ alignItems: "center",
176
+ gap: "2px"
177
+ },
178
+ iconButton: {
179
+ display: "flex",
180
+ alignItems: "center",
181
+ justifyContent: "center",
182
+ width: "24px",
183
+ height: "24px",
184
+ border: "none",
185
+ borderRadius: "4px",
186
+ backgroundColor: "transparent",
187
+ color: "var(--anyclick-menu-text-muted, #666)",
188
+ cursor: "pointer",
189
+ transition: "all 0.15s ease"
190
+ },
191
+ iconButtonActive: {
192
+ backgroundColor: "var(--anyclick-menu-accent, #0066cc)",
193
+ color: "#fff"
194
+ },
195
+ // Context badge in header
196
+ contextBadge: {
197
+ display: "inline-flex",
198
+ alignItems: "center",
199
+ gap: "4px",
200
+ padding: "3px 8px",
201
+ fontSize: "10px",
202
+ fontWeight: 500,
203
+ border: "1px solid var(--anyclick-menu-border, #e5e5e5)",
204
+ borderRadius: "10px",
205
+ backgroundColor: "transparent",
206
+ color: "var(--anyclick-menu-text-muted, #666)",
207
+ cursor: "pointer",
208
+ transition: "all 0.15s ease"
209
+ },
210
+ contextBadgeActive: {
211
+ borderColor: "var(--anyclick-menu-accent, #0066cc)",
212
+ backgroundColor: "rgba(0, 102, 204, 0.08)"
213
+ },
214
+ // Context dropdown (in normal flow, not absolute)
215
+ contextDropdown: {
216
+ display: "flex",
217
+ flexDirection: "column",
218
+ gap: "2px",
219
+ padding: "6px 8px",
220
+ margin: "0 8px 6px 8px",
221
+ backgroundColor: "var(--anyclick-menu-hover, #f5f5f5)",
222
+ borderRadius: "6px",
223
+ maxHeight: "80px",
224
+ overflowY: "auto"
225
+ },
226
+ // Messages area
227
+ messagesArea: {
228
+ flex: 1,
229
+ overflowY: "auto",
230
+ padding: "8px 12px",
231
+ display: "flex",
232
+ flexDirection: "column",
233
+ gap: "8px",
234
+ minHeight: "40px",
235
+ maxHeight: "140px"
236
+ },
237
+ emptyState: {
238
+ display: "none"
239
+ // Hide empty state, placeholder in input is enough
240
+ },
241
+ // Message bubble
242
+ message: {
243
+ display: "flex",
244
+ flexDirection: "column",
245
+ gap: "4px",
246
+ maxWidth: "100%"
247
+ },
248
+ messageUser: {
249
+ alignSelf: "flex-end",
250
+ backgroundColor: "var(--anyclick-menu-accent, #0066cc)",
251
+ color: "#fff",
252
+ padding: "6px 10px",
253
+ borderRadius: "12px 12px 4px 12px",
254
+ fontSize: "13px",
255
+ maxWidth: "85%",
256
+ wordBreak: "break-word"
257
+ },
258
+ messageAssistant: {
259
+ alignSelf: "flex-start",
260
+ backgroundColor: "var(--anyclick-menu-hover, #f5f5f5)",
261
+ color: "var(--anyclick-menu-text, #333)",
262
+ padding: "8px 10px",
263
+ borderRadius: "12px 12px 12px 4px",
264
+ fontSize: "13px",
265
+ maxWidth: "100%",
266
+ wordBreak: "break-word",
267
+ lineHeight: 1.4
268
+ },
269
+ messageActions: {
270
+ display: "flex",
271
+ gap: "4px",
272
+ marginTop: "4px",
273
+ flexWrap: "wrap"
274
+ },
275
+ actionButton: {
276
+ display: "inline-flex",
277
+ alignItems: "center",
278
+ gap: "4px",
279
+ padding: "3px 8px",
280
+ fontSize: "11px",
281
+ fontWeight: 500,
282
+ border: "1px solid var(--anyclick-menu-border, #e5e5e5)",
283
+ borderRadius: "4px",
284
+ backgroundColor: "transparent",
285
+ color: "var(--anyclick-menu-text-muted, #666)",
286
+ cursor: "pointer",
287
+ transition: "all 0.15s ease"
288
+ },
289
+ streamingIndicator: {
290
+ display: "inline-block",
291
+ width: "4px",
292
+ height: "14px",
293
+ backgroundColor: "var(--anyclick-menu-accent, #0066cc)",
294
+ borderRadius: "1px",
295
+ animation: "blink 1s infinite"
296
+ },
297
+ // Suggestions - compact horizontal scroll
298
+ suggestionsContainer: {
299
+ display: "flex",
300
+ flexDirection: "column",
301
+ gap: "6px",
302
+ padding: "6px 8px",
303
+ overflowX: "auto",
304
+ overflowY: "hidden",
305
+ scrollbarWidth: "none",
306
+ msOverflowStyle: "none"
307
+ },
308
+ suggestionChip: {
309
+ display: "inline-flex",
310
+ alignItems: "center",
311
+ padding: "4px 10px",
312
+ fontSize: "11px",
313
+ border: "1px solid var(--anyclick-menu-border, #e5e5e5)",
314
+ borderRadius: "12px",
315
+ backgroundColor: "var(--anyclick-menu-hover, #f5f5f5)",
316
+ color: "var(--anyclick-menu-text, #333)",
317
+ cursor: "pointer",
318
+ transition: "all 0.15s ease",
319
+ whiteSpace: "nowrap",
320
+ flexShrink: 0
321
+ },
322
+ suggestionChipHover: {
323
+ backgroundColor: "var(--anyclick-menu-bg, #fff)",
324
+ borderColor: "var(--anyclick-menu-accent, #0066cc)"
325
+ },
326
+ // Context redaction - now in dropdown
327
+ contextContainer: {
328
+ display: "flex",
329
+ flexDirection: "column",
330
+ gap: "4px"
331
+ },
332
+ contextHeader: {
333
+ display: "flex",
334
+ alignItems: "center",
335
+ justifyContent: "space-between",
336
+ marginBottom: "4px"
337
+ },
338
+ contextTitle: {
339
+ fontSize: "10px",
340
+ fontWeight: 600,
341
+ textTransform: "uppercase",
342
+ letterSpacing: "0.5px",
343
+ color: "var(--anyclick-menu-text-muted, #666)"
344
+ },
345
+ contextToggle: {
346
+ fontSize: "10px",
347
+ color: "var(--anyclick-menu-accent, #0066cc)",
348
+ background: "none",
349
+ border: "none",
350
+ cursor: "pointer",
351
+ padding: 0
352
+ },
353
+ contextToggleSmall: {
354
+ fontSize: "9px",
355
+ fontWeight: 500,
356
+ color: "var(--anyclick-menu-text-muted, #666)",
357
+ background: "none",
358
+ border: "none",
359
+ cursor: "pointer",
360
+ padding: "2px 4px",
361
+ borderRadius: "3px",
362
+ transition: "all 0.15s ease"
363
+ },
364
+ contextChunks: {
365
+ display: "flex",
366
+ flexDirection: "column",
367
+ gap: "4px"
368
+ },
369
+ contextChunk: {
370
+ display: "flex",
371
+ alignItems: "center",
372
+ gap: "6px",
373
+ padding: "4px 6px",
374
+ borderRadius: "4px",
375
+ fontSize: "11px",
376
+ backgroundColor: "var(--anyclick-menu-hover, #f5f5f5)",
377
+ cursor: "pointer",
378
+ transition: "opacity 0.15s ease"
379
+ },
380
+ contextChunkCompact: {
381
+ display: "flex",
382
+ alignItems: "center",
383
+ gap: "6px",
384
+ padding: "2px 4px",
385
+ borderRadius: "3px",
386
+ fontSize: "10px",
387
+ cursor: "pointer",
388
+ transition: "opacity 0.15s ease"
389
+ },
390
+ contextChunkExcluded: {
391
+ opacity: 0.5,
392
+ textDecoration: "line-through"
393
+ },
394
+ contextCheckbox: {
395
+ width: "12px",
396
+ height: "12px",
397
+ accentColor: "var(--anyclick-menu-accent, #0066cc)"
398
+ },
399
+ contextLabel: {
400
+ flex: 1,
401
+ overflow: "hidden",
402
+ textOverflow: "ellipsis",
403
+ whiteSpace: "nowrap",
404
+ color: "var(--anyclick-menu-text, #333)"
405
+ },
406
+ contextSize: {
407
+ fontSize: "10px",
408
+ color: "var(--anyclick-menu-text-muted, #666)"
409
+ },
410
+ // Input area
411
+ inputContainer: {
412
+ display: "flex",
413
+ alignItems: "flex-end",
414
+ gap: "8px",
415
+ padding: "8px"
416
+ },
417
+ input: {
418
+ flex: 1,
419
+ padding: "8px 12px",
420
+ fontSize: "13px",
421
+ border: "1px solid var(--anyclick-menu-input-border, #ddd)",
422
+ borderRadius: "18px",
423
+ backgroundColor: "var(--anyclick-menu-input-bg, #fff)",
424
+ color: "var(--anyclick-menu-text, #333)",
425
+ outline: "none",
426
+ resize: "none",
427
+ fontFamily: "inherit",
428
+ lineHeight: 1.4,
429
+ maxHeight: "80px",
430
+ minHeight: "36px"
431
+ },
432
+ inputFocused: {
433
+ borderColor: "var(--anyclick-menu-accent, #0066cc)",
434
+ boxShadow: "0 0 0 2px rgba(0, 102, 204, 0.1)"
435
+ },
436
+ sendButton: {
437
+ display: "flex",
438
+ alignItems: "center",
439
+ justifyContent: "center",
440
+ width: "32px",
441
+ height: "32px",
442
+ border: "none",
443
+ borderRadius: "50%",
444
+ backgroundColor: "var(--anyclick-menu-accent, #0066cc)",
445
+ color: "#fff",
446
+ cursor: "pointer",
447
+ transition: "all 0.15s ease",
448
+ flexShrink: 0
449
+ },
450
+ sendButtonDisabled: {
451
+ backgroundColor: "var(--anyclick-menu-border, #e5e5e5)",
452
+ cursor: "not-allowed"
453
+ },
454
+ // Loading states
455
+ loadingDots: {
456
+ display: "flex",
457
+ gap: "4px",
458
+ padding: "8px"
459
+ },
460
+ loadingDot: {
461
+ width: "6px",
462
+ height: "6px",
463
+ borderRadius: "50%",
464
+ backgroundColor: "var(--anyclick-menu-text-muted, #666)",
465
+ animation: "bounce 1.4s infinite ease-in-out both"
466
+ },
467
+ // Error states
468
+ errorContainer: {
469
+ display: "flex",
470
+ flexDirection: "column",
471
+ alignItems: "center",
472
+ gap: "8px",
473
+ padding: "16px",
474
+ textAlign: "center"
475
+ },
476
+ errorIcon: {
477
+ color: "#ef4444"
478
+ },
479
+ errorText: {
480
+ fontSize: "12px",
481
+ color: "#ef4444",
482
+ maxWidth: "200px"
483
+ },
484
+ errorRetry: {
485
+ display: "inline-flex",
486
+ alignItems: "center",
487
+ gap: "4px",
488
+ padding: "4px 10px",
489
+ fontSize: "11px",
490
+ fontWeight: 500,
491
+ border: "1px solid #ef4444",
492
+ borderRadius: "4px",
493
+ backgroundColor: "transparent",
494
+ color: "#ef4444",
495
+ cursor: "pointer",
496
+ transition: "all 0.15s ease"
497
+ },
498
+ // Truncation indicator
499
+ truncated: {
500
+ fontSize: "10px",
501
+ color: "var(--anyclick-menu-text-muted, #666)",
502
+ fontStyle: "italic",
503
+ marginTop: "4px"
504
+ }
505
+ };
506
+ var quickChatKeyframes = `
507
+ @keyframes blink {
508
+ 0%, 50% { opacity: 1; }
509
+ 51%, 100% { opacity: 0; }
510
+ }
511
+
512
+ @keyframes bounce {
513
+ 0%, 80%, 100% { transform: scale(0); }
514
+ 40% { transform: scale(1); }
515
+ }
516
+
517
+ @keyframes slideIn {
518
+ from {
519
+ opacity: 0;
520
+ transform: translateY(-8px);
521
+ }
522
+ to {
523
+ opacity: 1;
524
+ transform: translateY(0);
525
+ }
526
+ }
527
+
528
+ @keyframes slideInFromRight {
529
+ from {
530
+ transform: translateX(100%);
531
+ }
532
+ to {
533
+ transform: translateX(0);
534
+ }
535
+ }
536
+
537
+ @keyframes slideOutToRight {
538
+ from {
539
+ transform: translateX(0);
540
+ }
541
+ to {
542
+ transform: translateX(100%);
543
+ }
544
+ }
545
+
546
+ @keyframes fadeIn {
547
+ from { opacity: 0; }
548
+ to { opacity: 1; }
549
+ }
550
+
551
+ @keyframes spin {
552
+ from { transform: rotate(0deg); }
553
+ to { transform: rotate(360deg); }
554
+ }
555
+
556
+ @keyframes pulse {
557
+ 0%, 100% { opacity: 1; }
558
+ 50% { opacity: 0.5; }
559
+ }
560
+ `;
561
+
562
+ // src/QuickChat/useQuickChat.ts
563
+ var import_react = require("react");
564
+ var import_react2 = require("@ai-sdk/react");
565
+ var import_ai = require("ai");
566
+
567
+ // src/QuickChat/store.ts
568
+ var import_zustand = require("zustand");
569
+ var import_middleware = require("zustand/middleware");
570
+ var STORE_NAME = "anyclick-quick-chat-store";
571
+ var TWENTY_FOUR_HOURS_MS = 24 * 60 * 60 * 1e3;
572
+ function generateMessageId() {
573
+ return `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
574
+ }
575
+ var storage = (0, import_middleware.createJSONStorage)(() => localStorage);
576
+ function filterExpiredMessages(messages) {
577
+ const now = Date.now();
578
+ return messages.filter((msg) => msg.expiresAt > now);
579
+ }
580
+ var useQuickChatStore = (0, import_zustand.create)()(
581
+ (0, import_middleware.persist)(
582
+ (set, get) => ({
583
+ messages: [],
584
+ isPinned: false,
585
+ input: "",
586
+ contextChunks: [],
587
+ suggestedPrompts: [],
588
+ isLoadingSuggestions: false,
589
+ isSending: false,
590
+ isStreaming: false,
591
+ error: null,
592
+ lastSyncedAt: null,
593
+ setInput: (input) => set({ input }),
594
+ addMessage: (message) => {
595
+ const id = generateMessageId();
596
+ const now = Date.now();
597
+ const storedMessage = {
598
+ ...message,
599
+ id,
600
+ timestamp: now,
601
+ expiresAt: now + TWENTY_FOUR_HOURS_MS
602
+ };
603
+ set((state) => {
604
+ const newMessages = [...state.messages, storedMessage];
605
+ return {
606
+ messages: newMessages
607
+ };
608
+ });
609
+ return id;
610
+ },
611
+ updateMessage: (id, updates) => {
612
+ set((state) => ({
613
+ messages: state.messages.map(
614
+ (msg) => msg.id === id ? { ...msg, ...updates } : msg
615
+ )
616
+ }));
617
+ },
618
+ clearMessages: () => set({ messages: [], error: null }),
619
+ setIsPinned: (pinned) => set({ isPinned: pinned }),
620
+ setContextChunks: (chunks) => set({ contextChunks: chunks }),
621
+ toggleChunk: (chunkId) => {
622
+ set((state) => ({
623
+ contextChunks: state.contextChunks.map(
624
+ (chunk) => chunk.id === chunkId ? { ...chunk, included: !chunk.included } : chunk
625
+ )
626
+ }));
627
+ },
628
+ toggleAllChunks: (included) => {
629
+ set((state) => ({
630
+ contextChunks: state.contextChunks.map((chunk) => ({
631
+ ...chunk,
632
+ included
633
+ }))
634
+ }));
635
+ },
636
+ setSuggestedPrompts: (prompts) => set({ suggestedPrompts: prompts }),
637
+ setIsLoadingSuggestions: (loading) => set({ isLoadingSuggestions: loading }),
638
+ setIsSending: (sending) => set({ isSending: sending }),
639
+ setIsStreaming: (streaming) => set({ isStreaming: streaming }),
640
+ setError: (error) => set({ error }),
641
+ setLastSyncedAt: (timestamp) => set({ lastSyncedAt: timestamp }),
642
+ pruneExpiredMessages: () => {
643
+ set((state) => ({
644
+ messages: filterExpiredMessages(state.messages)
645
+ }));
646
+ },
647
+ getActiveMessages: () => {
648
+ const state = get();
649
+ const now = Date.now();
650
+ const active = state.messages.filter((msg) => msg.expiresAt > now).map(({ expiresAt, ...msg }) => msg);
651
+ console.log("[store] getActiveMessages", {
652
+ totalMessages: state.messages.length,
653
+ activeMessages: active.length,
654
+ activeIds: active.map((m) => m.id)
655
+ });
656
+ return active;
657
+ },
658
+ hydrate: (messages) => {
659
+ const now = Date.now();
660
+ const storedMessages = messages.map((msg) => ({
661
+ ...msg,
662
+ expiresAt: now + TWENTY_FOUR_HOURS_MS
663
+ }));
664
+ set({ messages: storedMessages, lastSyncedAt: now });
665
+ }
666
+ }),
667
+ {
668
+ name: STORE_NAME,
669
+ storage,
670
+ partialize: (state) => ({
671
+ messages: state.messages,
672
+ isPinned: state.isPinned,
673
+ lastSyncedAt: state.lastSyncedAt
674
+ }),
675
+ onRehydrateStorage: () => (state) => {
676
+ if (state) {
677
+ state.pruneExpiredMessages();
678
+ }
679
+ }
680
+ }
681
+ )
682
+ );
683
+ function useActiveMessages() {
684
+ const messages = useQuickChatStore((state) => state.messages);
685
+ const now = Date.now();
686
+ return messages.filter((msg) => msg.expiresAt > now).map(({ expiresAt, ...msg }) => msg);
687
+ }
688
+
689
+ // src/QuickChat/types.ts
690
+ var DEFAULT_T3CHAT_CONFIG = {
691
+ enabled: true,
692
+ baseUrl: "https://t3.chat",
693
+ label: "Ask t3.chat"
694
+ };
695
+ var DEFAULT_QUICK_CHAT_CONFIG = {
696
+ endpoint: "/api/anyclick/chat",
697
+ model: "gpt-5-nano",
698
+ prePassModel: "gpt-5-nano",
699
+ maxResponseLength: 1e4,
700
+ showRedactionUI: true,
701
+ showSuggestions: true,
702
+ systemPrompt: "You are a helpful assistant that provides quick, concise answers about web elements and UI. Keep responses brief and actionable.",
703
+ placeholder: "Ask about this element...",
704
+ title: "Quick Ask",
705
+ t3chat: DEFAULT_T3CHAT_CONFIG
706
+ };
707
+
708
+ // src/QuickChat/useQuickChat.context.ts
709
+ var import_anyclick_core = require("@ewjdev/anyclick-core");
710
+ function extractContextChunks(targetElement, _containerElement) {
711
+ const chunks = [];
712
+ if (!targetElement) return chunks;
713
+ try {
714
+ const info = (0, import_anyclick_core.getElementInspectInfo)(targetElement);
715
+ const tagContent = `<${info.tagName.toLowerCase()}${info.id ? ` id="${info.id}"` : ""}${info.classNames.length > 0 ? ` class="${info.classNames.join(" ")}"` : ""}>`;
716
+ chunks.push({
717
+ id: "element-tag",
718
+ label: "Element Tag",
719
+ content: tagContent,
720
+ type: "element",
721
+ included: true,
722
+ size: tagContent.length
723
+ });
724
+ if (info.innerText && info.innerText.length > 0) {
725
+ const textContent = info.innerText.slice(0, 200);
726
+ chunks.push({
727
+ id: "element-text",
728
+ label: "Text Content",
729
+ content: textContent,
730
+ type: "text",
731
+ included: true,
732
+ size: textContent.length
733
+ });
734
+ }
735
+ if (info.computedStyles) {
736
+ const styleEntries = [];
737
+ for (const [, styles] of Object.entries(info.computedStyles)) {
738
+ if (styles && typeof styles === "object") {
739
+ const entries = Object.entries(styles).slice(0, 2);
740
+ for (const [k, v] of entries) {
741
+ if (v) styleEntries.push(`${k}: ${v}`);
742
+ }
743
+ }
744
+ if (styleEntries.length >= 8) break;
745
+ }
746
+ const stylesContent = styleEntries.join("; ");
747
+ if (stylesContent) {
748
+ chunks.push({
749
+ id: "element-styles",
750
+ label: "Key Styles",
751
+ content: stylesContent,
752
+ type: "element",
753
+ included: true,
754
+ size: stylesContent.length
755
+ });
756
+ }
757
+ }
758
+ if (info.accessibility) {
759
+ const a11yContent = [
760
+ info.accessibility.role && `role="${info.accessibility.role}"`,
761
+ info.accessibility.accessibleName && `aria-label="${info.accessibility.accessibleName}"`
762
+ ].filter(Boolean).join(", ");
763
+ if (a11yContent) {
764
+ chunks.push({
765
+ id: "element-a11y",
766
+ label: "Accessibility",
767
+ content: a11yContent,
768
+ type: "element",
769
+ included: true,
770
+ size: a11yContent.length
771
+ });
772
+ }
773
+ }
774
+ if (info.boxModel) {
775
+ const boxContent = `${Math.round(info.boxModel.content.width)}x${Math.round(
776
+ info.boxModel.content.height
777
+ )}px`;
778
+ chunks.push({
779
+ id: "element-dimensions",
780
+ label: "Dimensions",
781
+ content: boxContent,
782
+ type: "element",
783
+ included: true,
784
+ size: boxContent.length
785
+ });
786
+ }
787
+ } catch (error) {
788
+ console.error("[useQuickChat] Failed to extract context:", error);
789
+ }
790
+ return chunks;
791
+ }
792
+ function buildContextString(chunks) {
793
+ const included = chunks.filter((c) => c.included);
794
+ if (included.length === 0) return "";
795
+ return included.map((c) => `[${c.label}]: ${c.content}`).join("\n");
796
+ }
797
+
798
+ // src/QuickChat/useQuickChat.debug.ts
799
+ function createDebugInfo(args) {
800
+ return {
801
+ status: args.status,
802
+ ok: args.ok,
803
+ contentType: args.contentType ?? null,
804
+ rawTextPreview: args.rawText ?? "",
805
+ timestamp: Date.now(),
806
+ error: args.error
807
+ };
808
+ }
809
+
810
+ // src/QuickChat/useQuickChat.messages.ts
811
+ function getUIMessageText(msg) {
812
+ const partsText = msg.parts?.map((p) => {
813
+ const part = p;
814
+ if (typeof part.text === "string") return part.text;
815
+ if (typeof part.content === "string") return part.content;
816
+ return "";
817
+ }).join("") ?? "";
818
+ if (partsText) return partsText;
819
+ const maybeContent = msg.content;
820
+ return typeof maybeContent === "string" ? maybeContent : "";
821
+ }
822
+ function chatMessagesToUiMessages(messages) {
823
+ return messages.map((msg) => ({
824
+ id: msg.id,
825
+ role: msg.role,
826
+ parts: [{ type: "text", text: msg.content }]
827
+ }));
828
+ }
829
+ function safeCopyToClipboard(text) {
830
+ try {
831
+ if (typeof navigator === "undefined") return;
832
+ void navigator.clipboard.writeText(text);
833
+ } catch {
834
+ }
835
+ }
836
+ function buildAssistantActions(messageText, setInput) {
837
+ return [
838
+ {
839
+ id: "copy",
840
+ label: "Copy",
841
+ onClick: () => safeCopyToClipboard(messageText)
842
+ },
843
+ {
844
+ id: "research",
845
+ label: "Research more",
846
+ onClick: () => setInput(`Tell me more about: ${messageText.slice(0, 50)}`)
847
+ }
848
+ ];
849
+ }
850
+ function uiMessagesToChatMessages(args) {
851
+ const { uiMessages, status, setInput } = args;
852
+ const last = uiMessages[uiMessages.length - 1];
853
+ return uiMessages.map((msg) => {
854
+ const text = getUIMessageText(msg);
855
+ const isStreaming = status === "streaming" && msg.role === "assistant" && msg === last;
856
+ const actions = msg.role === "assistant" && status === "ready" ? buildAssistantActions(text, setInput) : void 0;
857
+ return {
858
+ id: msg.id,
859
+ role: msg.role,
860
+ content: text,
861
+ timestamp: Date.now(),
862
+ isStreaming,
863
+ actions
864
+ };
865
+ });
866
+ }
867
+
868
+ // src/QuickChat/useQuickChat.rateLimit.ts
869
+ function safeJsonParse(text) {
870
+ try {
871
+ return JSON.parse(text);
872
+ } catch {
873
+ return null;
874
+ }
875
+ }
876
+ function formatRetryAt(retryAtMs) {
877
+ try {
878
+ const d = new Date(retryAtMs);
879
+ return new Intl.DateTimeFormat(void 0, {
880
+ hour: "numeric",
881
+ minute: "2-digit"
882
+ }).format(d);
883
+ } catch {
884
+ return new Date(retryAtMs).toLocaleTimeString();
885
+ }
886
+ }
887
+ function getRequestIdFromHeaders(res) {
888
+ return res?.headers?.get?.("X-Anyclick-Request-Id") ?? res?.headers?.get?.("x-anyclick-request-id") ?? void 0;
889
+ }
890
+ function parseRetryAfterSeconds(args) {
891
+ const { payload, res } = args;
892
+ if (typeof payload?.retryAfterSeconds === "number")
893
+ return payload.retryAfterSeconds;
894
+ const headerRetryAfter = res?.headers?.get?.("Retry-After");
895
+ if (!headerRetryAfter) return void 0;
896
+ const n = Number(headerRetryAfter);
897
+ return Number.isFinite(n) ? n : void 0;
898
+ }
899
+ function parseRetryAt(args) {
900
+ const { payload, retryAfterSeconds, nowMs } = args;
901
+ if (typeof payload?.retryAt === "number" && Number.isFinite(payload.retryAt)) {
902
+ return payload.retryAt;
903
+ }
904
+ if (typeof retryAfterSeconds === "number" && Number.isFinite(retryAfterSeconds)) {
905
+ return nowMs + Math.max(0, retryAfterSeconds) * 1e3;
906
+ }
907
+ return void 0;
908
+ }
909
+ function buildNotice(args) {
910
+ const { rawText, endpoint, res, nowMs } = args;
911
+ const parsed = safeJsonParse(rawText);
912
+ const payload = parsed && typeof parsed === "object" ? parsed : null;
913
+ const retryAfterSeconds = parseRetryAfterSeconds({ payload, res });
914
+ const retryAt = parseRetryAt({ payload, retryAfterSeconds, nowMs });
915
+ const timePart = retryAt ? `Try again at ${formatRetryAt(retryAt)}.` : "";
916
+ const message = timePart ? `Rate limited. ${timePart}` : "Rate limited.";
917
+ const requestId = payload?.requestId ?? getRequestIdFromHeaders(res);
918
+ return {
919
+ status: 429,
920
+ message,
921
+ retryAt,
922
+ retryAfterSeconds,
923
+ requestId,
924
+ endpoint,
925
+ raw: rawText
926
+ };
927
+ }
928
+ async function rateLimitNoticeFromResponse(res, endpoint) {
929
+ if (res.status !== 429) return null;
930
+ const raw = await res.text().catch(() => "");
931
+ return buildNotice({ rawText: raw, endpoint, res, nowMs: Date.now() });
932
+ }
933
+ function rateLimitNoticeFromError(args) {
934
+ const { statusCode, response, responseText, endpoint } = args;
935
+ if (statusCode !== 429) return null;
936
+ const raw = responseText ?? "";
937
+ return buildNotice({
938
+ rawText: raw,
939
+ endpoint,
940
+ res: response,
941
+ nowMs: Date.now()
942
+ });
943
+ }
944
+
945
+ // src/QuickChat/useQuickChat.ts
946
+ function useQuickChat(targetElement, containerElement, config = {}) {
947
+ console.count("useQuickChat");
948
+ const mergedConfig = { ...DEFAULT_QUICK_CHAT_CONFIG, ...config };
949
+ const initializedRef = (0, import_react.useRef)(false);
950
+ const syncTimeoutRef = (0, import_react.useRef)(null);
951
+ const [manualSending, setManualSending] = (0, import_react.useState)(false);
952
+ const [debugInfo, setDebugInfo] = (0, import_react.useState)(null);
953
+ const [rateLimitNotice, setRateLimitNotice] = (0, import_react.useState)(null);
954
+ const {
955
+ input,
956
+ setInput,
957
+ isPinned,
958
+ setIsPinned,
959
+ contextChunks,
960
+ setContextChunks,
961
+ toggleChunk,
962
+ toggleAllChunks,
963
+ suggestedPrompts,
964
+ setSuggestedPrompts,
965
+ isLoadingSuggestions,
966
+ setIsLoadingSuggestions,
967
+ error,
968
+ setError,
969
+ addMessage,
970
+ clearMessages: storeClearMessages,
971
+ setLastSyncedAt,
972
+ lastSyncedAt,
973
+ setIsSending: setStoreIsSending,
974
+ setIsStreaming: setStoreIsStreaming
975
+ } = useQuickChatStore();
976
+ const storedMessages = useActiveMessages();
977
+ const contextString = (0, import_react.useMemo)(
978
+ () => buildContextString(contextChunks),
979
+ [contextChunks]
980
+ );
981
+ const chatBody = (0, import_react.useMemo)(
982
+ () => ({
983
+ action: "chat",
984
+ context: contextString,
985
+ model: mergedConfig.model,
986
+ systemPrompt: mergedConfig.systemPrompt,
987
+ maxLength: mergedConfig.maxResponseLength
988
+ }),
989
+ [
990
+ contextString,
991
+ mergedConfig.model,
992
+ mergedConfig.systemPrompt,
993
+ mergedConfig.maxResponseLength
994
+ ]
995
+ );
996
+ const transport = (0, import_react.useMemo)(
997
+ () => new import_ai.DefaultChatTransport({
998
+ api: mergedConfig.endpoint,
999
+ body: chatBody
1000
+ }),
1001
+ [mergedConfig.endpoint, chatBody]
1002
+ );
1003
+ const handleRateLimitResponse = (0, import_react.useCallback)(
1004
+ async (res, endpoint) => {
1005
+ const notice = await rateLimitNoticeFromResponse(res, endpoint);
1006
+ if (!notice) return false;
1007
+ setRateLimitNotice(notice);
1008
+ setError(null);
1009
+ return true;
1010
+ },
1011
+ [setError]
1012
+ );
1013
+ const scheduleBackendSync = (0, import_react.useCallback)(() => {
1014
+ if (syncTimeoutRef.current) clearTimeout(syncTimeoutRef.current);
1015
+ syncTimeoutRef.current = setTimeout(async () => {
1016
+ try {
1017
+ const currentMessages = useQuickChatStore.getState().getActiveMessages();
1018
+ if (currentMessages.length === 0) return;
1019
+ const endpoint = `${mergedConfig.endpoint}/history`;
1020
+ const res = await fetch(endpoint, {
1021
+ method: "POST",
1022
+ headers: { "Content-Type": "application/json" },
1023
+ body: JSON.stringify({
1024
+ action: "save",
1025
+ messages: currentMessages
1026
+ })
1027
+ });
1028
+ if (await handleRateLimitResponse(res, endpoint)) return;
1029
+ if (res.ok) setLastSyncedAt(Date.now());
1030
+ } catch (err) {
1031
+ console.error("[useQuickChat] Failed to sync chat history:", err);
1032
+ }
1033
+ }, 1e3);
1034
+ }, [handleRateLimitResponse, mergedConfig.endpoint, setLastSyncedAt]);
1035
+ const {
1036
+ messages: aiMessages,
1037
+ sendMessage: aiSendMessage,
1038
+ status,
1039
+ stop,
1040
+ setMessages: setAiMessages
1041
+ } = (0, import_react2.useChat)({
1042
+ transport,
1043
+ onError: (err) => {
1044
+ const response = err.response ?? void 0;
1045
+ const statusCode = err.status ?? (response ? response.status : void 0) ?? -1;
1046
+ const responseText = err.responseText ?? err.message ?? "";
1047
+ setDebugInfo(
1048
+ createDebugInfo({
1049
+ status: statusCode,
1050
+ ok: false,
1051
+ contentType: response?.headers?.get?.("content-type") ?? null,
1052
+ rawText: responseText,
1053
+ error: err.message
1054
+ })
1055
+ );
1056
+ setStoreIsStreaming(false);
1057
+ setStoreIsSending(false);
1058
+ const notice = rateLimitNoticeFromError({
1059
+ statusCode,
1060
+ response,
1061
+ responseText,
1062
+ endpoint: mergedConfig.endpoint
1063
+ });
1064
+ if (notice) {
1065
+ setRateLimitNotice(notice);
1066
+ setError(null);
1067
+ return;
1068
+ }
1069
+ setRateLimitNotice(null);
1070
+ setError(err.message);
1071
+ },
1072
+ onFinish: ({ message }) => {
1073
+ const messageText = getUIMessageText(message);
1074
+ const current = useQuickChatStore.getState().getActiveMessages();
1075
+ const last = current[current.length - 1];
1076
+ const alreadyHaveSameTail = last?.role === "assistant" && last.content === messageText;
1077
+ if (!alreadyHaveSameTail && messageText) {
1078
+ addMessage({
1079
+ role: message.role,
1080
+ content: messageText,
1081
+ isStreaming: false
1082
+ });
1083
+ }
1084
+ scheduleBackendSync();
1085
+ setStoreIsStreaming(false);
1086
+ setStoreIsSending(false);
1087
+ }
1088
+ });
1089
+ const loadFromBackend = (0, import_react.useCallback)(async () => {
1090
+ try {
1091
+ const endpoint = `${mergedConfig.endpoint}/history`;
1092
+ const response = await fetch(endpoint, {
1093
+ method: "POST",
1094
+ headers: { "Content-Type": "application/json" },
1095
+ body: JSON.stringify({ action: "load" })
1096
+ });
1097
+ if (await handleRateLimitResponse(response, endpoint)) return;
1098
+ if (!response.ok) return;
1099
+ const data = await response.json().catch(() => null);
1100
+ if (!data?.messages || !Array.isArray(data.messages)) return;
1101
+ if (data.messages.length === 0) return;
1102
+ useQuickChatStore.getState().hydrate(data.messages);
1103
+ setAiMessages(chatMessagesToUiMessages(data.messages));
1104
+ } catch (err) {
1105
+ console.error("[useQuickChat] Failed to load chat history:", err);
1106
+ }
1107
+ }, [handleRateLimitResponse, mergedConfig.endpoint, setAiMessages]);
1108
+ const messages = (0, import_react.useMemo)(
1109
+ () => uiMessagesToChatMessages({
1110
+ uiMessages: aiMessages,
1111
+ status,
1112
+ setInput
1113
+ }),
1114
+ [aiMessages, setInput, status]
1115
+ );
1116
+ const isStreaming = status === "streaming";
1117
+ const isSending = status === "submitted" || status === "streaming" || manualSending;
1118
+ (0, import_react.useEffect)(() => {
1119
+ if (initializedRef.current) return;
1120
+ initializedRef.current = true;
1121
+ if (storedMessages.length > 0) {
1122
+ setAiMessages(chatMessagesToUiMessages(storedMessages));
1123
+ } else {
1124
+ void loadFromBackend();
1125
+ }
1126
+ }, [loadFromBackend, setAiMessages, storedMessages]);
1127
+ (0, import_react.useEffect)(() => {
1128
+ if (!targetElement) return;
1129
+ const chunks = extractContextChunks(targetElement, containerElement);
1130
+ setContextChunks(chunks);
1131
+ }, [targetElement, containerElement, setContextChunks]);
1132
+ (0, import_react.useEffect)(() => {
1133
+ if (!mergedConfig.showSuggestions) return;
1134
+ if (!contextString) return;
1135
+ if (suggestedPrompts.length > 0) return;
1136
+ let cancelled = false;
1137
+ const fetchSuggestions = async () => {
1138
+ setIsLoadingSuggestions(true);
1139
+ try {
1140
+ const response = await fetch(mergedConfig.endpoint, {
1141
+ method: "POST",
1142
+ headers: { "Content-Type": "application/json" },
1143
+ body: JSON.stringify({
1144
+ action: "suggest",
1145
+ context: contextString,
1146
+ model: mergedConfig.prePassModel
1147
+ })
1148
+ });
1149
+ if (await handleRateLimitResponse(response, mergedConfig.endpoint)) {
1150
+ return;
1151
+ }
1152
+ if (response.ok) {
1153
+ const data = await response.json().catch(() => null);
1154
+ if (data?.suggestions && Array.isArray(data.suggestions)) {
1155
+ if (!cancelled) {
1156
+ setSuggestedPrompts(
1157
+ data.suggestions.map((text, i) => ({
1158
+ id: `suggestion-${i}`,
1159
+ text
1160
+ }))
1161
+ );
1162
+ setIsLoadingSuggestions(false);
1163
+ }
1164
+ return;
1165
+ }
1166
+ }
1167
+ } catch (err) {
1168
+ console.error("[useQuickChat] Failed to fetch suggestions:", err);
1169
+ }
1170
+ if (!cancelled) {
1171
+ setSuggestedPrompts([
1172
+ { id: "s1", text: "What is this element?" },
1173
+ { id: "s2", text: "How can I style this?" },
1174
+ { id: "s3", text: "Is this accessible?" }
1175
+ ]);
1176
+ setIsLoadingSuggestions(false);
1177
+ }
1178
+ };
1179
+ void fetchSuggestions();
1180
+ return () => {
1181
+ cancelled = true;
1182
+ };
1183
+ }, [
1184
+ contextString,
1185
+ handleRateLimitResponse,
1186
+ mergedConfig.endpoint,
1187
+ mergedConfig.prePassModel,
1188
+ mergedConfig.showSuggestions,
1189
+ setIsLoadingSuggestions,
1190
+ setSuggestedPrompts,
1191
+ suggestedPrompts.length
1192
+ ]);
1193
+ const selectSuggestion = (0, import_react.useCallback)(
1194
+ (prompt) => {
1195
+ setInput(prompt.text);
1196
+ },
1197
+ [setInput]
1198
+ );
1199
+ const sendMessage = (0, import_react.useCallback)(
1200
+ async (messageText) => {
1201
+ const text = (messageText || input).trim();
1202
+ if (!text) return;
1203
+ setInput("");
1204
+ setError(null);
1205
+ setManualSending(true);
1206
+ setStoreIsSending(true);
1207
+ setStoreIsStreaming(true);
1208
+ setDebugInfo(null);
1209
+ try {
1210
+ addMessage({
1211
+ role: "user",
1212
+ content: text
1213
+ });
1214
+ await aiSendMessage({ text });
1215
+ } catch (err) {
1216
+ const msg = err instanceof Error ? err.message : String(err);
1217
+ setError(msg);
1218
+ } finally {
1219
+ setManualSending(false);
1220
+ setStoreIsSending(false);
1221
+ setStoreIsStreaming(false);
1222
+ }
1223
+ },
1224
+ [
1225
+ addMessage,
1226
+ aiSendMessage,
1227
+ input,
1228
+ setError,
1229
+ setInput,
1230
+ setStoreIsSending,
1231
+ setStoreIsStreaming
1232
+ ]
1233
+ );
1234
+ const clearMessages = (0, import_react.useCallback)(() => {
1235
+ stop();
1236
+ storeClearMessages();
1237
+ setAiMessages([]);
1238
+ const endpoint = `${mergedConfig.endpoint}/history`;
1239
+ fetch(endpoint, {
1240
+ method: "POST",
1241
+ headers: { "Content-Type": "application/json" },
1242
+ body: JSON.stringify({ action: "clear" })
1243
+ }).then((res) => handleRateLimitResponse(res, endpoint)).catch(
1244
+ (err) => console.error("[useQuickChat] Failed to clear backend history:", err)
1245
+ );
1246
+ }, [
1247
+ handleRateLimitResponse,
1248
+ mergedConfig.endpoint,
1249
+ setAiMessages,
1250
+ stop,
1251
+ storeClearMessages
1252
+ ]);
1253
+ (0, import_react.useEffect)(() => {
1254
+ return () => {
1255
+ if (syncTimeoutRef.current) clearTimeout(syncTimeoutRef.current);
1256
+ };
1257
+ }, []);
1258
+ return {
1259
+ input,
1260
+ messages,
1261
+ isLoadingSuggestions,
1262
+ isSending,
1263
+ isStreaming,
1264
+ suggestedPrompts,
1265
+ contextChunks,
1266
+ error,
1267
+ debugInfo,
1268
+ rateLimitNotice,
1269
+ isPinned,
1270
+ lastSyncedAt,
1271
+ config: mergedConfig,
1272
+ setInput,
1273
+ toggleChunk,
1274
+ toggleAllChunks,
1275
+ selectSuggestion,
1276
+ sendMessage,
1277
+ clearMessages,
1278
+ setIsPinned,
1279
+ clearRateLimitNotice: () => setRateLimitNotice(null)
1280
+ };
1281
+ }
1282
+
1283
+ // src/QuickChat/QuickChat.tsx
1284
+ var import_jsx_runtime = require("react/jsx-runtime");
1285
+ var stylesInjected = false;
1286
+ function injectStyles() {
1287
+ if (stylesInjected || typeof document === "undefined") return;
1288
+ const style = document.createElement("style");
1289
+ style.textContent = quickChatKeyframes;
1290
+ document.head.appendChild(style);
1291
+ stylesInjected = true;
1292
+ }
1293
+ var LoadingDots = import_react3.default.memo(function LoadingDots2() {
1294
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: quickChatStyles.loadingDots, children: [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1295
+ "div",
1296
+ {
1297
+ style: {
1298
+ ...quickChatStyles.loadingDot,
1299
+ animationDelay: `${i * 0.16}s`
1300
+ }
1301
+ },
1302
+ i
1303
+ )) });
1304
+ });
1305
+ function QuickChat({
1306
+ visible,
1307
+ targetElement,
1308
+ containerElement,
1309
+ onClose,
1310
+ onPin,
1311
+ isPinned: isPinnedProp = false,
1312
+ config,
1313
+ style,
1314
+ className,
1315
+ initialInput,
1316
+ onInitialInputConsumed
1317
+ }) {
1318
+ const inputRef = (0, import_react3.useRef)(null);
1319
+ const messagesEndRef = (0, import_react3.useRef)(null);
1320
+ const [inputFocused, setInputFocused] = (0, import_react3.useState)(false);
1321
+ const [showContext, setShowContext] = (0, import_react3.useState)(false);
1322
+ const [hoveredSuggestion, setHoveredSuggestion] = (0, import_react3.useState)(
1323
+ null
1324
+ );
1325
+ const {
1326
+ input,
1327
+ messages,
1328
+ isLoadingSuggestions,
1329
+ isSending,
1330
+ isStreaming,
1331
+ debugInfo,
1332
+ rateLimitNotice,
1333
+ suggestedPrompts,
1334
+ contextChunks,
1335
+ error,
1336
+ isPinned: storePinned,
1337
+ setInput,
1338
+ toggleChunk,
1339
+ toggleAllChunks,
1340
+ selectSuggestion,
1341
+ sendMessage,
1342
+ clearMessages,
1343
+ setIsPinned,
1344
+ clearRateLimitNotice,
1345
+ config: mergedConfig
1346
+ } = useQuickChat(targetElement, containerElement, config);
1347
+ const isPinned = isPinnedProp || storePinned;
1348
+ const handlePinToggle = (0, import_react3.useCallback)(() => {
1349
+ const newPinned = !isPinned;
1350
+ setIsPinned(newPinned);
1351
+ onPin?.(newPinned);
1352
+ }, [isPinned, setIsPinned, onPin]);
1353
+ const handleClose = (0, import_react3.useCallback)(() => {
1354
+ if (isPinned) {
1355
+ setIsPinned(false);
1356
+ onPin?.(false);
1357
+ }
1358
+ onClose();
1359
+ }, [isPinned, setIsPinned, onPin, onClose]);
1360
+ (0, import_react3.useEffect)(() => {
1361
+ injectStyles();
1362
+ }, []);
1363
+ (0, import_react3.useEffect)(() => {
1364
+ if (visible && inputRef.current) {
1365
+ const timer = setTimeout(() => {
1366
+ inputRef.current?.focus();
1367
+ }, 100);
1368
+ return () => clearTimeout(timer);
1369
+ }
1370
+ }, [visible]);
1371
+ (0, import_react3.useEffect)(() => {
1372
+ if (initialInput && visible) {
1373
+ setInput(initialInput);
1374
+ onInitialInputConsumed?.();
1375
+ if (inputRef.current) {
1376
+ inputRef.current.focus();
1377
+ inputRef.current.setSelectionRange(
1378
+ initialInput.length,
1379
+ initialInput.length
1380
+ );
1381
+ }
1382
+ }
1383
+ }, [initialInput, visible, setInput, onInitialInputConsumed]);
1384
+ (0, import_react3.useEffect)(() => {
1385
+ if (messagesEndRef.current) {
1386
+ messagesEndRef.current.scrollIntoView({ behavior: "smooth" });
1387
+ }
1388
+ }, [messages]);
1389
+ const handleInputChange = (0, import_react3.useCallback)(
1390
+ (e) => {
1391
+ setInput(e.target.value);
1392
+ const target = e.target;
1393
+ target.style.height = "auto";
1394
+ target.style.height = `${Math.min(target.scrollHeight, 80)}px`;
1395
+ },
1396
+ [setInput]
1397
+ );
1398
+ const handleKeyDown = (0, import_react3.useCallback)(
1399
+ (e) => {
1400
+ if (e.key === "Enter" && !e.shiftKey) {
1401
+ e.preventDefault();
1402
+ sendMessage();
1403
+ } else if (e.key === "Escape") {
1404
+ onClose();
1405
+ }
1406
+ },
1407
+ [sendMessage, onClose]
1408
+ );
1409
+ const handleSend = (0, import_react3.useCallback)(() => {
1410
+ sendMessage();
1411
+ }, [sendMessage]);
1412
+ const handleSendToT3Chat = (0, import_react3.useCallback)(() => {
1413
+ if (typeof window === "undefined") return;
1414
+ const query = input.trim();
1415
+ const baseUrl = mergedConfig.t3chat?.baseUrl ?? "https://t3.chat";
1416
+ const url = query ? `${baseUrl}/?q=${encodeURIComponent(query)}` : baseUrl;
1417
+ window.open(url, "_blank", "noopener,noreferrer");
1418
+ }, [input, mergedConfig.t3chat?.baseUrl]);
1419
+ const handleCopy = (0, import_react3.useCallback)((text) => {
1420
+ navigator.clipboard.writeText(text);
1421
+ }, []);
1422
+ const includedCount = (0, import_react3.useMemo)(
1423
+ () => contextChunks.filter((c) => c.included).length,
1424
+ [contextChunks]
1425
+ );
1426
+ const [rateLimitExpanded, setRateLimitExpanded] = (0, import_react3.useState)(false);
1427
+ const [reportStatus, setReportStatus] = (0, import_react3.useState)("idle");
1428
+ const [reportUrl, setReportUrl] = (0, import_react3.useState)(null);
1429
+ const [reportError, setReportError] = (0, import_react3.useState)(null);
1430
+ (0, import_react3.useEffect)(() => {
1431
+ if (rateLimitNotice) {
1432
+ setReportStatus("idle");
1433
+ setReportUrl(null);
1434
+ setReportError(null);
1435
+ setRateLimitExpanded(false);
1436
+ }
1437
+ }, [rateLimitNotice]);
1438
+ const handleReportIssue = (0, import_react3.useCallback)(async () => {
1439
+ if (!rateLimitNotice) return;
1440
+ if (!targetElement) {
1441
+ setReportStatus("error");
1442
+ setReportError("No target element available to report.");
1443
+ return;
1444
+ }
1445
+ setReportStatus("sending");
1446
+ setReportError(null);
1447
+ try {
1448
+ const payload = (0, import_anyclick_core2.buildAnyclickPayload)(targetElement, "issue", {
1449
+ comment: `QuickChat: ${rateLimitNotice.message}`,
1450
+ metadata: {
1451
+ source: "quickchat",
1452
+ kind: "rate_limit",
1453
+ endpoint: rateLimitNotice.endpoint ?? mergedConfig.endpoint,
1454
+ retryAt: rateLimitNotice.retryAt,
1455
+ retryAfterSeconds: rateLimitNotice.retryAfterSeconds,
1456
+ requestId: rateLimitNotice.requestId,
1457
+ debugInfo: debugInfo ?? void 0,
1458
+ raw: rateLimitNotice.raw ?? void 0
1459
+ }
1460
+ });
1461
+ const res = await fetch("/api/feedback", {
1462
+ method: "POST",
1463
+ headers: { "Content-Type": "application/json" },
1464
+ body: JSON.stringify(payload)
1465
+ });
1466
+ const json = await res.json().catch(() => null);
1467
+ if (!res.ok || !json?.success) {
1468
+ const msg = json?.error || (res.status ? `Failed to create issue (${res.status}).` : "Failed to create issue.");
1469
+ throw new Error(msg);
1470
+ }
1471
+ const firstUrl = json.results?.find(
1472
+ (r) => typeof r.url === "string"
1473
+ )?.url;
1474
+ setReportUrl(firstUrl ?? null);
1475
+ setReportStatus("sent");
1476
+ } catch (e) {
1477
+ setReportStatus("error");
1478
+ setReportError(e instanceof Error ? e.message : String(e));
1479
+ }
1480
+ }, [rateLimitNotice, targetElement, mergedConfig.endpoint, debugInfo]);
1481
+ if (!visible) return null;
1482
+ const containerStyles = isPinned ? {
1483
+ ...quickChatStyles.pinnedContainer,
1484
+ animation: "slideInFromRight 0.25s ease-out",
1485
+ ...style
1486
+ } : {
1487
+ ...quickChatStyles.container,
1488
+ animation: "fadeIn 0.15s ease-out",
1489
+ ...style
1490
+ };
1491
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className, style: containerStyles, children: [
1492
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1493
+ "div",
1494
+ {
1495
+ style: {
1496
+ ...quickChatStyles.header,
1497
+ padding: isPinned ? "12px 12px 8px 12px" : "6px 8px"
1498
+ },
1499
+ children: [
1500
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "4px" }, children: [
1501
+ !isPinned && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1502
+ "button",
1503
+ {
1504
+ type: "button",
1505
+ onClick: onClose,
1506
+ style: {
1507
+ ...quickChatStyles.iconButton,
1508
+ marginLeft: "-4px"
1509
+ },
1510
+ title: "Back to menu",
1511
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.ChevronLeft, { size: 16 })
1512
+ }
1513
+ ),
1514
+ mergedConfig.showRedactionUI && contextChunks.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1515
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1516
+ "button",
1517
+ {
1518
+ type: "button",
1519
+ onClick: () => setShowContext(!showContext),
1520
+ style: {
1521
+ ...quickChatStyles.contextBadge,
1522
+ ...showContext ? quickChatStyles.contextBadgeActive : {}
1523
+ },
1524
+ title: "Edit context",
1525
+ children: [
1526
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
1527
+ includedCount,
1528
+ "/",
1529
+ contextChunks.length
1530
+ ] }),
1531
+ showContext ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.ChevronUp, { size: 10 }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.ChevronDown, { size: 10 })
1532
+ ]
1533
+ }
1534
+ ),
1535
+ showContext && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", gap: "2px" }, children: [
1536
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1537
+ "button",
1538
+ {
1539
+ type: "button",
1540
+ onClick: () => toggleAllChunks(true),
1541
+ style: quickChatStyles.contextToggleSmall,
1542
+ title: "Include all",
1543
+ children: "All"
1544
+ }
1545
+ ),
1546
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1547
+ "button",
1548
+ {
1549
+ type: "button",
1550
+ onClick: () => toggleAllChunks(false),
1551
+ style: quickChatStyles.contextToggleSmall,
1552
+ title: "Exclude all",
1553
+ children: "None"
1554
+ }
1555
+ )
1556
+ ] })
1557
+ ] })
1558
+ ] }),
1559
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: quickChatStyles.headerActions, children: [
1560
+ messages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1561
+ "button",
1562
+ {
1563
+ type: "button",
1564
+ onClick: clearMessages,
1565
+ style: quickChatStyles.iconButton,
1566
+ title: "Clear chat",
1567
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.RefreshCw, { size: 14 })
1568
+ }
1569
+ ),
1570
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1571
+ "button",
1572
+ {
1573
+ type: "button",
1574
+ onClick: handlePinToggle,
1575
+ style: {
1576
+ ...quickChatStyles.iconButton,
1577
+ ...isPinned ? quickChatStyles.iconButtonActive : {}
1578
+ },
1579
+ title: isPinned ? "Unpin (closes with menu)" : "Pin (stays open)",
1580
+ children: isPinned ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.PinOff, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Pin, { size: 14 })
1581
+ }
1582
+ ),
1583
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1584
+ "button",
1585
+ {
1586
+ type: "button",
1587
+ onClick: handleClose,
1588
+ style: quickChatStyles.iconButton,
1589
+ title: "Close",
1590
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.X, { size: 14 })
1591
+ }
1592
+ )
1593
+ ] })
1594
+ ]
1595
+ }
1596
+ ),
1597
+ showContext && contextChunks.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: quickChatStyles.contextDropdown, children: contextChunks.map((chunk) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1598
+ "label",
1599
+ {
1600
+ style: {
1601
+ ...quickChatStyles.contextChunkCompact,
1602
+ ...chunk.included ? {} : quickChatStyles.contextChunkExcluded
1603
+ },
1604
+ children: [
1605
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1606
+ "input",
1607
+ {
1608
+ type: "checkbox",
1609
+ checked: chunk.included,
1610
+ onChange: () => toggleChunk(chunk.id),
1611
+ style: quickChatStyles.contextCheckbox
1612
+ }
1613
+ ),
1614
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: quickChatStyles.contextLabel, children: chunk.label })
1615
+ ]
1616
+ },
1617
+ chunk.id
1618
+ )) }),
1619
+ mergedConfig.showSuggestions && messages.length === 0 && suggestedPrompts.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: quickChatStyles.suggestionsContainer, children: isLoadingSuggestions ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LoadingDots, {}) : suggestedPrompts.map((prompt) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1620
+ "button",
1621
+ {
1622
+ type: "button",
1623
+ onClick: () => selectSuggestion(prompt),
1624
+ onMouseEnter: () => setHoveredSuggestion(prompt.id),
1625
+ onMouseLeave: () => setHoveredSuggestion(null),
1626
+ style: {
1627
+ ...quickChatStyles.suggestionChip,
1628
+ ...hoveredSuggestion === prompt.id ? quickChatStyles.suggestionChipHover : {}
1629
+ },
1630
+ children: prompt.text
1631
+ },
1632
+ prompt.id
1633
+ )) }),
1634
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1635
+ "div",
1636
+ {
1637
+ style: isPinned ? quickChatStyles.pinnedMessagesArea : quickChatStyles.messagesArea,
1638
+ children: [
1639
+ error && !rateLimitNotice && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: quickChatStyles.errorContainer, children: [
1640
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.AlertCircle, { size: 20, style: quickChatStyles.errorIcon }),
1641
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: quickChatStyles.errorText, children: error }),
1642
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1643
+ "button",
1644
+ {
1645
+ type: "button",
1646
+ onClick: () => sendMessage(),
1647
+ style: quickChatStyles.errorRetry,
1648
+ children: [
1649
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.RefreshCw, { size: 10 }),
1650
+ "Retry"
1651
+ ]
1652
+ }
1653
+ )
1654
+ ] }),
1655
+ debugInfo && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1656
+ "div",
1657
+ {
1658
+ style: {
1659
+ backgroundColor: "#0f172a",
1660
+ color: "#e2e8f0",
1661
+ border: "1px solid #334155",
1662
+ borderRadius: "8px",
1663
+ padding: "8px",
1664
+ margin: "0 0 8px",
1665
+ fontSize: "12px",
1666
+ lineHeight: 1.4,
1667
+ wordBreak: "break-word"
1668
+ },
1669
+ children: [
1670
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1671
+ "div",
1672
+ {
1673
+ style: {
1674
+ display: "flex",
1675
+ justifyContent: "space-between",
1676
+ gap: "8px"
1677
+ },
1678
+ children: [
1679
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
1680
+ "Last request: ",
1681
+ debugInfo.status,
1682
+ " ",
1683
+ debugInfo.ok ? "(ok)" : "(error)"
1684
+ ] }),
1685
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { opacity: 0.7 }, children: new Date(debugInfo.timestamp).toLocaleTimeString() })
1686
+ ]
1687
+ }
1688
+ ),
1689
+ debugInfo.error && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { color: "#f87171", marginTop: "4px" }, children: [
1690
+ "Error: ",
1691
+ debugInfo.error
1692
+ ] }),
1693
+ debugInfo.contentPreview && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { marginTop: "4px" }, children: [
1694
+ "Content: ",
1695
+ debugInfo.contentPreview
1696
+ ] }),
1697
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { marginTop: "4px", opacity: 0.8 }, children: [
1698
+ "Raw: ",
1699
+ debugInfo.rawTextPreview || "(empty)"
1700
+ ] })
1701
+ ]
1702
+ }
1703
+ ),
1704
+ messages.length > 0 && messages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1705
+ "div",
1706
+ {
1707
+ style: {
1708
+ ...quickChatStyles.message,
1709
+ animation: "fadeIn 0.2s ease-out"
1710
+ },
1711
+ children: [
1712
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1713
+ "div",
1714
+ {
1715
+ style: message.role === "user" ? quickChatStyles.messageUser : quickChatStyles.messageAssistant,
1716
+ children: [
1717
+ message.content,
1718
+ message.isStreaming && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: quickChatStyles.streamingIndicator }),
1719
+ message.role === "assistant" && !message.isStreaming && message.content.endsWith("...") && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: quickChatStyles.truncated, children: "(truncated)" })
1720
+ ]
1721
+ }
1722
+ ),
1723
+ message.role === "assistant" && !message.isStreaming && message.content && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: quickChatStyles.messageActions, children: [
1724
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1725
+ "button",
1726
+ {
1727
+ type: "button",
1728
+ onClick: () => handleCopy(message.content),
1729
+ style: quickChatStyles.actionButton,
1730
+ children: [
1731
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Copy, { size: 10 }),
1732
+ "Copy"
1733
+ ]
1734
+ }
1735
+ ),
1736
+ message.actions?.map((action) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1737
+ "button",
1738
+ {
1739
+ type: "button",
1740
+ onClick: action.onClick,
1741
+ style: quickChatStyles.actionButton,
1742
+ children: [
1743
+ action.icon,
1744
+ action.label
1745
+ ]
1746
+ },
1747
+ action.id
1748
+ ))
1749
+ ] })
1750
+ ]
1751
+ },
1752
+ message.id
1753
+ )),
1754
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref: messagesEndRef })
1755
+ ]
1756
+ }
1757
+ ),
1758
+ rateLimitNotice && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1759
+ "div",
1760
+ {
1761
+ style: {
1762
+ borderTop: "1px solid rgba(148, 163, 184, 0.25)",
1763
+ background: "linear-gradient(180deg, rgba(15, 23, 42, 0.92), rgba(15, 23, 42, 0.96))",
1764
+ color: "#e2e8f0",
1765
+ padding: "8px 10px"
1766
+ },
1767
+ children: [
1768
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1769
+ "div",
1770
+ {
1771
+ style: {
1772
+ display: "flex",
1773
+ alignItems: "center",
1774
+ justifyContent: "space-between",
1775
+ gap: "8px"
1776
+ },
1777
+ children: [
1778
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
1779
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.AlertCircle, { size: 16, style: { color: "#fbbf24" } }),
1780
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { fontSize: "13px", lineHeight: 1.2 }, children: rateLimitNotice.message })
1781
+ ] }),
1782
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "6px" }, children: [
1783
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1784
+ "button",
1785
+ {
1786
+ type: "button",
1787
+ onClick: () => setRateLimitExpanded((v) => !v),
1788
+ style: {
1789
+ border: "1px solid rgba(148, 163, 184, 0.25)",
1790
+ background: "rgba(30, 41, 59, 0.6)",
1791
+ color: "#e2e8f0",
1792
+ borderRadius: "6px",
1793
+ padding: "4px 8px",
1794
+ fontSize: "12px",
1795
+ cursor: "pointer"
1796
+ },
1797
+ children: rateLimitExpanded ? "Hide" : "Details"
1798
+ }
1799
+ ),
1800
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1801
+ "button",
1802
+ {
1803
+ type: "button",
1804
+ onClick: handleReportIssue,
1805
+ disabled: reportStatus === "sending" || reportStatus === "sent",
1806
+ style: {
1807
+ border: "1px solid rgba(148, 163, 184, 0.25)",
1808
+ background: reportStatus === "sent" ? "rgba(34, 197, 94, 0.22)" : "rgba(30, 41, 59, 0.6)",
1809
+ color: "#e2e8f0",
1810
+ borderRadius: "6px",
1811
+ padding: "4px 8px",
1812
+ fontSize: "12px",
1813
+ cursor: reportStatus === "sending" || reportStatus === "sent" ? "not-allowed" : "pointer",
1814
+ opacity: reportStatus === "sending" ? 0.7 : 1
1815
+ },
1816
+ title: "Create a GitHub issue via /api/feedback",
1817
+ children: reportStatus === "sending" ? "Reporting..." : reportStatus === "sent" ? "Reported" : "Report"
1818
+ }
1819
+ ),
1820
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1821
+ "button",
1822
+ {
1823
+ type: "button",
1824
+ onClick: () => {
1825
+ clearRateLimitNotice();
1826
+ setRateLimitExpanded(false);
1827
+ },
1828
+ style: {
1829
+ border: "none",
1830
+ background: "transparent",
1831
+ color: "rgba(226, 232, 240, 0.8)",
1832
+ padding: "4px",
1833
+ cursor: "pointer"
1834
+ },
1835
+ title: "Dismiss",
1836
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.X, { size: 14 })
1837
+ }
1838
+ )
1839
+ ] })
1840
+ ]
1841
+ }
1842
+ ),
1843
+ reportUrl && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { marginTop: "6px", fontSize: "12px" }, children: [
1844
+ "Created:",
1845
+ " ",
1846
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1847
+ "a",
1848
+ {
1849
+ href: reportUrl,
1850
+ target: "_blank",
1851
+ rel: "noopener noreferrer",
1852
+ style: { color: "#93c5fd" },
1853
+ children: "Open issue"
1854
+ }
1855
+ )
1856
+ ] }),
1857
+ reportError && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1858
+ "div",
1859
+ {
1860
+ style: { marginTop: "6px", fontSize: "12px", color: "#fca5a5" },
1861
+ children: reportError
1862
+ }
1863
+ ),
1864
+ rateLimitExpanded && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1865
+ "div",
1866
+ {
1867
+ style: {
1868
+ marginTop: "8px",
1869
+ fontSize: "12px",
1870
+ lineHeight: 1.4,
1871
+ backgroundColor: "rgba(2, 6, 23, 0.55)",
1872
+ border: "1px solid rgba(148, 163, 184, 0.25)",
1873
+ borderRadius: "8px",
1874
+ padding: "8px",
1875
+ wordBreak: "break-word"
1876
+ },
1877
+ children: [
1878
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { opacity: 0.85 }, children: [
1879
+ "Status: ",
1880
+ rateLimitNotice.status,
1881
+ rateLimitNotice.requestId ? ` \u2022 Request: ${rateLimitNotice.requestId}` : ""
1882
+ ] }),
1883
+ rateLimitNotice.endpoint && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { opacity: 0.75, marginTop: "4px" }, children: [
1884
+ "Endpoint: ",
1885
+ rateLimitNotice.endpoint
1886
+ ] }),
1887
+ rateLimitNotice.retryAt && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { opacity: 0.75, marginTop: "4px" }, children: [
1888
+ "RetryAt: ",
1889
+ new Date(rateLimitNotice.retryAt).toLocaleString()
1890
+ ] }),
1891
+ rateLimitNotice.raw && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { marginTop: "6px", opacity: 0.85 }, children: [
1892
+ "Raw: ",
1893
+ rateLimitNotice.raw
1894
+ ] }),
1895
+ debugInfo && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { marginTop: "6px", opacity: 0.85 }, children: [
1896
+ "Debug: ",
1897
+ debugInfo.rawTextPreview || "(empty)"
1898
+ ] })
1899
+ ]
1900
+ }
1901
+ )
1902
+ ]
1903
+ }
1904
+ ),
1905
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: quickChatStyles.inputContainer, children: [
1906
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1907
+ "textarea",
1908
+ {
1909
+ ref: inputRef,
1910
+ value: input,
1911
+ onChange: handleInputChange,
1912
+ onKeyDown: handleKeyDown,
1913
+ onFocus: () => setInputFocused(true),
1914
+ onBlur: () => setInputFocused(false),
1915
+ placeholder: mergedConfig.placeholder,
1916
+ disabled: isSending,
1917
+ rows: 1,
1918
+ style: {
1919
+ ...quickChatStyles.input,
1920
+ ...inputFocused ? quickChatStyles.inputFocused : {}
1921
+ }
1922
+ }
1923
+ ),
1924
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", gap: "4px" }, children: [
1925
+ mergedConfig.t3chat?.enabled !== false && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1926
+ "button",
1927
+ {
1928
+ type: "button",
1929
+ onClick: handleSendToT3Chat,
1930
+ disabled: !input.trim(),
1931
+ title: mergedConfig.t3chat?.label ?? "Ask t3.chat",
1932
+ style: {
1933
+ ...quickChatStyles.sendButton,
1934
+ backgroundColor: "#7c3aed",
1935
+ ...!input.trim() ? quickChatStyles.sendButtonDisabled : {}
1936
+ },
1937
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.ExternalLink, { size: 14 })
1938
+ }
1939
+ ),
1940
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1941
+ "button",
1942
+ {
1943
+ type: "button",
1944
+ onClick: handleSend,
1945
+ disabled: isSending || !input.trim(),
1946
+ style: {
1947
+ ...quickChatStyles.sendButton,
1948
+ ...isSending || !input.trim() ? quickChatStyles.sendButtonDisabled : {}
1949
+ },
1950
+ children: isSending ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1951
+ "div",
1952
+ {
1953
+ style: {
1954
+ width: "14px",
1955
+ height: "14px",
1956
+ border: "2px solid transparent",
1957
+ borderTopColor: "#fff",
1958
+ borderRadius: "50%",
1959
+ animation: "spin 0.8s linear infinite"
1960
+ }
1961
+ }
1962
+ ) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Send, { size: 14 })
1963
+ }
1964
+ )
1965
+ ] })
1966
+ ] })
1967
+ ] });
1968
+ }
102
1969
 
103
1970
  // src/ScreenshotPreview.tsx
104
- var import_react = __toESM(require("react"));
105
- var import_anyclick_core = require("@ewjdev/anyclick-core");
106
- var import_lucide_react = require("lucide-react");
1971
+ var import_react4 = __toESM(require("react"));
1972
+ var import_anyclick_core3 = require("@ewjdev/anyclick-core");
1973
+ var import_lucide_react2 = require("lucide-react");
107
1974
 
108
1975
  // src/styles.ts
109
1976
  var menuCSSVariables = {
@@ -578,8 +2445,8 @@ var screenshotPreviewStyles = {
578
2445
  };
579
2446
 
580
2447
  // src/ScreenshotPreview.tsx
581
- var import_jsx_runtime = require("react/jsx-runtime");
582
- var ScreenshotPreview = import_react.default.memo(function ScreenshotPreview2({
2448
+ var import_jsx_runtime2 = require("react/jsx-runtime");
2449
+ var ScreenshotPreview = import_react4.default.memo(function ScreenshotPreview2({
583
2450
  isLoading,
584
2451
  isSubmitting,
585
2452
  onCancel,
@@ -587,12 +2454,12 @@ var ScreenshotPreview = import_react.default.memo(function ScreenshotPreview2({
587
2454
  onRetake,
588
2455
  screenshots
589
2456
  }) {
590
- const [activeTab, setActiveTab] = (0, import_react.useState)("element");
591
- const [isExpanded, setIsExpanded] = (0, import_react.useState)(false);
2457
+ const [activeTab, setActiveTab] = (0, import_react4.useState)("element");
2458
+ const [isExpanded, setIsExpanded] = (0, import_react4.useState)(false);
592
2459
  const getError = (key) => {
593
2460
  return screenshots?.errors?.[key];
594
2461
  };
595
- const tabs = (0, import_react.useMemo)(() => {
2462
+ const tabs = (0, import_react4.useMemo)(() => {
596
2463
  if (!screenshots) return [];
597
2464
  const allTabs = [
598
2465
  {
@@ -616,29 +2483,29 @@ var ScreenshotPreview = import_react.default.memo(function ScreenshotPreview2({
616
2483
  ];
617
2484
  return allTabs.filter((tab) => tab.data || tab.error);
618
2485
  }, [screenshots]);
619
- const totalSize = (0, import_react.useMemo)(
620
- () => screenshots ? (0, import_anyclick_core.estimateTotalSize)(screenshots) : 0,
2486
+ const totalSize = (0, import_react4.useMemo)(
2487
+ () => screenshots ? (0, import_anyclick_core3.estimateTotalSize)(screenshots) : 0,
621
2488
  [screenshots]
622
2489
  );
623
2490
  if (isLoading) {
624
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: screenshotPreviewStyles.container, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: screenshotPreviewStyles.loadingContainer, children: [
625
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
626
- import_lucide_react.Loader2Icon,
2491
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: screenshotPreviewStyles.container, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: screenshotPreviewStyles.loadingContainer, children: [
2492
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2493
+ import_lucide_react2.Loader2Icon,
627
2494
  {
628
2495
  className: "w-6 h-6 animate-spin",
629
2496
  style: { color: "#3b82f6" }
630
2497
  }
631
2498
  ),
632
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: screenshotPreviewStyles.loadingText, children: "Capturing screenshots..." })
2499
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: screenshotPreviewStyles.loadingText, children: "Capturing screenshots..." })
633
2500
  ] }) });
634
2501
  }
635
2502
  if (!screenshots) {
636
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: screenshotPreviewStyles.container, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: screenshotPreviewStyles.emptyContainer, children: [
637
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.ImageIcon, { className: "w-8 h-8", style: { color: "#9ca3af" } }),
638
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: screenshotPreviewStyles.emptyText, children: "Screenshots unavailable" }),
639
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: screenshotPreviewStyles.emptySubtext, children: "Some elements can't be captured (e.g., gradient text)" }),
640
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: screenshotPreviewStyles.emptyActions, children: [
641
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
2503
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: screenshotPreviewStyles.container, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: screenshotPreviewStyles.emptyContainer, children: [
2504
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.ImageIcon, { className: "w-8 h-8", style: { color: "#9ca3af" } }),
2505
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: screenshotPreviewStyles.emptyText, children: "Screenshots unavailable" }),
2506
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: screenshotPreviewStyles.emptySubtext, children: "Some elements can't be captured (e.g., gradient text)" }),
2507
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: screenshotPreviewStyles.emptyActions, children: [
2508
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
642
2509
  "button",
643
2510
  {
644
2511
  type: "button",
@@ -646,12 +2513,12 @@ var ScreenshotPreview = import_react.default.memo(function ScreenshotPreview2({
646
2513
  onClick: onRetake,
647
2514
  style: screenshotPreviewStyles.retakeButtonOutline,
648
2515
  children: [
649
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.RefreshCwIcon, { className: "w-4 h-4" }),
2516
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.RefreshCwIcon, { className: "w-4 h-4" }),
650
2517
  "Try Again"
651
2518
  ]
652
2519
  }
653
2520
  ),
654
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
2521
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
655
2522
  "button",
656
2523
  {
657
2524
  type: "button",
@@ -659,7 +2526,7 @@ var ScreenshotPreview = import_react.default.memo(function ScreenshotPreview2({
659
2526
  onClick: () => onConfirm({ capturedAt: (/* @__PURE__ */ new Date()).toISOString() }),
660
2527
  style: screenshotPreviewStyles.continueButton,
661
2528
  children: [
662
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.CheckIcon, { className: "w-4 h-4" }),
2529
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.CheckIcon, { className: "w-4 h-4" }),
663
2530
  "Continue Without"
664
2531
  ]
665
2532
  }
@@ -669,7 +2536,7 @@ var ScreenshotPreview = import_react.default.memo(function ScreenshotPreview2({
669
2536
  }
670
2537
  const activeScreenshot = activeTab === "element" ? screenshots.element : activeTab === "container" ? screenshots.container : screenshots.viewport;
671
2538
  const activeError = getError(activeTab);
672
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
2539
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
673
2540
  "div",
674
2541
  {
675
2542
  style: {
@@ -678,23 +2545,23 @@ var ScreenshotPreview = import_react.default.memo(function ScreenshotPreview2({
678
2545
  padding: "8px"
679
2546
  },
680
2547
  children: [
681
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: screenshotPreviewStyles.header, children: [
682
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: screenshotPreviewStyles.headerTitle, children: "Review Screenshots" }),
683
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: screenshotPreviewStyles.headerActions, children: [
684
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: screenshotPreviewStyles.sizeLabel, children: (0, import_anyclick_core.formatBytes)(totalSize) }),
685
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2548
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: screenshotPreviewStyles.header, children: [
2549
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: screenshotPreviewStyles.headerTitle, children: "Review Screenshots" }),
2550
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: screenshotPreviewStyles.headerActions, children: [
2551
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: screenshotPreviewStyles.sizeLabel, children: (0, import_anyclick_core3.formatBytes)(totalSize) }),
2552
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
686
2553
  "button",
687
2554
  {
688
2555
  type: "button",
689
2556
  onClick: () => setIsExpanded(!isExpanded),
690
2557
  style: screenshotPreviewStyles.iconButton,
691
2558
  title: isExpanded ? "Collapse" : "Expand",
692
- children: isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.ShrinkIcon, { className: "w-4 h-4" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.ExpandIcon, { className: "w-4 h-4" })
2559
+ children: isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.ShrinkIcon, { className: "w-4 h-4" }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.ExpandIcon, { className: "w-4 h-4" })
693
2560
  }
694
2561
  )
695
2562
  ] })
696
2563
  ] }),
697
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: screenshotPreviewStyles.tabContainer, children: tabs.map((tab) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
2564
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: screenshotPreviewStyles.tabContainer, children: tabs.map((tab) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
698
2565
  "button",
699
2566
  {
700
2567
  type: "button",
@@ -705,40 +2572,40 @@ var ScreenshotPreview = import_react.default.memo(function ScreenshotPreview2({
705
2572
  ...tab.error && !tab.data ? screenshotPreviewStyles.tabError : {}
706
2573
  },
707
2574
  children: [
708
- tab.error && !tab.data && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
709
- import_lucide_react.AlertCircleIcon,
2575
+ tab.error && !tab.data && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2576
+ import_lucide_react2.AlertCircleIcon,
710
2577
  {
711
2578
  className: "w-3 h-3",
712
2579
  style: { color: "#ef4444" }
713
2580
  }
714
2581
  ),
715
2582
  tab.label,
716
- tab.data && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: screenshotPreviewStyles.tabSize, children: (0, import_anyclick_core.formatBytes)(tab.data.sizeBytes) })
2583
+ tab.data && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: screenshotPreviewStyles.tabSize, children: (0, import_anyclick_core3.formatBytes)(tab.data.sizeBytes) })
717
2584
  ]
718
2585
  },
719
2586
  tab.key
720
2587
  )) }),
721
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2588
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
722
2589
  "div",
723
2590
  {
724
2591
  style: {
725
2592
  ...screenshotPreviewStyles.previewContainer,
726
2593
  ...isExpanded ? screenshotPreviewStyles.previewContainerExpanded : {}
727
2594
  },
728
- children: activeScreenshot ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2595
+ children: activeScreenshot ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
729
2596
  "img",
730
2597
  {
731
2598
  alt: `${activeTab} screenshot`,
732
2599
  src: activeScreenshot.dataUrl,
733
2600
  style: screenshotPreviewStyles.previewImage
734
2601
  }
735
- ) : activeError ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: screenshotPreviewStyles.errorPreview, children: [
736
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.AlertCircleIcon, { className: "w-8 h-8", style: { color: "#ef4444" } }),
737
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: screenshotPreviewStyles.errorTitle, children: "Capture Failed" }),
738
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: screenshotPreviewStyles.errorMessage, children: activeError.message })
739
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: screenshotPreviewStyles.noPreview, children: [
740
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.ImageIcon, { className: "w-6 h-6", style: { color: "#9ca3af" } }),
741
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
2602
+ ) : activeError ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: screenshotPreviewStyles.errorPreview, children: [
2603
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.AlertCircleIcon, { className: "w-8 h-8", style: { color: "#ef4444" } }),
2604
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: screenshotPreviewStyles.errorTitle, children: "Capture Failed" }),
2605
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: screenshotPreviewStyles.errorMessage, children: activeError.message })
2606
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: screenshotPreviewStyles.noPreview, children: [
2607
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.ImageIcon, { className: "w-6 h-6", style: { color: "#9ca3af" } }),
2608
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { children: [
742
2609
  "No ",
743
2610
  activeTab,
744
2611
  " screenshot"
@@ -746,14 +2613,14 @@ var ScreenshotPreview = import_react.default.memo(function ScreenshotPreview2({
746
2613
  ] })
747
2614
  }
748
2615
  ),
749
- activeScreenshot && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: screenshotPreviewStyles.dimensionsInfo, children: [
2616
+ activeScreenshot && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: screenshotPreviewStyles.dimensionsInfo, children: [
750
2617
  activeScreenshot.width,
751
2618
  " \xD7 ",
752
2619
  activeScreenshot.height,
753
2620
  "px"
754
2621
  ] }),
755
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: screenshotPreviewStyles.actions, children: [
756
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
2622
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: screenshotPreviewStyles.actions, children: [
2623
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
757
2624
  "button",
758
2625
  {
759
2626
  type: "button",
@@ -761,13 +2628,13 @@ var ScreenshotPreview = import_react.default.memo(function ScreenshotPreview2({
761
2628
  onClick: onRetake,
762
2629
  style: screenshotPreviewStyles.retakeButtonSmall,
763
2630
  children: [
764
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.RefreshCwIcon, { className: "w-3 h-3" }),
2631
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.RefreshCwIcon, { className: "w-3 h-3" }),
765
2632
  "Retake"
766
2633
  ]
767
2634
  }
768
2635
  ),
769
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: screenshotPreviewStyles.actionsRight, children: [
770
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
2636
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: screenshotPreviewStyles.actionsRight, children: [
2637
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
771
2638
  "button",
772
2639
  {
773
2640
  type: "button",
@@ -776,12 +2643,12 @@ var ScreenshotPreview = import_react.default.memo(function ScreenshotPreview2({
776
2643
  onClick: onCancel,
777
2644
  style: { ...menuStyles.button, ...menuStyles.cancelButton },
778
2645
  children: [
779
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.XIcon, { className: "w-3 h-3" }),
2646
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.XIcon, { className: "w-3 h-3" }),
780
2647
  "Cancel"
781
2648
  ]
782
2649
  }
783
2650
  ),
784
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2651
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
785
2652
  "button",
786
2653
  {
787
2654
  type: "button",
@@ -792,11 +2659,11 @@ var ScreenshotPreview = import_react.default.memo(function ScreenshotPreview2({
792
2659
  ...menuStyles.submitButton,
793
2660
  ...isSubmitting ? menuStyles.submitButtonDisabled : {}
794
2661
  },
795
- children: isSubmitting ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
796
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Loader2Icon, { className: "w-3 h-3 animate-spin" }),
2662
+ children: isSubmitting ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
2663
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.Loader2Icon, { className: "w-3 h-3 animate-spin" }),
797
2664
  "Sending..."
798
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
799
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.CheckIcon, { className: "w-3 h-3" }),
2665
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
2666
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.CheckIcon, { className: "w-3 h-3" }),
800
2667
  "Send"
801
2668
  ] })
802
2669
  }
@@ -893,7 +2760,7 @@ svg.${HIGHLIGHT_CONTAINER_CLASS},
893
2760
  }
894
2761
  `;
895
2762
  }
896
- function injectStyles(colors) {
2763
+ function injectStyles2(colors) {
897
2764
  if (typeof document === "undefined") return;
898
2765
  const existingStyle = document.getElementById(STYLE_ID);
899
2766
  if (existingStyle) {
@@ -941,12 +2808,12 @@ function findContainerParent(element, config) {
941
2808
  }
942
2809
  function highlightTarget(element, colors) {
943
2810
  const mergedColors = { ...defaultHighlightColors, ...colors };
944
- injectStyles(mergedColors);
2811
+ injectStyles2(mergedColors);
945
2812
  element.classList.add(HIGHLIGHT_TARGET_CLASS);
946
2813
  }
947
2814
  function highlightContainer(element, colors) {
948
2815
  const mergedColors = { ...defaultHighlightColors, ...colors };
949
- injectStyles(mergedColors);
2816
+ injectStyles2(mergedColors);
950
2817
  element.classList.add(HIGHLIGHT_CONTAINER_CLASS);
951
2818
  }
952
2819
  function clearHighlights() {
@@ -973,12 +2840,12 @@ function applyHighlights(targetElement, config) {
973
2840
  }
974
2841
 
975
2842
  // src/ContextMenu.tsx
976
- var import_jsx_runtime2 = require("react/jsx-runtime");
2843
+ var import_jsx_runtime3 = require("react/jsx-runtime");
977
2844
  var VIEWPORT_PADDING = 10;
978
2845
  var defaultIcons = {
979
- feature: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.PlusIcon, { className: "w-4 h-4" }),
980
- issue: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.FlagIcon, { className: "w-4 h-4" }),
981
- like: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.ThumbsUpIcon, { className: "w-4 h-4" })
2846
+ feature: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react3.PlusIcon, { className: "w-4 h-4" }),
2847
+ issue: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react3.FlagIcon, { className: "w-4 h-4" }),
2848
+ like: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react3.ThumbsUpIcon, { className: "w-4 h-4" })
982
2849
  };
983
2850
  var OFFSCREEN_POSITION = { x: -9999, y: -9999 };
984
2851
  var DefaultHeader = ({
@@ -987,25 +2854,25 @@ var DefaultHeader = ({
987
2854
  styles,
988
2855
  title = "Send Feedback"
989
2856
  }) => {
990
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: styles, className, children: [
991
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: title }),
2857
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: styles, className, children: [
2858
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: title }),
992
2859
  children
993
2860
  ] });
994
2861
  };
995
- var MenuItem = import_react2.default.memo(function MenuItem2({
2862
+ var MenuItem = import_react5.default.memo(function MenuItem2({
996
2863
  disabled,
997
2864
  hasChildren,
998
2865
  item,
999
2866
  onClick
1000
2867
  }) {
1001
- const [isHovered, setIsHovered] = (0, import_react2.useState)(false);
1002
- const [isPressed, setIsPressed] = (0, import_react2.useState)(false);
2868
+ const [isHovered, setIsHovered] = (0, import_react5.useState)(false);
2869
+ const [isPressed, setIsPressed] = (0, import_react5.useState)(false);
1003
2870
  const isComingSoon = item.status === "comingSoon";
1004
2871
  const badgeLabel = item.badge?.label ?? (isComingSoon ? "Coming soon" : null);
1005
2872
  const badgeTone = item.badge?.tone ?? (isComingSoon ? "neutral" : "neutral");
1006
2873
  const badgeStyle = badgeLabel ? getBadgeStyle(badgeTone) : void 0;
1007
2874
  const iconNode = item.icon ?? defaultIcons[item.type];
1008
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
2875
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1009
2876
  "button",
1010
2877
  {
1011
2878
  type: "button",
@@ -1026,22 +2893,22 @@ var MenuItem = import_react2.default.memo(function MenuItem2({
1026
2893
  ...disabled ? menuStyles.itemDisabled : {}
1027
2894
  },
1028
2895
  children: [
1029
- iconNode ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: menuStyles.itemIcon, children: iconNode }) : null,
1030
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { style: menuStyles.itemLabel, children: [
2896
+ iconNode ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: menuStyles.itemIcon, children: iconNode }) : null,
2897
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { style: menuStyles.itemLabel, children: [
1031
2898
  item.label,
1032
- badgeLabel && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: badgeStyle, children: badgeLabel })
2899
+ badgeLabel && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: badgeStyle, children: badgeLabel })
1033
2900
  ] }),
1034
- hasChildren && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.ChevronRightIcon, { className: "w-4 h-4", style: menuStyles.submenuIcon })
2901
+ hasChildren && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react3.ChevronRightIcon, { className: "w-4 h-4", style: menuStyles.submenuIcon })
1035
2902
  ]
1036
2903
  }
1037
2904
  );
1038
2905
  });
1039
- var BackButton = import_react2.default.memo(function BackButton2({
2906
+ var BackButton = import_react5.default.memo(function BackButton2({
1040
2907
  onClick
1041
2908
  }) {
1042
- const [isHovered, setIsHovered] = (0, import_react2.useState)(false);
1043
- const [isPressed, setIsPressed] = (0, import_react2.useState)(false);
1044
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
2909
+ const [isHovered, setIsHovered] = (0, import_react5.useState)(false);
2910
+ const [isPressed, setIsPressed] = (0, import_react5.useState)(false);
2911
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1045
2912
  "button",
1046
2913
  {
1047
2914
  type: "button",
@@ -1061,26 +2928,26 @@ var BackButton = import_react2.default.memo(function BackButton2({
1061
2928
  ...isHovered || isPressed ? menuStyles.itemHover : {}
1062
2929
  },
1063
2930
  children: [
1064
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.ChevronLeftIcon, { className: "w-4 h-4", style: { opacity: 0.5 } }),
1065
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { opacity: 0.7 }, children: "Back" })
2931
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react3.ChevronLeftIcon, { className: "w-4 h-4", style: { opacity: 0.5 } }),
2932
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { opacity: 0.7 }, children: "Back" })
1066
2933
  ]
1067
2934
  }
1068
2935
  );
1069
2936
  });
1070
- var CommentForm = import_react2.default.memo(function CommentForm2({
2937
+ var CommentForm = import_react5.default.memo(function CommentForm2({
1071
2938
  isSubmitting,
1072
2939
  onCancel,
1073
2940
  onSubmit
1074
2941
  }) {
1075
- const [comment, setComment] = (0, import_react2.useState)("");
1076
- const inputRef = (0, import_react2.useRef)(null);
1077
- (0, import_react2.useEffect)(() => {
2942
+ const [comment, setComment] = (0, import_react5.useState)("");
2943
+ const inputRef = (0, import_react5.useRef)(null);
2944
+ (0, import_react5.useEffect)(() => {
1078
2945
  inputRef.current?.focus();
1079
2946
  }, []);
1080
- const handleSubmit = (0, import_react2.useCallback)(() => {
2947
+ const handleSubmit = (0, import_react5.useCallback)(() => {
1081
2948
  onSubmit(comment);
1082
2949
  }, [comment, onSubmit]);
1083
- const handleKeyDown = (0, import_react2.useCallback)(
2950
+ const handleKeyDown = (0, import_react5.useCallback)(
1084
2951
  (e) => {
1085
2952
  if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
1086
2953
  handleSubmit();
@@ -1090,8 +2957,8 @@ var CommentForm = import_react2.default.memo(function CommentForm2({
1090
2957
  },
1091
2958
  [handleSubmit, onCancel]
1092
2959
  );
1093
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: menuStyles.commentSection, children: [
1094
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2960
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: menuStyles.commentSection, children: [
2961
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1095
2962
  "textarea",
1096
2963
  {
1097
2964
  ref: inputRef,
@@ -1103,8 +2970,8 @@ var CommentForm = import_react2.default.memo(function CommentForm2({
1103
2970
  value: comment
1104
2971
  }
1105
2972
  ),
1106
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: menuStyles.buttonRow, children: [
1107
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2973
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: menuStyles.buttonRow, children: [
2974
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1108
2975
  "button",
1109
2976
  {
1110
2977
  type: "button",
@@ -1114,7 +2981,7 @@ var CommentForm = import_react2.default.memo(function CommentForm2({
1114
2981
  children: "Cancel"
1115
2982
  }
1116
2983
  ),
1117
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2984
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1118
2985
  "button",
1119
2986
  {
1120
2987
  type: "button",
@@ -1162,39 +3029,48 @@ function ContextMenu({
1162
3029
  onSelect,
1163
3030
  position,
1164
3031
  positionMode = "inView",
3032
+ quickChatConfig,
1165
3033
  screenshotConfig,
1166
3034
  style,
1167
3035
  targetElement,
1168
3036
  visible
1169
3037
  }) {
1170
- const [selectedType, setSelectedType] = (0, import_react2.useState)(null);
1171
- const [currentView, setCurrentView] = (0, import_react2.useState)("menu");
1172
- const [pendingComment, setPendingComment] = (0, import_react2.useState)();
1173
- const [submenuStack, setSubmenuStack] = (0, import_react2.useState)([]);
1174
- const [screenshots, setScreenshots] = (0, import_react2.useState)(null);
1175
- const [isCapturing, setIsCapturing] = (0, import_react2.useState)(false);
1176
- const menuRef = (0, import_react2.useRef)(null);
1177
- const [adjustedPosition, setAdjustedPosition] = (0, import_react2.useState)(OFFSCREEN_POSITION);
1178
- const [isDragging, setIsDragging] = (0, import_react2.useState)(false);
1179
- const [dragOffset, setDragOffset] = (0, import_react2.useState)({
3038
+ const [selectedType, setSelectedType] = (0, import_react5.useState)(null);
3039
+ const [currentView, setCurrentView] = (0, import_react5.useState)("menu");
3040
+ const [pendingComment, setPendingComment] = (0, import_react5.useState)();
3041
+ const [submenuStack, setSubmenuStack] = (0, import_react5.useState)([]);
3042
+ const [screenshots, setScreenshots] = (0, import_react5.useState)(null);
3043
+ const [isCapturing, setIsCapturing] = (0, import_react5.useState)(false);
3044
+ const [isQuickChatPinned, setIsQuickChatPinned] = (0, import_react5.useState)(() => {
3045
+ if (typeof window === "undefined") return false;
3046
+ try {
3047
+ return sessionStorage.getItem("anyclick-quick-chat-pinned") === "true";
3048
+ } catch {
3049
+ return false;
3050
+ }
3051
+ });
3052
+ const menuRef = (0, import_react5.useRef)(null);
3053
+ const [adjustedPosition, setAdjustedPosition] = (0, import_react5.useState)(OFFSCREEN_POSITION);
3054
+ const [isDragging, setIsDragging] = (0, import_react5.useState)(false);
3055
+ const [dragOffset, setDragOffset] = (0, import_react5.useState)({
1180
3056
  x: 0,
1181
3057
  y: 0
1182
3058
  });
1183
- const dragStartRef = (0, import_react2.useRef)(null);
1184
- const mergedScreenshotConfig = import_react2.default.useMemo(
3059
+ const dragStartRef = (0, import_react5.useRef)(null);
3060
+ const mergedScreenshotConfig = import_react5.default.useMemo(
1185
3061
  () => ({
1186
- ...import_anyclick_core2.DEFAULT_SCREENSHOT_CONFIG,
3062
+ ...import_anyclick_core4.DEFAULT_SCREENSHOT_CONFIG,
1187
3063
  ...screenshotConfig
1188
3064
  }),
1189
3065
  [screenshotConfig]
1190
3066
  );
1191
- const showPreview = mergedScreenshotConfig.showPreview && (0, import_anyclick_core2.isScreenshotSupported)();
3067
+ const showPreview = mergedScreenshotConfig.showPreview && (0, import_anyclick_core4.isScreenshotSupported)();
1192
3068
  const currentItems = submenuStack.length > 0 ? submenuStack[submenuStack.length - 1] : items;
1193
- const captureScreenshots = (0, import_react2.useCallback)(async () => {
3069
+ const captureScreenshots = (0, import_react5.useCallback)(async () => {
1194
3070
  if (!targetElement || !showPreview) return;
1195
3071
  setIsCapturing(true);
1196
3072
  try {
1197
- const captured = await (0, import_anyclick_core2.captureAllScreenshots)(
3073
+ const captured = await (0, import_anyclick_core4.captureAllScreenshots)(
1198
3074
  targetElement,
1199
3075
  containerElement,
1200
3076
  mergedScreenshotConfig
@@ -1207,7 +3083,7 @@ function ContextMenu({
1207
3083
  setIsCapturing(false);
1208
3084
  }
1209
3085
  }, [containerElement, mergedScreenshotConfig, showPreview, targetElement]);
1210
- (0, import_react2.useEffect)(() => {
3086
+ (0, import_react5.useEffect)(() => {
1211
3087
  if (!visible) {
1212
3088
  setSelectedType(null);
1213
3089
  setCurrentView("menu");
@@ -1221,7 +3097,7 @@ function ContextMenu({
1221
3097
  dragStartRef.current = null;
1222
3098
  }
1223
3099
  }, [visible]);
1224
- (0, import_react2.useEffect)(() => {
3100
+ (0, import_react5.useEffect)(() => {
1225
3101
  if (visible && targetElement) {
1226
3102
  clearHighlights();
1227
3103
  applyHighlights(targetElement, highlightConfig);
@@ -1232,7 +3108,7 @@ function ContextMenu({
1232
3108
  clearHighlights();
1233
3109
  };
1234
3110
  }, [highlightConfig, targetElement, visible]);
1235
- (0, import_react2.useEffect)(() => {
3111
+ (0, import_react5.useEffect)(() => {
1236
3112
  if (!visible) {
1237
3113
  return;
1238
3114
  }
@@ -1251,13 +3127,13 @@ function ContextMenu({
1251
3127
  document.removeEventListener("pointerdown", handlePointerDown);
1252
3128
  };
1253
3129
  }, [onClose, visible]);
1254
- (0, import_react2.useEffect)(() => {
3130
+ (0, import_react5.useEffect)(() => {
1255
3131
  if (visible) {
1256
3132
  setAdjustedPosition(position);
1257
3133
  setDragOffset({ x: 0, y: 0 });
1258
3134
  }
1259
3135
  }, [position.x, position.y, visible]);
1260
- (0, import_react2.useEffect)(() => {
3136
+ (0, import_react5.useEffect)(() => {
1261
3137
  if (!visible || !menuRef.current) return;
1262
3138
  const updatePosition = () => {
1263
3139
  const menuElement = menuRef.current;
@@ -1281,7 +3157,7 @@ function ContextMenu({
1281
3157
  window.addEventListener("resize", updatePosition);
1282
3158
  return () => window.removeEventListener("resize", updatePosition);
1283
3159
  }, [currentView, dragOffset, position, positionMode, visible]);
1284
- (0, import_react2.useEffect)(() => {
3160
+ (0, import_react5.useEffect)(() => {
1285
3161
  if (!visible || positionMode !== "dynamic") return;
1286
3162
  const handlePointerMove = (event) => {
1287
3163
  if (!isDragging || !dragStartRef.current) return;
@@ -1308,7 +3184,7 @@ function ContextMenu({
1308
3184
  };
1309
3185
  }
1310
3186
  }, [isDragging, positionMode, visible]);
1311
- const handleDragStart = (0, import_react2.useCallback)(
3187
+ const handleDragStart = (0, import_react5.useCallback)(
1312
3188
  (event) => {
1313
3189
  if (positionMode !== "dynamic") return;
1314
3190
  event.preventDefault();
@@ -1317,7 +3193,8 @@ function ContextMenu({
1317
3193
  },
1318
3194
  [positionMode]
1319
3195
  );
1320
- (0, import_react2.useEffect)(() => {
3196
+ const [initialChatInput, setInitialChatInput] = (0, import_react5.useState)("");
3197
+ (0, import_react5.useEffect)(() => {
1321
3198
  const handleKeyDown = (e) => {
1322
3199
  if (e.key === "Escape") {
1323
3200
  if (currentView === "screenshot-preview") {
@@ -1326,19 +3203,38 @@ function ContextMenu({
1326
3203
  setCurrentView("menu");
1327
3204
  setSelectedType(null);
1328
3205
  setPendingComment(void 0);
3206
+ } else if (currentView === "quick-chat") {
3207
+ if (!isQuickChatPinned) {
3208
+ setCurrentView("menu");
3209
+ }
1329
3210
  } else if (submenuStack.length > 0) {
1330
3211
  setSubmenuStack((prev) => prev.slice(0, -1));
1331
3212
  } else {
1332
3213
  onClose();
1333
3214
  }
3215
+ return;
3216
+ }
3217
+ if (quickChatConfig && currentView === "menu" && !isQuickChatPinned && !e.ctrlKey && !e.metaKey && !e.altKey) {
3218
+ if (e.key.length === 1 && e.key.match(/[a-zA-Z0-9\s.,!?'"]/)) {
3219
+ e.preventDefault();
3220
+ setInitialChatInput(e.key);
3221
+ setCurrentView("quick-chat");
3222
+ }
1334
3223
  }
1335
3224
  };
1336
3225
  if (visible) {
1337
3226
  document.addEventListener("keydown", handleKeyDown);
1338
3227
  return () => document.removeEventListener("keydown", handleKeyDown);
1339
3228
  }
1340
- }, [currentView, onClose, submenuStack.length, visible]);
1341
- (0, import_react2.useEffect)(() => {
3229
+ }, [
3230
+ currentView,
3231
+ isQuickChatPinned,
3232
+ onClose,
3233
+ quickChatConfig,
3234
+ submenuStack.length,
3235
+ visible
3236
+ ]);
3237
+ (0, import_react5.useEffect)(() => {
1342
3238
  const menuElement = menuRef.current;
1343
3239
  if (!visible || !menuElement) return;
1344
3240
  const preventTouchDefault = (e) => {
@@ -1355,9 +3251,6 @@ function ContextMenu({
1355
3251
  menuElement.removeEventListener("touchmove", preventTouchDefault);
1356
3252
  };
1357
3253
  }, [visible]);
1358
- if (!visible || !targetElement) {
1359
- return null;
1360
- }
1361
3254
  const handleItemClick = (item) => {
1362
3255
  if (item.status === "comingSoon") {
1363
3256
  return;
@@ -1426,100 +3319,181 @@ function ContextMenu({
1426
3319
  const handleRetakeScreenshots = () => {
1427
3320
  captureScreenshots();
1428
3321
  };
1429
- const containerWidth = currentView === "screenshot-preview" ? 360 : void 0;
1430
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1431
- "div",
1432
- {
1433
- ref: menuRef,
1434
- "aria-label": "Feedback options",
1435
- className,
1436
- role: "menu",
1437
- style: {
1438
- ...menuStyles.container,
1439
- left: adjustedPosition.x,
1440
- top: adjustedPosition.y,
1441
- ...containerWidth ? { minWidth: containerWidth, width: containerWidth } : {},
1442
- touchAction: "none",
1443
- userSelect: "none",
1444
- WebkitTouchCallout: "none",
1445
- WebkitUserSelect: "none",
1446
- ...isDragging ? { cursor: "grabbing" } : {},
1447
- ...style
1448
- },
1449
- children: [
1450
- !header && currentView !== "screenshot-preview" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(DefaultHeader, { styles: menuStyles.header, title: "Send Feedback", children: [
1451
- showPreview && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: menuStyles.screenshotIndicator, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.CameraIcon, { className: "w-3 h-3" }) }),
1452
- positionMode === "dynamic" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1453
- "div",
1454
- {
1455
- "data-drag-handle": true,
1456
- onMouseEnter: (e) => {
1457
- e.currentTarget.style.opacity = "1";
1458
- },
1459
- onMouseLeave: (e) => {
1460
- e.currentTarget.style.opacity = "0.5";
1461
- },
1462
- onPointerDown: handleDragStart,
1463
- style: {
1464
- ...menuStyles.dragHandle,
1465
- cursor: isDragging ? "grabbing" : "grab"
3322
+ const handleQuickChatToggle = () => {
3323
+ if (currentView === "quick-chat" && !isQuickChatPinned) {
3324
+ setCurrentView("menu");
3325
+ } else {
3326
+ setCurrentView("quick-chat");
3327
+ }
3328
+ };
3329
+ const handleQuickChatPin = (pinned) => {
3330
+ setIsQuickChatPinned(pinned);
3331
+ try {
3332
+ if (pinned) {
3333
+ sessionStorage.setItem("anyclick-quick-chat-pinned", "true");
3334
+ } else {
3335
+ sessionStorage.removeItem("anyclick-quick-chat-pinned");
3336
+ }
3337
+ } catch {
3338
+ }
3339
+ if (pinned) {
3340
+ setCurrentView("menu");
3341
+ }
3342
+ };
3343
+ const handleQuickChatClose = () => {
3344
+ setIsQuickChatPinned(false);
3345
+ try {
3346
+ sessionStorage.removeItem("anyclick-quick-chat-pinned");
3347
+ } catch {
3348
+ }
3349
+ setCurrentView("menu");
3350
+ };
3351
+ const containerWidth = currentView === "screenshot-preview" ? 360 : currentView === "quick-chat" && !isQuickChatPinned ? 320 : void 0;
3352
+ const showPinnedDrawer = isQuickChatPinned && quickChatConfig;
3353
+ const showMenu = visible && targetElement;
3354
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
3355
+ showPinnedDrawer && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3356
+ QuickChat,
3357
+ {
3358
+ visible: true,
3359
+ targetElement,
3360
+ containerElement,
3361
+ onClose: handleQuickChatClose,
3362
+ onPin: handleQuickChatPin,
3363
+ isPinned: true,
3364
+ config: quickChatConfig
3365
+ }
3366
+ ),
3367
+ showMenu && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
3368
+ "div",
3369
+ {
3370
+ ref: menuRef,
3371
+ "aria-label": "Feedback options",
3372
+ className,
3373
+ role: "menu",
3374
+ style: {
3375
+ ...menuStyles.container,
3376
+ left: adjustedPosition.x,
3377
+ top: adjustedPosition.y,
3378
+ ...containerWidth ? { minWidth: containerWidth, width: containerWidth } : {},
3379
+ touchAction: "none",
3380
+ userSelect: "none",
3381
+ WebkitTouchCallout: "none",
3382
+ WebkitUserSelect: "none",
3383
+ ...isDragging ? { cursor: "grabbing" } : {},
3384
+ ...style
3385
+ },
3386
+ children: [
3387
+ !header && currentView !== "screenshot-preview" && currentView !== "quick-chat" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(DefaultHeader, { styles: menuStyles.header, title: "Send Feedback", children: [
3388
+ positionMode === "dynamic" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3389
+ "div",
3390
+ {
3391
+ "data-drag-handle": true,
3392
+ onMouseEnter: (e) => {
3393
+ e.currentTarget.style.opacity = "1";
3394
+ },
3395
+ onMouseLeave: (e) => {
3396
+ e.currentTarget.style.opacity = "0.5";
3397
+ },
3398
+ onPointerDown: handleDragStart,
3399
+ style: {
3400
+ ...menuStyles.dragHandle,
3401
+ cursor: isDragging ? "grabbing" : "grab"
3402
+ },
3403
+ title: "Drag to move",
3404
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react3.GripVertical, { className: "w-4 h-4" })
3405
+ }
3406
+ ),
3407
+ showPreview && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: menuStyles.screenshotIndicator, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react3.CameraIcon, { className: "w-3 h-3" }) }),
3408
+ quickChatConfig && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3409
+ "button",
3410
+ {
3411
+ type: "button",
3412
+ onClick: handleQuickChatToggle,
3413
+ style: {
3414
+ display: "flex",
3415
+ alignItems: "center",
3416
+ justifyContent: "center",
3417
+ width: "24px",
3418
+ height: "24px",
3419
+ border: "none",
3420
+ borderRadius: "4px",
3421
+ backgroundColor: isQuickChatPinned ? "var(--anyclick-menu-accent, #0066cc)" : "transparent",
3422
+ color: isQuickChatPinned ? "#fff" : "var(--anyclick-menu-accent, #0066cc)",
3423
+ cursor: "pointer",
3424
+ transition: "all 0.15s ease"
3425
+ },
3426
+ title: isQuickChatPinned ? "Quick Chat (pinned)" : "Quick Ask AI",
3427
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react3.Sparkles, { className: "w-3.5 h-3.5" })
3428
+ }
3429
+ )
3430
+ ] }),
3431
+ !!header && header,
3432
+ currentView === "menu" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: menuStyles.itemList, children: [
3433
+ submenuStack.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(BackButton, { onClick: handleBack }),
3434
+ currentItems.map((item) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3435
+ MenuItem,
3436
+ {
3437
+ disabled: isSubmitting,
3438
+ hasChildren: item.children && item.children.length > 0,
3439
+ item,
3440
+ onClick: () => handleItemClick(item)
1466
3441
  },
1467
- title: "Drag to move",
1468
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.GripVertical, { className: "w-4 h-4" })
3442
+ item.type
3443
+ ))
3444
+ ] }),
3445
+ currentView === "comment" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3446
+ CommentForm,
3447
+ {
3448
+ isSubmitting,
3449
+ onCancel: handleCommentCancel,
3450
+ onSubmit: handleCommentSubmit
1469
3451
  }
1470
- )
1471
- ] }),
1472
- !!header && header,
1473
- currentView === "menu" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: menuStyles.itemList, children: [
1474
- submenuStack.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(BackButton, { onClick: handleBack }),
1475
- currentItems.map((item) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1476
- MenuItem,
3452
+ ),
3453
+ currentView === "screenshot-preview" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3454
+ ScreenshotPreview,
1477
3455
  {
1478
- disabled: isSubmitting,
1479
- hasChildren: item.children && item.children.length > 0,
1480
- item,
1481
- onClick: () => handleItemClick(item)
1482
- },
1483
- item.type
1484
- ))
1485
- ] }),
1486
- currentView === "comment" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1487
- CommentForm,
1488
- {
1489
- isSubmitting,
1490
- onCancel: handleCommentCancel,
1491
- onSubmit: handleCommentSubmit
1492
- }
1493
- ),
1494
- currentView === "screenshot-preview" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1495
- ScreenshotPreview,
1496
- {
1497
- isLoading: isCapturing,
1498
- isSubmitting,
1499
- onCancel: handleScreenshotCancel,
1500
- onConfirm: handleScreenshotConfirm,
1501
- onRetake: handleRetakeScreenshots,
1502
- screenshots
1503
- }
1504
- )
1505
- ]
1506
- }
1507
- );
3456
+ isLoading: isCapturing,
3457
+ isSubmitting,
3458
+ onCancel: handleScreenshotCancel,
3459
+ onConfirm: handleScreenshotConfirm,
3460
+ onRetake: handleRetakeScreenshots,
3461
+ screenshots
3462
+ }
3463
+ ),
3464
+ currentView === "quick-chat" && quickChatConfig && !isQuickChatPinned && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3465
+ QuickChat,
3466
+ {
3467
+ visible: true,
3468
+ targetElement,
3469
+ containerElement,
3470
+ onClose: handleQuickChatClose,
3471
+ onPin: handleQuickChatPin,
3472
+ isPinned: false,
3473
+ config: quickChatConfig,
3474
+ initialInput: initialChatInput,
3475
+ onInitialInputConsumed: () => setInitialChatInput("")
3476
+ }
3477
+ )
3478
+ ]
3479
+ }
3480
+ )
3481
+ ] });
1508
3482
  }
1509
3483
 
1510
3484
  // src/context.ts
1511
- var import_react3 = require("react");
1512
- var AnyclickContext = (0, import_react3.createContext)(null);
3485
+ var import_react6 = require("react");
3486
+ var AnyclickContext = (0, import_react6.createContext)(null);
1513
3487
  var FeedbackContext = AnyclickContext;
1514
3488
  function useAnyclick() {
1515
- const context = (0, import_react3.useContext)(AnyclickContext);
3489
+ const context = (0, import_react6.useContext)(AnyclickContext);
1516
3490
  if (!context) {
1517
3491
  throw new Error("useAnyclick must be used within an AnyclickProvider");
1518
3492
  }
1519
3493
  return context;
1520
3494
  }
1521
3495
  function useFeedback() {
1522
- const context = (0, import_react3.useContext)(AnyclickContext);
3496
+ const context = (0, import_react6.useContext)(AnyclickContext);
1523
3497
  if (!context) {
1524
3498
  throw new Error("useFeedback must be used within a FeedbackProvider");
1525
3499
  }
@@ -1527,12 +3501,12 @@ function useFeedback() {
1527
3501
  }
1528
3502
 
1529
3503
  // src/store.ts
1530
- var import_zustand = require("zustand");
3504
+ var import_zustand2 = require("zustand");
1531
3505
  var providerIdCounter = 0;
1532
3506
  function generateProviderId() {
1533
3507
  return `anyclick-provider-${++providerIdCounter}`;
1534
3508
  }
1535
- var useProviderStore = (0, import_zustand.create)((set, get) => ({
3509
+ var useProviderStore = (0, import_zustand2.create)((set, get) => ({
1536
3510
  providers: /* @__PURE__ */ new Map(),
1537
3511
  findProvidersForElement: (element) => {
1538
3512
  const { providers } = get();
@@ -1688,7 +3662,7 @@ function dispatchContextMenuEvent(event, element) {
1688
3662
  }
1689
3663
 
1690
3664
  // src/AnyclickProvider.tsx
1691
- var import_jsx_runtime3 = require("react/jsx-runtime");
3665
+ var import_jsx_runtime4 = require("react/jsx-runtime");
1692
3666
  var defaultMenuItems = [
1693
3667
  { label: "Report an issue", showComment: true, type: "issue" },
1694
3668
  { label: "Request a feature", showComment: true, type: "feature" },
@@ -1711,6 +3685,7 @@ function AnyclickProvider({
1711
3685
  metadata,
1712
3686
  onSubmitError,
1713
3687
  onSubmitSuccess,
3688
+ quickChatConfig,
1714
3689
  scoped = false,
1715
3690
  screenshotConfig,
1716
3691
  stripAttributes,
@@ -1719,18 +3694,18 @@ function AnyclickProvider({
1719
3694
  touchHoldDurationMs,
1720
3695
  touchMoveThreshold
1721
3696
  }) {
1722
- const [isSubmitting, setIsSubmitting] = (0, import_react4.useState)(false);
1723
- const [menuVisible, setMenuVisible] = (0, import_react4.useState)(false);
1724
- const [menuPosition, setMenuPosition] = (0, import_react4.useState)(OFFSCREEN_POSITION2);
1725
- const [targetElement, setTargetElement] = (0, import_react4.useState)(null);
1726
- const [containerElement, setContainerElement] = (0, import_react4.useState)(
3697
+ const [isSubmitting, setIsSubmitting] = (0, import_react7.useState)(false);
3698
+ const [menuVisible, setMenuVisible] = (0, import_react7.useState)(false);
3699
+ const [menuPosition, setMenuPosition] = (0, import_react7.useState)(OFFSCREEN_POSITION2);
3700
+ const [targetElement, setTargetElement] = (0, import_react7.useState)(null);
3701
+ const [containerElement, setContainerElement] = (0, import_react7.useState)(
1727
3702
  null
1728
3703
  );
1729
- const providerId = (0, import_react4.useId)();
1730
- const containerRef = (0, import_react4.useRef)(null);
1731
- const [containerReady, setContainerReady] = (0, import_react4.useState)(!scoped);
1732
- const clientRef = (0, import_react4.useRef)(null);
1733
- const setContainerRef = (0, import_react4.useCallback)(
3704
+ const providerId = (0, import_react7.useId)();
3705
+ const containerRef = (0, import_react7.useRef)(null);
3706
+ const [containerReady, setContainerReady] = (0, import_react7.useState)(!scoped);
3707
+ const clientRef = (0, import_react7.useRef)(null);
3708
+ const setContainerRef = (0, import_react7.useCallback)(
1734
3709
  (node) => {
1735
3710
  containerRef.current = node;
1736
3711
  if (scoped && node) {
@@ -1759,7 +3734,7 @@ function AnyclickProvider({
1759
3734
  updateProvider
1760
3735
  } = useProviderStore();
1761
3736
  const parentId = parentContext?.providerId ?? null;
1762
- const actualDepth = (0, import_react4.useMemo)(() => {
3737
+ const actualDepth = (0, import_react7.useMemo)(() => {
1763
3738
  if (!parentContext) return 0;
1764
3739
  let d = 0;
1765
3740
  let currentId = parentId;
@@ -1773,7 +3748,7 @@ function AnyclickProvider({
1773
3748
  }, [parentContext, parentId]);
1774
3749
  const isDisabledByTheme = theme === null || theme?.disabled === true;
1775
3750
  const effectiveDisabled = disabled || isDisabledByTheme;
1776
- const localTheme = (0, import_react4.useMemo)(() => {
3751
+ const localTheme = (0, import_react7.useMemo)(() => {
1777
3752
  if (theme === null) {
1778
3753
  return { disabled: true };
1779
3754
  }
@@ -1788,7 +3763,7 @@ function AnyclickProvider({
1788
3763
  ...theme
1789
3764
  };
1790
3765
  }, [highlightConfig, menuClassName, menuStyle, screenshotConfig, theme]);
1791
- const handleContextMenu = (0, import_react4.useCallback)(
3766
+ const handleContextMenu = (0, import_react7.useCallback)(
1792
3767
  (event, element) => {
1793
3768
  if (!scoped && isElementInDisabledScope(element)) {
1794
3769
  return false;
@@ -1816,7 +3791,7 @@ function AnyclickProvider({
1816
3791
  scoped
1817
3792
  ]
1818
3793
  );
1819
- (0, import_react4.useLayoutEffect)(() => {
3794
+ (0, import_react7.useLayoutEffect)(() => {
1820
3795
  const providerInstance = {
1821
3796
  containerRef,
1822
3797
  depth: actualDepth,
@@ -1842,7 +3817,7 @@ function AnyclickProvider({
1842
3817
  scoped,
1843
3818
  unregisterProvider
1844
3819
  ]);
1845
- (0, import_react4.useEffect)(() => {
3820
+ (0, import_react7.useEffect)(() => {
1846
3821
  updateProvider(providerId, {
1847
3822
  disabled: effectiveDisabled,
1848
3823
  onContextMenu: handleContextMenu,
@@ -1855,14 +3830,14 @@ function AnyclickProvider({
1855
3830
  providerId,
1856
3831
  updateProvider
1857
3832
  ]);
1858
- (0, import_react4.useEffect)(() => {
3833
+ (0, import_react7.useEffect)(() => {
1859
3834
  if (isDisabledByAncestor(providerId)) {
1860
3835
  return;
1861
3836
  }
1862
3837
  if (scoped && !containerReady) {
1863
3838
  return;
1864
3839
  }
1865
- const client = (0, import_anyclick_core3.createAnyclickClient)({
3840
+ const client = (0, import_anyclick_core5.createAnyclickClient)({
1866
3841
  adapter,
1867
3842
  container: scoped ? containerRef.current : null,
1868
3843
  cooldownMs,
@@ -1903,7 +3878,7 @@ function AnyclickProvider({
1903
3878
  touchHoldDurationMs,
1904
3879
  touchMoveThreshold
1905
3880
  ]);
1906
- const submitAnyclick = (0, import_react4.useCallback)(
3881
+ const submitAnyclick = (0, import_react7.useCallback)(
1907
3882
  async (element, type, comment, screenshots) => {
1908
3883
  const client = clientRef.current;
1909
3884
  if (!client) return;
@@ -1924,7 +3899,7 @@ function AnyclickProvider({
1924
3899
  },
1925
3900
  [metadata]
1926
3901
  );
1927
- const openMenu = (0, import_react4.useCallback)(
3902
+ const openMenu = (0, import_react7.useCallback)(
1928
3903
  (element, position) => {
1929
3904
  setTargetElement(element);
1930
3905
  const mergedTheme2 = getMergedTheme(providerId);
@@ -1938,13 +3913,13 @@ function AnyclickProvider({
1938
3913
  },
1939
3914
  [getMergedTheme, highlightConfig, providerId]
1940
3915
  );
1941
- const closeMenu = (0, import_react4.useCallback)(() => {
3916
+ const closeMenu = (0, import_react7.useCallback)(() => {
1942
3917
  setMenuVisible(false);
1943
3918
  setMenuPosition(OFFSCREEN_POSITION2);
1944
3919
  setTargetElement(null);
1945
3920
  setContainerElement(null);
1946
3921
  }, []);
1947
- const handleMenuSelect = (0, import_react4.useCallback)(
3922
+ const handleMenuSelect = (0, import_react7.useCallback)(
1948
3923
  (type, comment, screenshots) => {
1949
3924
  if (targetElement) {
1950
3925
  submitAnyclick(targetElement, type, comment, screenshots);
@@ -1953,7 +3928,7 @@ function AnyclickProvider({
1953
3928
  [submitAnyclick, targetElement]
1954
3929
  );
1955
3930
  const inheritedTheme = getMergedTheme(providerId);
1956
- const mergedTheme = (0, import_react4.useMemo)(
3931
+ const mergedTheme = (0, import_react7.useMemo)(
1957
3932
  () => ({
1958
3933
  ...inheritedTheme,
1959
3934
  ...localTheme,
@@ -1976,7 +3951,7 @@ function AnyclickProvider({
1976
3951
  const effectiveMenuClassName = mergedTheme.menuClassName ?? menuClassName;
1977
3952
  const effectiveHighlightConfig = mergedTheme.highlightConfig ?? highlightConfig;
1978
3953
  const effectiveScreenshotConfig = mergedTheme.screenshotConfig ?? screenshotConfig;
1979
- const contextValue = (0, import_react4.useMemo)(
3954
+ const contextValue = (0, import_react7.useMemo)(
1980
3955
  () => ({
1981
3956
  closeMenu,
1982
3957
  isEnabled: !effectiveDisabled && !isDisabledByAncestor(providerId),
@@ -1999,7 +3974,7 @@ function AnyclickProvider({
1999
3974
  submitAnyclick
2000
3975
  ]
2001
3976
  );
2002
- const content = scoped ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3977
+ const content = scoped ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2003
3978
  "div",
2004
3979
  {
2005
3980
  ref: setContainerRef,
@@ -2008,9 +3983,9 @@ function AnyclickProvider({
2008
3983
  children
2009
3984
  }
2010
3985
  ) : children;
2011
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(AnyclickContext.Provider, { value: contextValue, children: [
3986
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(AnyclickContext.Provider, { value: contextValue, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { "data-anyclick-root": true, children: [
2012
3987
  content,
2013
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3988
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2014
3989
  ContextMenu,
2015
3990
  {
2016
3991
  className: effectiveMenuClassName,
@@ -2022,18 +3997,19 @@ function AnyclickProvider({
2022
3997
  onClose: closeMenu,
2023
3998
  onSelect: handleMenuSelect,
2024
3999
  position: menuPosition,
4000
+ quickChatConfig,
2025
4001
  screenshotConfig: effectiveScreenshotConfig,
2026
4002
  style: effectiveMenuStyle,
2027
4003
  targetElement,
2028
4004
  visible: menuVisible && !effectiveDisabled
2029
4005
  }
2030
4006
  )
2031
- ] });
4007
+ ] }) });
2032
4008
  }
2033
4009
  var FeedbackProvider = AnyclickProvider;
2034
4010
 
2035
4011
  // src/FunModeBridge.tsx
2036
- var import_react5 = require("react");
4012
+ var import_react8 = require("react");
2037
4013
  var import_anyclick_pointer = require("@ewjdev/anyclick-pointer");
2038
4014
  function isFunModeEnabled(theme) {
2039
4015
  if (!theme) return false;
@@ -2080,9 +4056,9 @@ function buildFunConfig(theme, container) {
2080
4056
  function FunModeBridge() {
2081
4057
  const { setConfig } = (0, import_anyclick_pointer.usePointer)();
2082
4058
  const providerStore = useProviderStore();
2083
- const activeProviderRef = (0, import_react5.useRef)(null);
2084
- const cachedConfigs = (0, import_react5.useRef)({});
2085
- const findActiveFunProvider = (0, import_react5.useMemo)(() => {
4059
+ const activeProviderRef = (0, import_react8.useRef)(null);
4060
+ const cachedConfigs = (0, import_react8.useRef)({});
4061
+ const findActiveFunProvider = (0, import_react8.useMemo)(() => {
2086
4062
  return (el) => {
2087
4063
  if (!el) return null;
2088
4064
  const providers = providerStore.findProvidersForElement(el);
@@ -2094,7 +4070,7 @@ function FunModeBridge() {
2094
4070
  return null;
2095
4071
  };
2096
4072
  }, [providerStore]);
2097
- (0, import_react5.useEffect)(() => {
4073
+ (0, import_react8.useEffect)(() => {
2098
4074
  const handleMove = (event) => {
2099
4075
  const el = document.elementFromPoint(event.clientX, event.clientY);
2100
4076
  const provider = findActiveFunProvider(el);
@@ -2128,13 +4104,73 @@ function FunModeBridge() {
2128
4104
  return null;
2129
4105
  }
2130
4106
 
4107
+ // src/ui/button.tsx
4108
+ var import_react9 = require("react");
4109
+
4110
+ // src/utils/cn.ts
4111
+ function cn(...classes) {
4112
+ return classes.filter(Boolean).join(" ");
4113
+ }
4114
+
4115
+ // src/ui/button.tsx
4116
+ var import_jsx_runtime5 = require("react/jsx-runtime");
4117
+ var variantClasses = {
4118
+ default: "ac-bg-accent ac-text-accent-foreground hover:ac-bg-accent-muted ac-border ac-border-border",
4119
+ ghost: "ac-bg-transparent hover:ac-bg-surface-muted ac-text-text",
4120
+ outline: "ac-bg-transparent ac-text-text ac-border ac-border-border hover:ac-bg-surface-muted",
4121
+ destructive: "ac-bg-destructive ac-text-accent-foreground hover:ac-bg-destructive/80"
4122
+ };
4123
+ var sizeClasses = {
4124
+ sm: "ac-h-8 ac-px-3 ac-text-sm",
4125
+ md: "ac-h-10 ac-px-4 ac-text-sm",
4126
+ lg: "ac-h-11 ac-px-5 ac-text-base"
4127
+ };
4128
+ var Button = (0, import_react9.forwardRef)(
4129
+ ({ className, variant = "default", size = "md", ...props }, ref) => {
4130
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
4131
+ "button",
4132
+ {
4133
+ ref,
4134
+ className: cn(
4135
+ "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",
4136
+ variantClasses[variant],
4137
+ sizeClasses[size],
4138
+ className
4139
+ ),
4140
+ ...props
4141
+ }
4142
+ );
4143
+ }
4144
+ );
4145
+ Button.displayName = "Button";
4146
+
4147
+ // src/ui/input.tsx
4148
+ var import_react10 = require("react");
4149
+ var import_jsx_runtime6 = require("react/jsx-runtime");
4150
+ var Input = (0, import_react10.forwardRef)(
4151
+ ({ className, ...props }, ref) => {
4152
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
4153
+ "input",
4154
+ {
4155
+ ref,
4156
+ className: cn(
4157
+ "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",
4158
+ className
4159
+ ),
4160
+ ...props
4161
+ }
4162
+ );
4163
+ }
4164
+ );
4165
+ Input.displayName = "Input";
4166
+
2131
4167
  // src/InspectDialog/InspectDialogManager.tsx
2132
- var import_react7 = require("react");
4168
+ var import_react12 = require("react");
2133
4169
 
2134
4170
  // src/InspectDialog/InspectSimple.tsx
2135
- var import_react6 = require("react");
2136
- var import_anyclick_core4 = require("@ewjdev/anyclick-core");
2137
- var import_lucide_react3 = require("lucide-react");
4171
+ var import_react11 = require("react");
4172
+ var import_anyclick_core6 = require("@ewjdev/anyclick-core");
4173
+ var import_lucide_react4 = require("lucide-react");
2138
4174
 
2139
4175
  // src/ide.ts
2140
4176
  var DEFAULT_IDE_CONFIG = {
@@ -2236,7 +4272,7 @@ function formatSourceLocation(location) {
2236
4272
  }
2237
4273
 
2238
4274
  // src/InspectDialog/InspectSimple.tsx
2239
- var import_jsx_runtime4 = require("react/jsx-runtime");
4275
+ var import_jsx_runtime7 = require("react/jsx-runtime");
2240
4276
  var DEFAULT_COMPACT_CONFIG = {
2241
4277
  scale: 0.5,
2242
4278
  fonts: {
@@ -2298,8 +4334,8 @@ function downloadDataUrl(dataUrl, filename) {
2298
4334
  link.click();
2299
4335
  }
2300
4336
  function useIsMobile() {
2301
- const [isMobile, setIsMobile] = (0, import_react6.useState)(false);
2302
- (0, import_react6.useEffect)(() => {
4337
+ const [isMobile, setIsMobile] = (0, import_react11.useState)(false);
4338
+ (0, import_react11.useEffect)(() => {
2303
4339
  const mq = window.matchMedia("(max-width: 640px)");
2304
4340
  setIsMobile(mq.matches);
2305
4341
  const handler = (e) => setIsMobile(e.matches);
@@ -2317,20 +4353,20 @@ function InspectSimple({
2317
4353
  className,
2318
4354
  highlightColors
2319
4355
  }) {
2320
- const [info, setInfo] = (0, import_react6.useState)(null);
2321
- const [sourceLocation, setSourceLocation] = (0, import_react6.useState)(
4356
+ const [info, setInfo] = (0, import_react11.useState)(null);
4357
+ const [sourceLocation, setSourceLocation] = (0, import_react11.useState)(
2322
4358
  null
2323
4359
  );
2324
- const [status, setStatus] = (0, import_react6.useState)(null);
2325
- const [saving, setSaving] = (0, import_react6.useState)(false);
2326
- const dialogRef = (0, import_react6.useRef)(null);
4360
+ const [status, setStatus] = (0, import_react11.useState)(null);
4361
+ const [saving, setSaving] = (0, import_react11.useState)(false);
4362
+ const dialogRef = (0, import_react11.useRef)(null);
2327
4363
  const isMobile = useIsMobile();
2328
- (0, import_react6.useEffect)(() => {
4364
+ (0, import_react11.useEffect)(() => {
2329
4365
  if (!status) return;
2330
4366
  const timer = setTimeout(() => setStatus(null), 5e3);
2331
4367
  return () => clearTimeout(timer);
2332
4368
  }, [status]);
2333
- (0, import_react6.useEffect)(() => {
4369
+ (0, import_react11.useEffect)(() => {
2334
4370
  if (!visible || !targetElement) return;
2335
4371
  try {
2336
4372
  clearHighlights();
@@ -2339,7 +4375,7 @@ function InspectSimple({
2339
4375
  if (container) highlightContainer(container, highlightColors);
2340
4376
  } catch {
2341
4377
  }
2342
- const nextInfo = (0, import_anyclick_core4.getElementInspectInfo)(targetElement);
4378
+ const nextInfo = (0, import_anyclick_core6.getElementInspectInfo)(targetElement);
2343
4379
  setInfo(nextInfo);
2344
4380
  setSourceLocation(
2345
4381
  findSourceLocationInAncestors(targetElement) ?? nextInfo.sourceLocation ?? null
@@ -2348,7 +4384,7 @@ function InspectSimple({
2348
4384
  clearHighlights();
2349
4385
  };
2350
4386
  }, [visible, targetElement, highlightColors]);
2351
- (0, import_react6.useEffect)(() => {
4387
+ (0, import_react11.useEffect)(() => {
2352
4388
  if (!visible) return;
2353
4389
  const handleClickOutside = (e) => {
2354
4390
  if (dialogRef.current && !dialogRef.current.contains(e.target)) {
@@ -2363,7 +4399,7 @@ function InspectSimple({
2363
4399
  document.removeEventListener("mousedown", handleClickOutside);
2364
4400
  };
2365
4401
  }, [visible, onClose]);
2366
- const identityLabel = (0, import_react6.useMemo)(() => {
4402
+ const identityLabel = (0, import_react11.useMemo)(() => {
2367
4403
  if (!info) return "Select an element";
2368
4404
  const classes = info.classNames[0] ? `.${info.classNames[0]}` : "";
2369
4405
  const id = info.id ? `#${info.id}` : "";
@@ -2393,7 +4429,7 @@ function InspectSimple({
2393
4429
  if (!targetElement) return;
2394
4430
  setSaving(true);
2395
4431
  setStatus("Capturing screenshot\u2026");
2396
- const result = await (0, import_anyclick_core4.captureScreenshot)(targetElement, null, "element");
4432
+ const result = await (0, import_anyclick_core6.captureScreenshot)(targetElement, null, "element");
2397
4433
  setSaving(false);
2398
4434
  if (result.capture?.dataUrl) {
2399
4435
  downloadDataUrl(result.capture.dataUrl, "anyclick-inspect.png");
@@ -2449,8 +4485,8 @@ function InspectSimple({
2449
4485
  overflow: "hidden",
2450
4486
  ...style
2451
4487
  };
2452
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
2453
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4488
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
4489
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2454
4490
  "div",
2455
4491
  {
2456
4492
  style: {
@@ -2465,14 +4501,14 @@ function InspectSimple({
2465
4501
  role: "presentation"
2466
4502
  }
2467
4503
  ),
2468
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
4504
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2469
4505
  "div",
2470
4506
  {
2471
4507
  ref: dialogRef,
2472
4508
  className: `anyclick-tiny-inspect ${className ?? ""}`,
2473
4509
  style: dialogStyles,
2474
4510
  children: [
2475
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
4511
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2476
4512
  "div",
2477
4513
  {
2478
4514
  style: {
@@ -2484,7 +4520,7 @@ function InspectSimple({
2484
4520
  borderBottom: "1px solid #1e293b"
2485
4521
  },
2486
4522
  children: [
2487
- isMobile && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4523
+ isMobile && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2488
4524
  "div",
2489
4525
  {
2490
4526
  style: {
@@ -2499,8 +4535,8 @@ function InspectSimple({
2499
4535
  }
2500
4536
  }
2501
4537
  ),
2502
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
2503
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4538
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
4539
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2504
4540
  "span",
2505
4541
  {
2506
4542
  style: {
@@ -2515,31 +4551,31 @@ function InspectSimple({
2515
4551
  children: identityLabel
2516
4552
  }
2517
4553
  ),
2518
- sourceLocation && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4554
+ sourceLocation && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2519
4555
  "button",
2520
4556
  {
2521
4557
  type: "button",
2522
4558
  onClick: handleOpenIDE,
2523
4559
  title: formatSourceLocation(sourceLocation),
2524
4560
  style: iconBtnStyle,
2525
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react3.ExternalLink, { size: 14 })
4561
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react4.ExternalLink, { size: 14 })
2526
4562
  }
2527
4563
  )
2528
4564
  ] }),
2529
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4565
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2530
4566
  "button",
2531
4567
  {
2532
4568
  type: "button",
2533
4569
  onClick: onClose,
2534
4570
  style: iconBtnStyle,
2535
4571
  "aria-label": "Close inspector",
2536
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react3.X, { size: 16 })
4572
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react4.X, { size: 16 })
2537
4573
  }
2538
4574
  )
2539
4575
  ]
2540
4576
  }
2541
4577
  ),
2542
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
4578
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2543
4579
  "div",
2544
4580
  {
2545
4581
  style: {
@@ -2549,7 +4585,7 @@ function InspectSimple({
2549
4585
  gap: 8
2550
4586
  },
2551
4587
  children: [
2552
- info?.selector && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4588
+ info?.selector && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2553
4589
  "code",
2554
4590
  {
2555
4591
  style: {
@@ -2564,7 +4600,7 @@ function InspectSimple({
2564
4600
  children: info.selector
2565
4601
  }
2566
4602
  ),
2567
- status && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4603
+ status && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2568
4604
  "div",
2569
4605
  {
2570
4606
  style: {
@@ -2576,7 +4612,7 @@ function InspectSimple({
2576
4612
  children: status
2577
4613
  }
2578
4614
  ),
2579
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
4615
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2580
4616
  "div",
2581
4617
  {
2582
4618
  style: {
@@ -2585,7 +4621,7 @@ function InspectSimple({
2585
4621
  gap: 6
2586
4622
  },
2587
4623
  children: [
2588
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4624
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2589
4625
  "button",
2590
4626
  {
2591
4627
  type: "button",
@@ -2593,10 +4629,10 @@ function InspectSimple({
2593
4629
  style: iconActionStyle,
2594
4630
  title: "Copy CSS selector",
2595
4631
  "aria-label": "Copy CSS selector",
2596
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react3.Copy, { size: 15 })
4632
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react4.Copy, { size: 15 })
2597
4633
  }
2598
4634
  ),
2599
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4635
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2600
4636
  "button",
2601
4637
  {
2602
4638
  type: "button",
@@ -2604,10 +4640,10 @@ function InspectSimple({
2604
4640
  style: iconActionStyle,
2605
4641
  title: "Copy text content",
2606
4642
  "aria-label": "Copy text content",
2607
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react3.FileText, { size: 15 })
4643
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react4.FileText, { size: 15 })
2608
4644
  }
2609
4645
  ),
2610
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4646
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2611
4647
  "button",
2612
4648
  {
2613
4649
  type: "button",
@@ -2615,10 +4651,10 @@ function InspectSimple({
2615
4651
  style: iconActionStyle,
2616
4652
  title: "Copy HTML markup",
2617
4653
  "aria-label": "Copy HTML markup",
2618
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react3.Code, { size: 15 })
4654
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react4.Code, { size: 15 })
2619
4655
  }
2620
4656
  ),
2621
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4657
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2622
4658
  "button",
2623
4659
  {
2624
4660
  type: "button",
@@ -2630,7 +4666,7 @@ function InspectSimple({
2630
4666
  disabled: saving,
2631
4667
  title: "Save screenshot",
2632
4668
  "aria-label": "Save screenshot",
2633
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react3.Camera, { size: 15 })
4669
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react4.Camera, { size: 15 })
2634
4670
  }
2635
4671
  )
2636
4672
  ]
@@ -2673,7 +4709,7 @@ var iconActionStyle = {
2673
4709
  };
2674
4710
 
2675
4711
  // src/InspectDialog/InspectDialogManager.tsx
2676
- var import_jsx_runtime5 = require("react/jsx-runtime");
4712
+ var import_jsx_runtime8 = require("react/jsx-runtime");
2677
4713
  var INSPECT_DIALOG_EVENT = "anyclick:inspect";
2678
4714
  function openInspectDialog(targetElement) {
2679
4715
  if (typeof window === "undefined") return;
@@ -2696,16 +4732,16 @@ function InspectDialogManager({
2696
4732
  initialPinnedPosition = "floating",
2697
4733
  compactConfig
2698
4734
  }) {
2699
- const [visible, setVisible] = (0, import_react7.useState)(false);
2700
- const [targetElement, setTargetElement] = (0, import_react7.useState)(null);
2701
- const handleClose = (0, import_react7.useCallback)(() => {
4735
+ const [visible, setVisible] = (0, import_react12.useState)(false);
4736
+ const [targetElement, setTargetElement] = (0, import_react12.useState)(null);
4737
+ const handleClose = (0, import_react12.useCallback)(() => {
2702
4738
  setVisible(false);
2703
4739
  setTargetElement(null);
2704
4740
  }, []);
2705
- const handleSelectElement = (0, import_react7.useCallback)((element) => {
4741
+ const handleSelectElement = (0, import_react12.useCallback)((element) => {
2706
4742
  setTargetElement(element);
2707
4743
  }, []);
2708
- (0, import_react7.useEffect)(() => {
4744
+ (0, import_react12.useEffect)(() => {
2709
4745
  const handleInspectEvent = (event) => {
2710
4746
  const customEvent = event;
2711
4747
  if (customEvent.detail?.targetElement) {
@@ -2718,7 +4754,7 @@ function InspectDialogManager({
2718
4754
  window.removeEventListener(INSPECT_DIALOG_EVENT, handleInspectEvent);
2719
4755
  };
2720
4756
  }, []);
2721
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
4757
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2722
4758
  InspectSimple,
2723
4759
  {
2724
4760
  visible,
@@ -2780,6 +4816,20 @@ var presetDefaults = {
2780
4816
  showComment: false,
2781
4817
  type: "search_google"
2782
4818
  },
4819
+ {
4820
+ label: "Ask t3.chat",
4821
+ onClick: ({ closeMenu }) => {
4822
+ closeMenu();
4823
+ if (typeof window === "undefined") return false;
4824
+ const selection = window.getSelection()?.toString().trim();
4825
+ const query = selection && selection.length > 0 ? selection : "";
4826
+ const url = query ? `https://t3.chat/?q=${encodeURIComponent(query)}` : "https://t3.chat";
4827
+ window.open(url, "_blank", "noopener,noreferrer");
4828
+ return false;
4829
+ },
4830
+ showComment: false,
4831
+ type: "ask_t3chat"
4832
+ },
2783
4833
  {
2784
4834
  label: "Share\u2026",
2785
4835
  onClick: async ({ closeMenu }) => {
@@ -3049,6 +5099,172 @@ function listPresets() {
3049
5099
  }))
3050
5100
  }));
3051
5101
  }
5102
+ function createT3ChatMenuItem(options = {}) {
5103
+ const { label = "Ask t3.chat", baseUrl = "https://t3.chat" } = options;
5104
+ return {
5105
+ label,
5106
+ onClick: ({ closeMenu }) => {
5107
+ closeMenu();
5108
+ if (typeof window === "undefined") return false;
5109
+ const selection = window.getSelection()?.toString().trim();
5110
+ const query = selection && selection.length > 0 ? selection : "";
5111
+ const url = query ? `${baseUrl}/?q=${encodeURIComponent(query)}` : baseUrl;
5112
+ window.open(url, "_blank", "noopener,noreferrer");
5113
+ return false;
5114
+ },
5115
+ showComment: false,
5116
+ type: "ask_t3chat"
5117
+ };
5118
+ }
5119
+ function getSelectedText() {
5120
+ if (typeof window === "undefined") return "";
5121
+ return window.getSelection()?.toString().trim() ?? "";
5122
+ }
5123
+ function hasTextSelection() {
5124
+ return getSelectedText().length > 0;
5125
+ }
5126
+ function detectImageElement(element) {
5127
+ if (!element) return { isImage: false };
5128
+ if (element.tagName === "IMG") {
5129
+ const img = element;
5130
+ return {
5131
+ isImage: true,
5132
+ src: img.src || img.currentSrc,
5133
+ type: "img"
5134
+ };
5135
+ }
5136
+ if (element.tagName === "PICTURE") {
5137
+ const img = element.querySelector("img");
5138
+ return {
5139
+ isImage: true,
5140
+ src: img?.src || img?.currentSrc,
5141
+ type: "picture"
5142
+ };
5143
+ }
5144
+ if (element.tagName === "SVG" || element.tagName === "svg") {
5145
+ return {
5146
+ isImage: true,
5147
+ type: "svg"
5148
+ };
5149
+ }
5150
+ if (element.tagName === "CANVAS") {
5151
+ return {
5152
+ isImage: true,
5153
+ type: "canvas"
5154
+ };
5155
+ }
5156
+ if (typeof window !== "undefined") {
5157
+ const computedStyle = window.getComputedStyle(element);
5158
+ const backgroundImage = computedStyle.backgroundImage;
5159
+ if (backgroundImage && backgroundImage !== "none") {
5160
+ const urlMatch = backgroundImage.match(/url\(["']?(.+?)["']?\)/);
5161
+ if (urlMatch) {
5162
+ return {
5163
+ isImage: true,
5164
+ src: urlMatch[1],
5165
+ type: "background"
5166
+ };
5167
+ }
5168
+ }
5169
+ }
5170
+ const imgChild = element.querySelector("img");
5171
+ if (imgChild) {
5172
+ return {
5173
+ isImage: true,
5174
+ src: imgChild.src || imgChild.currentSrc,
5175
+ type: "img"
5176
+ };
5177
+ }
5178
+ return { isImage: false };
5179
+ }
5180
+ function createUploadThingMenuItem(options = {}) {
5181
+ const {
5182
+ label = "Upload to UploadThing",
5183
+ endpoint = "/api/uploadthing",
5184
+ onUploadComplete,
5185
+ onUploadError
5186
+ } = options;
5187
+ return {
5188
+ label,
5189
+ onClick: async ({ closeMenu, targetElement }) => {
5190
+ if (!targetElement) {
5191
+ onUploadError?.(new Error("No target element"));
5192
+ return false;
5193
+ }
5194
+ try {
5195
+ const imageInfo = detectImageElement(targetElement);
5196
+ if (imageInfo.isImage && imageInfo.src) {
5197
+ const response = await fetch(imageInfo.src);
5198
+ const blob = await response.blob();
5199
+ const formData = new FormData();
5200
+ const filename = `image-${Date.now()}.${blob.type.split("/")[1] || "png"}`;
5201
+ formData.append("file", blob, filename);
5202
+ const uploadResponse = await fetch(endpoint, {
5203
+ method: "POST",
5204
+ body: formData
5205
+ });
5206
+ if (!uploadResponse.ok) {
5207
+ throw new Error(`Upload failed: ${uploadResponse.status}`);
5208
+ }
5209
+ const result = await uploadResponse.json();
5210
+ onUploadComplete?.(result);
5211
+ } else if (imageInfo.isImage && imageInfo.type === "canvas") {
5212
+ const canvas = targetElement;
5213
+ const dataUrl = canvas.toDataURL("image/png");
5214
+ const response = await fetch(dataUrl);
5215
+ const blob = await response.blob();
5216
+ const formData = new FormData();
5217
+ formData.append("file", blob, `canvas-${Date.now()}.png`);
5218
+ const uploadResponse = await fetch(endpoint, {
5219
+ method: "POST",
5220
+ body: formData
5221
+ });
5222
+ if (!uploadResponse.ok) {
5223
+ throw new Error(`Upload failed: ${uploadResponse.status}`);
5224
+ }
5225
+ const result = await uploadResponse.json();
5226
+ onUploadComplete?.(result);
5227
+ } else {
5228
+ onUploadError?.(
5229
+ new Error(
5230
+ "Element is not an image. Screenshot upload requires anyclick-core."
5231
+ )
5232
+ );
5233
+ }
5234
+ } catch (error) {
5235
+ onUploadError?.(
5236
+ error instanceof Error ? error : new Error(String(error))
5237
+ );
5238
+ }
5239
+ closeMenu();
5240
+ return false;
5241
+ },
5242
+ showComment: false,
5243
+ type: "upload_image"
5244
+ };
5245
+ }
5246
+ function createUploadScreenshotMenuItem(options = {}) {
5247
+ const {
5248
+ label = "Upload Screenshot",
5249
+ endpoint = "/api/uploadthing",
5250
+ onUploadComplete,
5251
+ onUploadError
5252
+ } = options;
5253
+ return {
5254
+ label,
5255
+ badge: { label: "Coming soon", tone: "info" },
5256
+ status: "comingSoon",
5257
+ onClick: async ({ closeMenu }) => {
5258
+ onUploadError?.(
5259
+ new Error("Screenshot upload will be available in a future release")
5260
+ );
5261
+ closeMenu();
5262
+ return false;
5263
+ },
5264
+ showComment: false,
5265
+ type: "upload_screenshot"
5266
+ };
5267
+ }
3052
5268
 
3053
5269
  // src/types.ts
3054
5270
  function filterMenuItemsByRole(items, userContext) {
@@ -3067,10 +5283,10 @@ function filterMenuItemsByRole(items, userContext) {
3067
5283
  }
3068
5284
 
3069
5285
  // src/index.ts
3070
- var import_anyclick_core5 = require("@ewjdev/anyclick-core");
5286
+ var import_anyclick_core7 = require("@ewjdev/anyclick-core");
3071
5287
 
3072
5288
  // src/AnyclickLogo.tsx
3073
- var import_jsx_runtime6 = require("react/jsx-runtime");
5289
+ var import_jsx_runtime9 = require("react/jsx-runtime");
3074
5290
  function AnyclickLogo({
3075
5291
  size = 64,
3076
5292
  borderWidth = 2,
@@ -3082,7 +5298,7 @@ function AnyclickLogo({
3082
5298
  }) {
3083
5299
  const cursorSize = size * 0.45;
3084
5300
  const cursorStroke = borderWidth;
3085
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
5301
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
3086
5302
  "svg",
3087
5303
  {
3088
5304
  width: size,
@@ -3096,7 +5312,7 @@ function AnyclickLogo({
3096
5312
  role: onClick ? "button" : "img",
3097
5313
  "aria-label": "Anyclick Logo",
3098
5314
  children: [
3099
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
5315
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3100
5316
  "circle",
3101
5317
  {
3102
5318
  cx: size / 2,
@@ -3107,11 +5323,11 @@ function AnyclickLogo({
3107
5323
  strokeWidth: borderWidth
3108
5324
  }
3109
5325
  ),
3110
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
5326
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3111
5327
  "g",
3112
5328
  {
3113
5329
  transform: `translate(${(size - cursorSize) / 2}, ${(size - cursorSize) / 2})`,
3114
- children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
5330
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3115
5331
  "path",
3116
5332
  {
3117
5333
  d: `
@@ -3139,24 +5355,28 @@ function AnyclickLogo({
3139
5355
  }
3140
5356
 
3141
5357
  // src/index.ts
3142
- var import_anyclick_core6 = require("@ewjdev/anyclick-core");
5358
+ var import_anyclick_core8 = require("@ewjdev/anyclick-core");
3143
5359
  // Annotate the CommonJS export names for ESM import in node:
3144
5360
  0 && (module.exports = {
3145
5361
  ALL_CURATED_PROPERTIES,
3146
5362
  AnyclickContext,
3147
5363
  AnyclickLogo,
3148
5364
  AnyclickProvider,
5365
+ Button,
3149
5366
  CURATED_STYLE_PROPERTIES,
3150
5367
  ContextMenu,
3151
5368
  DEFAULT_COMPACT_CONFIG,
5369
+ DEFAULT_QUICK_CHAT_CONFIG,
3152
5370
  DEFAULT_SCREENSHOT_CONFIG,
3153
5371
  DEFAULT_SENSITIVE_SELECTORS,
3154
5372
  FeedbackContext,
3155
5373
  FeedbackProvider,
3156
5374
  FunModeBridge,
3157
5375
  INSPECT_DIALOG_EVENT,
5376
+ Input,
3158
5377
  InspectDialogManager,
3159
5378
  InspectSimple,
5379
+ QuickChat,
3160
5380
  ScreenshotPreview,
3161
5381
  applyHighlights,
3162
5382
  buildIDEUrl,
@@ -3165,9 +5385,13 @@ var import_anyclick_core6 = require("@ewjdev/anyclick-core");
3165
5385
  clearHighlights,
3166
5386
  createIDEOpener,
3167
5387
  createPresetMenu,
5388
+ createT3ChatMenuItem,
5389
+ createUploadScreenshotMenuItem,
5390
+ createUploadThingMenuItem,
3168
5391
  darkMenuStyles,
3169
5392
  defaultContainerSelectors,
3170
5393
  defaultHighlightColors,
5394
+ detectImageElement,
3171
5395
  detectPreferredIDE,
3172
5396
  dispatchContextMenuEvent,
3173
5397
  estimateTotalSize,
@@ -3185,7 +5409,9 @@ var import_anyclick_core6 = require("@ewjdev/anyclick-core");
3185
5409
  getBoxModelInfo,
3186
5410
  getComputedStyles,
3187
5411
  getElementInspectInfo,
5412
+ getSelectedText,
3188
5413
  getSourceLocationFromElement,
5414
+ hasTextSelection,
3189
5415
  highlightContainer,
3190
5416
  highlightTarget,
3191
5417
  isIDEProtocolSupported,
@@ -3196,8 +5422,11 @@ var import_anyclick_core6 = require("@ewjdev/anyclick-core");
3196
5422
  openInIDE,
3197
5423
  openInspectDialog,
3198
5424
  presetDefaults,
5425
+ quickChatKeyframes,
5426
+ quickChatStyles,
3199
5427
  useAnyclick,
3200
5428
  useFeedback,
3201
- useProviderStore
5429
+ useProviderStore,
5430
+ useQuickChat
3202
5431
  });
3203
5432
  //# sourceMappingURL=index.js.map