@ibealec/create-zed-bridge 1.0.13 → 1.0.15

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.
@@ -4,8 +4,8 @@ import {
4
4
  useContext,
5
5
  useReducer,
6
6
  useEffect as useEffect6,
7
- useCallback as useCallback4,
8
- useRef as useRef6
7
+ useCallback as useCallback8,
8
+ useRef as useRef5
9
9
  } from "react";
10
10
 
11
11
  // src/websocket-client.ts
@@ -669,275 +669,564 @@ function SelectionOverlay({
669
669
  ] });
670
670
  }
671
671
 
672
- // src/PromptDialog.tsx
673
- import { useState, useRef as useRef2, useEffect as useEffect2 } from "react";
672
+ // src/components/SessionPanel.tsx
673
+ import { useEffect as useEffect5, useState as useState3, useRef as useRef4, useCallback as useCallback6 } from "react";
674
+
675
+ // src/ClaudeTerminal.tsx
676
+ import { useEffect as useEffect2, useRef as useRef2, useCallback as useCallback2, useState } from "react";
677
+ import { Terminal } from "@xterm/xterm";
678
+ import { FitAddon } from "@xterm/addon-fit";
679
+ import { WebglAddon } from "@xterm/addon-webgl";
680
+ import "@xterm/xterm/css/xterm.css";
674
681
  import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
675
- function PromptDialog({
676
- open,
677
- element,
678
- onSubmit,
679
- onClose,
680
- onSwitchToQuestionMode,
681
- isSubmitting = false
682
+ var XTERM_CRITICAL_CSS = `
683
+ .xterm {
684
+ cursor: text;
685
+ position: relative;
686
+ user-select: none;
687
+ -ms-user-select: none;
688
+ -webkit-user-select: none;
689
+ }
690
+ .xterm.focus,
691
+ .xterm:focus {
692
+ outline: none;
693
+ }
694
+ .xterm .xterm-helpers {
695
+ position: absolute;
696
+ top: 0;
697
+ z-index: 5;
698
+ }
699
+ .xterm .xterm-helper-textarea {
700
+ padding: 0;
701
+ border: 0;
702
+ margin: 0;
703
+ position: absolute;
704
+ opacity: 0;
705
+ left: -9999em;
706
+ top: 0;
707
+ width: 0;
708
+ height: 0;
709
+ z-index: -5;
710
+ white-space: nowrap;
711
+ overflow: hidden;
712
+ resize: none;
713
+ }
714
+ .xterm .xterm-helper-textarea:focus {
715
+ outline: none;
716
+ }
717
+ .xterm .composition-view {
718
+ background: #000;
719
+ color: #FFF;
720
+ display: none;
721
+ position: absolute;
722
+ white-space: nowrap;
723
+ z-index: 1;
724
+ }
725
+ .xterm .composition-view.active {
726
+ display: block;
727
+ }
728
+ .xterm .xterm-viewport {
729
+ background-color: #000;
730
+ overflow-y: scroll;
731
+ cursor: default;
732
+ position: absolute;
733
+ right: 0;
734
+ left: 0;
735
+ top: 0;
736
+ bottom: 0;
737
+ }
738
+ .xterm .xterm-screen {
739
+ position: relative;
740
+ }
741
+ .xterm .xterm-screen canvas {
742
+ position: absolute;
743
+ left: 0;
744
+ top: 0;
745
+ }
746
+ .xterm-char-measure-element {
747
+ display: inline-block;
748
+ visibility: hidden;
749
+ position: absolute;
750
+ top: 0;
751
+ left: -9999em;
752
+ line-height: normal;
753
+ }
754
+ .xterm.enable-mouse-events {
755
+ cursor: default;
756
+ }
757
+ .xterm .xterm-cursor-pointer {
758
+ cursor: pointer;
759
+ }
760
+ .xterm.xterm-cursor-style-block .xterm-cursor:not(.xterm-cursor-blink),
761
+ .xterm.xterm-cursor-style-bar .xterm-cursor:not(.xterm-cursor-blink),
762
+ .xterm.xterm-cursor-style-underline .xterm-cursor:not(.xterm-cursor-blink) {
763
+ visibility: visible;
764
+ }
765
+ `;
766
+ function ClaudeTerminal({
767
+ serverUrl,
768
+ token,
769
+ sessionId,
770
+ onSessionSelect,
771
+ onConnectionChange,
772
+ onSessionsUpdate,
773
+ terminalOptions = {},
774
+ style,
775
+ className
682
776
  }) {
683
- const [prompt, setPrompt] = useState("");
684
- const inputRef = useRef2(null);
777
+ const containerRef = useRef2(null);
778
+ const terminalRef = useRef2(null);
779
+ const fitAddonRef = useRef2(null);
780
+ const wsRef = useRef2(null);
781
+ const handleMessageRef = useRef2(null);
782
+ const [connected, setConnected] = useState(false);
783
+ const [sessions, setSessions] = useState([]);
784
+ const [attachedSessionId, setAttachedSessionId] = useState(null);
785
+ const [error, setError] = useState(null);
685
786
  useEffect2(() => {
686
- if (open && inputRef.current) {
687
- inputRef.current.focus();
787
+ if (!containerRef.current) return;
788
+ const terminal = new Terminal({
789
+ cursorBlink: true,
790
+ cursorStyle: "block",
791
+ fontSize: terminalOptions.fontSize ?? 14,
792
+ fontFamily: terminalOptions.fontFamily ?? 'Menlo, Monaco, "Courier New", monospace',
793
+ theme: {
794
+ background: terminalOptions.theme?.background ?? "#1a1a1a",
795
+ foreground: terminalOptions.theme?.foreground ?? "#f0f0f0",
796
+ cursor: terminalOptions.theme?.cursor ?? "#f0f0f0",
797
+ cursorAccent: "#1a1a1a",
798
+ black: "#1a1a1a",
799
+ red: "#ff5555",
800
+ green: "#50fa7b",
801
+ yellow: "#f1fa8c",
802
+ blue: "#6272a4",
803
+ magenta: "#ff79c6",
804
+ cyan: "#8be9fd",
805
+ white: "#f8f8f2",
806
+ brightBlack: "#6272a4",
807
+ brightRed: "#ff6e6e",
808
+ brightGreen: "#69ff94",
809
+ brightYellow: "#ffffa5",
810
+ brightBlue: "#d6acff",
811
+ brightMagenta: "#ff92df",
812
+ brightCyan: "#a4ffff",
813
+ brightWhite: "#ffffff"
814
+ },
815
+ allowProposedApi: true,
816
+ scrollback: 1e4,
817
+ disableStdin: false,
818
+ allowTransparency: true
819
+ });
820
+ const fitAddon = new FitAddon();
821
+ terminal.loadAddon(fitAddon);
822
+ terminal.open(containerRef.current);
823
+ try {
824
+ const webglAddon = new WebglAddon();
825
+ webglAddon.onContextLoss(() => {
826
+ webglAddon.dispose();
827
+ });
828
+ terminal.loadAddon(webglAddon);
829
+ } catch (e) {
830
+ console.warn("[ClaudeTerminal] WebGL addon could not be loaded:", e);
688
831
  }
689
- }, [open]);
832
+ terminalRef.current = terminal;
833
+ fitAddonRef.current = fitAddon;
834
+ requestAnimationFrame(() => {
835
+ fitAddon.fit();
836
+ });
837
+ const resizeObserver = new ResizeObserver(() => {
838
+ requestAnimationFrame(() => {
839
+ fitAddon.fit();
840
+ if (wsRef.current?.readyState === WebSocket.OPEN && attachedSessionId) {
841
+ wsRef.current.send(JSON.stringify({
842
+ type: "terminal_resize",
843
+ sessionId: attachedSessionId,
844
+ cols: terminal.cols,
845
+ rows: terminal.rows
846
+ }));
847
+ }
848
+ });
849
+ });
850
+ resizeObserver.observe(containerRef.current);
851
+ return () => {
852
+ resizeObserver.disconnect();
853
+ terminal.dispose();
854
+ terminalRef.current = null;
855
+ fitAddonRef.current = null;
856
+ };
857
+ }, [terminalOptions.fontSize, terminalOptions.fontFamily, terminalOptions.theme]);
690
858
  useEffect2(() => {
691
- if (!open) {
692
- setPrompt("");
693
- }
694
- }, [open]);
695
- const handleSubmit = () => {
696
- if (!prompt.trim() || isSubmitting) return;
697
- onSubmit(prompt.trim());
698
- };
699
- const handleKeyDown = (e) => {
700
- if ((e.metaKey || e.ctrlKey) && e.key === "Enter") {
701
- e.preventDefault();
702
- handleSubmit();
703
- }
704
- if (e.key === "Escape") {
705
- e.preventDefault();
706
- onClose();
707
- }
708
- };
709
- if (!open || !element) return null;
710
- const context = captureContext(element);
711
- return /* @__PURE__ */ jsxs2(
712
- "div",
713
- {
714
- id: "claude-bridge-prompt-dialog",
715
- style: {
716
- position: "fixed",
717
- bottom: 24,
718
- left: "50%",
719
- transform: "translateX(-50%)",
720
- zIndex: 1e6,
721
- width: "100%",
722
- maxWidth: 600,
723
- background: "#1e1e2e",
724
- borderRadius: 12,
725
- boxShadow: "0 8px 32px rgba(0, 0, 0, 0.3)",
726
- fontFamily: "system-ui, -apple-system, sans-serif",
727
- overflow: "hidden"
728
- },
729
- children: [
730
- /* @__PURE__ */ jsxs2(
731
- "div",
732
- {
733
- style: {
734
- padding: "12px 16px",
735
- borderBottom: "1px solid rgba(255, 255, 255, 0.1)",
736
- display: "flex",
737
- alignItems: "center",
738
- gap: 8
739
- },
740
- children: [
741
- /* @__PURE__ */ jsx2(
742
- "div",
743
- {
744
- style: {
745
- width: 8,
746
- height: 8,
747
- borderRadius: "50%",
748
- background: "#6366f1"
749
- }
750
- }
751
- ),
752
- /* @__PURE__ */ jsx2(
753
- "span",
754
- {
755
- style: {
756
- color: "rgba(255, 255, 255, 0.9)",
757
- fontSize: 13,
758
- fontWeight: 500
759
- },
760
- children: context.componentName || context.selectedElement.tagName
761
- }
762
- ),
763
- context.sourceFile && /* @__PURE__ */ jsxs2(
764
- "span",
765
- {
766
- style: {
767
- color: "rgba(255, 255, 255, 0.5)",
768
- fontSize: 12,
769
- fontFamily: "ui-monospace, monospace"
770
- },
771
- children: [
772
- context.sourceFile,
773
- context.sourceLine ? `:${context.sourceLine}` : ""
774
- ]
775
- }
776
- ),
777
- /* @__PURE__ */ jsx2("div", { style: { flex: 1 } }),
778
- /* @__PURE__ */ jsx2(
779
- "button",
780
- {
781
- onClick: onClose,
782
- style: {
783
- background: "transparent",
784
- border: "none",
785
- color: "rgba(255, 255, 255, 0.5)",
786
- cursor: "pointer",
787
- padding: 4,
788
- borderRadius: 4,
789
- fontSize: 18,
790
- lineHeight: 1
791
- },
792
- children: "\xD7"
793
- }
794
- )
795
- ]
796
- }
797
- ),
798
- /* @__PURE__ */ jsxs2("div", { style: { padding: 16 }, children: [
799
- /* @__PURE__ */ jsx2(
800
- "textarea",
801
- {
802
- ref: inputRef,
803
- value: prompt,
804
- onChange: (e) => setPrompt(e.target.value),
805
- onKeyDown: handleKeyDown,
806
- placeholder: "What would you like Claude to change?",
807
- disabled: isSubmitting,
808
- style: {
809
- width: "100%",
810
- minHeight: 80,
811
- maxHeight: 200,
812
- padding: 12,
813
- background: "rgba(255, 255, 255, 0.05)",
814
- border: "1px solid rgba(255, 255, 255, 0.1)",
815
- borderRadius: 8,
816
- color: "white",
817
- fontSize: 14,
818
- fontFamily: "inherit",
819
- resize: "vertical",
820
- outline: "none"
821
- }
822
- }
823
- ),
824
- /* @__PURE__ */ jsx2(
825
- "div",
826
- {
827
- style: {
828
- display: "flex",
829
- flexWrap: "wrap",
830
- gap: 8,
831
- marginTop: 12
832
- },
833
- children: ["Make this bigger", "Change the color", "Add padding", "Hide this element"].map(
834
- (suggestion) => /* @__PURE__ */ jsx2(
835
- "button",
836
- {
837
- onClick: () => setPrompt(suggestion),
838
- disabled: isSubmitting,
839
- style: {
840
- background: "rgba(255, 255, 255, 0.05)",
841
- border: "1px solid rgba(255, 255, 255, 0.1)",
842
- borderRadius: 4,
843
- padding: "4px 8px",
844
- color: "rgba(255, 255, 255, 0.7)",
845
- fontSize: 12,
846
- cursor: "pointer"
847
- },
848
- children: suggestion
849
- },
850
- suggestion
851
- )
852
- )
859
+ const terminal = terminalRef.current;
860
+ if (!terminal) return;
861
+ const disposable = terminal.onData((data) => {
862
+ if (wsRef.current?.readyState === WebSocket.OPEN && attachedSessionId) {
863
+ wsRef.current.send(JSON.stringify({
864
+ type: "terminal_input",
865
+ sessionId: attachedSessionId,
866
+ data
867
+ }));
868
+ }
869
+ });
870
+ return () => disposable.dispose();
871
+ }, [attachedSessionId]);
872
+ const attachedSessionIdRef = useRef2(null);
873
+ attachedSessionIdRef.current = attachedSessionId;
874
+ useEffect2(() => {
875
+ handleMessageRef.current = (event) => {
876
+ try {
877
+ const message = JSON.parse(event.data);
878
+ switch (message.type) {
879
+ case "connected":
880
+ wsRef.current?.send(JSON.stringify({ type: "get_sessions" }));
881
+ break;
882
+ case "sessions_list":
883
+ if (message.sessions) {
884
+ setSessions(message.sessions);
885
+ onSessionsUpdate?.(message.sessions);
853
886
  }
854
- )
855
- ] }),
856
- /* @__PURE__ */ jsxs2(
857
- "div",
858
- {
859
- style: {
860
- padding: "12px 16px",
861
- borderTop: "1px solid rgba(255, 255, 255, 0.1)",
862
- display: "flex",
863
- justifyContent: "space-between",
864
- alignItems: "center"
887
+ break;
888
+ case "terminal_attached":
889
+ if (message.sessionId) {
890
+ setAttachedSessionId(message.sessionId);
891
+ setError(null);
892
+ if (message.scrollback && terminalRef.current) {
893
+ terminalRef.current.write(message.scrollback);
894
+ }
895
+ if (fitAddonRef.current && terminalRef.current) {
896
+ fitAddonRef.current.fit();
897
+ wsRef.current?.send(JSON.stringify({
898
+ type: "terminal_resize",
899
+ sessionId: message.sessionId,
900
+ cols: terminalRef.current.cols,
901
+ rows: terminalRef.current.rows
902
+ }));
903
+ }
904
+ }
905
+ break;
906
+ case "terminal_output":
907
+ if (message.data && terminalRef.current && message.sessionId === attachedSessionIdRef.current) {
908
+ terminalRef.current.write(message.data);
909
+ }
910
+ break;
911
+ case "terminal_detached":
912
+ if (message.sessionId === attachedSessionIdRef.current) {
913
+ setAttachedSessionId(null);
914
+ }
915
+ break;
916
+ case "error":
917
+ setError(message.error || "Unknown error");
918
+ console.error("[ClaudeTerminal] Error:", message.error);
919
+ break;
920
+ case "pong":
921
+ break;
922
+ }
923
+ } catch (e) {
924
+ console.error("[ClaudeTerminal] Failed to parse message:", e);
925
+ }
926
+ };
927
+ }, [onSessionsUpdate]);
928
+ useEffect2(() => {
929
+ const wsUrl = serverUrl.replace(/^http/, "ws").replace(/\/$/, "");
930
+ const ws = new WebSocket(`${wsUrl}/ws/bridge?token=${encodeURIComponent(token)}`);
931
+ ws.onopen = () => {
932
+ setConnected(true);
933
+ setError(null);
934
+ onConnectionChange?.(true);
935
+ };
936
+ ws.onmessage = (event) => {
937
+ handleMessageRef.current?.(event);
938
+ };
939
+ ws.onclose = () => {
940
+ setConnected(false);
941
+ setAttachedSessionId(null);
942
+ onConnectionChange?.(false);
943
+ };
944
+ ws.onerror = () => {
945
+ setError("WebSocket connection error");
946
+ };
947
+ wsRef.current = ws;
948
+ const pingInterval = setInterval(() => {
949
+ if (ws.readyState === WebSocket.OPEN) {
950
+ ws.send(JSON.stringify({ type: "ping" }));
951
+ }
952
+ }, 3e4);
953
+ return () => {
954
+ clearInterval(pingInterval);
955
+ ws.close();
956
+ wsRef.current = null;
957
+ };
958
+ }, [serverUrl, token, onConnectionChange]);
959
+ useEffect2(() => {
960
+ if (sessionId && connected && !attachedSessionId) {
961
+ wsRef.current?.send(JSON.stringify({
962
+ type: "attach_terminal",
963
+ sessionId
964
+ }));
965
+ }
966
+ }, [sessionId, connected, attachedSessionId]);
967
+ useEffect2(() => {
968
+ if (attachedSessionId && terminalRef.current && fitAddonRef.current) {
969
+ const timer = setTimeout(() => {
970
+ fitAddonRef.current?.fit();
971
+ terminalRef.current?.focus();
972
+ }, 50);
973
+ return () => clearTimeout(timer);
974
+ }
975
+ }, [attachedSessionId]);
976
+ const handleContainerClick = useCallback2(() => {
977
+ terminalRef.current?.focus();
978
+ }, []);
979
+ const handleSelectSession = useCallback2((session) => {
980
+ terminalRef.current?.clear();
981
+ if (attachedSessionId) {
982
+ wsRef.current?.send(JSON.stringify({
983
+ type: "detach_terminal",
984
+ sessionId: attachedSessionId
985
+ }));
986
+ }
987
+ wsRef.current?.send(JSON.stringify({
988
+ type: "attach_terminal",
989
+ sessionId: session.id
990
+ }));
991
+ onSessionSelect?.(session);
992
+ }, [attachedSessionId, onSessionSelect]);
993
+ if (!sessionId && !attachedSessionId) {
994
+ return /* @__PURE__ */ jsx2(
995
+ "div",
996
+ {
997
+ className,
998
+ style: {
999
+ background: "#1a1a1a",
1000
+ color: "#f0f0f0",
1001
+ padding: "20px",
1002
+ fontFamily: 'Menlo, Monaco, "Courier New", monospace',
1003
+ ...style
1004
+ },
1005
+ children: !connected ? /* @__PURE__ */ jsx2("div", { children: "Connecting to server..." }) : error ? /* @__PURE__ */ jsxs2("div", { style: { color: "#ff5555" }, children: [
1006
+ "Error: ",
1007
+ error
1008
+ ] }) : sessions.length === 0 ? /* @__PURE__ */ jsx2("div", { children: "No Claude sessions available" }) : /* @__PURE__ */ jsxs2("div", { children: [
1009
+ /* @__PURE__ */ jsx2("div", { style: { marginBottom: "10px", fontSize: "14px" }, children: "Select a Claude session:" }),
1010
+ sessions.map((session) => /* @__PURE__ */ jsxs2(
1011
+ "div",
1012
+ {
1013
+ onClick: () => handleSelectSession(session),
1014
+ style: {
1015
+ padding: "10px",
1016
+ margin: "5px 0",
1017
+ background: "#2a2a2a",
1018
+ borderRadius: "4px",
1019
+ cursor: "pointer",
1020
+ border: "1px solid #3a3a3a"
1021
+ },
1022
+ onMouseOver: (e) => {
1023
+ e.currentTarget.style.background = "#3a3a3a";
1024
+ },
1025
+ onMouseOut: (e) => {
1026
+ e.currentTarget.style.background = "#2a2a2a";
1027
+ },
1028
+ children: [
1029
+ /* @__PURE__ */ jsx2("div", { style: { fontWeight: "bold" }, children: session.displayName || session.name }),
1030
+ /* @__PURE__ */ jsx2("div", { style: { fontSize: "12px", color: "#888", marginTop: "4px" }, children: session.workingDir })
1031
+ ]
865
1032
  },
866
- children: [
867
- /* @__PURE__ */ jsxs2("div", { style: { display: "flex", alignItems: "center", gap: 12 }, children: [
868
- /* @__PURE__ */ jsxs2(
1033
+ session.id
1034
+ ))
1035
+ ] })
1036
+ }
1037
+ );
1038
+ }
1039
+ return /* @__PURE__ */ jsxs2(Fragment2, { children: [
1040
+ /* @__PURE__ */ jsx2("style", { children: XTERM_CRITICAL_CSS }),
1041
+ /* @__PURE__ */ jsx2(
1042
+ "div",
1043
+ {
1044
+ ref: containerRef,
1045
+ className,
1046
+ onClick: handleContainerClick,
1047
+ style: {
1048
+ width: "100%",
1049
+ height: "100%",
1050
+ ...style
1051
+ }
1052
+ }
1053
+ )
1054
+ ] });
1055
+ }
1056
+
1057
+ // src/components/SessionTabs.tsx
1058
+ import { useCallback as useCallback3 } from "react";
1059
+
1060
+ // src/hooks/useSessions.ts
1061
+ import { useMemo } from "react";
1062
+ function useSessions({
1063
+ sessions,
1064
+ projectRoot
1065
+ }) {
1066
+ const isProjectSession = useMemo(() => {
1067
+ return (session) => {
1068
+ if (!projectRoot) return true;
1069
+ return session.workingDir === projectRoot || session.workingDir.startsWith(projectRoot + "/");
1070
+ };
1071
+ }, [projectRoot]);
1072
+ const projectSessions = useMemo(() => {
1073
+ if (!projectRoot) return sessions;
1074
+ return sessions.filter(isProjectSession);
1075
+ }, [sessions, projectRoot, isProjectSession]);
1076
+ const sortedProjectSessions = useMemo(() => {
1077
+ return [...projectSessions].sort((a, b) => {
1078
+ return new Date(b.lastActivity).getTime() - new Date(a.lastActivity).getTime();
1079
+ });
1080
+ }, [projectSessions]);
1081
+ return {
1082
+ projectSessions: sortedProjectSessions,
1083
+ allSessions: sessions,
1084
+ isProjectSession
1085
+ };
1086
+ }
1087
+ function getSessionDisplayName(session) {
1088
+ return session.displayName || session.name || session.tmuxSession;
1089
+ }
1090
+ function isSessionActive(session) {
1091
+ const lastActivity = new Date(session.lastActivity).getTime();
1092
+ const now = Date.now();
1093
+ const sixtySecondsAgo = now - 60 * 1e3;
1094
+ return lastActivity > sixtySecondsAgo;
1095
+ }
1096
+
1097
+ // src/components/SessionTabs.tsx
1098
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
1099
+ function SessionTabs({
1100
+ sessions,
1101
+ activeSessionId,
1102
+ onSessionSelect,
1103
+ onSessionClose,
1104
+ taskSessionId
1105
+ }) {
1106
+ const handleTabClick = useCallback3(
1107
+ (e, sessionId) => {
1108
+ e.stopPropagation();
1109
+ onSessionSelect(sessionId);
1110
+ },
1111
+ [onSessionSelect]
1112
+ );
1113
+ const handleCloseClick = useCallback3(
1114
+ (e, sessionId) => {
1115
+ e.stopPropagation();
1116
+ onSessionClose?.(sessionId);
1117
+ },
1118
+ [onSessionClose]
1119
+ );
1120
+ if (sessions.length === 0) {
1121
+ return /* @__PURE__ */ jsx3(
1122
+ "div",
1123
+ {
1124
+ style: {
1125
+ padding: "8px 12px",
1126
+ color: "rgba(255, 255, 255, 0.5)",
1127
+ fontSize: 12
1128
+ },
1129
+ children: "No active sessions"
1130
+ }
1131
+ );
1132
+ }
1133
+ return /* @__PURE__ */ jsxs3(
1134
+ "div",
1135
+ {
1136
+ style: {
1137
+ display: "flex",
1138
+ alignItems: "center",
1139
+ gap: 2,
1140
+ padding: "4px 8px",
1141
+ overflowX: "auto",
1142
+ flex: 1,
1143
+ minWidth: 0
1144
+ },
1145
+ children: [
1146
+ sessions.map((session) => {
1147
+ const isActive = session.id === activeSessionId;
1148
+ const isRunning = isSessionActive(session);
1149
+ const isTaskSession = session.id === taskSessionId;
1150
+ const displayName = getSessionDisplayName(session);
1151
+ return /* @__PURE__ */ jsxs3(
1152
+ "button",
1153
+ {
1154
+ onClick: (e) => handleTabClick(e, session.id),
1155
+ title: `${displayName}
1156
+ ${session.workingDir}`,
1157
+ style: {
1158
+ display: "flex",
1159
+ alignItems: "center",
1160
+ gap: 6,
1161
+ padding: "6px 10px",
1162
+ background: isActive ? "rgba(99, 102, 241, 0.2)" : "rgba(255, 255, 255, 0.05)",
1163
+ border: isActive ? "1px solid rgba(99, 102, 241, 0.4)" : "1px solid transparent",
1164
+ borderRadius: 6,
1165
+ color: isActive ? "rgba(255, 255, 255, 0.95)" : "rgba(255, 255, 255, 0.7)",
1166
+ fontSize: 12,
1167
+ fontWeight: isActive ? 500 : 400,
1168
+ cursor: "pointer",
1169
+ whiteSpace: "nowrap",
1170
+ maxWidth: 180,
1171
+ overflow: "hidden",
1172
+ textOverflow: "ellipsis",
1173
+ transition: "all 0.15s ease"
1174
+ },
1175
+ children: [
1176
+ /* @__PURE__ */ jsx3(
869
1177
  "span",
870
1178
  {
871
1179
  style: {
872
- color: "rgba(255, 255, 255, 0.4)",
873
- fontSize: 12
874
- },
875
- children: [
876
- navigator.platform.includes("Mac") ? "\u2318" : "Ctrl",
877
- "+Enter to submit"
878
- ]
1180
+ width: 6,
1181
+ height: 6,
1182
+ borderRadius: "50%",
1183
+ background: isTaskSession ? "#22c55e" : isRunning ? "#f59e0b" : "rgba(255, 255, 255, 0.3)",
1184
+ flexShrink: 0,
1185
+ animation: isTaskSession ? "claude-bridge-pulse 1.5s infinite" : "none"
1186
+ }
879
1187
  }
880
1188
  ),
881
- onSwitchToQuestionMode && /* @__PURE__ */ jsx2(
882
- "button",
1189
+ /* @__PURE__ */ jsx3(
1190
+ "span",
883
1191
  {
884
- onClick: onSwitchToQuestionMode,
885
- disabled: isSubmitting,
886
1192
  style: {
887
- background: "transparent",
888
- border: "none",
889
- color: "#22c55e",
890
- fontSize: 12,
891
- cursor: "pointer",
892
- padding: 0,
893
- textDecoration: "underline"
1193
+ overflow: "hidden",
1194
+ textOverflow: "ellipsis"
894
1195
  },
895
- children: "Just ask a question instead"
1196
+ children: displayName
896
1197
  }
897
- )
898
- ] }),
899
- /* @__PURE__ */ jsx2(
900
- "button",
901
- {
902
- onClick: handleSubmit,
903
- disabled: !prompt.trim() || isSubmitting,
904
- style: {
905
- background: prompt.trim() && !isSubmitting ? "#6366f1" : "rgba(99, 102, 241, 0.3)",
906
- border: "none",
907
- borderRadius: 6,
908
- padding: "8px 16px",
909
- color: "white",
910
- fontSize: 14,
911
- fontWeight: 500,
912
- cursor: prompt.trim() && !isSubmitting ? "pointer" : "not-allowed",
913
- display: "flex",
914
- alignItems: "center",
915
- gap: 6
916
- },
917
- children: isSubmitting ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
918
- /* @__PURE__ */ jsx2(
919
- "span",
920
- {
921
- style: {
922
- width: 14,
923
- height: 14,
924
- border: "2px solid rgba(255, 255, 255, 0.3)",
925
- borderTopColor: "white",
926
- borderRadius: "50%",
927
- animation: "claude-bridge-spin 0.8s linear infinite"
928
- }
929
- }
930
- ),
931
- "Sending..."
932
- ] }) : "Send to Claude"
933
- }
934
- )
935
- ]
936
- }
937
- ),
938
- /* @__PURE__ */ jsx2("style", { children: `
939
- @keyframes claude-bridge-spin {
940
- to { transform: rotate(360deg); }
1198
+ ),
1199
+ onSessionClose && /* @__PURE__ */ jsx3(
1200
+ "span",
1201
+ {
1202
+ onClick: (e) => handleCloseClick(e, session.id),
1203
+ style: {
1204
+ marginLeft: 2,
1205
+ padding: "0 2px",
1206
+ color: "rgba(255, 255, 255, 0.4)",
1207
+ cursor: "pointer",
1208
+ fontSize: 14,
1209
+ lineHeight: 1,
1210
+ flexShrink: 0
1211
+ },
1212
+ onMouseOver: (e) => {
1213
+ e.currentTarget.style.color = "rgba(255, 255, 255, 0.8)";
1214
+ },
1215
+ onMouseOut: (e) => {
1216
+ e.currentTarget.style.color = "rgba(255, 255, 255, 0.4)";
1217
+ },
1218
+ children: "\xD7"
1219
+ }
1220
+ )
1221
+ ]
1222
+ },
1223
+ session.id
1224
+ );
1225
+ }),
1226
+ /* @__PURE__ */ jsx3("style", { children: `
1227
+ @keyframes claude-bridge-pulse {
1228
+ 0%, 100% { opacity: 1; }
1229
+ 50% { opacity: 0.5; }
941
1230
  }
942
1231
  ` })
943
1232
  ]
@@ -945,911 +1234,502 @@ function PromptDialog({
945
1234
  );
946
1235
  }
947
1236
 
948
- // src/QuestionDialog.tsx
949
- import { useState as useState2, useRef as useRef3, useEffect as useEffect3 } from "react";
950
- import { Fragment as Fragment3, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
951
- function QuestionDialog({
952
- open,
953
- selectedElement,
954
- onSubmit,
955
- onClose,
956
- onSwitchToTaskMode,
957
- isSubmitting = false
958
- }) {
959
- const [prompt, setPrompt] = useState2("");
960
- const [includeElement, setIncludeElement] = useState2(true);
961
- const inputRef = useRef3(null);
1237
+ // src/components/VoiceInput.tsx
1238
+ import React4, { useCallback as useCallback5, useEffect as useEffect4 } from "react";
1239
+
1240
+ // src/hooks/useVoiceInput.ts
1241
+ import { useState as useState2, useCallback as useCallback4, useRef as useRef3, useEffect as useEffect3 } from "react";
1242
+ function useVoiceInput(options = {}) {
1243
+ const {
1244
+ onTranscript,
1245
+ onError,
1246
+ onStart,
1247
+ onEnd,
1248
+ language = "en-US"
1249
+ } = options;
1250
+ const [isListening, setIsListening] = useState2(false);
1251
+ const [transcript, setTranscript] = useState2("");
1252
+ const [interimTranscript, setInterimTranscript] = useState2("");
1253
+ const [error, setError] = useState2(null);
1254
+ const recognitionRef = useRef3(null);
1255
+ const isSupported = typeof window !== "undefined" && (!!window.SpeechRecognition || !!window.webkitSpeechRecognition);
962
1256
  useEffect3(() => {
963
- if (open && inputRef.current) {
964
- inputRef.current.focus();
1257
+ return () => {
1258
+ if (recognitionRef.current) {
1259
+ recognitionRef.current.abort();
1260
+ recognitionRef.current = null;
1261
+ }
1262
+ };
1263
+ }, []);
1264
+ const startListening = useCallback4(() => {
1265
+ if (!isSupported) {
1266
+ const errMsg = "Speech recognition is not supported in this browser";
1267
+ setError(errMsg);
1268
+ onError?.(errMsg);
1269
+ return;
965
1270
  }
966
- }, [open]);
967
- useEffect3(() => {
968
- if (!open) {
969
- setPrompt("");
1271
+ if (recognitionRef.current) {
1272
+ recognitionRef.current.abort();
970
1273
  }
971
- }, [open]);
972
- useEffect3(() => {
973
- setIncludeElement(!!selectedElement);
974
- }, [selectedElement]);
975
- const handleSubmit = () => {
976
- if (!prompt.trim() || isSubmitting) return;
977
- onSubmit(prompt.trim(), includeElement && !!selectedElement);
978
- };
979
- const handleKeyDown = (e) => {
980
- if ((e.metaKey || e.ctrlKey) && e.key === "Enter") {
981
- e.preventDefault();
982
- handleSubmit();
1274
+ setError(null);
1275
+ setTranscript("");
1276
+ setInterimTranscript("");
1277
+ const SpeechRecognitionClass = window.SpeechRecognition || window.webkitSpeechRecognition;
1278
+ if (!SpeechRecognitionClass) return;
1279
+ const recognition = new SpeechRecognitionClass();
1280
+ recognitionRef.current = recognition;
1281
+ recognition.continuous = false;
1282
+ recognition.interimResults = true;
1283
+ recognition.lang = language;
1284
+ recognition.onstart = () => {
1285
+ setIsListening(true);
1286
+ onStart?.();
1287
+ };
1288
+ recognition.onresult = (event) => {
1289
+ let finalTranscript = "";
1290
+ let interimText = "";
1291
+ for (let i = event.resultIndex; i < event.results.length; i++) {
1292
+ const result = event.results[i];
1293
+ if (result.isFinal) {
1294
+ finalTranscript += result[0].transcript;
1295
+ } else {
1296
+ interimText += result[0].transcript;
1297
+ }
1298
+ }
1299
+ if (finalTranscript) {
1300
+ setTranscript((prev) => prev + finalTranscript);
1301
+ setInterimTranscript("");
1302
+ onTranscript?.(finalTranscript, true);
1303
+ } else {
1304
+ setInterimTranscript(interimText);
1305
+ onTranscript?.(interimText, false);
1306
+ }
1307
+ };
1308
+ recognition.onerror = (event) => {
1309
+ if (event.error === "no-speech" || event.error === "aborted") {
1310
+ return;
1311
+ }
1312
+ const errMsg = `Speech recognition error: ${event.error}`;
1313
+ setError(errMsg);
1314
+ setIsListening(false);
1315
+ onError?.(errMsg);
1316
+ };
1317
+ recognition.onend = () => {
1318
+ setIsListening(false);
1319
+ setInterimTranscript("");
1320
+ onEnd?.();
1321
+ recognitionRef.current = null;
1322
+ };
1323
+ try {
1324
+ recognition.start();
1325
+ } catch (err) {
1326
+ const errMsg = "Failed to start speech recognition";
1327
+ setError(errMsg);
1328
+ onError?.(errMsg);
983
1329
  }
984
- if (e.key === "Escape") {
985
- e.preventDefault();
986
- onClose();
1330
+ }, [isSupported, language, onTranscript, onError, onStart, onEnd]);
1331
+ const stopListening = useCallback4(() => {
1332
+ if (recognitionRef.current) {
1333
+ recognitionRef.current.stop();
987
1334
  }
1335
+ }, []);
1336
+ return {
1337
+ isListening,
1338
+ isSupported,
1339
+ transcript,
1340
+ interimTranscript,
1341
+ startListening,
1342
+ stopListening,
1343
+ error
988
1344
  };
989
- if (!open) return null;
990
- const pageContext = capturePageContext();
991
- const elementContext = selectedElement ? captureContext(selectedElement) : null;
992
- return /* @__PURE__ */ jsxs3(
993
- "div",
994
- {
995
- id: "claude-bridge-question-dialog",
996
- style: {
997
- position: "fixed",
998
- bottom: 24,
999
- left: "50%",
1000
- transform: "translateX(-50%)",
1001
- zIndex: 1e6,
1002
- width: "100%",
1003
- maxWidth: 600,
1004
- background: "#1e1e2e",
1005
- borderRadius: 12,
1006
- boxShadow: "0 8px 32px rgba(0, 0, 0, 0.3)",
1007
- fontFamily: "system-ui, -apple-system, sans-serif",
1008
- overflow: "hidden"
1009
- },
1010
- children: [
1011
- /* @__PURE__ */ jsxs3(
1012
- "div",
1013
- {
1014
- style: {
1015
- padding: "12px 16px",
1016
- borderBottom: "1px solid rgba(255, 255, 255, 0.1)",
1017
- display: "flex",
1018
- alignItems: "center",
1019
- gap: 8
1020
- },
1021
- children: [
1022
- /* @__PURE__ */ jsx3(
1023
- "div",
1024
- {
1025
- style: {
1026
- width: 8,
1027
- height: 8,
1028
- borderRadius: "50%",
1029
- background: "#22c55e"
1030
- // Green to differentiate from task dialog
1031
- }
1032
- }
1033
- ),
1034
- /* @__PURE__ */ jsx3(
1035
- "span",
1036
- {
1037
- style: {
1038
- color: "rgba(255, 255, 255, 0.9)",
1039
- fontSize: 13,
1040
- fontWeight: 500
1041
- },
1042
- children: "Ask a Question"
1043
- }
1044
- ),
1045
- /* @__PURE__ */ jsx3(
1046
- "span",
1047
- {
1048
- style: {
1049
- color: "rgba(255, 255, 255, 0.5)",
1050
- fontSize: 12
1051
- },
1052
- children: pageContext.pageTitle
1053
- }
1054
- ),
1055
- /* @__PURE__ */ jsx3("div", { style: { flex: 1 } }),
1056
- /* @__PURE__ */ jsx3(
1057
- "button",
1058
- {
1059
- onClick: onClose,
1060
- style: {
1061
- background: "transparent",
1062
- border: "none",
1063
- color: "rgba(255, 255, 255, 0.5)",
1064
- cursor: "pointer",
1065
- padding: 4,
1066
- borderRadius: 4,
1067
- fontSize: 18,
1068
- lineHeight: 1
1069
- },
1070
- children: "\xD7"
1345
+ }
1346
+
1347
+ // src/components/VoiceInput.tsx
1348
+ import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1349
+ function VoiceInput({
1350
+ onTranscript,
1351
+ onListeningChange,
1352
+ disabled = false,
1353
+ size = "medium",
1354
+ autoStart = false
1355
+ }) {
1356
+ const autoStartTriggeredRef = React4.useRef(false);
1357
+ const {
1358
+ isListening,
1359
+ isSupported,
1360
+ transcript,
1361
+ interimTranscript,
1362
+ startListening,
1363
+ stopListening
1364
+ } = useVoiceInput({
1365
+ onTranscript: (text, isFinal) => {
1366
+ if (isFinal) {
1367
+ onTranscript(text);
1368
+ }
1369
+ }
1370
+ });
1371
+ useEffect4(() => {
1372
+ if (autoStart && isSupported && !disabled && !autoStartTriggeredRef.current) {
1373
+ autoStartTriggeredRef.current = true;
1374
+ const timer = setTimeout(() => {
1375
+ startListening();
1376
+ }, 100);
1377
+ return () => clearTimeout(timer);
1378
+ }
1379
+ }, [autoStart, isSupported, disabled, startListening]);
1380
+ useEffect4(() => {
1381
+ onListeningChange?.(isListening);
1382
+ }, [isListening, onListeningChange]);
1383
+ useEffect4(() => {
1384
+ if (!isListening && transcript) {
1385
+ onTranscript(transcript);
1386
+ }
1387
+ }, [isListening, transcript, onTranscript]);
1388
+ const handleClick = useCallback5(() => {
1389
+ if (isListening) {
1390
+ stopListening();
1391
+ } else {
1392
+ startListening();
1393
+ }
1394
+ }, [isListening, startListening, stopListening]);
1395
+ if (!isSupported) {
1396
+ return null;
1397
+ }
1398
+ const buttonSize = size === "small" ? 32 : 40;
1399
+ const iconSize = size === "small" ? 16 : 20;
1400
+ return /* @__PURE__ */ jsxs4("div", { style: { position: "relative", display: "inline-flex", alignItems: "center" }, children: [
1401
+ /* @__PURE__ */ jsxs4(
1402
+ "button",
1403
+ {
1404
+ type: "button",
1405
+ onClick: handleClick,
1406
+ disabled,
1407
+ title: isListening ? "Stop listening" : "Start voice input",
1408
+ style: {
1409
+ width: buttonSize,
1410
+ height: buttonSize,
1411
+ borderRadius: "50%",
1412
+ border: "none",
1413
+ background: isListening ? "#ef4444" : disabled ? "rgba(255, 255, 255, 0.1)" : "rgba(255, 255, 255, 0.1)",
1414
+ cursor: disabled ? "not-allowed" : "pointer",
1415
+ display: "flex",
1416
+ alignItems: "center",
1417
+ justifyContent: "center",
1418
+ transition: "all 0.2s ease",
1419
+ position: "relative",
1420
+ overflow: "visible"
1421
+ },
1422
+ children: [
1423
+ isListening && /* @__PURE__ */ jsxs4(Fragment3, { children: [
1424
+ /* @__PURE__ */ jsx4(
1425
+ "span",
1426
+ {
1427
+ style: {
1428
+ position: "absolute",
1429
+ top: -4,
1430
+ left: -4,
1431
+ right: -4,
1432
+ bottom: -4,
1433
+ borderRadius: "50%",
1434
+ border: "2px solid #ef4444",
1435
+ animation: "claude-voice-pulse 1.5s ease-out infinite"
1071
1436
  }
1072
- )
1073
- ]
1074
- }
1075
- ),
1076
- selectedElement && /* @__PURE__ */ jsx3(
1077
- "div",
1078
- {
1079
- style: {
1080
- padding: "8px 16px",
1081
- background: "rgba(34, 197, 94, 0.1)",
1082
- borderBottom: "1px solid rgba(255, 255, 255, 0.1)",
1083
- display: "flex",
1084
- alignItems: "center",
1085
- gap: 8
1086
- },
1087
- children: /* @__PURE__ */ jsxs3(
1088
- "label",
1437
+ }
1438
+ ),
1439
+ /* @__PURE__ */ jsx4(
1440
+ "span",
1089
1441
  {
1090
1442
  style: {
1091
- display: "flex",
1092
- alignItems: "center",
1093
- gap: 8,
1094
- cursor: "pointer",
1095
- flex: 1
1096
- },
1097
- children: [
1098
- /* @__PURE__ */ jsx3(
1099
- "input",
1100
- {
1101
- type: "checkbox",
1102
- checked: includeElement,
1103
- onChange: (e) => setIncludeElement(e.target.checked),
1104
- style: { cursor: "pointer" }
1105
- }
1106
- ),
1107
- /* @__PURE__ */ jsx3(
1108
- "span",
1109
- {
1110
- style: {
1111
- color: "rgba(255, 255, 255, 0.7)",
1112
- fontSize: 12
1113
- },
1114
- children: "Include selected element:"
1115
- }
1116
- ),
1117
- /* @__PURE__ */ jsxs3(
1118
- "span",
1119
- {
1120
- style: {
1121
- color: "rgba(255, 255, 255, 0.9)",
1122
- fontSize: 12,
1123
- fontFamily: "ui-monospace, monospace"
1124
- },
1125
- children: [
1126
- elementContext?.componentName || elementContext?.selectedElement.tagName,
1127
- elementContext?.sourceFile && /* @__PURE__ */ jsxs3("span", { style: { color: "rgba(255, 255, 255, 0.5)", marginLeft: 4 }, children: [
1128
- "(",
1129
- elementContext.sourceFile,
1130
- elementContext.sourceLine ? `:${elementContext.sourceLine}` : "",
1131
- ")"
1132
- ] })
1133
- ]
1134
- }
1135
- )
1136
- ]
1443
+ position: "absolute",
1444
+ top: -8,
1445
+ left: -8,
1446
+ right: -8,
1447
+ bottom: -8,
1448
+ borderRadius: "50%",
1449
+ border: "2px solid #ef4444",
1450
+ animation: "claude-voice-pulse 1.5s ease-out infinite 0.5s"
1451
+ }
1137
1452
  }
1138
1453
  )
1139
- }
1140
- ),
1141
- /* @__PURE__ */ jsxs3("div", { style: { padding: 16 }, children: [
1142
- /* @__PURE__ */ jsx3(
1143
- "textarea",
1144
- {
1145
- ref: inputRef,
1146
- value: prompt,
1147
- onChange: (e) => setPrompt(e.target.value),
1148
- onKeyDown: handleKeyDown,
1149
- placeholder: "Ask a question about this page or codebase...",
1150
- disabled: isSubmitting,
1151
- style: {
1152
- width: "100%",
1153
- minHeight: 80,
1154
- maxHeight: 200,
1155
- padding: 12,
1156
- background: "rgba(255, 255, 255, 0.05)",
1157
- border: "1px solid rgba(255, 255, 255, 0.1)",
1158
- borderRadius: 8,
1159
- color: "white",
1160
- fontSize: 14,
1161
- fontFamily: "inherit",
1162
- resize: "vertical",
1163
- outline: "none"
1164
- }
1165
- }
1166
- ),
1167
- /* @__PURE__ */ jsx3(
1168
- "div",
1454
+ ] }),
1455
+ /* @__PURE__ */ jsxs4(
1456
+ "svg",
1169
1457
  {
1170
- style: {
1171
- display: "flex",
1172
- flexWrap: "wrap",
1173
- gap: 8,
1174
- marginTop: 12
1175
- },
1458
+ width: iconSize,
1459
+ height: iconSize,
1460
+ viewBox: "0 0 24 24",
1461
+ fill: "none",
1462
+ stroke: isListening ? "white" : disabled ? "rgba(255, 255, 255, 0.3)" : "rgba(255, 255, 255, 0.7)",
1463
+ strokeWidth: "2",
1464
+ strokeLinecap: "round",
1465
+ strokeLinejoin: "round",
1176
1466
  children: [
1177
- "What does this page do?",
1178
- "Where are the backend routes?",
1179
- "How does this component work?",
1180
- "What API calls does this make?"
1181
- ].map((suggestion) => /* @__PURE__ */ jsx3(
1182
- "button",
1183
- {
1184
- onClick: () => setPrompt(suggestion),
1185
- disabled: isSubmitting,
1186
- style: {
1187
- background: "rgba(255, 255, 255, 0.05)",
1188
- border: "1px solid rgba(255, 255, 255, 0.1)",
1189
- borderRadius: 4,
1190
- padding: "4px 8px",
1191
- color: "rgba(255, 255, 255, 0.7)",
1192
- fontSize: 12,
1193
- cursor: "pointer"
1194
- },
1195
- children: suggestion
1196
- },
1197
- suggestion
1198
- ))
1467
+ /* @__PURE__ */ jsx4("path", { d: "M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z" }),
1468
+ /* @__PURE__ */ jsx4("path", { d: "M19 10v2a7 7 0 0 1-14 0v-2" }),
1469
+ /* @__PURE__ */ jsx4("line", { x1: "12", x2: "12", y1: "19", y2: "22" })
1470
+ ]
1199
1471
  }
1200
1472
  )
1201
- ] }),
1202
- /* @__PURE__ */ jsxs3(
1203
- "div",
1204
- {
1205
- style: {
1206
- padding: "12px 16px",
1207
- borderTop: "1px solid rgba(255, 255, 255, 0.1)",
1208
- display: "flex",
1209
- justifyContent: "space-between",
1210
- alignItems: "center"
1211
- },
1212
- children: [
1213
- /* @__PURE__ */ jsxs3("div", { style: { display: "flex", alignItems: "center", gap: 12 }, children: [
1214
- /* @__PURE__ */ jsxs3(
1215
- "span",
1216
- {
1217
- style: {
1218
- color: "rgba(255, 255, 255, 0.4)",
1219
- fontSize: 12
1220
- },
1221
- children: [
1222
- navigator.platform.includes("Mac") ? "\u2318" : "Ctrl",
1223
- "+Enter to submit"
1224
- ]
1225
- }
1226
- ),
1227
- onSwitchToTaskMode && /* @__PURE__ */ jsx3(
1228
- "button",
1229
- {
1230
- onClick: onSwitchToTaskMode,
1231
- disabled: isSubmitting,
1232
- style: {
1233
- background: "transparent",
1234
- border: "none",
1235
- color: "#6366f1",
1236
- fontSize: 12,
1237
- cursor: "pointer",
1238
- padding: 0,
1239
- textDecoration: "underline"
1240
- },
1241
- children: "Make changes instead"
1242
- }
1243
- )
1244
- ] }),
1245
- /* @__PURE__ */ jsx3(
1246
- "button",
1247
- {
1248
- onClick: handleSubmit,
1249
- disabled: !prompt.trim() || isSubmitting,
1250
- style: {
1251
- background: prompt.trim() && !isSubmitting ? "#22c55e" : "rgba(34, 197, 94, 0.3)",
1252
- border: "none",
1253
- borderRadius: 6,
1254
- padding: "8px 16px",
1255
- color: "white",
1256
- fontSize: 14,
1257
- fontWeight: 500,
1258
- cursor: prompt.trim() && !isSubmitting ? "pointer" : "not-allowed",
1259
- display: "flex",
1260
- alignItems: "center",
1261
- gap: 6
1262
- },
1263
- children: isSubmitting ? /* @__PURE__ */ jsxs3(Fragment3, { children: [
1264
- /* @__PURE__ */ jsx3(
1265
- "span",
1266
- {
1267
- style: {
1268
- width: 14,
1269
- height: 14,
1270
- border: "2px solid rgba(255, 255, 255, 0.3)",
1271
- borderTopColor: "white",
1272
- borderRadius: "50%",
1273
- animation: "claude-bridge-spin 0.8s linear infinite"
1274
- }
1275
- }
1276
- ),
1277
- "Asking..."
1278
- ] }) : "Ask Claude"
1279
- }
1280
- )
1281
- ]
1282
- }
1283
- ),
1284
- /* @__PURE__ */ jsx3("style", { children: `
1285
- @keyframes claude-bridge-spin {
1286
- to { transform: rotate(360deg); }
1287
- }
1288
- ` })
1289
- ]
1290
- }
1291
- );
1473
+ ]
1474
+ }
1475
+ ),
1476
+ isListening && (interimTranscript || transcript) && /* @__PURE__ */ jsxs4(
1477
+ "div",
1478
+ {
1479
+ style: {
1480
+ position: "absolute",
1481
+ bottom: "100%",
1482
+ left: "50%",
1483
+ transform: "translateX(-50%)",
1484
+ marginBottom: 8,
1485
+ padding: "8px 12px",
1486
+ background: "rgba(0, 0, 0, 0.9)",
1487
+ borderRadius: 8,
1488
+ maxWidth: 300,
1489
+ whiteSpace: "pre-wrap",
1490
+ color: "white",
1491
+ fontSize: 13,
1492
+ boxShadow: "0 4px 12px rgba(0, 0, 0, 0.3)"
1493
+ },
1494
+ children: [
1495
+ /* @__PURE__ */ jsx4("div", { style: { color: "rgba(255, 255, 255, 0.5)", fontSize: 11, marginBottom: 4 }, children: "Listening..." }),
1496
+ transcript,
1497
+ interimTranscript && /* @__PURE__ */ jsx4("span", { style: { color: "rgba(255, 255, 255, 0.5)" }, children: interimTranscript })
1498
+ ]
1499
+ }
1500
+ ),
1501
+ /* @__PURE__ */ jsx4("style", { children: `
1502
+ @keyframes claude-voice-pulse {
1503
+ 0% {
1504
+ transform: scale(1);
1505
+ opacity: 0.8;
1506
+ }
1507
+ 100% {
1508
+ transform: scale(1.5);
1509
+ opacity: 0;
1510
+ }
1511
+ }
1512
+ ` })
1513
+ ] });
1292
1514
  }
1293
1515
 
1294
- // src/TaskStatusToast.tsx
1295
- import { useEffect as useEffect5, useState as useState4, useRef as useRef5, useCallback as useCallback3 } from "react";
1296
-
1297
- // src/ClaudeTerminal.tsx
1298
- import { useEffect as useEffect4, useRef as useRef4, useCallback as useCallback2, useState as useState3 } from "react";
1299
- import { Terminal } from "@xterm/xterm";
1300
- import { FitAddon } from "@xterm/addon-fit";
1301
- import { WebglAddon } from "@xterm/addon-webgl";
1302
- import "@xterm/xterm/css/xterm.css";
1303
- import { Fragment as Fragment4, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1304
- var XTERM_CRITICAL_CSS = `
1305
- .xterm {
1306
- cursor: text;
1307
- position: relative;
1308
- user-select: none;
1309
- -ms-user-select: none;
1310
- -webkit-user-select: none;
1311
- }
1312
- .xterm.focus,
1313
- .xterm:focus {
1314
- outline: none;
1315
- }
1316
- .xterm .xterm-helpers {
1317
- position: absolute;
1318
- top: 0;
1319
- z-index: 5;
1320
- }
1321
- .xterm .xterm-helper-textarea {
1322
- padding: 0;
1323
- border: 0;
1324
- margin: 0;
1325
- position: absolute;
1326
- opacity: 0;
1327
- left: -9999em;
1328
- top: 0;
1329
- width: 0;
1330
- height: 0;
1331
- z-index: -5;
1332
- white-space: nowrap;
1333
- overflow: hidden;
1334
- resize: none;
1335
- }
1336
- .xterm .xterm-helper-textarea:focus {
1337
- outline: none;
1338
- }
1339
- .xterm .composition-view {
1340
- background: #000;
1341
- color: #FFF;
1342
- display: none;
1343
- position: absolute;
1344
- white-space: nowrap;
1345
- z-index: 1;
1346
- }
1347
- .xterm .composition-view.active {
1348
- display: block;
1349
- }
1350
- .xterm .xterm-viewport {
1351
- background-color: #000;
1352
- overflow-y: scroll;
1353
- cursor: default;
1354
- position: absolute;
1355
- right: 0;
1356
- left: 0;
1357
- top: 0;
1358
- bottom: 0;
1359
- }
1360
- .xterm .xterm-screen {
1361
- position: relative;
1362
- }
1363
- .xterm .xterm-screen canvas {
1364
- position: absolute;
1365
- left: 0;
1366
- top: 0;
1367
- }
1368
- .xterm-char-measure-element {
1369
- display: inline-block;
1370
- visibility: hidden;
1371
- position: absolute;
1372
- top: 0;
1373
- left: -9999em;
1374
- line-height: normal;
1375
- }
1376
- .xterm.enable-mouse-events {
1377
- cursor: default;
1378
- }
1379
- .xterm .xterm-cursor-pointer {
1380
- cursor: pointer;
1381
- }
1382
- .xterm.xterm-cursor-style-block .xterm-cursor:not(.xterm-cursor-blink),
1383
- .xterm.xterm-cursor-style-bar .xterm-cursor:not(.xterm-cursor-blink),
1384
- .xterm.xterm-cursor-style-underline .xterm-cursor:not(.xterm-cursor-blink) {
1385
- visibility: visible;
1386
- }
1387
- `;
1388
- function ClaudeTerminal({
1516
+ // src/components/SessionPanel.tsx
1517
+ import { Fragment as Fragment4, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1518
+ var MIN_WIDTH = 400;
1519
+ var MIN_HEIGHT = 300;
1520
+ var DEFAULT_WIDTH = 700;
1521
+ var DEFAULT_HEIGHT = 500;
1522
+ function SessionPanel({
1523
+ sessions,
1524
+ activeSessionId,
1525
+ onSessionSelect,
1526
+ onSessionClose,
1389
1527
  serverUrl,
1390
1528
  token,
1391
- sessionId,
1392
- onSessionSelect,
1393
- onConnectionChange,
1394
- onSessionsUpdate,
1395
- terminalOptions = {},
1396
- style,
1397
- className
1529
+ expanded,
1530
+ onExpandedChange,
1531
+ selectedElement,
1532
+ mode,
1533
+ onModeChange,
1534
+ onPromptSubmit,
1535
+ onQuestionSubmit,
1536
+ onClose,
1537
+ isSubmitting,
1538
+ voiceInputActive = false,
1539
+ onVoiceInputStart,
1540
+ activeTask
1398
1541
  }) {
1399
- const containerRef = useRef4(null);
1400
- const terminalRef = useRef4(null);
1401
- const fitAddonRef = useRef4(null);
1402
- const wsRef = useRef4(null);
1403
- const handleMessageRef = useRef4(null);
1404
- const [connected, setConnected] = useState3(false);
1405
- const [sessions, setSessions] = useState3([]);
1406
- const [attachedSessionId, setAttachedSessionId] = useState3(null);
1407
- const [error, setError] = useState3(null);
1408
- useEffect4(() => {
1409
- if (!containerRef.current) return;
1410
- const terminal = new Terminal({
1411
- cursorBlink: true,
1412
- cursorStyle: "block",
1413
- fontSize: terminalOptions.fontSize ?? 14,
1414
- fontFamily: terminalOptions.fontFamily ?? 'Menlo, Monaco, "Courier New", monospace',
1415
- theme: {
1416
- background: terminalOptions.theme?.background ?? "#1a1a1a",
1417
- foreground: terminalOptions.theme?.foreground ?? "#f0f0f0",
1418
- cursor: terminalOptions.theme?.cursor ?? "#f0f0f0",
1419
- cursorAccent: "#1a1a1a",
1420
- black: "#1a1a1a",
1421
- red: "#ff5555",
1422
- green: "#50fa7b",
1423
- yellow: "#f1fa8c",
1424
- blue: "#6272a4",
1425
- magenta: "#ff79c6",
1426
- cyan: "#8be9fd",
1427
- white: "#f8f8f2",
1428
- brightBlack: "#6272a4",
1429
- brightRed: "#ff6e6e",
1430
- brightGreen: "#69ff94",
1431
- brightYellow: "#ffffa5",
1432
- brightBlue: "#d6acff",
1433
- brightMagenta: "#ff92df",
1434
- brightCyan: "#a4ffff",
1435
- brightWhite: "#ffffff"
1436
- },
1437
- allowProposedApi: true,
1438
- scrollback: 1e4,
1439
- disableStdin: false,
1440
- allowTransparency: true
1542
+ const [size, setSize] = useState3({ width: DEFAULT_WIDTH, height: DEFAULT_HEIGHT });
1543
+ const [isResizing, setIsResizing] = useState3(false);
1544
+ const [isSaving, setIsSaving] = useState3(false);
1545
+ const [prompt, setPrompt] = useState3("");
1546
+ const [includeElement, setIncludeElement] = useState3(true);
1547
+ const [isVoiceListening, setIsVoiceListening] = useState3(false);
1548
+ const resizeRef = useRef4(null);
1549
+ const inputRef = useRef4(null);
1550
+ useEffect5(() => {
1551
+ setIncludeElement(!!selectedElement);
1552
+ }, [selectedElement]);
1553
+ useEffect5(() => {
1554
+ if (mode === "terminal") {
1555
+ setPrompt("");
1556
+ }
1557
+ }, [mode]);
1558
+ useEffect5(() => {
1559
+ if ((mode === "prompt" || mode === "question") && inputRef.current) {
1560
+ inputRef.current.focus();
1561
+ }
1562
+ }, [mode]);
1563
+ const handleResizeStart = useCallback6((e, direction) => {
1564
+ e.preventDefault();
1565
+ e.stopPropagation();
1566
+ setIsResizing(true);
1567
+ resizeRef.current = {
1568
+ startX: e.clientX,
1569
+ startY: e.clientY,
1570
+ startWidth: size.width,
1571
+ startHeight: size.height
1572
+ };
1573
+ const handleMouseMove = (moveEvent) => {
1574
+ if (!resizeRef.current) return;
1575
+ let newWidth = resizeRef.current.startWidth;
1576
+ let newHeight = resizeRef.current.startHeight;
1577
+ if (direction.includes("w")) {
1578
+ newWidth = resizeRef.current.startWidth - (moveEvent.clientX - resizeRef.current.startX);
1579
+ }
1580
+ if (direction.includes("n")) {
1581
+ newHeight = resizeRef.current.startHeight - (moveEvent.clientY - resizeRef.current.startY);
1582
+ }
1583
+ setSize({
1584
+ width: Math.max(MIN_WIDTH, newWidth),
1585
+ height: Math.max(MIN_HEIGHT, newHeight)
1586
+ });
1587
+ };
1588
+ const handleMouseUp = () => {
1589
+ setIsResizing(false);
1590
+ resizeRef.current = null;
1591
+ document.removeEventListener("mousemove", handleMouseMove);
1592
+ document.removeEventListener("mouseup", handleMouseUp);
1593
+ };
1594
+ document.addEventListener("mousemove", handleMouseMove);
1595
+ document.addEventListener("mouseup", handleMouseUp);
1596
+ }, [size]);
1597
+ const killSession = useCallback6((sessionId, taskPrompt) => {
1598
+ return new Promise((resolve) => {
1599
+ const wsUrl = serverUrl.replace(/^http/, "ws").replace(/\/$/, "");
1600
+ const ws = new WebSocket(`${wsUrl}/ws/bridge?token=${encodeURIComponent(token)}`);
1601
+ let resolved = false;
1602
+ const cleanup = () => {
1603
+ if (!resolved) {
1604
+ resolved = true;
1605
+ ws.close();
1606
+ resolve();
1607
+ }
1608
+ };
1609
+ ws.onopen = () => {
1610
+ ws.send(JSON.stringify({
1611
+ type: "kill_session",
1612
+ sessionId,
1613
+ taskPrompt,
1614
+ autoCommit: true
1615
+ }));
1616
+ };
1617
+ ws.onmessage = (event) => {
1618
+ try {
1619
+ const message = JSON.parse(event.data);
1620
+ if (message.type === "session_killed" || message.type === "error") {
1621
+ cleanup();
1622
+ }
1623
+ } catch {
1624
+ }
1625
+ };
1626
+ ws.onerror = () => cleanup();
1627
+ ws.onclose = () => {
1628
+ if (!resolved) {
1629
+ resolved = true;
1630
+ resolve();
1631
+ }
1632
+ };
1633
+ setTimeout(cleanup, 35e3);
1441
1634
  });
1442
- const fitAddon = new FitAddon();
1443
- terminal.loadAddon(fitAddon);
1444
- terminal.open(containerRef.current);
1635
+ }, [serverUrl, token]);
1636
+ const handleSessionClose = useCallback6(async (sessionId) => {
1637
+ setIsSaving(true);
1445
1638
  try {
1446
- const webglAddon = new WebglAddon();
1447
- webglAddon.onContextLoss(() => {
1448
- webglAddon.dispose();
1449
- });
1450
- terminal.loadAddon(webglAddon);
1451
- } catch (e) {
1452
- console.warn("[ClaudeTerminal] WebGL addon could not be loaded:", e);
1639
+ await killSession(sessionId);
1640
+ onSessionClose?.(sessionId);
1641
+ } finally {
1642
+ setIsSaving(false);
1453
1643
  }
1454
- terminalRef.current = terminal;
1455
- fitAddonRef.current = fitAddon;
1456
- requestAnimationFrame(() => {
1457
- fitAddon.fit();
1644
+ }, [killSession, onSessionClose]);
1645
+ const handleToggleExpand = useCallback6(() => {
1646
+ onExpandedChange(!expanded);
1647
+ }, [expanded, onExpandedChange]);
1648
+ const handleDismiss = useCallback6(() => {
1649
+ onClose();
1650
+ onExpandedChange(false);
1651
+ }, [onClose, onExpandedChange]);
1652
+ const handleVoiceTranscript = useCallback6((text) => {
1653
+ setPrompt((prev) => {
1654
+ const separator = prev.trim() ? " " : "";
1655
+ return prev + separator + text;
1458
1656
  });
1459
- const resizeObserver = new ResizeObserver(() => {
1460
- requestAnimationFrame(() => {
1461
- fitAddon.fit();
1462
- if (wsRef.current?.readyState === WebSocket.OPEN && attachedSessionId) {
1463
- wsRef.current.send(JSON.stringify({
1464
- type: "terminal_resize",
1465
- sessionId: attachedSessionId,
1466
- cols: terminal.cols,
1467
- rows: terminal.rows
1468
- }));
1469
- }
1470
- });
1471
- });
1472
- resizeObserver.observe(containerRef.current);
1473
- return () => {
1474
- resizeObserver.disconnect();
1475
- terminal.dispose();
1476
- terminalRef.current = null;
1477
- fitAddonRef.current = null;
1478
- };
1479
- }, [terminalOptions.fontSize, terminalOptions.fontFamily, terminalOptions.theme]);
1480
- useEffect4(() => {
1481
- const terminal = terminalRef.current;
1482
- if (!terminal) return;
1483
- const disposable = terminal.onData((data) => {
1484
- if (wsRef.current?.readyState === WebSocket.OPEN && attachedSessionId) {
1485
- wsRef.current.send(JSON.stringify({
1486
- type: "terminal_input",
1487
- sessionId: attachedSessionId,
1488
- data
1489
- }));
1490
- }
1491
- });
1492
- return () => disposable.dispose();
1493
- }, [attachedSessionId]);
1494
- const attachedSessionIdRef = useRef4(null);
1495
- attachedSessionIdRef.current = attachedSessionId;
1496
- useEffect4(() => {
1497
- handleMessageRef.current = (event) => {
1498
- try {
1499
- const message = JSON.parse(event.data);
1500
- switch (message.type) {
1501
- case "connected":
1502
- wsRef.current?.send(JSON.stringify({ type: "get_sessions" }));
1503
- break;
1504
- case "sessions_list":
1505
- if (message.sessions) {
1506
- setSessions(message.sessions);
1507
- onSessionsUpdate?.(message.sessions);
1508
- }
1509
- break;
1510
- case "terminal_attached":
1511
- if (message.sessionId) {
1512
- setAttachedSessionId(message.sessionId);
1513
- setError(null);
1514
- if (message.scrollback && terminalRef.current) {
1515
- terminalRef.current.write(message.scrollback);
1516
- }
1517
- if (fitAddonRef.current && terminalRef.current) {
1518
- fitAddonRef.current.fit();
1519
- wsRef.current?.send(JSON.stringify({
1520
- type: "terminal_resize",
1521
- sessionId: message.sessionId,
1522
- cols: terminalRef.current.cols,
1523
- rows: terminalRef.current.rows
1524
- }));
1525
- }
1526
- }
1527
- break;
1528
- case "terminal_output":
1529
- if (message.data && terminalRef.current && message.sessionId === attachedSessionIdRef.current) {
1530
- terminalRef.current.write(message.data);
1531
- }
1532
- break;
1533
- case "terminal_detached":
1534
- if (message.sessionId === attachedSessionIdRef.current) {
1535
- setAttachedSessionId(null);
1536
- }
1537
- break;
1538
- case "error":
1539
- setError(message.error || "Unknown error");
1540
- console.error("[ClaudeTerminal] Error:", message.error);
1541
- break;
1542
- case "pong":
1543
- break;
1544
- }
1545
- } catch (e) {
1546
- console.error("[ClaudeTerminal] Failed to parse message:", e);
1547
- }
1548
- };
1549
- }, [onSessionsUpdate]);
1550
- useEffect4(() => {
1551
- const wsUrl = serverUrl.replace(/^http/, "ws").replace(/\/$/, "");
1552
- const ws = new WebSocket(`${wsUrl}/ws/bridge?token=${encodeURIComponent(token)}`);
1553
- ws.onopen = () => {
1554
- setConnected(true);
1555
- setError(null);
1556
- onConnectionChange?.(true);
1557
- };
1558
- ws.onmessage = (event) => {
1559
- handleMessageRef.current?.(event);
1560
- };
1561
- ws.onclose = () => {
1562
- setConnected(false);
1563
- setAttachedSessionId(null);
1564
- onConnectionChange?.(false);
1565
- };
1566
- ws.onerror = () => {
1567
- setError("WebSocket connection error");
1568
- };
1569
- wsRef.current = ws;
1570
- const pingInterval = setInterval(() => {
1571
- if (ws.readyState === WebSocket.OPEN) {
1572
- ws.send(JSON.stringify({ type: "ping" }));
1573
- }
1574
- }, 3e4);
1575
- return () => {
1576
- clearInterval(pingInterval);
1577
- ws.close();
1578
- wsRef.current = null;
1579
- };
1580
- }, [serverUrl, token, onConnectionChange]);
1581
- useEffect4(() => {
1582
- if (sessionId && connected && !attachedSessionId) {
1583
- wsRef.current?.send(JSON.stringify({
1584
- type: "attach_terminal",
1585
- sessionId
1586
- }));
1657
+ inputRef.current?.focus();
1658
+ }, []);
1659
+ const handleVoiceListeningChange = useCallback6((listening) => {
1660
+ setIsVoiceListening(listening);
1661
+ if (listening) {
1662
+ onVoiceInputStart?.();
1587
1663
  }
1588
- }, [sessionId, connected, attachedSessionId]);
1589
- useEffect4(() => {
1590
- if (attachedSessionId && terminalRef.current && fitAddonRef.current) {
1591
- const timer = setTimeout(() => {
1592
- fitAddonRef.current?.fit();
1593
- terminalRef.current?.focus();
1594
- }, 50);
1595
- return () => clearTimeout(timer);
1664
+ }, [onVoiceInputStart]);
1665
+ const handleSubmit = useCallback6(() => {
1666
+ if (!prompt.trim() || isSubmitting) return;
1667
+ if (mode === "prompt") {
1668
+ onPromptSubmit(prompt.trim());
1669
+ } else if (mode === "question") {
1670
+ onQuestionSubmit(prompt.trim(), includeElement && !!selectedElement);
1596
1671
  }
1597
- }, [attachedSessionId]);
1598
- const handleContainerClick = useCallback2(() => {
1599
- terminalRef.current?.focus();
1600
- }, []);
1601
- const handleSelectSession = useCallback2((session) => {
1602
- terminalRef.current?.clear();
1603
- if (attachedSessionId) {
1604
- wsRef.current?.send(JSON.stringify({
1605
- type: "detach_terminal",
1606
- sessionId: attachedSessionId
1607
- }));
1672
+ setPrompt("");
1673
+ }, [prompt, isSubmitting, mode, onPromptSubmit, onQuestionSubmit, includeElement, selectedElement]);
1674
+ const handleKeyDown = useCallback6((e) => {
1675
+ if ((e.metaKey || e.ctrlKey) && e.key === "Enter") {
1676
+ e.preventDefault();
1677
+ handleSubmit();
1608
1678
  }
1609
- wsRef.current?.send(JSON.stringify({
1610
- type: "attach_terminal",
1611
- sessionId: session.id
1612
- }));
1613
- onSessionSelect?.(session);
1614
- }, [attachedSessionId, onSessionSelect]);
1615
- if (!sessionId && !attachedSessionId) {
1616
- return /* @__PURE__ */ jsx4(
1679
+ if (e.key === "Escape") {
1680
+ e.preventDefault();
1681
+ if (mode !== "terminal") {
1682
+ onModeChange("terminal");
1683
+ } else {
1684
+ handleDismiss();
1685
+ }
1686
+ }
1687
+ }, [handleSubmit, mode, onModeChange, handleDismiss]);
1688
+ const handleOpenZed = useCallback6(() => {
1689
+ const zedUrl = activeSessionId ? `${serverUrl}?token=${encodeURIComponent(token)}&session=${activeSessionId}` : `${serverUrl}?token=${encodeURIComponent(token)}`;
1690
+ window.open(zedUrl, "_blank");
1691
+ }, [serverUrl, token, activeSessionId]);
1692
+ const hasContent = sessions.length > 0 || mode !== "terminal";
1693
+ if (!hasContent && !activeTask) return null;
1694
+ const elementContext = selectedElement ? captureContext(selectedElement) : null;
1695
+ const pageContext = capturePageContext();
1696
+ const statusColors = {
1697
+ queued: { bg: "#6366f1", text: "Queued" },
1698
+ starting: { bg: "#f59e0b", text: "Starting..." },
1699
+ running: { bg: "#22c55e", text: "Running" },
1700
+ completed: { bg: "#22c55e", text: "Completed" },
1701
+ failed: { bg: "#ef4444", text: "Failed" },
1702
+ saving: { bg: "#f59e0b", text: "Saving..." }
1703
+ };
1704
+ const status = isSaving ? statusColors.saving : activeTask ? statusColors[activeTask.status] || statusColors.queued : null;
1705
+ if (!expanded) {
1706
+ return /* @__PURE__ */ jsxs5(
1617
1707
  "div",
1618
1708
  {
1619
- className,
1620
1709
  style: {
1621
- background: "#1a1a1a",
1622
- color: "#f0f0f0",
1623
- padding: "20px",
1624
- fontFamily: 'Menlo, Monaco, "Courier New", monospace',
1625
- ...style
1710
+ position: "fixed",
1711
+ bottom: 24,
1712
+ right: 24,
1713
+ zIndex: 1000001,
1714
+ background: "#1e1e2e",
1715
+ borderRadius: 12,
1716
+ boxShadow: "0 8px 32px rgba(0, 0, 0, 0.3)",
1717
+ fontFamily: "system-ui, -apple-system, sans-serif",
1718
+ minWidth: 280,
1719
+ maxWidth: 400
1626
1720
  },
1627
- children: !connected ? /* @__PURE__ */ jsx4("div", { children: "Connecting to server..." }) : error ? /* @__PURE__ */ jsxs4("div", { style: { color: "#ff5555" }, children: [
1628
- "Error: ",
1629
- error
1630
- ] }) : sessions.length === 0 ? /* @__PURE__ */ jsx4("div", { children: "No Claude sessions available" }) : /* @__PURE__ */ jsxs4("div", { children: [
1631
- /* @__PURE__ */ jsx4("div", { style: { marginBottom: "10px", fontSize: "14px" }, children: "Select a Claude session:" }),
1632
- sessions.map((session) => /* @__PURE__ */ jsxs4(
1721
+ children: [
1722
+ /* @__PURE__ */ jsxs5(
1633
1723
  "div",
1634
1724
  {
1635
- onClick: () => handleSelectSession(session),
1636
1725
  style: {
1637
- padding: "10px",
1638
- margin: "5px 0",
1639
- background: "#2a2a2a",
1640
- borderRadius: "4px",
1641
- cursor: "pointer",
1642
- border: "1px solid #3a3a3a"
1643
- },
1644
- onMouseOver: (e) => {
1645
- e.currentTarget.style.background = "#3a3a3a";
1646
- },
1647
- onMouseOut: (e) => {
1648
- e.currentTarget.style.background = "#2a2a2a";
1726
+ padding: "12px 16px",
1727
+ display: "flex",
1728
+ alignItems: "center",
1729
+ gap: 10
1649
1730
  },
1650
1731
  children: [
1651
- /* @__PURE__ */ jsx4("div", { style: { fontWeight: "bold" }, children: session.displayName || session.name }),
1652
- /* @__PURE__ */ jsx4("div", { style: { fontSize: "12px", color: "#888", marginTop: "4px" }, children: session.workingDir })
1653
- ]
1654
- },
1655
- session.id
1656
- ))
1657
- ] })
1658
- }
1659
- );
1660
- }
1661
- return /* @__PURE__ */ jsxs4(Fragment4, { children: [
1662
- /* @__PURE__ */ jsx4("style", { children: XTERM_CRITICAL_CSS }),
1663
- /* @__PURE__ */ jsx4(
1664
- "div",
1665
- {
1666
- ref: containerRef,
1667
- className,
1668
- onClick: handleContainerClick,
1669
- style: {
1670
- width: "100%",
1671
- height: "100%",
1672
- ...style
1673
- }
1674
- }
1675
- )
1676
- ] });
1677
- }
1678
-
1679
- // src/TaskStatusToast.tsx
1680
- import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1681
- var MIN_WIDTH = 400;
1682
- var MIN_HEIGHT = 300;
1683
- var DEFAULT_WIDTH = 700;
1684
- var DEFAULT_HEIGHT = 500;
1685
- function TaskStatusToast({
1686
- task,
1687
- serverUrl,
1688
- token,
1689
- onDismiss,
1690
- killOnClose = true
1691
- }) {
1692
- const [visible, setVisible] = useState4(false);
1693
- const [expanded, setExpanded] = useState4(false);
1694
- const [size, setSize] = useState4({ width: DEFAULT_WIDTH, height: DEFAULT_HEIGHT });
1695
- const [isResizing, setIsResizing] = useState4(false);
1696
- const [isSaving, setIsSaving] = useState4(false);
1697
- const resizeRef = useRef5(null);
1698
- useEffect5(() => {
1699
- if (task) {
1700
- setVisible(true);
1701
- if (task.sessionId && (task.status === "running" || task.status === "starting")) {
1702
- setExpanded(true);
1703
- }
1704
- }
1705
- }, [task, task?.sessionId, task?.status]);
1706
- useEffect5(() => {
1707
- if (task && !expanded && (task.status === "completed" || task.status === "failed")) {
1708
- const timer = setTimeout(() => {
1709
- setVisible(false);
1710
- setTimeout(onDismiss, 300);
1711
- }, 1e4);
1712
- return () => clearTimeout(timer);
1713
- }
1714
- }, [task?.status, expanded, onDismiss]);
1715
- const handleResizeStart = useCallback3((e, direction) => {
1716
- e.preventDefault();
1717
- e.stopPropagation();
1718
- setIsResizing(true);
1719
- resizeRef.current = {
1720
- startX: e.clientX,
1721
- startY: e.clientY,
1722
- startWidth: size.width,
1723
- startHeight: size.height
1724
- };
1725
- const handleMouseMove = (moveEvent) => {
1726
- if (!resizeRef.current) return;
1727
- let newWidth = resizeRef.current.startWidth;
1728
- let newHeight = resizeRef.current.startHeight;
1729
- if (direction.includes("w")) {
1730
- newWidth = resizeRef.current.startWidth - (moveEvent.clientX - resizeRef.current.startX);
1731
- }
1732
- if (direction.includes("n")) {
1733
- newHeight = resizeRef.current.startHeight - (moveEvent.clientY - resizeRef.current.startY);
1734
- }
1735
- setSize({
1736
- width: Math.max(MIN_WIDTH, newWidth),
1737
- height: Math.max(MIN_HEIGHT, newHeight)
1738
- });
1739
- };
1740
- const handleMouseUp = () => {
1741
- setIsResizing(false);
1742
- resizeRef.current = null;
1743
- document.removeEventListener("mousemove", handleMouseMove);
1744
- document.removeEventListener("mouseup", handleMouseUp);
1745
- };
1746
- document.addEventListener("mousemove", handleMouseMove);
1747
- document.addEventListener("mouseup", handleMouseUp);
1748
- }, [size]);
1749
- const killSession = useCallback3((sessionId, taskPrompt) => {
1750
- return new Promise((resolve) => {
1751
- const wsUrl = serverUrl.replace(/^http/, "ws").replace(/\/$/, "");
1752
- const ws = new WebSocket(`${wsUrl}/ws/bridge?token=${encodeURIComponent(token)}`);
1753
- let resolved = false;
1754
- const cleanup = () => {
1755
- if (!resolved) {
1756
- resolved = true;
1757
- ws.close();
1758
- resolve();
1759
- }
1760
- };
1761
- ws.onopen = () => {
1762
- ws.send(JSON.stringify({
1763
- type: "kill_session",
1764
- sessionId,
1765
- // Include task prompt for auto-commit message
1766
- taskPrompt,
1767
- autoCommit: true
1768
- }));
1769
- };
1770
- ws.onmessage = (event) => {
1771
- try {
1772
- const message = JSON.parse(event.data);
1773
- if (message.type === "session_killed" || message.type === "error") {
1774
- cleanup();
1775
- }
1776
- } catch {
1777
- }
1778
- };
1779
- ws.onerror = () => {
1780
- cleanup();
1781
- };
1782
- ws.onclose = () => {
1783
- if (!resolved) {
1784
- resolved = true;
1785
- resolve();
1786
- }
1787
- };
1788
- setTimeout(cleanup, 35e3);
1789
- });
1790
- }, [serverUrl, token]);
1791
- const handleDismiss = useCallback3(async () => {
1792
- if (killOnClose && task?.sessionId) {
1793
- setIsSaving(true);
1794
- try {
1795
- await killSession(task.sessionId, task.prompt);
1796
- } finally {
1797
- setIsSaving(false);
1798
- }
1799
- }
1800
- setVisible(false);
1801
- setExpanded(false);
1802
- setTimeout(onDismiss, 300);
1803
- }, [killOnClose, task?.sessionId, task?.prompt, killSession, onDismiss]);
1804
- const handleToggleExpand = useCallback3(() => {
1805
- setExpanded(!expanded);
1806
- }, [expanded]);
1807
- if (!task || !visible) return null;
1808
- const statusColors = {
1809
- queued: { bg: "#6366f1", text: "Queued" },
1810
- starting: { bg: "#f59e0b", text: "Starting..." },
1811
- running: { bg: "#22c55e", text: "Running" },
1812
- completed: { bg: "#22c55e", text: "Completed" },
1813
- failed: { bg: "#ef4444", text: "Failed" },
1814
- saving: { bg: "#f59e0b", text: "Saving..." }
1815
- };
1816
- const status = isSaving ? statusColors.saving : statusColors[task.status] || statusColors.queued;
1817
- const zedUrl = task.sessionId ? `${serverUrl}?token=${encodeURIComponent(token)}&session=${task.sessionId}` : `${serverUrl}?token=${encodeURIComponent(token)}`;
1818
- const handleOpenZed = () => {
1819
- window.open(zedUrl, "_blank");
1820
- };
1821
- if (!expanded) {
1822
- return /* @__PURE__ */ jsxs5(
1823
- "div",
1824
- {
1825
- style: {
1826
- position: "fixed",
1827
- bottom: 24,
1828
- right: 24,
1829
- zIndex: 1000001,
1830
- background: "#1e1e2e",
1831
- borderRadius: 12,
1832
- boxShadow: "0 8px 32px rgba(0, 0, 0, 0.3)",
1833
- fontFamily: "system-ui, -apple-system, sans-serif",
1834
- minWidth: 320,
1835
- maxWidth: 400,
1836
- opacity: visible ? 1 : 0,
1837
- transform: visible ? "translateY(0)" : "translateY(20px)",
1838
- transition: "opacity 0.3s, transform 0.3s"
1839
- },
1840
- children: [
1841
- /* @__PURE__ */ jsxs5(
1842
- "div",
1843
- {
1844
- style: {
1845
- padding: "12px 16px",
1846
- borderBottom: "1px solid rgba(255, 255, 255, 0.1)",
1847
- display: "flex",
1848
- alignItems: "center",
1849
- gap: 10
1850
- },
1851
- children: [
1852
- /* @__PURE__ */ jsx5(
1732
+ status && /* @__PURE__ */ jsx5(
1853
1733
  "div",
1854
1734
  {
1855
1735
  style: {
@@ -1857,7 +1737,7 @@ function TaskStatusToast({
1857
1737
  height: 10,
1858
1738
  borderRadius: "50%",
1859
1739
  background: status.bg,
1860
- animation: task.status === "running" || task.status === "starting" ? "claude-bridge-pulse 1.5s infinite" : "none"
1740
+ animation: activeTask?.status === "running" || activeTask?.status === "starting" ? "claude-bridge-pulse 1.5s infinite" : "none"
1861
1741
  }
1862
1742
  }
1863
1743
  ),
@@ -1871,76 +1751,12 @@ function TaskStatusToast({
1871
1751
  flex: 1
1872
1752
  },
1873
1753
  children: [
1874
- "Claude Task: ",
1875
- status.text
1754
+ sessions.length > 0 ? `${sessions.length} session${sessions.length > 1 ? "s" : ""}` : "Claude",
1755
+ status && `: ${status.text}`
1876
1756
  ]
1877
1757
  }
1878
1758
  ),
1879
1759
  /* @__PURE__ */ jsx5(
1880
- "button",
1881
- {
1882
- onClick: handleDismiss,
1883
- disabled: isSaving,
1884
- style: {
1885
- background: "transparent",
1886
- border: "none",
1887
- color: isSaving ? "rgba(255, 255, 255, 0.3)" : "rgba(255, 255, 255, 0.5)",
1888
- cursor: isSaving ? "not-allowed" : "pointer",
1889
- padding: 4,
1890
- fontSize: 18,
1891
- lineHeight: 1
1892
- },
1893
- children: isSaving ? "..." : "\xD7"
1894
- }
1895
- )
1896
- ]
1897
- }
1898
- ),
1899
- /* @__PURE__ */ jsxs5("div", { style: { padding: "12px 16px" }, children: [
1900
- /* @__PURE__ */ jsx5(
1901
- "p",
1902
- {
1903
- style: {
1904
- color: "rgba(255, 255, 255, 0.7)",
1905
- fontSize: 13,
1906
- margin: 0,
1907
- lineHeight: 1.5,
1908
- overflow: "hidden",
1909
- textOverflow: "ellipsis",
1910
- display: "-webkit-box",
1911
- WebkitLineClamp: 2,
1912
- WebkitBoxOrient: "vertical"
1913
- },
1914
- children: task.prompt
1915
- }
1916
- ),
1917
- task.error && /* @__PURE__ */ jsxs5(
1918
- "p",
1919
- {
1920
- style: {
1921
- color: "#ef4444",
1922
- fontSize: 12,
1923
- margin: "8px 0 0"
1924
- },
1925
- children: [
1926
- "Error: ",
1927
- task.error
1928
- ]
1929
- }
1930
- )
1931
- ] }),
1932
- /* @__PURE__ */ jsxs5(
1933
- "div",
1934
- {
1935
- style: {
1936
- padding: "12px 16px",
1937
- borderTop: "1px solid rgba(255, 255, 255, 0.1)",
1938
- display: "flex",
1939
- justifyContent: "flex-end",
1940
- gap: 8
1941
- },
1942
- children: [
1943
- task.sessionId && /* @__PURE__ */ jsx5(
1944
1760
  "button",
1945
1761
  {
1946
1762
  onClick: handleToggleExpand,
@@ -1948,39 +1764,29 @@ function TaskStatusToast({
1948
1764
  background: "#374151",
1949
1765
  border: "none",
1950
1766
  borderRadius: 6,
1951
- padding: "8px 16px",
1767
+ padding: "6px 12px",
1952
1768
  color: "white",
1953
- fontSize: 13,
1769
+ fontSize: 12,
1954
1770
  fontWeight: 500,
1955
- cursor: "pointer",
1956
- display: "flex",
1957
- alignItems: "center",
1958
- gap: 6
1771
+ cursor: "pointer"
1959
1772
  },
1960
- children: "Show Terminal"
1773
+ children: "Expand"
1961
1774
  }
1962
1775
  ),
1963
- /* @__PURE__ */ jsxs5(
1776
+ /* @__PURE__ */ jsx5(
1964
1777
  "button",
1965
1778
  {
1966
- onClick: handleOpenZed,
1779
+ onClick: handleDismiss,
1967
1780
  style: {
1968
- background: "#6366f1",
1781
+ background: "transparent",
1969
1782
  border: "none",
1970
- borderRadius: 6,
1971
- padding: "8px 16px",
1972
- color: "white",
1973
- fontSize: 13,
1974
- fontWeight: 500,
1783
+ color: "rgba(255, 255, 255, 0.5)",
1975
1784
  cursor: "pointer",
1976
- display: "flex",
1977
- alignItems: "center",
1978
- gap: 6
1785
+ padding: 4,
1786
+ fontSize: 18,
1787
+ lineHeight: 1
1979
1788
  },
1980
- children: [
1981
- "Open in Zed Controller",
1982
- /* @__PURE__ */ jsx5("span", { style: { fontSize: 11 }, children: "\u2197" })
1983
- ]
1789
+ children: "\xD7"
1984
1790
  }
1985
1791
  )
1986
1792
  ]
@@ -2012,7 +1818,6 @@ function TaskStatusToast({
2012
1818
  fontFamily: "system-ui, -apple-system, sans-serif",
2013
1819
  display: "flex",
2014
1820
  flexDirection: "column",
2015
- opacity: visible ? 1 : 0,
2016
1821
  transition: isResizing ? "none" : "opacity 0.3s",
2017
1822
  overflow: "hidden"
2018
1823
  },
@@ -2066,99 +1871,76 @@ function TaskStatusToast({
2066
1871
  "div",
2067
1872
  {
2068
1873
  style: {
2069
- padding: "10px 16px",
2070
1874
  borderBottom: "1px solid rgba(255, 255, 255, 0.1)",
2071
1875
  display: "flex",
2072
1876
  alignItems: "center",
2073
- gap: 10,
2074
1877
  flexShrink: 0
2075
1878
  },
2076
1879
  children: [
2077
1880
  /* @__PURE__ */ jsx5(
2078
- "div",
1881
+ SessionTabs,
2079
1882
  {
2080
- style: {
2081
- width: 10,
2082
- height: 10,
2083
- borderRadius: "50%",
2084
- background: status.bg,
2085
- animation: task.status === "running" || task.status === "starting" ? "claude-bridge-pulse 1.5s infinite" : "none"
2086
- }
1883
+ sessions,
1884
+ activeSessionId,
1885
+ onSessionSelect,
1886
+ onSessionClose: handleSessionClose,
1887
+ taskSessionId: activeTask?.sessionId
2087
1888
  }
2088
1889
  ),
2089
- /* @__PURE__ */ jsxs5(
2090
- "span",
2091
- {
2092
- style: {
2093
- color: "rgba(255, 255, 255, 0.9)",
2094
- fontSize: 14,
2095
- fontWeight: 500,
2096
- flex: 1,
2097
- overflow: "hidden",
2098
- textOverflow: "ellipsis",
2099
- whiteSpace: "nowrap"
2100
- },
2101
- title: task.prompt,
2102
- children: [
2103
- status.text,
2104
- ": ",
2105
- task.prompt.slice(0, 50),
2106
- task.prompt.length > 50 ? "..." : ""
2107
- ]
2108
- }
2109
- ),
2110
- /* @__PURE__ */ jsx5(
2111
- "button",
2112
- {
2113
- onClick: handleToggleExpand,
2114
- title: "Minimize",
2115
- style: {
2116
- background: "transparent",
2117
- border: "none",
2118
- color: "rgba(255, 255, 255, 0.5)",
2119
- cursor: "pointer",
2120
- padding: 4,
2121
- fontSize: 14,
2122
- lineHeight: 1
2123
- },
2124
- children: "\u2212"
2125
- }
2126
- ),
2127
- /* @__PURE__ */ jsx5(
2128
- "button",
2129
- {
2130
- onClick: handleOpenZed,
2131
- title: "Open in Zed Controller",
2132
- style: {
2133
- background: "transparent",
2134
- border: "none",
2135
- color: "rgba(255, 255, 255, 0.5)",
2136
- cursor: "pointer",
2137
- padding: 4,
2138
- fontSize: 14,
2139
- lineHeight: 1
2140
- },
2141
- children: "\u2197"
2142
- }
2143
- ),
2144
- /* @__PURE__ */ jsx5(
2145
- "button",
2146
- {
2147
- onClick: handleDismiss,
2148
- disabled: isSaving,
2149
- title: isSaving ? "Saving changes..." : "Close",
2150
- style: {
2151
- background: "transparent",
2152
- border: "none",
2153
- color: isSaving ? "rgba(255, 255, 255, 0.3)" : "rgba(255, 255, 255, 0.5)",
2154
- cursor: isSaving ? "not-allowed" : "pointer",
2155
- padding: 4,
2156
- fontSize: 18,
2157
- lineHeight: 1
2158
- },
2159
- children: isSaving ? "..." : "\xD7"
2160
- }
2161
- )
1890
+ /* @__PURE__ */ jsxs5("div", { style: { display: "flex", alignItems: "center", gap: 4, padding: "4px 8px" }, children: [
1891
+ /* @__PURE__ */ jsx5(
1892
+ "button",
1893
+ {
1894
+ onClick: handleToggleExpand,
1895
+ title: "Minimize",
1896
+ style: {
1897
+ background: "transparent",
1898
+ border: "none",
1899
+ color: "rgba(255, 255, 255, 0.5)",
1900
+ cursor: "pointer",
1901
+ padding: 4,
1902
+ fontSize: 14,
1903
+ lineHeight: 1
1904
+ },
1905
+ children: "\u2212"
1906
+ }
1907
+ ),
1908
+ /* @__PURE__ */ jsx5(
1909
+ "button",
1910
+ {
1911
+ onClick: handleOpenZed,
1912
+ title: "Open in Zed Controller",
1913
+ style: {
1914
+ background: "transparent",
1915
+ border: "none",
1916
+ color: "rgba(255, 255, 255, 0.5)",
1917
+ cursor: "pointer",
1918
+ padding: 4,
1919
+ fontSize: 14,
1920
+ lineHeight: 1
1921
+ },
1922
+ children: "\u2197"
1923
+ }
1924
+ ),
1925
+ /* @__PURE__ */ jsx5(
1926
+ "button",
1927
+ {
1928
+ onClick: handleDismiss,
1929
+ disabled: isSaving,
1930
+ title: isSaving ? "Saving changes..." : "Close",
1931
+ style: {
1932
+ background: "transparent",
1933
+ border: "none",
1934
+ color: isSaving ? "rgba(255, 255, 255, 0.3)" : "rgba(255, 255, 255, 0.5)",
1935
+ cursor: isSaving ? "not-allowed" : "pointer",
1936
+ padding: 4,
1937
+ fontSize: 18,
1938
+ lineHeight: 1
1939
+ },
1940
+ children: isSaving ? "..." : "\xD7"
1941
+ }
1942
+ )
1943
+ ] })
2162
1944
  ]
2163
1945
  }
2164
1946
  ),
@@ -2180,39 +1962,32 @@ function TaskStatusToast({
2180
1962
  borderRadius: 12
2181
1963
  },
2182
1964
  children: [
2183
- /* @__PURE__ */ jsx5(
2184
- "div",
2185
- {
2186
- style: {
2187
- color: "#f59e0b",
2188
- fontSize: 14,
2189
- fontWeight: 500,
2190
- marginBottom: 8
2191
- },
2192
- children: "Saving changes..."
2193
- }
2194
- ),
2195
- /* @__PURE__ */ jsx5(
2196
- "div",
2197
- {
2198
- style: {
2199
- color: "rgba(255, 255, 255, 0.6)",
2200
- fontSize: 12
2201
- },
2202
- children: "Generating commit message with AI"
2203
- }
2204
- )
1965
+ /* @__PURE__ */ jsx5("div", { style: { color: "#f59e0b", fontSize: 14, fontWeight: 500, marginBottom: 8 }, children: "Saving changes..." }),
1966
+ /* @__PURE__ */ jsx5("div", { style: { color: "rgba(255, 255, 255, 0.6)", fontSize: 12 }, children: "Generating commit message with AI" })
2205
1967
  ]
2206
1968
  }
2207
1969
  ),
2208
- /* @__PURE__ */ jsx5("div", { style: { flex: 1, minHeight: 0 }, children: task.sessionId ? /* @__PURE__ */ jsx5(
1970
+ /* @__PURE__ */ jsx5("div", { style: { flex: 1, minHeight: 0 }, children: activeSessionId ? /* @__PURE__ */ jsx5(
2209
1971
  ClaudeTerminal,
2210
1972
  {
2211
1973
  serverUrl,
2212
1974
  token,
2213
- sessionId: task.sessionId,
1975
+ sessionId: activeSessionId,
2214
1976
  style: { width: "100%", height: "100%" }
2215
1977
  }
1978
+ ) : sessions.length > 0 ? /* @__PURE__ */ jsx5(
1979
+ "div",
1980
+ {
1981
+ style: {
1982
+ display: "flex",
1983
+ alignItems: "center",
1984
+ justifyContent: "center",
1985
+ height: "100%",
1986
+ color: "rgba(255, 255, 255, 0.5)",
1987
+ fontSize: 14
1988
+ },
1989
+ children: "Select a session to view"
1990
+ }
2216
1991
  ) : /* @__PURE__ */ jsx5(
2217
1992
  "div",
2218
1993
  {
@@ -2221,12 +1996,201 @@ function TaskStatusToast({
2221
1996
  alignItems: "center",
2222
1997
  justifyContent: "center",
2223
1998
  height: "100%",
2224
- color: "rgba(255, 255, 255, 0.5)"
1999
+ color: "rgba(255, 255, 255, 0.5)",
2000
+ fontSize: 14
2225
2001
  },
2226
- children: "Waiting for session..."
2002
+ children: "No active sessions"
2227
2003
  }
2228
2004
  ) }),
2229
- task.error && /* @__PURE__ */ jsxs5(
2005
+ mode !== "terminal" && /* @__PURE__ */ jsxs5(
2006
+ "div",
2007
+ {
2008
+ style: {
2009
+ borderTop: "1px solid rgba(255, 255, 255, 0.1)",
2010
+ padding: 12,
2011
+ flexShrink: 0
2012
+ },
2013
+ children: [
2014
+ /* @__PURE__ */ jsxs5(
2015
+ "div",
2016
+ {
2017
+ style: {
2018
+ display: "flex",
2019
+ alignItems: "center",
2020
+ gap: 8,
2021
+ marginBottom: 8
2022
+ },
2023
+ children: [
2024
+ /* @__PURE__ */ jsx5(
2025
+ "div",
2026
+ {
2027
+ style: {
2028
+ width: 8,
2029
+ height: 8,
2030
+ borderRadius: "50%",
2031
+ background: mode === "prompt" ? "#6366f1" : "#22c55e"
2032
+ }
2033
+ }
2034
+ ),
2035
+ /* @__PURE__ */ jsxs5(
2036
+ "span",
2037
+ {
2038
+ style: {
2039
+ color: "rgba(255, 255, 255, 0.9)",
2040
+ fontSize: 12,
2041
+ fontWeight: 500
2042
+ },
2043
+ children: [
2044
+ mode === "prompt" ? "Task" : "Question",
2045
+ elementContext && /* @__PURE__ */ jsxs5("span", { style: { color: "rgba(255, 255, 255, 0.5)", marginLeft: 8 }, children: [
2046
+ elementContext.componentName || elementContext.selectedElement.tagName,
2047
+ elementContext.sourceFile && ` (${elementContext.sourceFile})`
2048
+ ] })
2049
+ ]
2050
+ }
2051
+ ),
2052
+ /* @__PURE__ */ jsx5("div", { style: { flex: 1 } }),
2053
+ /* @__PURE__ */ jsxs5(
2054
+ "button",
2055
+ {
2056
+ onClick: () => onModeChange(mode === "prompt" ? "question" : "prompt"),
2057
+ style: {
2058
+ background: "transparent",
2059
+ border: "none",
2060
+ color: mode === "prompt" ? "#22c55e" : "#6366f1",
2061
+ fontSize: 11,
2062
+ cursor: "pointer",
2063
+ padding: 0
2064
+ },
2065
+ children: [
2066
+ "Switch to ",
2067
+ mode === "prompt" ? "Question" : "Task"
2068
+ ]
2069
+ }
2070
+ )
2071
+ ]
2072
+ }
2073
+ ),
2074
+ mode === "question" && selectedElement && /* @__PURE__ */ jsxs5(
2075
+ "label",
2076
+ {
2077
+ style: {
2078
+ display: "flex",
2079
+ alignItems: "center",
2080
+ gap: 8,
2081
+ marginBottom: 8,
2082
+ fontSize: 12,
2083
+ color: "rgba(255, 255, 255, 0.7)",
2084
+ cursor: "pointer"
2085
+ },
2086
+ children: [
2087
+ /* @__PURE__ */ jsx5(
2088
+ "input",
2089
+ {
2090
+ type: "checkbox",
2091
+ checked: includeElement,
2092
+ onChange: (e) => setIncludeElement(e.target.checked)
2093
+ }
2094
+ ),
2095
+ "Include selected element"
2096
+ ]
2097
+ }
2098
+ ),
2099
+ /* @__PURE__ */ jsxs5("div", { style: { position: "relative" }, children: [
2100
+ /* @__PURE__ */ jsx5(
2101
+ "textarea",
2102
+ {
2103
+ ref: inputRef,
2104
+ value: prompt,
2105
+ onChange: (e) => setPrompt(e.target.value),
2106
+ onKeyDown: handleKeyDown,
2107
+ placeholder: isVoiceListening ? "Listening..." : mode === "prompt" ? "What would you like Claude to change?" : "Ask a question about this page or codebase...",
2108
+ disabled: isSubmitting || isVoiceListening,
2109
+ style: {
2110
+ width: "100%",
2111
+ minHeight: 60,
2112
+ maxHeight: 120,
2113
+ padding: 10,
2114
+ paddingRight: 44,
2115
+ background: isVoiceListening ? "rgba(239, 68, 68, 0.1)" : "rgba(255, 255, 255, 0.05)",
2116
+ border: isVoiceListening ? "1px solid rgba(239, 68, 68, 0.3)" : "1px solid rgba(255, 255, 255, 0.1)",
2117
+ borderRadius: 8,
2118
+ color: "white",
2119
+ fontSize: 13,
2120
+ fontFamily: "inherit",
2121
+ resize: "none",
2122
+ outline: "none"
2123
+ }
2124
+ }
2125
+ ),
2126
+ /* @__PURE__ */ jsx5("div", { style: { position: "absolute", right: 8, top: 8 }, children: /* @__PURE__ */ jsx5(
2127
+ VoiceInput,
2128
+ {
2129
+ onTranscript: handleVoiceTranscript,
2130
+ onListeningChange: handleVoiceListeningChange,
2131
+ disabled: isSubmitting,
2132
+ size: "small",
2133
+ autoStart: voiceInputActive
2134
+ }
2135
+ ) })
2136
+ ] }),
2137
+ /* @__PURE__ */ jsxs5(
2138
+ "div",
2139
+ {
2140
+ style: {
2141
+ display: "flex",
2142
+ justifyContent: "space-between",
2143
+ alignItems: "center",
2144
+ marginTop: 8
2145
+ },
2146
+ children: [
2147
+ /* @__PURE__ */ jsxs5("span", { style: { color: "rgba(255, 255, 255, 0.4)", fontSize: 11 }, children: [
2148
+ navigator.platform.includes("Mac") ? "\u2318" : "Ctrl",
2149
+ "+Enter to submit"
2150
+ ] }),
2151
+ /* @__PURE__ */ jsx5(
2152
+ "button",
2153
+ {
2154
+ onClick: handleSubmit,
2155
+ disabled: !prompt.trim() || isSubmitting,
2156
+ style: {
2157
+ background: prompt.trim() && !isSubmitting ? mode === "prompt" ? "#6366f1" : "#22c55e" : "rgba(99, 102, 241, 0.3)",
2158
+ border: "none",
2159
+ borderRadius: 6,
2160
+ padding: "6px 14px",
2161
+ color: "white",
2162
+ fontSize: 13,
2163
+ fontWeight: 500,
2164
+ cursor: prompt.trim() && !isSubmitting ? "pointer" : "not-allowed",
2165
+ display: "flex",
2166
+ alignItems: "center",
2167
+ gap: 6
2168
+ },
2169
+ children: isSubmitting ? /* @__PURE__ */ jsxs5(Fragment4, { children: [
2170
+ /* @__PURE__ */ jsx5(
2171
+ "span",
2172
+ {
2173
+ style: {
2174
+ width: 12,
2175
+ height: 12,
2176
+ border: "2px solid rgba(255, 255, 255, 0.3)",
2177
+ borderTopColor: "white",
2178
+ borderRadius: "50%",
2179
+ animation: "claude-bridge-spin 0.8s linear infinite"
2180
+ }
2181
+ }
2182
+ ),
2183
+ "Sending..."
2184
+ ] }) : mode === "prompt" ? "Send to Claude" : "Ask Claude"
2185
+ }
2186
+ )
2187
+ ]
2188
+ }
2189
+ )
2190
+ ]
2191
+ }
2192
+ ),
2193
+ activeTask?.error && /* @__PURE__ */ jsxs5(
2230
2194
  "div",
2231
2195
  {
2232
2196
  style: {
@@ -2239,7 +2203,7 @@ function TaskStatusToast({
2239
2203
  },
2240
2204
  children: [
2241
2205
  "Error: ",
2242
- task.error
2206
+ activeTask.error
2243
2207
  ]
2244
2208
  }
2245
2209
  ),
@@ -2248,12 +2212,58 @@ function TaskStatusToast({
2248
2212
  0%, 100% { opacity: 1; }
2249
2213
  50% { opacity: 0.5; }
2250
2214
  }
2215
+ @keyframes claude-bridge-spin {
2216
+ to { transform: rotate(360deg); }
2217
+ }
2251
2218
  ` })
2252
2219
  ]
2253
2220
  }
2254
2221
  );
2255
2222
  }
2256
2223
 
2224
+ // src/hooks/useSessionStorage.ts
2225
+ import { useCallback as useCallback7 } from "react";
2226
+ var STORAGE_KEY = "claude-bridge-session";
2227
+ var EXPIRY_MS = 24 * 60 * 60 * 1e3;
2228
+ function useSessionStorage(projectRoot) {
2229
+ const getStoredSessionId = useCallback7(() => {
2230
+ if (!projectRoot) return null;
2231
+ try {
2232
+ const stored = localStorage.getItem(STORAGE_KEY);
2233
+ if (!stored) return null;
2234
+ const data = JSON.parse(stored);
2235
+ if (data.projectRoot === projectRoot && Date.now() - data.timestamp < EXPIRY_MS) {
2236
+ return data.lastSessionId;
2237
+ }
2238
+ } catch {
2239
+ }
2240
+ return null;
2241
+ }, [projectRoot]);
2242
+ const setStoredSessionId = useCallback7(
2243
+ (sessionId) => {
2244
+ if (sessionId && projectRoot) {
2245
+ const data = {
2246
+ lastSessionId: sessionId,
2247
+ projectRoot,
2248
+ timestamp: Date.now()
2249
+ };
2250
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
2251
+ } else {
2252
+ localStorage.removeItem(STORAGE_KEY);
2253
+ }
2254
+ },
2255
+ [projectRoot]
2256
+ );
2257
+ const clearStoredSession = useCallback7(() => {
2258
+ localStorage.removeItem(STORAGE_KEY);
2259
+ }, []);
2260
+ return {
2261
+ getStoredSessionId,
2262
+ setStoredSessionId,
2263
+ clearStoredSession
2264
+ };
2265
+ }
2266
+
2257
2267
  // src/ClaudeBridgeProvider.tsx
2258
2268
  import { Fragment as Fragment5, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
2259
2269
  var initialState = {
@@ -2262,7 +2272,13 @@ var initialState = {
2262
2272
  questionMode: false,
2263
2273
  selectedElement: null,
2264
2274
  activeTask: null,
2265
- tasks: []
2275
+ tasks: [],
2276
+ voiceInputPending: false,
2277
+ // Session state
2278
+ sessions: [],
2279
+ activeSessionId: null,
2280
+ panelExpanded: false,
2281
+ panelMode: "terminal"
2266
2282
  };
2267
2283
  function reducer(state, action) {
2268
2284
  switch (action.type) {
@@ -2273,7 +2289,6 @@ function reducer(state, action) {
2273
2289
  ...state,
2274
2290
  selectionMode: action.enabled,
2275
2291
  questionMode: action.enabled ? false : state.questionMode,
2276
- // Turn off question mode when entering selection mode
2277
2292
  selectedElement: action.enabled ? state.selectedElement : null
2278
2293
  };
2279
2294
  case "SET_QUESTION_MODE":
@@ -2281,10 +2296,16 @@ function reducer(state, action) {
2281
2296
  ...state,
2282
2297
  questionMode: action.enabled,
2283
2298
  selectionMode: action.enabled ? false : state.selectionMode
2284
- // Turn off selection mode when entering question mode
2285
2299
  };
2286
2300
  case "SET_SELECTED_ELEMENT":
2287
2301
  return { ...state, selectedElement: action.element };
2302
+ case "ELEMENT_SELECTED":
2303
+ return {
2304
+ ...state,
2305
+ selectedElement: action.element,
2306
+ selectionMode: false,
2307
+ panelExpanded: true
2308
+ };
2288
2309
  case "SET_ACTIVE_TASK":
2289
2310
  return { ...state, activeTask: action.task };
2290
2311
  case "ADD_TASK":
@@ -2299,6 +2320,17 @@ function reducer(state, action) {
2299
2320
  };
2300
2321
  case "CLEAR_TASKS":
2301
2322
  return { ...state, tasks: [], activeTask: null };
2323
+ case "SET_VOICE_INPUT_PENDING":
2324
+ return { ...state, voiceInputPending: action.pending };
2325
+ // Session actions
2326
+ case "SET_SESSIONS":
2327
+ return { ...state, sessions: action.sessions };
2328
+ case "SET_ACTIVE_SESSION":
2329
+ return { ...state, activeSessionId: action.sessionId };
2330
+ case "SET_PANEL_EXPANDED":
2331
+ return { ...state, panelExpanded: action.expanded };
2332
+ case "SET_PANEL_MODE":
2333
+ return { ...state, panelMode: action.mode };
2302
2334
  default:
2303
2335
  return state;
2304
2336
  }
@@ -2312,6 +2344,7 @@ function ClaudeBridgeProvider({
2312
2344
  projectRoot,
2313
2345
  shortcut = "Meta+Shift+K",
2314
2346
  questionShortcut = "Meta+Shift+?",
2347
+ voiceShortcut = "Meta+Shift+V",
2315
2348
  onTaskCreated,
2316
2349
  onTaskProgress,
2317
2350
  onTaskCompleted,
@@ -2319,8 +2352,8 @@ function ClaudeBridgeProvider({
2319
2352
  onError
2320
2353
  }) {
2321
2354
  const [state, dispatch] = useReducer(reducer, initialState);
2322
- const wsClientRef = useRef6(null);
2323
- const pendingTaskResolveRef = useRef6(null);
2355
+ const wsClientRef = useRef5(null);
2356
+ const pendingTaskResolveRef = useRef5(null);
2324
2357
  const config = {
2325
2358
  serverUrl,
2326
2359
  token,
@@ -2328,6 +2361,7 @@ function ClaudeBridgeProvider({
2328
2361
  projectRoot,
2329
2362
  shortcut,
2330
2363
  questionShortcut,
2364
+ voiceShortcut,
2331
2365
  onTaskCreated,
2332
2366
  onTaskProgress,
2333
2367
  onTaskCompleted,
@@ -2352,7 +2386,40 @@ function ClaudeBridgeProvider({
2352
2386
  wsClientRef.current = null;
2353
2387
  };
2354
2388
  }, [enabled, serverUrl, token]);
2355
- const handleMessage = useCallback4((message) => {
2389
+ const { getStoredSessionId, setStoredSessionId } = useSessionStorage(projectRoot);
2390
+ const { projectSessions } = useSessions({
2391
+ sessions: state.sessions,
2392
+ projectRoot
2393
+ });
2394
+ useEffect6(() => {
2395
+ if (state.connected && wsClientRef.current) {
2396
+ wsClientRef.current.send({ type: "get_sessions" });
2397
+ }
2398
+ }, [state.connected]);
2399
+ useEffect6(() => {
2400
+ if (state.connected && projectSessions.length > 0 && !state.activeSessionId) {
2401
+ const storedSessionId = getStoredSessionId();
2402
+ if (storedSessionId) {
2403
+ const sessionExists = projectSessions.some((s) => s.id === storedSessionId);
2404
+ if (sessionExists) {
2405
+ dispatch({ type: "SET_ACTIVE_SESSION", sessionId: storedSessionId });
2406
+ dispatch({ type: "SET_PANEL_EXPANDED", expanded: true });
2407
+ }
2408
+ }
2409
+ }
2410
+ }, [state.connected, projectSessions, state.activeSessionId, getStoredSessionId]);
2411
+ useEffect6(() => {
2412
+ if (state.activeSessionId) {
2413
+ setStoredSessionId(state.activeSessionId);
2414
+ }
2415
+ }, [state.activeSessionId, setStoredSessionId]);
2416
+ useEffect6(() => {
2417
+ if (state.activeTask?.sessionId) {
2418
+ dispatch({ type: "SET_ACTIVE_SESSION", sessionId: state.activeTask.sessionId });
2419
+ dispatch({ type: "SET_PANEL_EXPANDED", expanded: true });
2420
+ }
2421
+ }, [state.activeTask?.sessionId]);
2422
+ const handleMessage = useCallback8((message) => {
2356
2423
  switch (message.type) {
2357
2424
  case "task_created":
2358
2425
  if (message.task) {
@@ -2404,11 +2471,16 @@ function ClaudeBridgeProvider({
2404
2471
  break;
2405
2472
  case "pong":
2406
2473
  break;
2474
+ case "sessions_list":
2475
+ if (message.sessions) {
2476
+ dispatch({ type: "SET_SESSIONS", sessions: message.sessions });
2477
+ }
2478
+ break;
2407
2479
  default:
2408
2480
  console.log("[ClaudeBridge] Unknown message type:", message.type);
2409
2481
  }
2410
2482
  }, [onTaskCreated, onTaskProgress, onTaskCompleted, onTaskFailed]);
2411
- const matchesShortcut = useCallback4((e, shortcutStr) => {
2483
+ const matchesShortcut = useCallback8((e, shortcutStr) => {
2412
2484
  const keys = shortcutStr.split("+");
2413
2485
  const requiresMeta = keys.includes("Meta");
2414
2486
  const requiresCtrl = keys.includes("Ctrl");
@@ -2436,6 +2508,15 @@ function ClaudeBridgeProvider({
2436
2508
  dispatch({ type: "SET_QUESTION_MODE", enabled: !state.questionMode });
2437
2509
  return;
2438
2510
  }
2511
+ if (matchesShortcut(e, voiceShortcut)) {
2512
+ e.preventDefault();
2513
+ if (!state.selectionMode && !state.selectedElement) {
2514
+ dispatch({ type: "SET_ACTIVE_TASK", task: null });
2515
+ dispatch({ type: "SET_SELECTION_MODE", enabled: true });
2516
+ }
2517
+ dispatch({ type: "SET_VOICE_INPUT_PENDING", pending: true });
2518
+ return;
2519
+ }
2439
2520
  if (e.key === "Escape") {
2440
2521
  if (state.selectionMode) {
2441
2522
  dispatch({ type: "SET_SELECTION_MODE", enabled: false });
@@ -2443,26 +2524,27 @@ function ClaudeBridgeProvider({
2443
2524
  if (state.questionMode) {
2444
2525
  dispatch({ type: "SET_QUESTION_MODE", enabled: false });
2445
2526
  }
2527
+ dispatch({ type: "SET_VOICE_INPUT_PENDING", pending: false });
2446
2528
  }
2447
2529
  };
2448
2530
  window.addEventListener("keydown", handleKeyDown);
2449
2531
  return () => window.removeEventListener("keydown", handleKeyDown);
2450
- }, [enabled, shortcut, questionShortcut, state.selectionMode, state.questionMode, matchesShortcut]);
2451
- const enableSelectionMode = useCallback4(() => {
2532
+ }, [enabled, shortcut, questionShortcut, voiceShortcut, state.selectionMode, state.questionMode, state.selectedElement, matchesShortcut]);
2533
+ const enableSelectionMode = useCallback8(() => {
2452
2534
  dispatch({ type: "SET_ACTIVE_TASK", task: null });
2453
2535
  dispatch({ type: "SET_SELECTION_MODE", enabled: true });
2454
2536
  }, []);
2455
- const disableSelectionMode = useCallback4(() => {
2537
+ const disableSelectionMode = useCallback8(() => {
2456
2538
  dispatch({ type: "SET_SELECTION_MODE", enabled: false });
2457
2539
  }, []);
2458
- const enableQuestionMode = useCallback4(() => {
2540
+ const enableQuestionMode = useCallback8(() => {
2459
2541
  dispatch({ type: "SET_ACTIVE_TASK", task: null });
2460
2542
  dispatch({ type: "SET_QUESTION_MODE", enabled: true });
2461
2543
  }, []);
2462
- const disableQuestionMode = useCallback4(() => {
2544
+ const disableQuestionMode = useCallback8(() => {
2463
2545
  dispatch({ type: "SET_QUESTION_MODE", enabled: false });
2464
2546
  }, []);
2465
- const submitTask = useCallback4(async (prompt) => {
2547
+ const submitTask = useCallback8(async (prompt) => {
2466
2548
  if (!state.selectedElement || !wsClientRef.current?.isConnected) {
2467
2549
  return null;
2468
2550
  }
@@ -2495,7 +2577,7 @@ function ClaudeBridgeProvider({
2495
2577
  dispatch({ type: "SET_SELECTION_MODE", enabled: false });
2496
2578
  return taskIdPromise;
2497
2579
  }, [state.selectedElement]);
2498
- const continueTask = useCallback4(async (taskId, prompt) => {
2580
+ const continueTask = useCallback8(async (taskId, prompt) => {
2499
2581
  if (!wsClientRef.current?.isConnected) {
2500
2582
  throw new Error("Not connected");
2501
2583
  }
@@ -2510,7 +2592,7 @@ function ClaudeBridgeProvider({
2510
2592
  throw new Error("Failed to send continue task message");
2511
2593
  }
2512
2594
  }, [state.selectedElement]);
2513
- const submitQuestion = useCallback4(async (prompt, includeSelectedElement = false) => {
2595
+ const submitQuestion = useCallback8(async (prompt, includeSelectedElement = false) => {
2514
2596
  if (!wsClientRef.current?.isConnected) {
2515
2597
  return null;
2516
2598
  }
@@ -2542,35 +2624,71 @@ ${prompt}`;
2542
2624
  dispatch({ type: "SET_QUESTION_MODE", enabled: false });
2543
2625
  return taskIdPromise;
2544
2626
  }, [state.selectedElement]);
2545
- const handleElementSelect = useCallback4((element) => {
2546
- dispatch({ type: "SET_SELECTED_ELEMENT", element });
2627
+ const handleElementSelect = useCallback8((element) => {
2628
+ dispatch({ type: "ELEMENT_SELECTED", element });
2547
2629
  }, []);
2548
- const handlePromptSubmit = useCallback4(async (prompt) => {
2630
+ const handlePromptSubmit = useCallback8(async (prompt) => {
2549
2631
  await submitTask(prompt);
2550
2632
  }, [submitTask]);
2551
- const handlePromptClose = useCallback4(() => {
2633
+ const handlePromptClose = useCallback8(() => {
2552
2634
  dispatch({ type: "SET_SELECTED_ELEMENT", element: null });
2553
2635
  }, []);
2554
- const handleQuestionSubmit = useCallback4(async (prompt, includeElement) => {
2636
+ const handleQuestionSubmit = useCallback8(async (prompt, includeElement) => {
2555
2637
  await submitQuestion(prompt, includeElement);
2556
2638
  }, [submitQuestion]);
2557
- const handleQuestionClose = useCallback4(() => {
2639
+ const handleQuestionClose = useCallback8(() => {
2558
2640
  dispatch({ type: "SET_QUESTION_MODE", enabled: false });
2559
2641
  }, []);
2560
- const handleSwitchToQuestionMode = useCallback4(() => {
2642
+ const handleSwitchToQuestionMode = useCallback8(() => {
2561
2643
  dispatch({ type: "SET_QUESTION_MODE", enabled: true });
2562
2644
  }, []);
2563
- const handleSwitchToTaskMode = useCallback4(() => {
2645
+ const handleSwitchToTaskMode = useCallback8(() => {
2564
2646
  dispatch({ type: "SET_QUESTION_MODE", enabled: false });
2565
2647
  if (!state.selectedElement) {
2566
2648
  dispatch({ type: "SET_SELECTION_MODE", enabled: true });
2567
2649
  }
2568
2650
  }, [state.selectedElement]);
2569
- const handleToastDismiss = useCallback4(() => {
2651
+ const handleToastDismiss = useCallback8(() => {
2570
2652
  dispatch({ type: "SET_ACTIVE_TASK", task: null });
2571
2653
  }, []);
2572
- const contextValue = {
2573
- state,
2654
+ const handleSessionClose = useCallback8((sessionId) => {
2655
+ if (state.activeSessionId === sessionId) {
2656
+ dispatch({ type: "SET_ACTIVE_SESSION", sessionId: null });
2657
+ }
2658
+ }, [state.activeSessionId]);
2659
+ const handleVoiceInputStart = useCallback8(() => {
2660
+ dispatch({ type: "SET_VOICE_INPUT_PENDING", pending: false });
2661
+ }, []);
2662
+ const setActiveSession = useCallback8((sessionId) => {
2663
+ dispatch({ type: "SET_ACTIVE_SESSION", sessionId });
2664
+ }, []);
2665
+ const setPanelExpanded = useCallback8((expanded) => {
2666
+ dispatch({ type: "SET_PANEL_EXPANDED", expanded });
2667
+ }, []);
2668
+ const setPanelMode = useCallback8((mode) => {
2669
+ dispatch({ type: "SET_PANEL_MODE", mode });
2670
+ }, []);
2671
+ const handlePanelModeChange = useCallback8((mode) => {
2672
+ dispatch({ type: "SET_PANEL_MODE", mode });
2673
+ if (mode === "prompt") {
2674
+ dispatch({ type: "SET_QUESTION_MODE", enabled: false });
2675
+ } else if (mode === "question") {
2676
+ dispatch({ type: "SET_QUESTION_MODE", enabled: true });
2677
+ } else {
2678
+ dispatch({ type: "SET_QUESTION_MODE", enabled: false });
2679
+ dispatch({ type: "SET_SELECTION_MODE", enabled: false });
2680
+ }
2681
+ }, []);
2682
+ const handlePanelClose = useCallback8(() => {
2683
+ dispatch({ type: "SET_PANEL_EXPANDED", expanded: false });
2684
+ dispatch({ type: "SET_PANEL_MODE", mode: "terminal" });
2685
+ dispatch({ type: "SET_SELECTION_MODE", enabled: false });
2686
+ dispatch({ type: "SET_QUESTION_MODE", enabled: false });
2687
+ dispatch({ type: "SET_SELECTED_ELEMENT", element: null });
2688
+ }, []);
2689
+ const effectivePanelMode = state.questionMode ? "question" : state.selectedElement !== null ? "prompt" : "terminal";
2690
+ const contextValue = {
2691
+ state,
2574
2692
  config,
2575
2693
  enableSelectionMode,
2576
2694
  disableSelectionMode,
@@ -2578,8 +2696,12 @@ ${prompt}`;
2578
2696
  disableQuestionMode,
2579
2697
  submitTask,
2580
2698
  submitQuestion,
2581
- continueTask
2699
+ continueTask,
2700
+ setActiveSession,
2701
+ setPanelExpanded,
2702
+ setPanelMode
2582
2703
  };
2704
+ const shouldShowPanel = projectSessions.length > 0 || state.activeTask !== null || effectivePanelMode !== "terminal";
2583
2705
  return /* @__PURE__ */ jsxs6(ClaudeBridgeContext.Provider, { value: contextValue, children: [
2584
2706
  children,
2585
2707
  enabled && /* @__PURE__ */ jsxs6(Fragment5, { children: [
@@ -2591,35 +2713,27 @@ ${prompt}`;
2591
2713
  selectedElement: state.selectedElement
2592
2714
  }
2593
2715
  ),
2594
- /* @__PURE__ */ jsx6(
2595
- PromptDialog,
2596
- {
2597
- open: state.selectedElement !== null && !state.questionMode,
2598
- element: state.selectedElement,
2599
- onSubmit: handlePromptSubmit,
2600
- onClose: handlePromptClose,
2601
- onSwitchToQuestionMode: handleSwitchToQuestionMode,
2602
- isSubmitting: state.activeTask?.status === "starting" || state.activeTask?.status === "running"
2603
- }
2604
- ),
2605
- /* @__PURE__ */ jsx6(
2606
- QuestionDialog,
2607
- {
2608
- open: state.questionMode,
2609
- selectedElement: state.selectedElement,
2610
- onSubmit: handleQuestionSubmit,
2611
- onClose: handleQuestionClose,
2612
- onSwitchToTaskMode: handleSwitchToTaskMode,
2613
- isSubmitting: state.activeTask?.status === "starting" || state.activeTask?.status === "running"
2614
- }
2615
- ),
2616
- /* @__PURE__ */ jsx6(
2617
- TaskStatusToast,
2716
+ shouldShowPanel && /* @__PURE__ */ jsx6(
2717
+ SessionPanel,
2618
2718
  {
2619
- task: state.activeTask,
2719
+ sessions: projectSessions,
2720
+ activeSessionId: state.activeSessionId,
2721
+ onSessionSelect: setActiveSession,
2722
+ onSessionClose: handleSessionClose,
2620
2723
  serverUrl,
2621
2724
  token,
2622
- onDismiss: handleToastDismiss
2725
+ expanded: state.panelExpanded || effectivePanelMode !== "terminal",
2726
+ onExpandedChange: setPanelExpanded,
2727
+ selectedElement: state.selectedElement,
2728
+ mode: effectivePanelMode,
2729
+ onModeChange: handlePanelModeChange,
2730
+ onPromptSubmit: handlePromptSubmit,
2731
+ onQuestionSubmit: handleQuestionSubmit,
2732
+ onClose: handlePanelClose,
2733
+ isSubmitting: state.activeTask?.status === "starting" || state.activeTask?.status === "running",
2734
+ voiceInputActive: state.voiceInputPending,
2735
+ onVoiceInputStart: handleVoiceInputStart,
2736
+ activeTask: state.activeTask
2623
2737
  }
2624
2738
  )
2625
2739
  ] })
@@ -2632,13 +2746,600 @@ function useClaudeBridge() {
2632
2746
  }
2633
2747
  return context;
2634
2748
  }
2749
+
2750
+ // src/TaskStatusToast.tsx
2751
+ import { useEffect as useEffect7, useState as useState4, useRef as useRef6, useCallback as useCallback9 } from "react";
2752
+ import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
2753
+ var MIN_WIDTH2 = 400;
2754
+ var MIN_HEIGHT2 = 300;
2755
+ var DEFAULT_WIDTH2 = 700;
2756
+ var DEFAULT_HEIGHT2 = 500;
2757
+ function TaskStatusToast({
2758
+ task,
2759
+ serverUrl,
2760
+ token,
2761
+ onDismiss,
2762
+ killOnClose = true
2763
+ }) {
2764
+ const [visible, setVisible] = useState4(false);
2765
+ const [expanded, setExpanded] = useState4(false);
2766
+ const [size, setSize] = useState4({ width: DEFAULT_WIDTH2, height: DEFAULT_HEIGHT2 });
2767
+ const [isResizing, setIsResizing] = useState4(false);
2768
+ const [isSaving, setIsSaving] = useState4(false);
2769
+ const resizeRef = useRef6(null);
2770
+ useEffect7(() => {
2771
+ if (task) {
2772
+ setVisible(true);
2773
+ if (task.sessionId && (task.status === "running" || task.status === "starting")) {
2774
+ setExpanded(true);
2775
+ }
2776
+ }
2777
+ }, [task, task?.sessionId, task?.status]);
2778
+ useEffect7(() => {
2779
+ if (task && !expanded && (task.status === "completed" || task.status === "failed")) {
2780
+ const timer = setTimeout(() => {
2781
+ setVisible(false);
2782
+ setTimeout(onDismiss, 300);
2783
+ }, 1e4);
2784
+ return () => clearTimeout(timer);
2785
+ }
2786
+ }, [task?.status, expanded, onDismiss]);
2787
+ const handleResizeStart = useCallback9((e, direction) => {
2788
+ e.preventDefault();
2789
+ e.stopPropagation();
2790
+ setIsResizing(true);
2791
+ resizeRef.current = {
2792
+ startX: e.clientX,
2793
+ startY: e.clientY,
2794
+ startWidth: size.width,
2795
+ startHeight: size.height
2796
+ };
2797
+ const handleMouseMove = (moveEvent) => {
2798
+ if (!resizeRef.current) return;
2799
+ let newWidth = resizeRef.current.startWidth;
2800
+ let newHeight = resizeRef.current.startHeight;
2801
+ if (direction.includes("w")) {
2802
+ newWidth = resizeRef.current.startWidth - (moveEvent.clientX - resizeRef.current.startX);
2803
+ }
2804
+ if (direction.includes("n")) {
2805
+ newHeight = resizeRef.current.startHeight - (moveEvent.clientY - resizeRef.current.startY);
2806
+ }
2807
+ setSize({
2808
+ width: Math.max(MIN_WIDTH2, newWidth),
2809
+ height: Math.max(MIN_HEIGHT2, newHeight)
2810
+ });
2811
+ };
2812
+ const handleMouseUp = () => {
2813
+ setIsResizing(false);
2814
+ resizeRef.current = null;
2815
+ document.removeEventListener("mousemove", handleMouseMove);
2816
+ document.removeEventListener("mouseup", handleMouseUp);
2817
+ };
2818
+ document.addEventListener("mousemove", handleMouseMove);
2819
+ document.addEventListener("mouseup", handleMouseUp);
2820
+ }, [size]);
2821
+ const killSession = useCallback9((sessionId, taskPrompt) => {
2822
+ return new Promise((resolve) => {
2823
+ const wsUrl = serverUrl.replace(/^http/, "ws").replace(/\/$/, "");
2824
+ const ws = new WebSocket(`${wsUrl}/ws/bridge?token=${encodeURIComponent(token)}`);
2825
+ let resolved = false;
2826
+ const cleanup = () => {
2827
+ if (!resolved) {
2828
+ resolved = true;
2829
+ ws.close();
2830
+ resolve();
2831
+ }
2832
+ };
2833
+ ws.onopen = () => {
2834
+ ws.send(JSON.stringify({
2835
+ type: "kill_session",
2836
+ sessionId,
2837
+ // Include task prompt for auto-commit message
2838
+ taskPrompt,
2839
+ autoCommit: true
2840
+ }));
2841
+ };
2842
+ ws.onmessage = (event) => {
2843
+ try {
2844
+ const message = JSON.parse(event.data);
2845
+ if (message.type === "session_killed" || message.type === "error") {
2846
+ cleanup();
2847
+ }
2848
+ } catch {
2849
+ }
2850
+ };
2851
+ ws.onerror = () => {
2852
+ cleanup();
2853
+ };
2854
+ ws.onclose = () => {
2855
+ if (!resolved) {
2856
+ resolved = true;
2857
+ resolve();
2858
+ }
2859
+ };
2860
+ setTimeout(cleanup, 35e3);
2861
+ });
2862
+ }, [serverUrl, token]);
2863
+ const handleDismiss = useCallback9(async () => {
2864
+ if (killOnClose && task?.sessionId) {
2865
+ setIsSaving(true);
2866
+ try {
2867
+ await killSession(task.sessionId, task.prompt);
2868
+ } finally {
2869
+ setIsSaving(false);
2870
+ }
2871
+ }
2872
+ setVisible(false);
2873
+ setExpanded(false);
2874
+ setTimeout(onDismiss, 300);
2875
+ }, [killOnClose, task?.sessionId, task?.prompt, killSession, onDismiss]);
2876
+ const handleToggleExpand = useCallback9(() => {
2877
+ setExpanded(!expanded);
2878
+ }, [expanded]);
2879
+ if (!task || !visible) return null;
2880
+ const statusColors = {
2881
+ queued: { bg: "#6366f1", text: "Queued" },
2882
+ starting: { bg: "#f59e0b", text: "Starting..." },
2883
+ running: { bg: "#22c55e", text: "Running" },
2884
+ completed: { bg: "#22c55e", text: "Completed" },
2885
+ failed: { bg: "#ef4444", text: "Failed" },
2886
+ saving: { bg: "#f59e0b", text: "Saving..." }
2887
+ };
2888
+ const status = isSaving ? statusColors.saving : statusColors[task.status] || statusColors.queued;
2889
+ const zedUrl = task.sessionId ? `${serverUrl}?token=${encodeURIComponent(token)}&session=${task.sessionId}` : `${serverUrl}?token=${encodeURIComponent(token)}`;
2890
+ const handleOpenZed = () => {
2891
+ window.open(zedUrl, "_blank");
2892
+ };
2893
+ if (!expanded) {
2894
+ return /* @__PURE__ */ jsxs7(
2895
+ "div",
2896
+ {
2897
+ style: {
2898
+ position: "fixed",
2899
+ bottom: 24,
2900
+ right: 24,
2901
+ zIndex: 1000001,
2902
+ background: "#1e1e2e",
2903
+ borderRadius: 12,
2904
+ boxShadow: "0 8px 32px rgba(0, 0, 0, 0.3)",
2905
+ fontFamily: "system-ui, -apple-system, sans-serif",
2906
+ minWidth: 320,
2907
+ maxWidth: 400,
2908
+ opacity: visible ? 1 : 0,
2909
+ transform: visible ? "translateY(0)" : "translateY(20px)",
2910
+ transition: "opacity 0.3s, transform 0.3s"
2911
+ },
2912
+ children: [
2913
+ /* @__PURE__ */ jsxs7(
2914
+ "div",
2915
+ {
2916
+ style: {
2917
+ padding: "12px 16px",
2918
+ borderBottom: "1px solid rgba(255, 255, 255, 0.1)",
2919
+ display: "flex",
2920
+ alignItems: "center",
2921
+ gap: 10
2922
+ },
2923
+ children: [
2924
+ /* @__PURE__ */ jsx7(
2925
+ "div",
2926
+ {
2927
+ style: {
2928
+ width: 10,
2929
+ height: 10,
2930
+ borderRadius: "50%",
2931
+ background: status.bg,
2932
+ animation: task.status === "running" || task.status === "starting" ? "claude-bridge-pulse 1.5s infinite" : "none"
2933
+ }
2934
+ }
2935
+ ),
2936
+ /* @__PURE__ */ jsxs7(
2937
+ "span",
2938
+ {
2939
+ style: {
2940
+ color: "rgba(255, 255, 255, 0.9)",
2941
+ fontSize: 14,
2942
+ fontWeight: 500,
2943
+ flex: 1
2944
+ },
2945
+ children: [
2946
+ "Claude Task: ",
2947
+ status.text
2948
+ ]
2949
+ }
2950
+ ),
2951
+ /* @__PURE__ */ jsx7(
2952
+ "button",
2953
+ {
2954
+ onClick: handleDismiss,
2955
+ disabled: isSaving,
2956
+ style: {
2957
+ background: "transparent",
2958
+ border: "none",
2959
+ color: isSaving ? "rgba(255, 255, 255, 0.3)" : "rgba(255, 255, 255, 0.5)",
2960
+ cursor: isSaving ? "not-allowed" : "pointer",
2961
+ padding: 4,
2962
+ fontSize: 18,
2963
+ lineHeight: 1
2964
+ },
2965
+ children: isSaving ? "..." : "\xD7"
2966
+ }
2967
+ )
2968
+ ]
2969
+ }
2970
+ ),
2971
+ /* @__PURE__ */ jsxs7("div", { style: { padding: "12px 16px" }, children: [
2972
+ /* @__PURE__ */ jsx7(
2973
+ "p",
2974
+ {
2975
+ style: {
2976
+ color: "rgba(255, 255, 255, 0.7)",
2977
+ fontSize: 13,
2978
+ margin: 0,
2979
+ lineHeight: 1.5,
2980
+ overflow: "hidden",
2981
+ textOverflow: "ellipsis",
2982
+ display: "-webkit-box",
2983
+ WebkitLineClamp: 2,
2984
+ WebkitBoxOrient: "vertical"
2985
+ },
2986
+ children: task.prompt
2987
+ }
2988
+ ),
2989
+ task.error && /* @__PURE__ */ jsxs7(
2990
+ "p",
2991
+ {
2992
+ style: {
2993
+ color: "#ef4444",
2994
+ fontSize: 12,
2995
+ margin: "8px 0 0"
2996
+ },
2997
+ children: [
2998
+ "Error: ",
2999
+ task.error
3000
+ ]
3001
+ }
3002
+ )
3003
+ ] }),
3004
+ /* @__PURE__ */ jsxs7(
3005
+ "div",
3006
+ {
3007
+ style: {
3008
+ padding: "12px 16px",
3009
+ borderTop: "1px solid rgba(255, 255, 255, 0.1)",
3010
+ display: "flex",
3011
+ justifyContent: "flex-end",
3012
+ gap: 8
3013
+ },
3014
+ children: [
3015
+ task.sessionId && /* @__PURE__ */ jsx7(
3016
+ "button",
3017
+ {
3018
+ onClick: handleToggleExpand,
3019
+ style: {
3020
+ background: "#374151",
3021
+ border: "none",
3022
+ borderRadius: 6,
3023
+ padding: "8px 16px",
3024
+ color: "white",
3025
+ fontSize: 13,
3026
+ fontWeight: 500,
3027
+ cursor: "pointer",
3028
+ display: "flex",
3029
+ alignItems: "center",
3030
+ gap: 6
3031
+ },
3032
+ children: "Show Terminal"
3033
+ }
3034
+ ),
3035
+ /* @__PURE__ */ jsxs7(
3036
+ "button",
3037
+ {
3038
+ onClick: handleOpenZed,
3039
+ style: {
3040
+ background: "#6366f1",
3041
+ border: "none",
3042
+ borderRadius: 6,
3043
+ padding: "8px 16px",
3044
+ color: "white",
3045
+ fontSize: 13,
3046
+ fontWeight: 500,
3047
+ cursor: "pointer",
3048
+ display: "flex",
3049
+ alignItems: "center",
3050
+ gap: 6
3051
+ },
3052
+ children: [
3053
+ "Open in Zed Controller",
3054
+ /* @__PURE__ */ jsx7("span", { style: { fontSize: 11 }, children: "\u2197" })
3055
+ ]
3056
+ }
3057
+ )
3058
+ ]
3059
+ }
3060
+ ),
3061
+ /* @__PURE__ */ jsx7("style", { children: `
3062
+ @keyframes claude-bridge-pulse {
3063
+ 0%, 100% { opacity: 1; }
3064
+ 50% { opacity: 0.5; }
3065
+ }
3066
+ ` })
3067
+ ]
3068
+ }
3069
+ );
3070
+ }
3071
+ return /* @__PURE__ */ jsxs7(
3072
+ "div",
3073
+ {
3074
+ style: {
3075
+ position: "fixed",
3076
+ bottom: 24,
3077
+ right: 24,
3078
+ zIndex: 1000001,
3079
+ width: size.width,
3080
+ height: size.height,
3081
+ background: "#1e1e2e",
3082
+ borderRadius: 12,
3083
+ boxShadow: "0 8px 32px rgba(0, 0, 0, 0.4)",
3084
+ fontFamily: "system-ui, -apple-system, sans-serif",
3085
+ display: "flex",
3086
+ flexDirection: "column",
3087
+ opacity: visible ? 1 : 0,
3088
+ transition: isResizing ? "none" : "opacity 0.3s",
3089
+ overflow: "hidden"
3090
+ },
3091
+ children: [
3092
+ /* @__PURE__ */ jsx7(
3093
+ "div",
3094
+ {
3095
+ onMouseDown: (e) => handleResizeStart(e, "n"),
3096
+ style: {
3097
+ position: "absolute",
3098
+ top: 0,
3099
+ left: 20,
3100
+ right: 20,
3101
+ height: 6,
3102
+ cursor: "ns-resize",
3103
+ zIndex: 10
3104
+ }
3105
+ }
3106
+ ),
3107
+ /* @__PURE__ */ jsx7(
3108
+ "div",
3109
+ {
3110
+ onMouseDown: (e) => handleResizeStart(e, "w"),
3111
+ style: {
3112
+ position: "absolute",
3113
+ left: 0,
3114
+ top: 20,
3115
+ bottom: 20,
3116
+ width: 6,
3117
+ cursor: "ew-resize",
3118
+ zIndex: 10
3119
+ }
3120
+ }
3121
+ ),
3122
+ /* @__PURE__ */ jsx7(
3123
+ "div",
3124
+ {
3125
+ onMouseDown: (e) => handleResizeStart(e, "nw"),
3126
+ style: {
3127
+ position: "absolute",
3128
+ top: 0,
3129
+ left: 0,
3130
+ width: 20,
3131
+ height: 20,
3132
+ cursor: "nwse-resize",
3133
+ zIndex: 11
3134
+ }
3135
+ }
3136
+ ),
3137
+ /* @__PURE__ */ jsxs7(
3138
+ "div",
3139
+ {
3140
+ style: {
3141
+ padding: "10px 16px",
3142
+ borderBottom: "1px solid rgba(255, 255, 255, 0.1)",
3143
+ display: "flex",
3144
+ alignItems: "center",
3145
+ gap: 10,
3146
+ flexShrink: 0
3147
+ },
3148
+ children: [
3149
+ /* @__PURE__ */ jsx7(
3150
+ "div",
3151
+ {
3152
+ style: {
3153
+ width: 10,
3154
+ height: 10,
3155
+ borderRadius: "50%",
3156
+ background: status.bg,
3157
+ animation: task.status === "running" || task.status === "starting" ? "claude-bridge-pulse 1.5s infinite" : "none"
3158
+ }
3159
+ }
3160
+ ),
3161
+ /* @__PURE__ */ jsxs7(
3162
+ "span",
3163
+ {
3164
+ style: {
3165
+ color: "rgba(255, 255, 255, 0.9)",
3166
+ fontSize: 14,
3167
+ fontWeight: 500,
3168
+ flex: 1,
3169
+ overflow: "hidden",
3170
+ textOverflow: "ellipsis",
3171
+ whiteSpace: "nowrap"
3172
+ },
3173
+ title: task.prompt,
3174
+ children: [
3175
+ status.text,
3176
+ ": ",
3177
+ task.prompt.slice(0, 50),
3178
+ task.prompt.length > 50 ? "..." : ""
3179
+ ]
3180
+ }
3181
+ ),
3182
+ /* @__PURE__ */ jsx7(
3183
+ "button",
3184
+ {
3185
+ onClick: handleToggleExpand,
3186
+ title: "Minimize",
3187
+ style: {
3188
+ background: "transparent",
3189
+ border: "none",
3190
+ color: "rgba(255, 255, 255, 0.5)",
3191
+ cursor: "pointer",
3192
+ padding: 4,
3193
+ fontSize: 14,
3194
+ lineHeight: 1
3195
+ },
3196
+ children: "\u2212"
3197
+ }
3198
+ ),
3199
+ /* @__PURE__ */ jsx7(
3200
+ "button",
3201
+ {
3202
+ onClick: handleOpenZed,
3203
+ title: "Open in Zed Controller",
3204
+ style: {
3205
+ background: "transparent",
3206
+ border: "none",
3207
+ color: "rgba(255, 255, 255, 0.5)",
3208
+ cursor: "pointer",
3209
+ padding: 4,
3210
+ fontSize: 14,
3211
+ lineHeight: 1
3212
+ },
3213
+ children: "\u2197"
3214
+ }
3215
+ ),
3216
+ /* @__PURE__ */ jsx7(
3217
+ "button",
3218
+ {
3219
+ onClick: handleDismiss,
3220
+ disabled: isSaving,
3221
+ title: isSaving ? "Saving changes..." : "Close",
3222
+ style: {
3223
+ background: "transparent",
3224
+ border: "none",
3225
+ color: isSaving ? "rgba(255, 255, 255, 0.3)" : "rgba(255, 255, 255, 0.5)",
3226
+ cursor: isSaving ? "not-allowed" : "pointer",
3227
+ padding: 4,
3228
+ fontSize: 18,
3229
+ lineHeight: 1
3230
+ },
3231
+ children: isSaving ? "..." : "\xD7"
3232
+ }
3233
+ )
3234
+ ]
3235
+ }
3236
+ ),
3237
+ isSaving && /* @__PURE__ */ jsxs7(
3238
+ "div",
3239
+ {
3240
+ style: {
3241
+ position: "absolute",
3242
+ top: 0,
3243
+ left: 0,
3244
+ right: 0,
3245
+ bottom: 0,
3246
+ background: "rgba(0, 0, 0, 0.7)",
3247
+ display: "flex",
3248
+ flexDirection: "column",
3249
+ alignItems: "center",
3250
+ justifyContent: "center",
3251
+ zIndex: 10,
3252
+ borderRadius: 12
3253
+ },
3254
+ children: [
3255
+ /* @__PURE__ */ jsx7(
3256
+ "div",
3257
+ {
3258
+ style: {
3259
+ color: "#f59e0b",
3260
+ fontSize: 14,
3261
+ fontWeight: 500,
3262
+ marginBottom: 8
3263
+ },
3264
+ children: "Saving changes..."
3265
+ }
3266
+ ),
3267
+ /* @__PURE__ */ jsx7(
3268
+ "div",
3269
+ {
3270
+ style: {
3271
+ color: "rgba(255, 255, 255, 0.6)",
3272
+ fontSize: 12
3273
+ },
3274
+ children: "Generating commit message with AI"
3275
+ }
3276
+ )
3277
+ ]
3278
+ }
3279
+ ),
3280
+ /* @__PURE__ */ jsx7("div", { style: { flex: 1, minHeight: 0 }, children: task.sessionId ? /* @__PURE__ */ jsx7(
3281
+ ClaudeTerminal,
3282
+ {
3283
+ serverUrl,
3284
+ token,
3285
+ sessionId: task.sessionId,
3286
+ style: { width: "100%", height: "100%" }
3287
+ }
3288
+ ) : /* @__PURE__ */ jsx7(
3289
+ "div",
3290
+ {
3291
+ style: {
3292
+ display: "flex",
3293
+ alignItems: "center",
3294
+ justifyContent: "center",
3295
+ height: "100%",
3296
+ color: "rgba(255, 255, 255, 0.5)"
3297
+ },
3298
+ children: "Waiting for session..."
3299
+ }
3300
+ ) }),
3301
+ task.error && /* @__PURE__ */ jsxs7(
3302
+ "div",
3303
+ {
3304
+ style: {
3305
+ padding: "8px 16px",
3306
+ background: "rgba(239, 68, 68, 0.2)",
3307
+ borderTop: "1px solid rgba(239, 68, 68, 0.3)",
3308
+ color: "#ef4444",
3309
+ fontSize: 12,
3310
+ flexShrink: 0
3311
+ },
3312
+ children: [
3313
+ "Error: ",
3314
+ task.error
3315
+ ]
3316
+ }
3317
+ ),
3318
+ /* @__PURE__ */ jsx7("style", { children: `
3319
+ @keyframes claude-bridge-pulse {
3320
+ 0%, 100% { opacity: 1; }
3321
+ 50% { opacity: 0.5; }
3322
+ }
3323
+ ` })
3324
+ ]
3325
+ }
3326
+ );
3327
+ }
2635
3328
  export {
2636
3329
  ClaudeBridgeProvider,
2637
3330
  ClaudeTerminal,
3331
+ SessionPanel,
3332
+ SessionTabs,
2638
3333
  TaskStatusToast,
3334
+ VoiceInput,
2639
3335
  captureContext,
2640
3336
  captureElementScreenshot,
2641
3337
  capturePageContext,
2642
- useClaudeBridge
3338
+ getSessionDisplayName,
3339
+ isSessionActive,
3340
+ useClaudeBridge,
3341
+ useSessionStorage,
3342
+ useSessions,
3343
+ useVoiceInput
2643
3344
  };
2644
3345
  //# sourceMappingURL=index.js.map