@ewjdev/anyclick-react 3.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,28 +31,30 @@ 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_core7.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_core7.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
42
  DEFAULT_QUICK_CHAT_CONFIG: () => DEFAULT_QUICK_CHAT_CONFIG,
42
- DEFAULT_SCREENSHOT_CONFIG: () => import_anyclick_core6.DEFAULT_SCREENSHOT_CONFIG,
43
- DEFAULT_SENSITIVE_SELECTORS: () => import_anyclick_core6.DEFAULT_SENSITIVE_SELECTORS,
43
+ DEFAULT_SCREENSHOT_CONFIG: () => import_anyclick_core7.DEFAULT_SCREENSHOT_CONFIG,
44
+ DEFAULT_SENSITIVE_SELECTORS: () => import_anyclick_core7.DEFAULT_SENSITIVE_SELECTORS,
44
45
  FeedbackContext: () => FeedbackContext,
45
46
  FeedbackProvider: () => FeedbackProvider,
46
47
  FunModeBridge: () => FunModeBridge,
47
48
  INSPECT_DIALOG_EVENT: () => INSPECT_DIALOG_EVENT,
49
+ Input: () => Input,
48
50
  InspectDialogManager: () => InspectDialogManager,
49
51
  InspectSimple: () => InspectSimple,
50
52
  QuickChat: () => QuickChat,
51
53
  ScreenshotPreview: () => ScreenshotPreview,
52
54
  applyHighlights: () => applyHighlights,
53
55
  buildIDEUrl: () => buildIDEUrl,
54
- captureAllScreenshots: () => import_anyclick_core6.captureAllScreenshots,
55
- captureScreenshot: () => import_anyclick_core6.captureScreenshot,
56
+ captureAllScreenshots: () => import_anyclick_core7.captureAllScreenshots,
57
+ captureScreenshot: () => import_anyclick_core7.captureScreenshot,
56
58
  clearHighlights: () => clearHighlights,
57
59
  createIDEOpener: () => createIDEOpener,
58
60
  createPresetMenu: () => createPresetMenu,
@@ -65,28 +67,28 @@ __export(index_exports, {
65
67
  detectImageElement: () => detectImageElement,
66
68
  detectPreferredIDE: () => detectPreferredIDE,
67
69
  dispatchContextMenuEvent: () => dispatchContextMenuEvent,
68
- estimateTotalSize: () => import_anyclick_core6.estimateTotalSize,
70
+ estimateTotalSize: () => import_anyclick_core7.estimateTotalSize,
69
71
  filterMenuItemsByRole: () => filterMenuItemsByRole,
70
72
  findContainerParent: () => findContainerParent,
71
73
  findSourceLocationInAncestors: () => findSourceLocationInAncestors,
72
- formatBoxModel: () => import_anyclick_core7.formatBoxModel,
73
- formatBytes: () => import_anyclick_core6.formatBytes,
74
+ formatBoxModel: () => import_anyclick_core8.formatBoxModel,
75
+ formatBytes: () => import_anyclick_core7.formatBytes,
74
76
  formatSourceLocation: () => formatSourceLocation,
75
- formatStylesAsCSS: () => import_anyclick_core7.formatStylesAsCSS,
77
+ formatStylesAsCSS: () => import_anyclick_core8.formatStylesAsCSS,
76
78
  generateProviderId: () => generateProviderId,
77
- getAccessibilityInfo: () => import_anyclick_core7.getAccessibilityInfo,
78
- getAttributes: () => import_anyclick_core7.getAttributes,
79
+ getAccessibilityInfo: () => import_anyclick_core8.getAccessibilityInfo,
80
+ getAttributes: () => import_anyclick_core8.getAttributes,
79
81
  getBadgeStyle: () => getBadgeStyle,
80
- getBoxModelInfo: () => import_anyclick_core7.getBoxModelInfo,
81
- getComputedStyles: () => import_anyclick_core7.getComputedStyles,
82
- getElementInspectInfo: () => import_anyclick_core7.getElementInspectInfo,
82
+ getBoxModelInfo: () => import_anyclick_core8.getBoxModelInfo,
83
+ getComputedStyles: () => import_anyclick_core8.getComputedStyles,
84
+ getElementInspectInfo: () => import_anyclick_core8.getElementInspectInfo,
83
85
  getSelectedText: () => getSelectedText,
84
86
  getSourceLocationFromElement: () => getSourceLocationFromElement,
85
87
  hasTextSelection: () => hasTextSelection,
86
88
  highlightContainer: () => highlightContainer,
87
89
  highlightTarget: () => highlightTarget,
88
90
  isIDEProtocolSupported: () => isIDEProtocolSupported,
89
- isScreenshotSupported: () => import_anyclick_core6.isScreenshotSupported,
91
+ isScreenshotSupported: () => import_anyclick_core7.isScreenshotSupported,
90
92
  listPresets: () => listPresets,
91
93
  menuCSSVariables: () => menuCSSVariables,
92
94
  menuStyles: () => menuStyles,
@@ -103,16 +105,17 @@ __export(index_exports, {
103
105
  module.exports = __toCommonJS(index_exports);
104
106
 
105
107
  // src/AnyclickProvider.tsx
106
- var import_react6 = require("react");
107
- var import_anyclick_core4 = require("@ewjdev/anyclick-core");
108
+ var import_react7 = require("react");
109
+ var import_anyclick_core5 = require("@ewjdev/anyclick-core");
108
110
 
109
111
  // src/ContextMenu.tsx
110
- var import_react4 = __toESM(require("react"));
111
- var import_anyclick_core3 = require("@ewjdev/anyclick-core");
112
+ var import_react5 = __toESM(require("react"));
113
+ var import_anyclick_core4 = require("@ewjdev/anyclick-core");
112
114
  var import_lucide_react3 = require("lucide-react");
113
115
 
114
116
  // src/QuickChat/QuickChat.tsx
115
- var import_react2 = __toESM(require("react"));
117
+ var import_react3 = __toESM(require("react"));
118
+ var import_anyclick_core2 = require("@ewjdev/anyclick-core");
116
119
  var import_lucide_react = require("lucide-react");
117
120
 
118
121
  // src/QuickChat/styles.ts
@@ -294,6 +297,7 @@ var quickChatStyles = {
294
297
  // Suggestions - compact horizontal scroll
295
298
  suggestionsContainer: {
296
299
  display: "flex",
300
+ flexDirection: "column",
297
301
  gap: "6px",
298
302
  padding: "6px 8px",
299
303
  overflowX: "auto",
@@ -557,7 +561,130 @@ var quickChatKeyframes = `
557
561
 
558
562
  // src/QuickChat/useQuickChat.ts
559
563
  var import_react = require("react");
560
- var import_anyclick_core = require("@ewjdev/anyclick-core");
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
+ }
561
688
 
562
689
  // src/QuickChat/types.ts
563
690
  var DEFAULT_T3CHAT_CONFIG = {
@@ -567,9 +694,9 @@ var DEFAULT_T3CHAT_CONFIG = {
567
694
  };
568
695
  var DEFAULT_QUICK_CHAT_CONFIG = {
569
696
  endpoint: "/api/anyclick/chat",
570
- model: "gpt-5-mini",
697
+ model: "gpt-5-nano",
571
698
  prePassModel: "gpt-5-nano",
572
- maxResponseLength: 500,
699
+ maxResponseLength: 1e4,
573
700
  showRedactionUI: true,
574
701
  showSuggestions: true,
575
702
  systemPrompt: "You are a helpful assistant that provides quick, concise answers about web elements and UI. Keep responses brief and actionable.",
@@ -578,57 +705,9 @@ var DEFAULT_QUICK_CHAT_CONFIG = {
578
705
  t3chat: DEFAULT_T3CHAT_CONFIG
579
706
  };
580
707
 
581
- // src/QuickChat/useQuickChat.ts
582
- var PINNED_STATE_KEY = "anyclick-quick-chat-pinned";
583
- var CHAT_HISTORY_KEY = "anyclick-quick-chat-history";
584
- function generateId() {
585
- return `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
586
- }
587
- function getPinnedState() {
588
- if (typeof window === "undefined") return false;
589
- try {
590
- return sessionStorage.getItem(PINNED_STATE_KEY) === "true";
591
- } catch {
592
- return false;
593
- }
594
- }
595
- function getChatHistory() {
596
- if (typeof window === "undefined") return [];
597
- try {
598
- const stored = sessionStorage.getItem(CHAT_HISTORY_KEY);
599
- if (stored) {
600
- const parsed = JSON.parse(stored);
601
- return parsed.map((msg) => ({
602
- ...msg,
603
- isStreaming: false,
604
- actions: void 0
605
- }));
606
- }
607
- } catch {
608
- }
609
- return [];
610
- }
611
- function saveChatHistory(messages) {
612
- if (typeof window === "undefined") return;
613
- try {
614
- const toStore = messages.slice(-10).map((msg) => ({
615
- id: msg.id,
616
- role: msg.role,
617
- content: msg.content,
618
- timestamp: msg.timestamp
619
- }));
620
- sessionStorage.setItem(CHAT_HISTORY_KEY, JSON.stringify(toStore));
621
- } catch {
622
- }
623
- }
624
- function clearChatHistory() {
625
- if (typeof window === "undefined") return;
626
- try {
627
- sessionStorage.removeItem(CHAT_HISTORY_KEY);
628
- } catch {
629
- }
630
- }
631
- function extractContextChunks(targetElement, containerElement) {
708
+ // src/QuickChat/useQuickChat.context.ts
709
+ var import_anyclick_core = require("@ewjdev/anyclick-core");
710
+ function extractContextChunks(targetElement, _containerElement) {
632
711
  const chunks = [];
633
712
  if (!targetElement) return chunks;
634
713
  try {
@@ -655,7 +734,7 @@ function extractContextChunks(targetElement, containerElement) {
655
734
  }
656
735
  if (info.computedStyles) {
657
736
  const styleEntries = [];
658
- for (const [category, styles] of Object.entries(info.computedStyles)) {
737
+ for (const [, styles] of Object.entries(info.computedStyles)) {
659
738
  if (styles && typeof styles === "object") {
660
739
  const entries = Object.entries(styles).slice(0, 2);
661
740
  for (const [k, v] of entries) {
@@ -693,7 +772,9 @@ function extractContextChunks(targetElement, containerElement) {
693
772
  }
694
773
  }
695
774
  if (info.boxModel) {
696
- const boxContent = `${Math.round(info.boxModel.content.width)}x${Math.round(info.boxModel.content.height)}px`;
775
+ const boxContent = `${Math.round(info.boxModel.content.width)}x${Math.round(
776
+ info.boxModel.content.height
777
+ )}px`;
697
778
  chunks.push({
698
779
  id: "element-dimensions",
699
780
  label: "Dimensions",
@@ -704,54 +785,358 @@ function extractContextChunks(targetElement, containerElement) {
704
785
  });
705
786
  }
706
787
  } catch (error) {
707
- console.error("Failed to extract context:", error);
788
+ console.error("[useQuickChat] Failed to extract context:", error);
708
789
  }
709
790
  return chunks;
710
791
  }
711
792
  function buildContextString(chunks) {
712
- const includedChunks = chunks.filter((c) => c.included);
713
- if (includedChunks.length === 0) return "";
714
- return includedChunks.map((c) => `[${c.label}]: ${c.content}`).join("\n");
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;
715
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
716
946
  function useQuickChat(targetElement, containerElement, config = {}) {
947
+ console.count("useQuickChat");
717
948
  const mergedConfig = { ...DEFAULT_QUICK_CHAT_CONFIG, ...config };
718
- const abortControllerRef = (0, import_react.useRef)(null);
719
949
  const initializedRef = (0, import_react.useRef)(false);
720
- const [state, setState] = (0, import_react.useState)({
721
- input: "",
722
- messages: [],
723
- isLoadingSuggestions: false,
724
- isSending: false,
725
- isStreaming: false,
726
- suggestedPrompts: [],
727
- contextChunks: [],
728
- error: null
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
+ }
729
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;
730
1118
  (0, import_react.useEffect)(() => {
731
1119
  if (initializedRef.current) return;
732
1120
  initializedRef.current = true;
733
- const isPinned = getPinnedState();
734
- if (isPinned) {
735
- const history = getChatHistory();
736
- if (history.length > 0) {
737
- setState((prev) => ({ ...prev, messages: history }));
738
- }
1121
+ if (storedMessages.length > 0) {
1122
+ setAiMessages(chatMessagesToUiMessages(storedMessages));
1123
+ } else {
1124
+ void loadFromBackend();
739
1125
  }
740
- }, []);
1126
+ }, [loadFromBackend, setAiMessages, storedMessages]);
741
1127
  (0, import_react.useEffect)(() => {
742
- if (targetElement) {
743
- const chunks = extractContextChunks(targetElement, containerElement);
744
- setState((prev) => ({ ...prev, contextChunks: chunks }));
745
- }
746
- }, [targetElement, containerElement]);
1128
+ if (!targetElement) return;
1129
+ const chunks = extractContextChunks(targetElement, containerElement);
1130
+ setContextChunks(chunks);
1131
+ }, [targetElement, containerElement, setContextChunks]);
747
1132
  (0, import_react.useEffect)(() => {
748
- if (!mergedConfig.showSuggestions || state.contextChunks.length === 0 || state.suggestedPrompts.length > 0) {
749
- return;
750
- }
1133
+ if (!mergedConfig.showSuggestions) return;
1134
+ if (!contextString) return;
1135
+ if (suggestedPrompts.length > 0) return;
1136
+ let cancelled = false;
751
1137
  const fetchSuggestions = async () => {
752
- setState((prev) => ({ ...prev, isLoadingSuggestions: true }));
1138
+ setIsLoadingSuggestions(true);
753
1139
  try {
754
- const contextString = buildContextString(state.contextChunks);
755
1140
  const response = await fetch(mergedConfig.endpoint, {
756
1141
  method: "POST",
757
1142
  headers: { "Content-Type": "application/json" },
@@ -761,250 +1146,142 @@ function useQuickChat(targetElement, containerElement, config = {}) {
761
1146
  model: mergedConfig.prePassModel
762
1147
  })
763
1148
  });
1149
+ if (await handleRateLimitResponse(response, mergedConfig.endpoint)) {
1150
+ return;
1151
+ }
764
1152
  if (response.ok) {
765
- const data = await response.json();
766
- if (data.suggestions && Array.isArray(data.suggestions)) {
767
- setState((prev) => ({
768
- ...prev,
769
- suggestedPrompts: data.suggestions.map(
770
- (text, i) => ({
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) => ({
771
1158
  id: `suggestion-${i}`,
772
1159
  text
773
- })
774
- ),
775
- isLoadingSuggestions: false
776
- }));
1160
+ }))
1161
+ );
1162
+ setIsLoadingSuggestions(false);
1163
+ }
777
1164
  return;
778
1165
  }
779
1166
  }
780
- } catch (error) {
781
- console.error("Failed to fetch suggestions:", error);
1167
+ } catch (err) {
1168
+ console.error("[useQuickChat] Failed to fetch suggestions:", err);
782
1169
  }
783
- setState((prev) => ({
784
- ...prev,
785
- suggestedPrompts: [
1170
+ if (!cancelled) {
1171
+ setSuggestedPrompts([
786
1172
  { id: "s1", text: "What is this element?" },
787
1173
  { id: "s2", text: "How can I style this?" },
788
1174
  { id: "s3", text: "Is this accessible?" }
789
- ],
790
- isLoadingSuggestions: false
791
- }));
1175
+ ]);
1176
+ setIsLoadingSuggestions(false);
1177
+ }
1178
+ };
1179
+ void fetchSuggestions();
1180
+ return () => {
1181
+ cancelled = true;
792
1182
  };
793
- fetchSuggestions();
794
1183
  }, [
795
- state.contextChunks,
796
- state.suggestedPrompts.length,
797
- mergedConfig.showSuggestions,
1184
+ contextString,
1185
+ handleRateLimitResponse,
798
1186
  mergedConfig.endpoint,
799
- mergedConfig.prePassModel
1187
+ mergedConfig.prePassModel,
1188
+ mergedConfig.showSuggestions,
1189
+ setIsLoadingSuggestions,
1190
+ setSuggestedPrompts,
1191
+ suggestedPrompts.length
800
1192
  ]);
801
- const setInput = (0, import_react.useCallback)((value) => {
802
- setState((prev) => ({ ...prev, input: value }));
803
- }, []);
804
- const toggleChunk = (0, import_react.useCallback)((chunkId) => {
805
- setState((prev) => ({
806
- ...prev,
807
- contextChunks: prev.contextChunks.map(
808
- (chunk) => chunk.id === chunkId ? { ...chunk, included: !chunk.included } : chunk
809
- )
810
- }));
811
- }, []);
812
- const toggleAllChunks = (0, import_react.useCallback)((included) => {
813
- setState((prev) => ({
814
- ...prev,
815
- contextChunks: prev.contextChunks.map((chunk) => ({
816
- ...chunk,
817
- included
818
- }))
819
- }));
820
- }, []);
821
- const selectSuggestion = (0, import_react.useCallback)((prompt) => {
822
- setState((prev) => ({ ...prev, input: prompt.text }));
823
- }, []);
1193
+ const selectSuggestion = (0, import_react.useCallback)(
1194
+ (prompt) => {
1195
+ setInput(prompt.text);
1196
+ },
1197
+ [setInput]
1198
+ );
824
1199
  const sendMessage = (0, import_react.useCallback)(
825
1200
  async (messageText) => {
826
- const text = (messageText || state.input).trim();
1201
+ const text = (messageText || input).trim();
827
1202
  if (!text) return;
828
- if (abortControllerRef.current) {
829
- abortControllerRef.current.abort();
830
- }
831
- abortControllerRef.current = new AbortController();
832
- const userMessage = {
833
- id: generateId(),
834
- role: "user",
835
- content: text,
836
- timestamp: Date.now()
837
- };
838
- const assistantMessageId = generateId();
839
- const assistantMessage = {
840
- id: assistantMessageId,
841
- role: "assistant",
842
- content: "",
843
- timestamp: Date.now(),
844
- isStreaming: true
845
- };
846
- setState((prev) => ({
847
- ...prev,
848
- input: "",
849
- messages: [...prev.messages, userMessage, assistantMessage],
850
- isSending: true,
851
- isStreaming: true,
852
- error: null
853
- }));
1203
+ setInput("");
1204
+ setError(null);
1205
+ setManualSending(true);
1206
+ setStoreIsSending(true);
1207
+ setStoreIsStreaming(true);
1208
+ setDebugInfo(null);
854
1209
  try {
855
- const contextString = buildContextString(state.contextChunks);
856
- const response = await fetch(mergedConfig.endpoint, {
857
- method: "POST",
858
- headers: { "Content-Type": "application/json" },
859
- body: JSON.stringify({
860
- action: "chat",
861
- message: text,
862
- context: contextString,
863
- model: mergedConfig.model,
864
- systemPrompt: mergedConfig.systemPrompt,
865
- maxLength: mergedConfig.maxResponseLength
866
- }),
867
- signal: abortControllerRef.current.signal
1210
+ addMessage({
1211
+ role: "user",
1212
+ content: text
868
1213
  });
869
- if (!response.ok) {
870
- throw new Error(`Request failed: ${response.status}`);
871
- }
872
- const reader = response.body?.getReader();
873
- if (!reader) {
874
- throw new Error("No response body");
875
- }
876
- const decoder = new TextDecoder();
877
- let fullContent = "";
878
- while (true) {
879
- const { done, value } = await reader.read();
880
- if (done) break;
881
- const chunk = decoder.decode(value, { stream: true });
882
- fullContent += chunk;
883
- if (fullContent.length > mergedConfig.maxResponseLength) {
884
- fullContent = fullContent.slice(0, mergedConfig.maxResponseLength) + "...";
885
- }
886
- setState((prev) => ({
887
- ...prev,
888
- messages: prev.messages.map(
889
- (msg) => msg.id === assistantMessageId ? { ...msg, content: fullContent } : msg
890
- )
891
- }));
892
- }
893
- setState((prev) => ({
894
- ...prev,
895
- messages: prev.messages.map(
896
- (msg) => msg.id === assistantMessageId ? {
897
- ...msg,
898
- content: fullContent,
899
- isStreaming: false,
900
- actions: [
901
- {
902
- id: "copy",
903
- label: "Copy",
904
- onClick: () => {
905
- navigator.clipboard.writeText(fullContent);
906
- }
907
- },
908
- {
909
- id: "research",
910
- label: "Research more",
911
- onClick: () => {
912
- setState((p) => ({
913
- ...p,
914
- input: `Tell me more about: ${text}`
915
- }));
916
- }
917
- }
918
- ]
919
- } : msg
920
- ),
921
- isSending: false,
922
- isStreaming: false
923
- }));
924
- } catch (error) {
925
- if (error.name === "AbortError") {
926
- return;
927
- }
928
- console.error("Chat error:", error);
929
- setState((prev) => ({
930
- ...prev,
931
- messages: prev.messages.map(
932
- (msg) => msg.id === assistantMessageId ? {
933
- ...msg,
934
- content: "Sorry, I couldn't process your request. Please try again.",
935
- isStreaming: false
936
- } : msg
937
- ),
938
- isSending: false,
939
- isStreaming: false,
940
- error: error.message
941
- }));
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);
942
1222
  }
943
1223
  },
944
- [state.input, state.contextChunks, mergedConfig]
1224
+ [
1225
+ addMessage,
1226
+ aiSendMessage,
1227
+ input,
1228
+ setError,
1229
+ setInput,
1230
+ setStoreIsSending,
1231
+ setStoreIsStreaming
1232
+ ]
945
1233
  );
946
1234
  const clearMessages = (0, import_react.useCallback)(() => {
947
- if (abortControllerRef.current) {
948
- abortControllerRef.current.abort();
949
- }
950
- clearChatHistory();
951
- setState((prev) => ({
952
- ...prev,
953
- messages: [],
954
- error: null
955
- }));
956
- }, []);
957
- (0, import_react.useEffect)(() => {
958
- if (state.messages.length > 0 && getPinnedState()) {
959
- const completedMessages = state.messages.filter(
960
- (msg) => !msg.isStreaming
961
- );
962
- if (completedMessages.length > 0) {
963
- saveChatHistory(completedMessages);
964
- }
965
- }
966
- }, [state.messages]);
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
+ ]);
967
1253
  (0, import_react.useEffect)(() => {
968
1254
  return () => {
969
- if (abortControllerRef.current) {
970
- abortControllerRef.current.abort();
971
- }
1255
+ if (syncTimeoutRef.current) clearTimeout(syncTimeoutRef.current);
972
1256
  };
973
1257
  }, []);
974
1258
  return {
975
- ...state,
1259
+ input,
1260
+ messages,
1261
+ isLoadingSuggestions,
1262
+ isSending,
1263
+ isStreaming,
1264
+ suggestedPrompts,
1265
+ contextChunks,
1266
+ error,
1267
+ debugInfo,
1268
+ rateLimitNotice,
1269
+ isPinned,
1270
+ lastSyncedAt,
976
1271
  config: mergedConfig,
977
1272
  setInput,
978
1273
  toggleChunk,
979
1274
  toggleAllChunks,
980
1275
  selectSuggestion,
981
1276
  sendMessage,
982
- clearMessages
1277
+ clearMessages,
1278
+ setIsPinned,
1279
+ clearRateLimitNotice: () => setRateLimitNotice(null)
983
1280
  };
984
1281
  }
985
1282
 
986
1283
  // src/QuickChat/QuickChat.tsx
987
1284
  var import_jsx_runtime = require("react/jsx-runtime");
988
- var PINNED_STATE_KEY2 = "anyclick-quick-chat-pinned";
989
- function getStoredPinnedState() {
990
- if (typeof window === "undefined") return false;
991
- try {
992
- return sessionStorage.getItem(PINNED_STATE_KEY2) === "true";
993
- } catch {
994
- return false;
995
- }
996
- }
997
- function setStoredPinnedState(pinned) {
998
- if (typeof window === "undefined") return;
999
- try {
1000
- if (pinned) {
1001
- sessionStorage.setItem(PINNED_STATE_KEY2, "true");
1002
- } else {
1003
- sessionStorage.removeItem(PINNED_STATE_KEY2);
1004
- }
1005
- } catch {
1006
- }
1007
- }
1008
1285
  var stylesInjected = false;
1009
1286
  function injectStyles() {
1010
1287
  if (stylesInjected || typeof document === "undefined") return;
@@ -1013,7 +1290,7 @@ function injectStyles() {
1013
1290
  document.head.appendChild(style);
1014
1291
  stylesInjected = true;
1015
1292
  }
1016
- var LoadingDots = import_react2.default.memo(function LoadingDots2() {
1293
+ var LoadingDots = import_react3.default.memo(function LoadingDots2() {
1017
1294
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: quickChatStyles.loadingDots, children: [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1018
1295
  "div",
1019
1296
  {
@@ -1038,50 +1315,52 @@ function QuickChat({
1038
1315
  initialInput,
1039
1316
  onInitialInputConsumed
1040
1317
  }) {
1041
- const inputRef = (0, import_react2.useRef)(null);
1042
- const messagesEndRef = (0, import_react2.useRef)(null);
1043
- const [inputFocused, setInputFocused] = (0, import_react2.useState)(false);
1044
- const [showContext, setShowContext] = (0, import_react2.useState)(false);
1045
- const [hoveredSuggestion, setHoveredSuggestion] = (0, import_react2.useState)(
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)(
1046
1323
  null
1047
1324
  );
1048
- const [localPinned, setLocalPinned] = (0, import_react2.useState)(() => getStoredPinnedState());
1049
- const isPinned = isPinnedProp || localPinned;
1050
- const handlePinToggle = (0, import_react2.useCallback)(() => {
1051
- const newPinned = !isPinned;
1052
- setLocalPinned(newPinned);
1053
- setStoredPinnedState(newPinned);
1054
- onPin?.(newPinned);
1055
- }, [isPinned, onPin]);
1056
- const handleClose = (0, import_react2.useCallback)(() => {
1057
- if (isPinned) {
1058
- setLocalPinned(false);
1059
- setStoredPinnedState(false);
1060
- onPin?.(false);
1061
- }
1062
- onClose();
1063
- }, [isPinned, onPin, onClose]);
1064
1325
  const {
1065
1326
  input,
1066
1327
  messages,
1067
1328
  isLoadingSuggestions,
1068
1329
  isSending,
1069
1330
  isStreaming,
1331
+ debugInfo,
1332
+ rateLimitNotice,
1070
1333
  suggestedPrompts,
1071
1334
  contextChunks,
1072
1335
  error,
1336
+ isPinned: storePinned,
1073
1337
  setInput,
1074
1338
  toggleChunk,
1075
1339
  toggleAllChunks,
1076
1340
  selectSuggestion,
1077
1341
  sendMessage,
1078
1342
  clearMessages,
1343
+ setIsPinned,
1344
+ clearRateLimitNotice,
1079
1345
  config: mergedConfig
1080
1346
  } = useQuickChat(targetElement, containerElement, config);
1081
- (0, import_react2.useEffect)(() => {
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)(() => {
1082
1361
  injectStyles();
1083
1362
  }, []);
1084
- (0, import_react2.useEffect)(() => {
1363
+ (0, import_react3.useEffect)(() => {
1085
1364
  if (visible && inputRef.current) {
1086
1365
  const timer = setTimeout(() => {
1087
1366
  inputRef.current?.focus();
@@ -1089,7 +1368,7 @@ function QuickChat({
1089
1368
  return () => clearTimeout(timer);
1090
1369
  }
1091
1370
  }, [visible]);
1092
- (0, import_react2.useEffect)(() => {
1371
+ (0, import_react3.useEffect)(() => {
1093
1372
  if (initialInput && visible) {
1094
1373
  setInput(initialInput);
1095
1374
  onInitialInputConsumed?.();
@@ -1102,12 +1381,12 @@ function QuickChat({
1102
1381
  }
1103
1382
  }
1104
1383
  }, [initialInput, visible, setInput, onInitialInputConsumed]);
1105
- (0, import_react2.useEffect)(() => {
1384
+ (0, import_react3.useEffect)(() => {
1106
1385
  if (messagesEndRef.current) {
1107
1386
  messagesEndRef.current.scrollIntoView({ behavior: "smooth" });
1108
1387
  }
1109
1388
  }, [messages]);
1110
- const handleInputChange = (0, import_react2.useCallback)(
1389
+ const handleInputChange = (0, import_react3.useCallback)(
1111
1390
  (e) => {
1112
1391
  setInput(e.target.value);
1113
1392
  const target = e.target;
@@ -1116,7 +1395,7 @@ function QuickChat({
1116
1395
  },
1117
1396
  [setInput]
1118
1397
  );
1119
- const handleKeyDown = (0, import_react2.useCallback)(
1398
+ const handleKeyDown = (0, import_react3.useCallback)(
1120
1399
  (e) => {
1121
1400
  if (e.key === "Enter" && !e.shiftKey) {
1122
1401
  e.preventDefault();
@@ -1127,23 +1406,78 @@ function QuickChat({
1127
1406
  },
1128
1407
  [sendMessage, onClose]
1129
1408
  );
1130
- const handleSend = (0, import_react2.useCallback)(() => {
1409
+ const handleSend = (0, import_react3.useCallback)(() => {
1131
1410
  sendMessage();
1132
1411
  }, [sendMessage]);
1133
- const handleSendToT3Chat = (0, import_react2.useCallback)(() => {
1412
+ const handleSendToT3Chat = (0, import_react3.useCallback)(() => {
1134
1413
  if (typeof window === "undefined") return;
1135
1414
  const query = input.trim();
1136
1415
  const baseUrl = mergedConfig.t3chat?.baseUrl ?? "https://t3.chat";
1137
1416
  const url = query ? `${baseUrl}/?q=${encodeURIComponent(query)}` : baseUrl;
1138
1417
  window.open(url, "_blank", "noopener,noreferrer");
1139
1418
  }, [input, mergedConfig.t3chat?.baseUrl]);
1140
- const handleCopy = (0, import_react2.useCallback)((text) => {
1419
+ const handleCopy = (0, import_react3.useCallback)((text) => {
1141
1420
  navigator.clipboard.writeText(text);
1142
1421
  }, []);
1143
- const includedCount = (0, import_react2.useMemo)(
1422
+ const includedCount = (0, import_react3.useMemo)(
1144
1423
  () => contextChunks.filter((c) => c.included).length,
1145
1424
  [contextChunks]
1146
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]);
1147
1481
  if (!visible) return null;
1148
1482
  const containerStyles = isPinned ? {
1149
1483
  ...quickChatStyles.pinnedContainer,
@@ -1302,7 +1636,7 @@ function QuickChat({
1302
1636
  {
1303
1637
  style: isPinned ? quickChatStyles.pinnedMessagesArea : quickChatStyles.messagesArea,
1304
1638
  children: [
1305
- error && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: quickChatStyles.errorContainer, children: [
1639
+ error && !rateLimitNotice && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: quickChatStyles.errorContainer, children: [
1306
1640
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.AlertCircle, { size: 20, style: quickChatStyles.errorIcon }),
1307
1641
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: quickChatStyles.errorText, children: error }),
1308
1642
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
@@ -1318,6 +1652,55 @@ function QuickChat({
1318
1652
  }
1319
1653
  )
1320
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
+ ),
1321
1704
  messages.length > 0 && messages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1322
1705
  "div",
1323
1706
  {
@@ -1372,6 +1755,153 @@ function QuickChat({
1372
1755
  ]
1373
1756
  }
1374
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
+ ),
1375
1905
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: quickChatStyles.inputContainer, children: [
1376
1906
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1377
1907
  "textarea",
@@ -1438,8 +1968,8 @@ function QuickChat({
1438
1968
  }
1439
1969
 
1440
1970
  // src/ScreenshotPreview.tsx
1441
- var import_react3 = __toESM(require("react"));
1442
- var import_anyclick_core2 = require("@ewjdev/anyclick-core");
1971
+ var import_react4 = __toESM(require("react"));
1972
+ var import_anyclick_core3 = require("@ewjdev/anyclick-core");
1443
1973
  var import_lucide_react2 = require("lucide-react");
1444
1974
 
1445
1975
  // src/styles.ts
@@ -1916,7 +2446,7 @@ var screenshotPreviewStyles = {
1916
2446
 
1917
2447
  // src/ScreenshotPreview.tsx
1918
2448
  var import_jsx_runtime2 = require("react/jsx-runtime");
1919
- var ScreenshotPreview = import_react3.default.memo(function ScreenshotPreview2({
2449
+ var ScreenshotPreview = import_react4.default.memo(function ScreenshotPreview2({
1920
2450
  isLoading,
1921
2451
  isSubmitting,
1922
2452
  onCancel,
@@ -1924,12 +2454,12 @@ var ScreenshotPreview = import_react3.default.memo(function ScreenshotPreview2({
1924
2454
  onRetake,
1925
2455
  screenshots
1926
2456
  }) {
1927
- const [activeTab, setActiveTab] = (0, import_react3.useState)("element");
1928
- const [isExpanded, setIsExpanded] = (0, import_react3.useState)(false);
2457
+ const [activeTab, setActiveTab] = (0, import_react4.useState)("element");
2458
+ const [isExpanded, setIsExpanded] = (0, import_react4.useState)(false);
1929
2459
  const getError = (key) => {
1930
2460
  return screenshots?.errors?.[key];
1931
2461
  };
1932
- const tabs = (0, import_react3.useMemo)(() => {
2462
+ const tabs = (0, import_react4.useMemo)(() => {
1933
2463
  if (!screenshots) return [];
1934
2464
  const allTabs = [
1935
2465
  {
@@ -1953,8 +2483,8 @@ var ScreenshotPreview = import_react3.default.memo(function ScreenshotPreview2({
1953
2483
  ];
1954
2484
  return allTabs.filter((tab) => tab.data || tab.error);
1955
2485
  }, [screenshots]);
1956
- const totalSize = (0, import_react3.useMemo)(
1957
- () => screenshots ? (0, import_anyclick_core2.estimateTotalSize)(screenshots) : 0,
2486
+ const totalSize = (0, import_react4.useMemo)(
2487
+ () => screenshots ? (0, import_anyclick_core3.estimateTotalSize)(screenshots) : 0,
1958
2488
  [screenshots]
1959
2489
  );
1960
2490
  if (isLoading) {
@@ -2018,7 +2548,7 @@ var ScreenshotPreview = import_react3.default.memo(function ScreenshotPreview2({
2018
2548
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: screenshotPreviewStyles.header, children: [
2019
2549
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: screenshotPreviewStyles.headerTitle, children: "Review Screenshots" }),
2020
2550
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: screenshotPreviewStyles.headerActions, children: [
2021
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: screenshotPreviewStyles.sizeLabel, children: (0, import_anyclick_core2.formatBytes)(totalSize) }),
2551
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: screenshotPreviewStyles.sizeLabel, children: (0, import_anyclick_core3.formatBytes)(totalSize) }),
2022
2552
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2023
2553
  "button",
2024
2554
  {
@@ -2050,7 +2580,7 @@ var ScreenshotPreview = import_react3.default.memo(function ScreenshotPreview2({
2050
2580
  }
2051
2581
  ),
2052
2582
  tab.label,
2053
- tab.data && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: screenshotPreviewStyles.tabSize, children: (0, import_anyclick_core2.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) })
2054
2584
  ]
2055
2585
  },
2056
2586
  tab.key
@@ -2329,14 +2859,14 @@ var DefaultHeader = ({
2329
2859
  children
2330
2860
  ] });
2331
2861
  };
2332
- var MenuItem = import_react4.default.memo(function MenuItem2({
2862
+ var MenuItem = import_react5.default.memo(function MenuItem2({
2333
2863
  disabled,
2334
2864
  hasChildren,
2335
2865
  item,
2336
2866
  onClick
2337
2867
  }) {
2338
- const [isHovered, setIsHovered] = (0, import_react4.useState)(false);
2339
- const [isPressed, setIsPressed] = (0, import_react4.useState)(false);
2868
+ const [isHovered, setIsHovered] = (0, import_react5.useState)(false);
2869
+ const [isPressed, setIsPressed] = (0, import_react5.useState)(false);
2340
2870
  const isComingSoon = item.status === "comingSoon";
2341
2871
  const badgeLabel = item.badge?.label ?? (isComingSoon ? "Coming soon" : null);
2342
2872
  const badgeTone = item.badge?.tone ?? (isComingSoon ? "neutral" : "neutral");
@@ -2373,11 +2903,11 @@ var MenuItem = import_react4.default.memo(function MenuItem2({
2373
2903
  }
2374
2904
  );
2375
2905
  });
2376
- var BackButton = import_react4.default.memo(function BackButton2({
2906
+ var BackButton = import_react5.default.memo(function BackButton2({
2377
2907
  onClick
2378
2908
  }) {
2379
- const [isHovered, setIsHovered] = (0, import_react4.useState)(false);
2380
- const [isPressed, setIsPressed] = (0, import_react4.useState)(false);
2909
+ const [isHovered, setIsHovered] = (0, import_react5.useState)(false);
2910
+ const [isPressed, setIsPressed] = (0, import_react5.useState)(false);
2381
2911
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
2382
2912
  "button",
2383
2913
  {
@@ -2404,20 +2934,20 @@ var BackButton = import_react4.default.memo(function BackButton2({
2404
2934
  }
2405
2935
  );
2406
2936
  });
2407
- var CommentForm = import_react4.default.memo(function CommentForm2({
2937
+ var CommentForm = import_react5.default.memo(function CommentForm2({
2408
2938
  isSubmitting,
2409
2939
  onCancel,
2410
2940
  onSubmit
2411
2941
  }) {
2412
- const [comment, setComment] = (0, import_react4.useState)("");
2413
- const inputRef = (0, import_react4.useRef)(null);
2414
- (0, import_react4.useEffect)(() => {
2942
+ const [comment, setComment] = (0, import_react5.useState)("");
2943
+ const inputRef = (0, import_react5.useRef)(null);
2944
+ (0, import_react5.useEffect)(() => {
2415
2945
  inputRef.current?.focus();
2416
2946
  }, []);
2417
- const handleSubmit = (0, import_react4.useCallback)(() => {
2947
+ const handleSubmit = (0, import_react5.useCallback)(() => {
2418
2948
  onSubmit(comment);
2419
2949
  }, [comment, onSubmit]);
2420
- const handleKeyDown = (0, import_react4.useCallback)(
2950
+ const handleKeyDown = (0, import_react5.useCallback)(
2421
2951
  (e) => {
2422
2952
  if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
2423
2953
  handleSubmit();
@@ -2505,13 +3035,13 @@ function ContextMenu({
2505
3035
  targetElement,
2506
3036
  visible
2507
3037
  }) {
2508
- const [selectedType, setSelectedType] = (0, import_react4.useState)(null);
2509
- const [currentView, setCurrentView] = (0, import_react4.useState)("menu");
2510
- const [pendingComment, setPendingComment] = (0, import_react4.useState)();
2511
- const [submenuStack, setSubmenuStack] = (0, import_react4.useState)([]);
2512
- const [screenshots, setScreenshots] = (0, import_react4.useState)(null);
2513
- const [isCapturing, setIsCapturing] = (0, import_react4.useState)(false);
2514
- const [isQuickChatPinned, setIsQuickChatPinned] = (0, import_react4.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)(() => {
2515
3045
  if (typeof window === "undefined") return false;
2516
3046
  try {
2517
3047
  return sessionStorage.getItem("anyclick-quick-chat-pinned") === "true";
@@ -2519,28 +3049,28 @@ function ContextMenu({
2519
3049
  return false;
2520
3050
  }
2521
3051
  });
2522
- const menuRef = (0, import_react4.useRef)(null);
2523
- const [adjustedPosition, setAdjustedPosition] = (0, import_react4.useState)(OFFSCREEN_POSITION);
2524
- const [isDragging, setIsDragging] = (0, import_react4.useState)(false);
2525
- const [dragOffset, setDragOffset] = (0, import_react4.useState)({
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)({
2526
3056
  x: 0,
2527
3057
  y: 0
2528
3058
  });
2529
- const dragStartRef = (0, import_react4.useRef)(null);
2530
- const mergedScreenshotConfig = import_react4.default.useMemo(
3059
+ const dragStartRef = (0, import_react5.useRef)(null);
3060
+ const mergedScreenshotConfig = import_react5.default.useMemo(
2531
3061
  () => ({
2532
- ...import_anyclick_core3.DEFAULT_SCREENSHOT_CONFIG,
3062
+ ...import_anyclick_core4.DEFAULT_SCREENSHOT_CONFIG,
2533
3063
  ...screenshotConfig
2534
3064
  }),
2535
3065
  [screenshotConfig]
2536
3066
  );
2537
- const showPreview = mergedScreenshotConfig.showPreview && (0, import_anyclick_core3.isScreenshotSupported)();
3067
+ const showPreview = mergedScreenshotConfig.showPreview && (0, import_anyclick_core4.isScreenshotSupported)();
2538
3068
  const currentItems = submenuStack.length > 0 ? submenuStack[submenuStack.length - 1] : items;
2539
- const captureScreenshots = (0, import_react4.useCallback)(async () => {
3069
+ const captureScreenshots = (0, import_react5.useCallback)(async () => {
2540
3070
  if (!targetElement || !showPreview) return;
2541
3071
  setIsCapturing(true);
2542
3072
  try {
2543
- const captured = await (0, import_anyclick_core3.captureAllScreenshots)(
3073
+ const captured = await (0, import_anyclick_core4.captureAllScreenshots)(
2544
3074
  targetElement,
2545
3075
  containerElement,
2546
3076
  mergedScreenshotConfig
@@ -2553,7 +3083,7 @@ function ContextMenu({
2553
3083
  setIsCapturing(false);
2554
3084
  }
2555
3085
  }, [containerElement, mergedScreenshotConfig, showPreview, targetElement]);
2556
- (0, import_react4.useEffect)(() => {
3086
+ (0, import_react5.useEffect)(() => {
2557
3087
  if (!visible) {
2558
3088
  setSelectedType(null);
2559
3089
  setCurrentView("menu");
@@ -2567,7 +3097,7 @@ function ContextMenu({
2567
3097
  dragStartRef.current = null;
2568
3098
  }
2569
3099
  }, [visible]);
2570
- (0, import_react4.useEffect)(() => {
3100
+ (0, import_react5.useEffect)(() => {
2571
3101
  if (visible && targetElement) {
2572
3102
  clearHighlights();
2573
3103
  applyHighlights(targetElement, highlightConfig);
@@ -2578,7 +3108,7 @@ function ContextMenu({
2578
3108
  clearHighlights();
2579
3109
  };
2580
3110
  }, [highlightConfig, targetElement, visible]);
2581
- (0, import_react4.useEffect)(() => {
3111
+ (0, import_react5.useEffect)(() => {
2582
3112
  if (!visible) {
2583
3113
  return;
2584
3114
  }
@@ -2597,13 +3127,13 @@ function ContextMenu({
2597
3127
  document.removeEventListener("pointerdown", handlePointerDown);
2598
3128
  };
2599
3129
  }, [onClose, visible]);
2600
- (0, import_react4.useEffect)(() => {
3130
+ (0, import_react5.useEffect)(() => {
2601
3131
  if (visible) {
2602
3132
  setAdjustedPosition(position);
2603
3133
  setDragOffset({ x: 0, y: 0 });
2604
3134
  }
2605
3135
  }, [position.x, position.y, visible]);
2606
- (0, import_react4.useEffect)(() => {
3136
+ (0, import_react5.useEffect)(() => {
2607
3137
  if (!visible || !menuRef.current) return;
2608
3138
  const updatePosition = () => {
2609
3139
  const menuElement = menuRef.current;
@@ -2627,7 +3157,7 @@ function ContextMenu({
2627
3157
  window.addEventListener("resize", updatePosition);
2628
3158
  return () => window.removeEventListener("resize", updatePosition);
2629
3159
  }, [currentView, dragOffset, position, positionMode, visible]);
2630
- (0, import_react4.useEffect)(() => {
3160
+ (0, import_react5.useEffect)(() => {
2631
3161
  if (!visible || positionMode !== "dynamic") return;
2632
3162
  const handlePointerMove = (event) => {
2633
3163
  if (!isDragging || !dragStartRef.current) return;
@@ -2654,7 +3184,7 @@ function ContextMenu({
2654
3184
  };
2655
3185
  }
2656
3186
  }, [isDragging, positionMode, visible]);
2657
- const handleDragStart = (0, import_react4.useCallback)(
3187
+ const handleDragStart = (0, import_react5.useCallback)(
2658
3188
  (event) => {
2659
3189
  if (positionMode !== "dynamic") return;
2660
3190
  event.preventDefault();
@@ -2663,8 +3193,8 @@ function ContextMenu({
2663
3193
  },
2664
3194
  [positionMode]
2665
3195
  );
2666
- const [initialChatInput, setInitialChatInput] = (0, import_react4.useState)("");
2667
- (0, import_react4.useEffect)(() => {
3196
+ const [initialChatInput, setInitialChatInput] = (0, import_react5.useState)("");
3197
+ (0, import_react5.useEffect)(() => {
2668
3198
  const handleKeyDown = (e) => {
2669
3199
  if (e.key === "Escape") {
2670
3200
  if (currentView === "screenshot-preview") {
@@ -2704,7 +3234,7 @@ function ContextMenu({
2704
3234
  submenuStack.length,
2705
3235
  visible
2706
3236
  ]);
2707
- (0, import_react4.useEffect)(() => {
3237
+ (0, import_react5.useEffect)(() => {
2708
3238
  const menuElement = menuRef.current;
2709
3239
  if (!visible || !menuElement) return;
2710
3240
  const preventTouchDefault = (e) => {
@@ -2952,18 +3482,18 @@ function ContextMenu({
2952
3482
  }
2953
3483
 
2954
3484
  // src/context.ts
2955
- var import_react5 = require("react");
2956
- var AnyclickContext = (0, import_react5.createContext)(null);
3485
+ var import_react6 = require("react");
3486
+ var AnyclickContext = (0, import_react6.createContext)(null);
2957
3487
  var FeedbackContext = AnyclickContext;
2958
3488
  function useAnyclick() {
2959
- const context = (0, import_react5.useContext)(AnyclickContext);
3489
+ const context = (0, import_react6.useContext)(AnyclickContext);
2960
3490
  if (!context) {
2961
3491
  throw new Error("useAnyclick must be used within an AnyclickProvider");
2962
3492
  }
2963
3493
  return context;
2964
3494
  }
2965
3495
  function useFeedback() {
2966
- const context = (0, import_react5.useContext)(AnyclickContext);
3496
+ const context = (0, import_react6.useContext)(AnyclickContext);
2967
3497
  if (!context) {
2968
3498
  throw new Error("useFeedback must be used within a FeedbackProvider");
2969
3499
  }
@@ -2971,12 +3501,12 @@ function useFeedback() {
2971
3501
  }
2972
3502
 
2973
3503
  // src/store.ts
2974
- var import_zustand = require("zustand");
3504
+ var import_zustand2 = require("zustand");
2975
3505
  var providerIdCounter = 0;
2976
3506
  function generateProviderId() {
2977
3507
  return `anyclick-provider-${++providerIdCounter}`;
2978
3508
  }
2979
- var useProviderStore = (0, import_zustand.create)((set, get) => ({
3509
+ var useProviderStore = (0, import_zustand2.create)((set, get) => ({
2980
3510
  providers: /* @__PURE__ */ new Map(),
2981
3511
  findProvidersForElement: (element) => {
2982
3512
  const { providers } = get();
@@ -3164,18 +3694,18 @@ function AnyclickProvider({
3164
3694
  touchHoldDurationMs,
3165
3695
  touchMoveThreshold
3166
3696
  }) {
3167
- const [isSubmitting, setIsSubmitting] = (0, import_react6.useState)(false);
3168
- const [menuVisible, setMenuVisible] = (0, import_react6.useState)(false);
3169
- const [menuPosition, setMenuPosition] = (0, import_react6.useState)(OFFSCREEN_POSITION2);
3170
- const [targetElement, setTargetElement] = (0, import_react6.useState)(null);
3171
- const [containerElement, setContainerElement] = (0, import_react6.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)(
3172
3702
  null
3173
3703
  );
3174
- const providerId = (0, import_react6.useId)();
3175
- const containerRef = (0, import_react6.useRef)(null);
3176
- const [containerReady, setContainerReady] = (0, import_react6.useState)(!scoped);
3177
- const clientRef = (0, import_react6.useRef)(null);
3178
- const setContainerRef = (0, import_react6.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)(
3179
3709
  (node) => {
3180
3710
  containerRef.current = node;
3181
3711
  if (scoped && node) {
@@ -3204,7 +3734,7 @@ function AnyclickProvider({
3204
3734
  updateProvider
3205
3735
  } = useProviderStore();
3206
3736
  const parentId = parentContext?.providerId ?? null;
3207
- const actualDepth = (0, import_react6.useMemo)(() => {
3737
+ const actualDepth = (0, import_react7.useMemo)(() => {
3208
3738
  if (!parentContext) return 0;
3209
3739
  let d = 0;
3210
3740
  let currentId = parentId;
@@ -3218,7 +3748,7 @@ function AnyclickProvider({
3218
3748
  }, [parentContext, parentId]);
3219
3749
  const isDisabledByTheme = theme === null || theme?.disabled === true;
3220
3750
  const effectiveDisabled = disabled || isDisabledByTheme;
3221
- const localTheme = (0, import_react6.useMemo)(() => {
3751
+ const localTheme = (0, import_react7.useMemo)(() => {
3222
3752
  if (theme === null) {
3223
3753
  return { disabled: true };
3224
3754
  }
@@ -3233,7 +3763,7 @@ function AnyclickProvider({
3233
3763
  ...theme
3234
3764
  };
3235
3765
  }, [highlightConfig, menuClassName, menuStyle, screenshotConfig, theme]);
3236
- const handleContextMenu = (0, import_react6.useCallback)(
3766
+ const handleContextMenu = (0, import_react7.useCallback)(
3237
3767
  (event, element) => {
3238
3768
  if (!scoped && isElementInDisabledScope(element)) {
3239
3769
  return false;
@@ -3261,7 +3791,7 @@ function AnyclickProvider({
3261
3791
  scoped
3262
3792
  ]
3263
3793
  );
3264
- (0, import_react6.useLayoutEffect)(() => {
3794
+ (0, import_react7.useLayoutEffect)(() => {
3265
3795
  const providerInstance = {
3266
3796
  containerRef,
3267
3797
  depth: actualDepth,
@@ -3287,7 +3817,7 @@ function AnyclickProvider({
3287
3817
  scoped,
3288
3818
  unregisterProvider
3289
3819
  ]);
3290
- (0, import_react6.useEffect)(() => {
3820
+ (0, import_react7.useEffect)(() => {
3291
3821
  updateProvider(providerId, {
3292
3822
  disabled: effectiveDisabled,
3293
3823
  onContextMenu: handleContextMenu,
@@ -3300,14 +3830,14 @@ function AnyclickProvider({
3300
3830
  providerId,
3301
3831
  updateProvider
3302
3832
  ]);
3303
- (0, import_react6.useEffect)(() => {
3833
+ (0, import_react7.useEffect)(() => {
3304
3834
  if (isDisabledByAncestor(providerId)) {
3305
3835
  return;
3306
3836
  }
3307
3837
  if (scoped && !containerReady) {
3308
3838
  return;
3309
3839
  }
3310
- const client = (0, import_anyclick_core4.createAnyclickClient)({
3840
+ const client = (0, import_anyclick_core5.createAnyclickClient)({
3311
3841
  adapter,
3312
3842
  container: scoped ? containerRef.current : null,
3313
3843
  cooldownMs,
@@ -3348,7 +3878,7 @@ function AnyclickProvider({
3348
3878
  touchHoldDurationMs,
3349
3879
  touchMoveThreshold
3350
3880
  ]);
3351
- const submitAnyclick = (0, import_react6.useCallback)(
3881
+ const submitAnyclick = (0, import_react7.useCallback)(
3352
3882
  async (element, type, comment, screenshots) => {
3353
3883
  const client = clientRef.current;
3354
3884
  if (!client) return;
@@ -3369,7 +3899,7 @@ function AnyclickProvider({
3369
3899
  },
3370
3900
  [metadata]
3371
3901
  );
3372
- const openMenu = (0, import_react6.useCallback)(
3902
+ const openMenu = (0, import_react7.useCallback)(
3373
3903
  (element, position) => {
3374
3904
  setTargetElement(element);
3375
3905
  const mergedTheme2 = getMergedTheme(providerId);
@@ -3383,13 +3913,13 @@ function AnyclickProvider({
3383
3913
  },
3384
3914
  [getMergedTheme, highlightConfig, providerId]
3385
3915
  );
3386
- const closeMenu = (0, import_react6.useCallback)(() => {
3916
+ const closeMenu = (0, import_react7.useCallback)(() => {
3387
3917
  setMenuVisible(false);
3388
3918
  setMenuPosition(OFFSCREEN_POSITION2);
3389
3919
  setTargetElement(null);
3390
3920
  setContainerElement(null);
3391
3921
  }, []);
3392
- const handleMenuSelect = (0, import_react6.useCallback)(
3922
+ const handleMenuSelect = (0, import_react7.useCallback)(
3393
3923
  (type, comment, screenshots) => {
3394
3924
  if (targetElement) {
3395
3925
  submitAnyclick(targetElement, type, comment, screenshots);
@@ -3398,7 +3928,7 @@ function AnyclickProvider({
3398
3928
  [submitAnyclick, targetElement]
3399
3929
  );
3400
3930
  const inheritedTheme = getMergedTheme(providerId);
3401
- const mergedTheme = (0, import_react6.useMemo)(
3931
+ const mergedTheme = (0, import_react7.useMemo)(
3402
3932
  () => ({
3403
3933
  ...inheritedTheme,
3404
3934
  ...localTheme,
@@ -3421,7 +3951,7 @@ function AnyclickProvider({
3421
3951
  const effectiveMenuClassName = mergedTheme.menuClassName ?? menuClassName;
3422
3952
  const effectiveHighlightConfig = mergedTheme.highlightConfig ?? highlightConfig;
3423
3953
  const effectiveScreenshotConfig = mergedTheme.screenshotConfig ?? screenshotConfig;
3424
- const contextValue = (0, import_react6.useMemo)(
3954
+ const contextValue = (0, import_react7.useMemo)(
3425
3955
  () => ({
3426
3956
  closeMenu,
3427
3957
  isEnabled: !effectiveDisabled && !isDisabledByAncestor(providerId),
@@ -3453,7 +3983,7 @@ function AnyclickProvider({
3453
3983
  children
3454
3984
  }
3455
3985
  ) : children;
3456
- return /* @__PURE__ */ (0, import_jsx_runtime4.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: [
3457
3987
  content,
3458
3988
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3459
3989
  ContextMenu,
@@ -3474,12 +4004,12 @@ function AnyclickProvider({
3474
4004
  visible: menuVisible && !effectiveDisabled
3475
4005
  }
3476
4006
  )
3477
- ] });
4007
+ ] }) });
3478
4008
  }
3479
4009
  var FeedbackProvider = AnyclickProvider;
3480
4010
 
3481
4011
  // src/FunModeBridge.tsx
3482
- var import_react7 = require("react");
4012
+ var import_react8 = require("react");
3483
4013
  var import_anyclick_pointer = require("@ewjdev/anyclick-pointer");
3484
4014
  function isFunModeEnabled(theme) {
3485
4015
  if (!theme) return false;
@@ -3526,9 +4056,9 @@ function buildFunConfig(theme, container) {
3526
4056
  function FunModeBridge() {
3527
4057
  const { setConfig } = (0, import_anyclick_pointer.usePointer)();
3528
4058
  const providerStore = useProviderStore();
3529
- const activeProviderRef = (0, import_react7.useRef)(null);
3530
- const cachedConfigs = (0, import_react7.useRef)({});
3531
- const findActiveFunProvider = (0, import_react7.useMemo)(() => {
4059
+ const activeProviderRef = (0, import_react8.useRef)(null);
4060
+ const cachedConfigs = (0, import_react8.useRef)({});
4061
+ const findActiveFunProvider = (0, import_react8.useMemo)(() => {
3532
4062
  return (el) => {
3533
4063
  if (!el) return null;
3534
4064
  const providers = providerStore.findProvidersForElement(el);
@@ -3540,7 +4070,7 @@ function FunModeBridge() {
3540
4070
  return null;
3541
4071
  };
3542
4072
  }, [providerStore]);
3543
- (0, import_react7.useEffect)(() => {
4073
+ (0, import_react8.useEffect)(() => {
3544
4074
  const handleMove = (event) => {
3545
4075
  const el = document.elementFromPoint(event.clientX, event.clientY);
3546
4076
  const provider = findActiveFunProvider(el);
@@ -3574,12 +4104,72 @@ function FunModeBridge() {
3574
4104
  return null;
3575
4105
  }
3576
4106
 
3577
- // src/InspectDialog/InspectDialogManager.tsx
4107
+ // src/ui/button.tsx
3578
4108
  var import_react9 = require("react");
3579
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
+
4167
+ // src/InspectDialog/InspectDialogManager.tsx
4168
+ var import_react12 = require("react");
4169
+
3580
4170
  // src/InspectDialog/InspectSimple.tsx
3581
- var import_react8 = require("react");
3582
- var import_anyclick_core5 = require("@ewjdev/anyclick-core");
4171
+ var import_react11 = require("react");
4172
+ var import_anyclick_core6 = require("@ewjdev/anyclick-core");
3583
4173
  var import_lucide_react4 = require("lucide-react");
3584
4174
 
3585
4175
  // src/ide.ts
@@ -3682,7 +4272,7 @@ function formatSourceLocation(location) {
3682
4272
  }
3683
4273
 
3684
4274
  // src/InspectDialog/InspectSimple.tsx
3685
- var import_jsx_runtime5 = require("react/jsx-runtime");
4275
+ var import_jsx_runtime7 = require("react/jsx-runtime");
3686
4276
  var DEFAULT_COMPACT_CONFIG = {
3687
4277
  scale: 0.5,
3688
4278
  fonts: {
@@ -3744,8 +4334,8 @@ function downloadDataUrl(dataUrl, filename) {
3744
4334
  link.click();
3745
4335
  }
3746
4336
  function useIsMobile() {
3747
- const [isMobile, setIsMobile] = (0, import_react8.useState)(false);
3748
- (0, import_react8.useEffect)(() => {
4337
+ const [isMobile, setIsMobile] = (0, import_react11.useState)(false);
4338
+ (0, import_react11.useEffect)(() => {
3749
4339
  const mq = window.matchMedia("(max-width: 640px)");
3750
4340
  setIsMobile(mq.matches);
3751
4341
  const handler = (e) => setIsMobile(e.matches);
@@ -3763,20 +4353,20 @@ function InspectSimple({
3763
4353
  className,
3764
4354
  highlightColors
3765
4355
  }) {
3766
- const [info, setInfo] = (0, import_react8.useState)(null);
3767
- const [sourceLocation, setSourceLocation] = (0, import_react8.useState)(
4356
+ const [info, setInfo] = (0, import_react11.useState)(null);
4357
+ const [sourceLocation, setSourceLocation] = (0, import_react11.useState)(
3768
4358
  null
3769
4359
  );
3770
- const [status, setStatus] = (0, import_react8.useState)(null);
3771
- const [saving, setSaving] = (0, import_react8.useState)(false);
3772
- const dialogRef = (0, import_react8.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);
3773
4363
  const isMobile = useIsMobile();
3774
- (0, import_react8.useEffect)(() => {
4364
+ (0, import_react11.useEffect)(() => {
3775
4365
  if (!status) return;
3776
4366
  const timer = setTimeout(() => setStatus(null), 5e3);
3777
4367
  return () => clearTimeout(timer);
3778
4368
  }, [status]);
3779
- (0, import_react8.useEffect)(() => {
4369
+ (0, import_react11.useEffect)(() => {
3780
4370
  if (!visible || !targetElement) return;
3781
4371
  try {
3782
4372
  clearHighlights();
@@ -3785,7 +4375,7 @@ function InspectSimple({
3785
4375
  if (container) highlightContainer(container, highlightColors);
3786
4376
  } catch {
3787
4377
  }
3788
- const nextInfo = (0, import_anyclick_core5.getElementInspectInfo)(targetElement);
4378
+ const nextInfo = (0, import_anyclick_core6.getElementInspectInfo)(targetElement);
3789
4379
  setInfo(nextInfo);
3790
4380
  setSourceLocation(
3791
4381
  findSourceLocationInAncestors(targetElement) ?? nextInfo.sourceLocation ?? null
@@ -3794,7 +4384,7 @@ function InspectSimple({
3794
4384
  clearHighlights();
3795
4385
  };
3796
4386
  }, [visible, targetElement, highlightColors]);
3797
- (0, import_react8.useEffect)(() => {
4387
+ (0, import_react11.useEffect)(() => {
3798
4388
  if (!visible) return;
3799
4389
  const handleClickOutside = (e) => {
3800
4390
  if (dialogRef.current && !dialogRef.current.contains(e.target)) {
@@ -3809,7 +4399,7 @@ function InspectSimple({
3809
4399
  document.removeEventListener("mousedown", handleClickOutside);
3810
4400
  };
3811
4401
  }, [visible, onClose]);
3812
- const identityLabel = (0, import_react8.useMemo)(() => {
4402
+ const identityLabel = (0, import_react11.useMemo)(() => {
3813
4403
  if (!info) return "Select an element";
3814
4404
  const classes = info.classNames[0] ? `.${info.classNames[0]}` : "";
3815
4405
  const id = info.id ? `#${info.id}` : "";
@@ -3839,7 +4429,7 @@ function InspectSimple({
3839
4429
  if (!targetElement) return;
3840
4430
  setSaving(true);
3841
4431
  setStatus("Capturing screenshot\u2026");
3842
- const result = await (0, import_anyclick_core5.captureScreenshot)(targetElement, null, "element");
4432
+ const result = await (0, import_anyclick_core6.captureScreenshot)(targetElement, null, "element");
3843
4433
  setSaving(false);
3844
4434
  if (result.capture?.dataUrl) {
3845
4435
  downloadDataUrl(result.capture.dataUrl, "anyclick-inspect.png");
@@ -3895,8 +4485,8 @@ function InspectSimple({
3895
4485
  overflow: "hidden",
3896
4486
  ...style
3897
4487
  };
3898
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
3899
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
4488
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
4489
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3900
4490
  "div",
3901
4491
  {
3902
4492
  style: {
@@ -3911,14 +4501,14 @@ function InspectSimple({
3911
4501
  role: "presentation"
3912
4502
  }
3913
4503
  ),
3914
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
4504
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
3915
4505
  "div",
3916
4506
  {
3917
4507
  ref: dialogRef,
3918
4508
  className: `anyclick-tiny-inspect ${className ?? ""}`,
3919
4509
  style: dialogStyles,
3920
4510
  children: [
3921
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
4511
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
3922
4512
  "div",
3923
4513
  {
3924
4514
  style: {
@@ -3930,7 +4520,7 @@ function InspectSimple({
3930
4520
  borderBottom: "1px solid #1e293b"
3931
4521
  },
3932
4522
  children: [
3933
- isMobile && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
4523
+ isMobile && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3934
4524
  "div",
3935
4525
  {
3936
4526
  style: {
@@ -3945,8 +4535,8 @@ function InspectSimple({
3945
4535
  }
3946
4536
  }
3947
4537
  ),
3948
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
3949
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
4538
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
4539
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3950
4540
  "span",
3951
4541
  {
3952
4542
  style: {
@@ -3961,31 +4551,31 @@ function InspectSimple({
3961
4551
  children: identityLabel
3962
4552
  }
3963
4553
  ),
3964
- sourceLocation && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
4554
+ sourceLocation && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3965
4555
  "button",
3966
4556
  {
3967
4557
  type: "button",
3968
4558
  onClick: handleOpenIDE,
3969
4559
  title: formatSourceLocation(sourceLocation),
3970
4560
  style: iconBtnStyle,
3971
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.ExternalLink, { size: 14 })
4561
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react4.ExternalLink, { size: 14 })
3972
4562
  }
3973
4563
  )
3974
4564
  ] }),
3975
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
4565
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3976
4566
  "button",
3977
4567
  {
3978
4568
  type: "button",
3979
4569
  onClick: onClose,
3980
4570
  style: iconBtnStyle,
3981
4571
  "aria-label": "Close inspector",
3982
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.X, { size: 16 })
4572
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react4.X, { size: 16 })
3983
4573
  }
3984
4574
  )
3985
4575
  ]
3986
4576
  }
3987
4577
  ),
3988
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
4578
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
3989
4579
  "div",
3990
4580
  {
3991
4581
  style: {
@@ -3995,7 +4585,7 @@ function InspectSimple({
3995
4585
  gap: 8
3996
4586
  },
3997
4587
  children: [
3998
- info?.selector && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
4588
+ info?.selector && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3999
4589
  "code",
4000
4590
  {
4001
4591
  style: {
@@ -4010,7 +4600,7 @@ function InspectSimple({
4010
4600
  children: info.selector
4011
4601
  }
4012
4602
  ),
4013
- status && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
4603
+ status && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
4014
4604
  "div",
4015
4605
  {
4016
4606
  style: {
@@ -4022,7 +4612,7 @@ function InspectSimple({
4022
4612
  children: status
4023
4613
  }
4024
4614
  ),
4025
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
4615
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
4026
4616
  "div",
4027
4617
  {
4028
4618
  style: {
@@ -4031,7 +4621,7 @@ function InspectSimple({
4031
4621
  gap: 6
4032
4622
  },
4033
4623
  children: [
4034
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
4624
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
4035
4625
  "button",
4036
4626
  {
4037
4627
  type: "button",
@@ -4039,10 +4629,10 @@ function InspectSimple({
4039
4629
  style: iconActionStyle,
4040
4630
  title: "Copy CSS selector",
4041
4631
  "aria-label": "Copy CSS selector",
4042
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.Copy, { size: 15 })
4632
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react4.Copy, { size: 15 })
4043
4633
  }
4044
4634
  ),
4045
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
4635
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
4046
4636
  "button",
4047
4637
  {
4048
4638
  type: "button",
@@ -4050,10 +4640,10 @@ function InspectSimple({
4050
4640
  style: iconActionStyle,
4051
4641
  title: "Copy text content",
4052
4642
  "aria-label": "Copy text content",
4053
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.FileText, { size: 15 })
4643
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react4.FileText, { size: 15 })
4054
4644
  }
4055
4645
  ),
4056
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
4646
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
4057
4647
  "button",
4058
4648
  {
4059
4649
  type: "button",
@@ -4061,10 +4651,10 @@ function InspectSimple({
4061
4651
  style: iconActionStyle,
4062
4652
  title: "Copy HTML markup",
4063
4653
  "aria-label": "Copy HTML markup",
4064
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.Code, { size: 15 })
4654
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react4.Code, { size: 15 })
4065
4655
  }
4066
4656
  ),
4067
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
4657
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
4068
4658
  "button",
4069
4659
  {
4070
4660
  type: "button",
@@ -4076,7 +4666,7 @@ function InspectSimple({
4076
4666
  disabled: saving,
4077
4667
  title: "Save screenshot",
4078
4668
  "aria-label": "Save screenshot",
4079
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.Camera, { size: 15 })
4669
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react4.Camera, { size: 15 })
4080
4670
  }
4081
4671
  )
4082
4672
  ]
@@ -4119,7 +4709,7 @@ var iconActionStyle = {
4119
4709
  };
4120
4710
 
4121
4711
  // src/InspectDialog/InspectDialogManager.tsx
4122
- var import_jsx_runtime6 = require("react/jsx-runtime");
4712
+ var import_jsx_runtime8 = require("react/jsx-runtime");
4123
4713
  var INSPECT_DIALOG_EVENT = "anyclick:inspect";
4124
4714
  function openInspectDialog(targetElement) {
4125
4715
  if (typeof window === "undefined") return;
@@ -4142,16 +4732,16 @@ function InspectDialogManager({
4142
4732
  initialPinnedPosition = "floating",
4143
4733
  compactConfig
4144
4734
  }) {
4145
- const [visible, setVisible] = (0, import_react9.useState)(false);
4146
- const [targetElement, setTargetElement] = (0, import_react9.useState)(null);
4147
- const handleClose = (0, import_react9.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)(() => {
4148
4738
  setVisible(false);
4149
4739
  setTargetElement(null);
4150
4740
  }, []);
4151
- const handleSelectElement = (0, import_react9.useCallback)((element) => {
4741
+ const handleSelectElement = (0, import_react12.useCallback)((element) => {
4152
4742
  setTargetElement(element);
4153
4743
  }, []);
4154
- (0, import_react9.useEffect)(() => {
4744
+ (0, import_react12.useEffect)(() => {
4155
4745
  const handleInspectEvent = (event) => {
4156
4746
  const customEvent = event;
4157
4747
  if (customEvent.detail?.targetElement) {
@@ -4164,7 +4754,7 @@ function InspectDialogManager({
4164
4754
  window.removeEventListener(INSPECT_DIALOG_EVENT, handleInspectEvent);
4165
4755
  };
4166
4756
  }, []);
4167
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
4757
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4168
4758
  InspectSimple,
4169
4759
  {
4170
4760
  visible,
@@ -4693,10 +5283,10 @@ function filterMenuItemsByRole(items, userContext) {
4693
5283
  }
4694
5284
 
4695
5285
  // src/index.ts
4696
- var import_anyclick_core6 = require("@ewjdev/anyclick-core");
5286
+ var import_anyclick_core7 = require("@ewjdev/anyclick-core");
4697
5287
 
4698
5288
  // src/AnyclickLogo.tsx
4699
- var import_jsx_runtime7 = require("react/jsx-runtime");
5289
+ var import_jsx_runtime9 = require("react/jsx-runtime");
4700
5290
  function AnyclickLogo({
4701
5291
  size = 64,
4702
5292
  borderWidth = 2,
@@ -4708,7 +5298,7 @@ function AnyclickLogo({
4708
5298
  }) {
4709
5299
  const cursorSize = size * 0.45;
4710
5300
  const cursorStroke = borderWidth;
4711
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
5301
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
4712
5302
  "svg",
4713
5303
  {
4714
5304
  width: size,
@@ -4722,7 +5312,7 @@ function AnyclickLogo({
4722
5312
  role: onClick ? "button" : "img",
4723
5313
  "aria-label": "Anyclick Logo",
4724
5314
  children: [
4725
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
5315
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4726
5316
  "circle",
4727
5317
  {
4728
5318
  cx: size / 2,
@@ -4733,11 +5323,11 @@ function AnyclickLogo({
4733
5323
  strokeWidth: borderWidth
4734
5324
  }
4735
5325
  ),
4736
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
5326
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4737
5327
  "g",
4738
5328
  {
4739
5329
  transform: `translate(${(size - cursorSize) / 2}, ${(size - cursorSize) / 2})`,
4740
- children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
5330
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4741
5331
  "path",
4742
5332
  {
4743
5333
  d: `
@@ -4765,13 +5355,14 @@ function AnyclickLogo({
4765
5355
  }
4766
5356
 
4767
5357
  // src/index.ts
4768
- var import_anyclick_core7 = require("@ewjdev/anyclick-core");
5358
+ var import_anyclick_core8 = require("@ewjdev/anyclick-core");
4769
5359
  // Annotate the CommonJS export names for ESM import in node:
4770
5360
  0 && (module.exports = {
4771
5361
  ALL_CURATED_PROPERTIES,
4772
5362
  AnyclickContext,
4773
5363
  AnyclickLogo,
4774
5364
  AnyclickProvider,
5365
+ Button,
4775
5366
  CURATED_STYLE_PROPERTIES,
4776
5367
  ContextMenu,
4777
5368
  DEFAULT_COMPACT_CONFIG,
@@ -4782,6 +5373,7 @@ var import_anyclick_core7 = require("@ewjdev/anyclick-core");
4782
5373
  FeedbackProvider,
4783
5374
  FunModeBridge,
4784
5375
  INSPECT_DIALOG_EVENT,
5376
+ Input,
4785
5377
  InspectDialogManager,
4786
5378
  InspectSimple,
4787
5379
  QuickChat,