@thunderphone/widget 0.2.3 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -1,4 +1,5 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode } from 'react';
2
3
 
3
4
  interface WidgetError {
4
5
  error: string;
@@ -17,4 +18,28 @@ interface ThunderPhoneWidgetProps {
17
18
 
18
19
  declare function ThunderPhoneWidget({ apiKey, agentId, apiBase, onConnect, onDisconnect, onError, className, }: ThunderPhoneWidgetProps): react_jsx_runtime.JSX.Element;
19
20
 
20
- export { ThunderPhoneWidget, type ThunderPhoneWidgetProps, type WidgetError, type WidgetState };
21
+ interface UseThunderPhoneOptions {
22
+ apiKey: string;
23
+ agentId: number;
24
+ apiBase?: string;
25
+ onConnect?: () => void;
26
+ onDisconnect?: () => void;
27
+ onError?: (error: {
28
+ error: string;
29
+ message: string;
30
+ }) => void;
31
+ }
32
+ interface UseThunderPhoneReturn {
33
+ state: WidgetState;
34
+ connect: () => void;
35
+ disconnect: () => void;
36
+ toggleMute: () => void;
37
+ isMuted: boolean;
38
+ error: string | undefined;
39
+ agentName: string | undefined;
40
+ /** Render this somewhere in your tree — it's invisible but handles audio. */
41
+ audio: ReactNode;
42
+ }
43
+ declare function useThunderPhone(opts: UseThunderPhoneOptions): UseThunderPhoneReturn;
44
+
45
+ export { ThunderPhoneWidget, type ThunderPhoneWidgetProps, type UseThunderPhoneOptions, type UseThunderPhoneReturn, type WidgetError, type WidgetState, useThunderPhone };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode } from 'react';
2
3
 
3
4
  interface WidgetError {
4
5
  error: string;
@@ -17,4 +18,28 @@ interface ThunderPhoneWidgetProps {
17
18
 
18
19
  declare function ThunderPhoneWidget({ apiKey, agentId, apiBase, onConnect, onDisconnect, onError, className, }: ThunderPhoneWidgetProps): react_jsx_runtime.JSX.Element;
19
20
 
20
- export { ThunderPhoneWidget, type ThunderPhoneWidgetProps, type WidgetError, type WidgetState };
21
+ interface UseThunderPhoneOptions {
22
+ apiKey: string;
23
+ agentId: number;
24
+ apiBase?: string;
25
+ onConnect?: () => void;
26
+ onDisconnect?: () => void;
27
+ onError?: (error: {
28
+ error: string;
29
+ message: string;
30
+ }) => void;
31
+ }
32
+ interface UseThunderPhoneReturn {
33
+ state: WidgetState;
34
+ connect: () => void;
35
+ disconnect: () => void;
36
+ toggleMute: () => void;
37
+ isMuted: boolean;
38
+ error: string | undefined;
39
+ agentName: string | undefined;
40
+ /** Render this somewhere in your tree — it's invisible but handles audio. */
41
+ audio: ReactNode;
42
+ }
43
+ declare function useThunderPhone(opts: UseThunderPhoneOptions): UseThunderPhoneReturn;
44
+
45
+ export { ThunderPhoneWidget, type ThunderPhoneWidgetProps, type UseThunderPhoneOptions, type UseThunderPhoneReturn, type WidgetError, type WidgetState, useThunderPhone };
package/dist/index.js CHANGED
@@ -20,101 +20,54 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
- ThunderPhoneWidget: () => ThunderPhoneWidget
23
+ ThunderPhoneWidget: () => ThunderPhoneWidget,
24
+ useThunderPhone: () => useThunderPhone
24
25
  });
25
26
  module.exports = __toCommonJS(index_exports);
26
27
 
27
- // src/ThunderPhoneWidget.tsx
28
- var import_react3 = require("react");
29
- var import_components_react2 = require("@livekit/components-react");
30
-
31
- // src/AudioHandler.tsx
32
- var import_react = require("react");
33
- var import_components_react = require("@livekit/components-react");
34
- var import_livekit_client = require("livekit-client");
35
- var import_jsx_runtime = require("react/jsx-runtime");
36
- function AudioHandler({ onAgentConnected, onDisconnected }) {
37
- const room = (0, import_components_react.useRoomContext)();
38
- const audioRef = (0, import_react.useRef)(null);
39
- (0, import_react.useEffect)(() => {
40
- if (room.remoteParticipants.size > 0) {
41
- onAgentConnected();
42
- }
43
- const handleParticipantConnected = () => onAgentConnected();
44
- const handleDisconnect = () => onDisconnected();
45
- const attachTrack = (track, _pub, _participant) => {
46
- if (track.kind === import_livekit_client.Track.Kind.Audio && audioRef.current) {
47
- const stream = new MediaStream([track.mediaStreamTrack]);
48
- audioRef.current.srcObject = stream;
49
- audioRef.current.play().catch(() => {
50
- });
51
- }
52
- };
53
- room.on(import_livekit_client.RoomEvent.ParticipantConnected, handleParticipantConnected);
54
- room.on(import_livekit_client.RoomEvent.Disconnected, handleDisconnect);
55
- room.on(import_livekit_client.RoomEvent.TrackSubscribed, attachTrack);
56
- for (const participant of room.remoteParticipants.values()) {
57
- for (const pub of participant.trackPublications.values()) {
58
- if (pub.track && pub.isSubscribed && pub.track.kind === import_livekit_client.Track.Kind.Audio && audioRef.current) {
59
- const stream = new MediaStream([pub.track.mediaStreamTrack]);
60
- audioRef.current.srcObject = stream;
61
- audioRef.current.play().catch(() => {
62
- });
63
- }
64
- }
65
- }
66
- return () => {
67
- room.off(import_livekit_client.RoomEvent.ParticipantConnected, handleParticipantConnected);
68
- room.off(import_livekit_client.RoomEvent.Disconnected, handleDisconnect);
69
- room.off(import_livekit_client.RoomEvent.TrackSubscribed, attachTrack);
70
- };
71
- }, [room, onAgentConnected, onDisconnected]);
72
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("audio", { ref: audioRef, autoPlay: true });
73
- }
74
-
75
28
  // src/WidgetButton.tsx
76
- var import_jsx_runtime2 = require("react/jsx-runtime");
29
+ var import_jsx_runtime = require("react/jsx-runtime");
77
30
  function WidgetButton({ state, muted, onClick, onMuteToggle }) {
78
31
  if (state === "connected") {
79
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "tp-button-group", children: [
80
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: "tp-button tp-button--mute", onClick: onMuteToggle, type: "button", children: muted ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { className: "tp-icon", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
81
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "1", y1: "1", x2: "23", y2: "23" }),
82
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M9 9v3a3 3 0 0 0 5.12 2.12M15 9.34V4a3 3 0 0 0-5.94-.6" }),
83
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M17 16.95A7 7 0 0 1 5 12v-2m14 0v2c0 .76-.13 1.49-.36 2.18" }),
84
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "12", y1: "19", x2: "12", y2: "23" }),
85
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "8", y1: "23", x2: "16", y2: "23" })
86
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { className: "tp-icon", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
87
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z" }),
88
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M19 10v2a7 7 0 0 1-14 0v-2" }),
89
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "12", y1: "19", x2: "12", y2: "23" }),
90
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "8", y1: "23", x2: "16", y2: "23" })
32
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "tp-button-group", children: [
33
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "tp-button tp-button--mute", onClick: onMuteToggle, type: "button", children: muted ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { className: "tp-icon", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
34
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "1", y1: "1", x2: "23", y2: "23" }),
35
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M9 9v3a3 3 0 0 0 5.12 2.12M15 9.34V4a3 3 0 0 0-5.94-.6" }),
36
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M17 16.95A7 7 0 0 1 5 12v-2m14 0v2c0 .76-.13 1.49-.36 2.18" }),
37
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "12", y1: "19", x2: "12", y2: "23" }),
38
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "8", y1: "23", x2: "16", y2: "23" })
39
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { className: "tp-icon", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
40
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z" }),
41
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M19 10v2a7 7 0 0 1-14 0v-2" }),
42
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "12", y1: "19", x2: "12", y2: "23" }),
43
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "8", y1: "23", x2: "16", y2: "23" })
91
44
  ] }) }),
92
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: "tp-button tp-button--end", onClick, type: "button", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { className: "tp-icon", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "6", y: "6", width: "12", height: "12", rx: "2" }) }) })
45
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "tp-button tp-button--end", onClick, type: "button", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { className: "tp-icon", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "6", y: "6", width: "12", height: "12", rx: "2" }) }) })
93
46
  ] });
94
47
  }
95
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
48
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
96
49
  "button",
97
50
  {
98
51
  className: `tp-button tp-button--start ${state === "connecting" ? "tp-button--loading" : ""}`,
99
52
  onClick,
100
53
  disabled: state === "connecting",
101
54
  type: "button",
102
- children: state === "connecting" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { className: "tp-icon tp-spin", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }) }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { className: "tp-icon", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
103
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z" }),
104
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M19 10v2a7 7 0 0 1-14 0v-2" }),
105
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "12", y1: "19", x2: "12", y2: "23" }),
106
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "8", y1: "23", x2: "16", y2: "23" })
55
+ children: state === "connecting" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { className: "tp-icon tp-spin", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { className: "tp-icon", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
56
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z" }),
57
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M19 10v2a7 7 0 0 1-14 0v-2" }),
58
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "12", y1: "19", x2: "12", y2: "23" }),
59
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "8", y1: "23", x2: "16", y2: "23" })
107
60
  ] })
108
61
  }
109
62
  );
110
63
  }
111
64
 
112
65
  // src/WidgetStatus.tsx
113
- var import_react2 = require("react");
114
- var import_jsx_runtime3 = require("react/jsx-runtime");
66
+ var import_react = require("react");
67
+ var import_jsx_runtime2 = require("react/jsx-runtime");
115
68
  function WidgetStatus({ state, agentName, errorMessage }) {
116
- const [elapsed, setElapsed] = (0, import_react2.useState)(0);
117
- (0, import_react2.useEffect)(() => {
69
+ const [elapsed, setElapsed] = (0, import_react.useState)(0);
70
+ (0, import_react.useEffect)(() => {
118
71
  if (state !== "connected") {
119
72
  setElapsed(0);
120
73
  return;
@@ -134,15 +87,63 @@ function WidgetStatus({ state, agentName, errorMessage }) {
134
87
  disconnected: "Disconnected",
135
88
  error: "Unable to connect"
136
89
  };
137
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "tp-status", children: [
138
- agentName && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "tp-status__name", children: agentName }),
139
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: `tp-status__text tp-status--${state}`, children: [
140
- state === "connected" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "tp-status__dot" }),
90
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "tp-status", children: [
91
+ agentName && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "tp-status__name", children: agentName }),
92
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: `tp-status__text tp-status--${state}`, children: [
93
+ state === "connected" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "tp-status__dot" }),
141
94
  errorMessage && state === "error" ? errorMessage : statusText[state]
142
95
  ] })
143
96
  ] });
144
97
  }
145
98
 
99
+ // src/useThunderPhone.ts
100
+ var import_react3 = require("react");
101
+ var import_components_react2 = require("@livekit/components-react");
102
+
103
+ // src/AudioHandler.tsx
104
+ var import_react2 = require("react");
105
+ var import_components_react = require("@livekit/components-react");
106
+ var import_livekit_client = require("livekit-client");
107
+ var import_jsx_runtime3 = require("react/jsx-runtime");
108
+ function AudioHandler({ onAgentConnected, onDisconnected }) {
109
+ const room = (0, import_components_react.useRoomContext)();
110
+ const audioRef = (0, import_react2.useRef)(null);
111
+ (0, import_react2.useEffect)(() => {
112
+ if (room.remoteParticipants.size > 0) {
113
+ onAgentConnected();
114
+ }
115
+ const handleParticipantConnected = () => onAgentConnected();
116
+ const handleDisconnect = () => onDisconnected();
117
+ const attachTrack = (track, _pub, _participant) => {
118
+ if (track.kind === import_livekit_client.Track.Kind.Audio && audioRef.current) {
119
+ const stream = new MediaStream([track.mediaStreamTrack]);
120
+ audioRef.current.srcObject = stream;
121
+ audioRef.current.play().catch(() => {
122
+ });
123
+ }
124
+ };
125
+ room.on(import_livekit_client.RoomEvent.ParticipantConnected, handleParticipantConnected);
126
+ room.on(import_livekit_client.RoomEvent.Disconnected, handleDisconnect);
127
+ room.on(import_livekit_client.RoomEvent.TrackSubscribed, attachTrack);
128
+ for (const participant of room.remoteParticipants.values()) {
129
+ for (const pub of participant.trackPublications.values()) {
130
+ if (pub.track && pub.isSubscribed && pub.track.kind === import_livekit_client.Track.Kind.Audio && audioRef.current) {
131
+ const stream = new MediaStream([pub.track.mediaStreamTrack]);
132
+ audioRef.current.srcObject = stream;
133
+ audioRef.current.play().catch(() => {
134
+ });
135
+ }
136
+ }
137
+ }
138
+ return () => {
139
+ room.off(import_livekit_client.RoomEvent.ParticipantConnected, handleParticipantConnected);
140
+ room.off(import_livekit_client.RoomEvent.Disconnected, handleDisconnect);
141
+ room.off(import_livekit_client.RoomEvent.TrackSubscribed, attachTrack);
142
+ };
143
+ }, [room, onAgentConnected, onDisconnected]);
144
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("audio", { ref: audioRef, autoPlay: true });
145
+ }
146
+
146
147
  // src/api.ts
147
148
  var DEFAULT_API_BASE = "https://api.thunderphone.com/v1";
148
149
  var WidgetAPIError = class extends Error {
@@ -172,99 +173,114 @@ async function createWidgetSession(apiKey, agentId, apiBase) {
172
173
  return response.json();
173
174
  }
174
175
 
175
- // src/ThunderPhoneWidget.tsx
176
- var import_jsx_runtime4 = require("react/jsx-runtime");
177
- function ThunderPhoneWidget({
178
- apiKey,
179
- agentId,
180
- apiBase,
181
- onConnect,
182
- onDisconnect,
183
- onError,
184
- className
185
- }) {
176
+ // src/useThunderPhone.ts
177
+ function useThunderPhone(opts) {
186
178
  const [state, setState] = (0, import_react3.useState)("idle");
187
179
  const [session, setSession] = (0, import_react3.useState)(null);
188
180
  const [muted, setMuted] = (0, import_react3.useState)(false);
189
- const [errorMessage, setErrorMessage] = (0, import_react3.useState)();
190
- const handleConnect = (0, import_react3.useCallback)(async () => {
181
+ const [error, setError] = (0, import_react3.useState)();
182
+ const handleDisconnect = (0, import_react3.useCallback)(() => {
183
+ setState("disconnected");
184
+ setSession(null);
185
+ setMuted(false);
186
+ opts.onDisconnect?.();
187
+ setTimeout(() => setState("idle"), 1500);
188
+ }, [opts.onDisconnect]);
189
+ const handleAgentConnected = (0, import_react3.useCallback)(() => {
190
+ setState("connected");
191
+ opts.onConnect?.();
192
+ }, [opts.onConnect]);
193
+ const connect = (0, import_react3.useCallback)(async () => {
191
194
  if (state === "connecting" || state === "connected") return;
192
195
  setState("connecting");
193
- setErrorMessage(void 0);
196
+ setError(void 0);
194
197
  try {
195
- const sess = await createWidgetSession(apiKey, agentId, apiBase);
198
+ const sess = await createWidgetSession(opts.apiKey, opts.agentId, opts.apiBase);
196
199
  setSession(sess);
197
200
  } catch (err) {
198
201
  setState("error");
199
202
  if (err instanceof WidgetAPIError) {
200
- setErrorMessage(err.message);
201
- onError?.({ error: err.code, message: err.message });
203
+ setError(err.message);
204
+ opts.onError?.({ error: err.code, message: err.message });
202
205
  } else {
203
- setErrorMessage("Unable to connect.");
204
- onError?.({ error: "unknown", message: "Unable to connect." });
206
+ setError("Unable to connect.");
207
+ opts.onError?.({ error: "unknown", message: "Unable to connect." });
205
208
  }
206
209
  }
207
- }, [apiKey, agentId, apiBase, state, onError]);
208
- const handleDisconnect = (0, import_react3.useCallback)(() => {
209
- setState("disconnected");
210
- setSession(null);
211
- setMuted(false);
212
- onDisconnect?.();
213
- setTimeout(() => setState("idle"), 1500);
214
- }, [onDisconnect]);
215
- const handleAgentConnected = (0, import_react3.useCallback)(() => {
216
- setState("connected");
217
- onConnect?.();
218
- }, [onConnect]);
210
+ }, [opts.apiKey, opts.agentId, opts.apiBase, state, opts.onError]);
211
+ const disconnect = (0, import_react3.useCallback)(() => {
212
+ handleDisconnect();
213
+ }, [handleDisconnect]);
214
+ const toggleMute = (0, import_react3.useCallback)(() => setMuted((m) => !m), []);
215
+ const audio = session ? (0, import_react3.createElement)(
216
+ import_components_react2.LiveKitRoom,
217
+ {
218
+ token: session.token,
219
+ serverUrl: session.server_url,
220
+ audio: !muted,
221
+ video: false,
222
+ connect: true
223
+ },
224
+ (0, import_react3.createElement)(AudioHandler, {
225
+ onAgentConnected: handleAgentConnected,
226
+ onDisconnected: handleDisconnect
227
+ })
228
+ ) : null;
229
+ return {
230
+ state,
231
+ connect,
232
+ disconnect,
233
+ toggleMute,
234
+ isMuted: muted,
235
+ error,
236
+ agentName: session?.agent_name,
237
+ audio
238
+ };
239
+ }
240
+
241
+ // src/ThunderPhoneWidget.tsx
242
+ var import_jsx_runtime4 = require("react/jsx-runtime");
243
+ function ThunderPhoneWidget({
244
+ apiKey,
245
+ agentId,
246
+ apiBase,
247
+ onConnect,
248
+ onDisconnect,
249
+ onError,
250
+ className
251
+ }) {
252
+ const phone = useThunderPhone({ apiKey, agentId, apiBase, onConnect, onDisconnect, onError });
219
253
  const handleClick = () => {
220
- if (state === "connected") {
221
- handleDisconnect();
222
- } else if (state === "idle" || state === "error" || state === "disconnected") {
223
- handleConnect();
254
+ if (phone.state === "connected") {
255
+ phone.disconnect();
256
+ } else if (phone.state === "idle" || phone.state === "error" || phone.state === "disconnected") {
257
+ phone.connect();
224
258
  }
225
259
  };
226
- const handleMuteToggle = (0, import_react3.useCallback)(() => {
227
- setMuted((m) => !m);
228
- }, []);
229
260
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: `tp-widget ${className || ""}`, children: [
230
261
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
231
262
  WidgetStatus,
232
263
  {
233
- state,
234
- agentName: session?.agent_name || null,
235
- errorMessage
264
+ state: phone.state,
265
+ agentName: phone.agentName || null,
266
+ errorMessage: phone.error
236
267
  }
237
268
  ),
238
269
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
239
270
  WidgetButton,
240
271
  {
241
- state,
242
- muted,
272
+ state: phone.state,
273
+ muted: phone.isMuted,
243
274
  onClick: handleClick,
244
- onMuteToggle: handleMuteToggle
275
+ onMuteToggle: phone.toggleMute
245
276
  }
246
277
  ),
247
- session && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
248
- import_components_react2.LiveKitRoom,
249
- {
250
- token: session.token,
251
- serverUrl: session.server_url,
252
- audio: !muted,
253
- video: false,
254
- connect: true,
255
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
256
- AudioHandler,
257
- {
258
- onAgentConnected: handleAgentConnected,
259
- onDisconnected: handleDisconnect
260
- }
261
- )
262
- }
263
- )
278
+ phone.audio
264
279
  ] });
265
280
  }
266
281
  // Annotate the CommonJS export names for ESM import in node:
267
282
  0 && (module.exports = {
268
- ThunderPhoneWidget
283
+ ThunderPhoneWidget,
284
+ useThunderPhone
269
285
  });
270
286
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/ThunderPhoneWidget.tsx","../src/AudioHandler.tsx","../src/WidgetButton.tsx","../src/WidgetStatus.tsx","../src/api.ts"],"sourcesContent":["import './style.css'\n\nexport { ThunderPhoneWidget } from './ThunderPhoneWidget'\nexport type { ThunderPhoneWidgetProps, WidgetError, WidgetState } from './types'\n","import { useCallback, useState } from 'react'\nimport { LiveKitRoom } from '@livekit/components-react'\nimport { AudioHandler } from './AudioHandler'\nimport { WidgetButton } from './WidgetButton'\nimport { WidgetStatus } from './WidgetStatus'\nimport { createWidgetSession, WidgetAPIError } from './api'\nimport type { ThunderPhoneWidgetProps, WidgetState, WidgetSessionResponse } from './types'\n\nexport function ThunderPhoneWidget({\n apiKey,\n agentId,\n apiBase,\n onConnect,\n onDisconnect,\n onError,\n className,\n}: ThunderPhoneWidgetProps) {\n const [state, setState] = useState<WidgetState>('idle')\n const [session, setSession] = useState<WidgetSessionResponse | null>(null)\n const [muted, setMuted] = useState(false)\n const [errorMessage, setErrorMessage] = useState<string | undefined>()\n\n const handleConnect = useCallback(async () => {\n if (state === 'connecting' || state === 'connected') return\n setState('connecting')\n setErrorMessage(undefined)\n try {\n const sess = await createWidgetSession(apiKey, agentId, apiBase)\n setSession(sess)\n } catch (err) {\n setState('error')\n if (err instanceof WidgetAPIError) {\n setErrorMessage(err.message)\n onError?.({ error: err.code, message: err.message })\n } else {\n setErrorMessage('Unable to connect.')\n onError?.({ error: 'unknown', message: 'Unable to connect.' })\n }\n }\n }, [apiKey, agentId, apiBase, state, onError])\n\n const handleDisconnect = useCallback(() => {\n setState('disconnected')\n setSession(null)\n setMuted(false)\n onDisconnect?.()\n setTimeout(() => setState('idle'), 1500)\n }, [onDisconnect])\n\n const handleAgentConnected = useCallback(() => {\n setState('connected')\n onConnect?.()\n }, [onConnect])\n\n const handleClick = () => {\n if (state === 'connected') {\n handleDisconnect()\n } else if (state === 'idle' || state === 'error' || state === 'disconnected') {\n handleConnect()\n }\n }\n\n const handleMuteToggle = useCallback(() => {\n setMuted(m => !m)\n }, [])\n\n return (\n <div className={`tp-widget ${className || ''}`}>\n <WidgetStatus\n state={state}\n agentName={session?.agent_name || null}\n errorMessage={errorMessage}\n />\n <WidgetButton\n state={state}\n muted={muted}\n onClick={handleClick}\n onMuteToggle={handleMuteToggle}\n />\n {session && (\n <LiveKitRoom\n token={session.token}\n serverUrl={session.server_url}\n audio={!muted}\n video={false}\n connect={true}\n >\n <AudioHandler\n onAgentConnected={handleAgentConnected}\n onDisconnected={handleDisconnect}\n />\n </LiveKitRoom>\n )}\n </div>\n )\n}\n","import { useEffect, useRef } from 'react'\nimport { useRoomContext } from '@livekit/components-react'\nimport { RoomEvent, Track, type RemoteTrackPublication, type RemoteParticipant } from 'livekit-client'\n\ninterface AudioHandlerProps {\n onAgentConnected: () => void\n onDisconnected: () => void\n}\n\nexport function AudioHandler({ onAgentConnected, onDisconnected }: AudioHandlerProps) {\n const room = useRoomContext()\n const audioRef = useRef<HTMLAudioElement>(null)\n\n useEffect(() => {\n if (room.remoteParticipants.size > 0) {\n onAgentConnected()\n }\n\n const handleParticipantConnected = () => onAgentConnected()\n const handleDisconnect = () => onDisconnected()\n\n const attachTrack = (\n track: { kind: Track.Kind; mediaStreamTrack: MediaStreamTrack },\n _pub: RemoteTrackPublication,\n _participant: RemoteParticipant,\n ) => {\n if (track.kind === Track.Kind.Audio && audioRef.current) {\n const stream = new MediaStream([track.mediaStreamTrack])\n audioRef.current.srcObject = stream\n audioRef.current.play().catch(() => {})\n }\n }\n\n room.on(RoomEvent.ParticipantConnected, handleParticipantConnected)\n room.on(RoomEvent.Disconnected, handleDisconnect)\n room.on(RoomEvent.TrackSubscribed, attachTrack)\n\n for (const participant of room.remoteParticipants.values()) {\n for (const pub of participant.trackPublications.values()) {\n if (pub.track && pub.isSubscribed && pub.track.kind === Track.Kind.Audio && audioRef.current) {\n const stream = new MediaStream([pub.track.mediaStreamTrack])\n audioRef.current.srcObject = stream\n audioRef.current.play().catch(() => {})\n }\n }\n }\n\n return () => {\n room.off(RoomEvent.ParticipantConnected, handleParticipantConnected)\n room.off(RoomEvent.Disconnected, handleDisconnect)\n room.off(RoomEvent.TrackSubscribed, attachTrack)\n }\n }, [room, onAgentConnected, onDisconnected])\n\n return <audio ref={audioRef} autoPlay />\n}\n","import type { WidgetState } from './types'\n\ninterface WidgetButtonProps {\n state: WidgetState\n muted: boolean\n onClick: () => void\n onMuteToggle: () => void\n}\n\nexport function WidgetButton({ state, muted, onClick, onMuteToggle }: WidgetButtonProps) {\n if (state === 'connected') {\n return (\n <div className=\"tp-button-group\">\n <button className=\"tp-button tp-button--mute\" onClick={onMuteToggle} type=\"button\">\n {muted ? (\n <svg className=\"tp-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <line x1=\"1\" y1=\"1\" x2=\"23\" y2=\"23\" />\n <path d=\"M9 9v3a3 3 0 0 0 5.12 2.12M15 9.34V4a3 3 0 0 0-5.94-.6\" />\n <path d=\"M17 16.95A7 7 0 0 1 5 12v-2m14 0v2c0 .76-.13 1.49-.36 2.18\" />\n <line x1=\"12\" y1=\"19\" x2=\"12\" y2=\"23\" />\n <line x1=\"8\" y1=\"23\" x2=\"16\" y2=\"23\" />\n </svg>\n ) : (\n <svg className=\"tp-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <path d=\"M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z\" />\n <path d=\"M19 10v2a7 7 0 0 1-14 0v-2\" />\n <line x1=\"12\" y1=\"19\" x2=\"12\" y2=\"23\" />\n <line x1=\"8\" y1=\"23\" x2=\"16\" y2=\"23\" />\n </svg>\n )}\n </button>\n <button className=\"tp-button tp-button--end\" onClick={onClick} type=\"button\">\n <svg className=\"tp-icon\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <rect x=\"6\" y=\"6\" width=\"12\" height=\"12\" rx=\"2\" />\n </svg>\n </button>\n </div>\n )\n }\n\n return (\n <button\n className={`tp-button tp-button--start ${state === 'connecting' ? 'tp-button--loading' : ''}`}\n onClick={onClick}\n disabled={state === 'connecting'}\n type=\"button\"\n >\n {state === 'connecting' ? (\n <svg className=\"tp-icon tp-spin\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <path d=\"M21 12a9 9 0 1 1-6.219-8.56\" />\n </svg>\n ) : (\n <svg className=\"tp-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <path d=\"M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z\" />\n <path d=\"M19 10v2a7 7 0 0 1-14 0v-2\" />\n <line x1=\"12\" y1=\"19\" x2=\"12\" y2=\"23\" />\n <line x1=\"8\" y1=\"23\" x2=\"16\" y2=\"23\" />\n </svg>\n )}\n </button>\n )\n}\n","import { useEffect, useState } from 'react'\nimport type { WidgetState } from './types'\n\ninterface WidgetStatusProps {\n state: WidgetState\n agentName: string | null\n errorMessage?: string\n}\n\nexport function WidgetStatus({ state, agentName, errorMessage }: WidgetStatusProps) {\n const [elapsed, setElapsed] = useState(0)\n\n useEffect(() => {\n if (state !== 'connected') {\n setElapsed(0)\n return\n }\n const interval = setInterval(() => setElapsed(s => s + 1), 1000)\n return () => clearInterval(interval)\n }, [state])\n\n const formatTime = (seconds: number) => {\n const m = Math.floor(seconds / 60)\n const s = seconds % 60\n return `${m}:${s.toString().padStart(2, '0')}`\n }\n\n const statusText: Record<WidgetState, string> = {\n idle: 'Ready',\n connecting: 'Connecting...',\n connected: formatTime(elapsed),\n disconnected: 'Disconnected',\n error: 'Unable to connect',\n }\n\n return (\n <div className=\"tp-status\">\n {agentName && <div className=\"tp-status__name\">{agentName}</div>}\n <div className={`tp-status__text tp-status--${state}`}>\n {state === 'connected' && <span className=\"tp-status__dot\" />}\n {errorMessage && state === 'error' ? errorMessage : statusText[state]}\n </div>\n </div>\n )\n}\n","import type { WidgetSessionResponse, WidgetError } from './types'\n\nconst DEFAULT_API_BASE = 'https://api.thunderphone.com/v1'\n\nexport class WidgetAPIError extends Error {\n constructor(public code: string, message: string) {\n super(message)\n this.name = 'WidgetAPIError'\n }\n}\n\nexport async function createWidgetSession(\n apiKey: string,\n agentId: number,\n apiBase?: string,\n): Promise<WidgetSessionResponse> {\n const base = apiBase || DEFAULT_API_BASE\n const response = await fetch(`${base}/widget/session`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-API-Key': apiKey,\n },\n body: JSON.stringify({ agent_id: agentId }),\n })\n\n if (!response.ok) {\n const data: WidgetError = await response.json().catch(() => ({\n error: 'unknown',\n message: 'Unable to connect.',\n }))\n throw new WidgetAPIError(data.error, data.message)\n }\n\n return response.json()\n}\n"],"mappings":"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","names":["import_react","import_components_react","import_jsx_runtime","import_react","import_jsx_runtime","import_jsx_runtime"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/WidgetButton.tsx","../src/WidgetStatus.tsx","../src/useThunderPhone.ts","../src/AudioHandler.tsx","../src/api.ts","../src/ThunderPhoneWidget.tsx"],"sourcesContent":["import './style.css'\n\nexport { ThunderPhoneWidget } from './ThunderPhoneWidget'\nexport { useThunderPhone } from './useThunderPhone'\nexport type { UseThunderPhoneOptions, UseThunderPhoneReturn } from './useThunderPhone'\nexport type { ThunderPhoneWidgetProps, WidgetError, WidgetState } from './types'\n","import type { WidgetState } from './types'\n\ninterface WidgetButtonProps {\n state: WidgetState\n muted: boolean\n onClick: () => void\n onMuteToggle: () => void\n}\n\nexport function WidgetButton({ state, muted, onClick, onMuteToggle }: WidgetButtonProps) {\n if (state === 'connected') {\n return (\n <div className=\"tp-button-group\">\n <button className=\"tp-button tp-button--mute\" onClick={onMuteToggle} type=\"button\">\n {muted ? (\n <svg className=\"tp-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <line x1=\"1\" y1=\"1\" x2=\"23\" y2=\"23\" />\n <path d=\"M9 9v3a3 3 0 0 0 5.12 2.12M15 9.34V4a3 3 0 0 0-5.94-.6\" />\n <path d=\"M17 16.95A7 7 0 0 1 5 12v-2m14 0v2c0 .76-.13 1.49-.36 2.18\" />\n <line x1=\"12\" y1=\"19\" x2=\"12\" y2=\"23\" />\n <line x1=\"8\" y1=\"23\" x2=\"16\" y2=\"23\" />\n </svg>\n ) : (\n <svg className=\"tp-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <path d=\"M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z\" />\n <path d=\"M19 10v2a7 7 0 0 1-14 0v-2\" />\n <line x1=\"12\" y1=\"19\" x2=\"12\" y2=\"23\" />\n <line x1=\"8\" y1=\"23\" x2=\"16\" y2=\"23\" />\n </svg>\n )}\n </button>\n <button className=\"tp-button tp-button--end\" onClick={onClick} type=\"button\">\n <svg className=\"tp-icon\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <rect x=\"6\" y=\"6\" width=\"12\" height=\"12\" rx=\"2\" />\n </svg>\n </button>\n </div>\n )\n }\n\n return (\n <button\n className={`tp-button tp-button--start ${state === 'connecting' ? 'tp-button--loading' : ''}`}\n onClick={onClick}\n disabled={state === 'connecting'}\n type=\"button\"\n >\n {state === 'connecting' ? (\n <svg className=\"tp-icon tp-spin\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <path d=\"M21 12a9 9 0 1 1-6.219-8.56\" />\n </svg>\n ) : (\n <svg className=\"tp-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <path d=\"M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z\" />\n <path d=\"M19 10v2a7 7 0 0 1-14 0v-2\" />\n <line x1=\"12\" y1=\"19\" x2=\"12\" y2=\"23\" />\n <line x1=\"8\" y1=\"23\" x2=\"16\" y2=\"23\" />\n </svg>\n )}\n </button>\n )\n}\n","import { useEffect, useState } from 'react'\nimport type { WidgetState } from './types'\n\ninterface WidgetStatusProps {\n state: WidgetState\n agentName: string | null\n errorMessage?: string\n}\n\nexport function WidgetStatus({ state, agentName, errorMessage }: WidgetStatusProps) {\n const [elapsed, setElapsed] = useState(0)\n\n useEffect(() => {\n if (state !== 'connected') {\n setElapsed(0)\n return\n }\n const interval = setInterval(() => setElapsed(s => s + 1), 1000)\n return () => clearInterval(interval)\n }, [state])\n\n const formatTime = (seconds: number) => {\n const m = Math.floor(seconds / 60)\n const s = seconds % 60\n return `${m}:${s.toString().padStart(2, '0')}`\n }\n\n const statusText: Record<WidgetState, string> = {\n idle: 'Ready',\n connecting: 'Connecting...',\n connected: formatTime(elapsed),\n disconnected: 'Disconnected',\n error: 'Unable to connect',\n }\n\n return (\n <div className=\"tp-status\">\n {agentName && <div className=\"tp-status__name\">{agentName}</div>}\n <div className={`tp-status__text tp-status--${state}`}>\n {state === 'connected' && <span className=\"tp-status__dot\" />}\n {errorMessage && state === 'error' ? errorMessage : statusText[state]}\n </div>\n </div>\n )\n}\n","import { useCallback, useState, type ReactNode, createElement } from 'react'\nimport { LiveKitRoom } from '@livekit/components-react'\nimport { AudioHandler } from './AudioHandler'\nimport { createWidgetSession, WidgetAPIError } from './api'\nimport type { WidgetState, WidgetSessionResponse } from './types'\n\nexport interface UseThunderPhoneOptions {\n apiKey: string\n agentId: number\n apiBase?: string\n onConnect?: () => void\n onDisconnect?: () => void\n onError?: (error: { error: string; message: string }) => void\n}\n\nexport interface UseThunderPhoneReturn {\n state: WidgetState\n connect: () => void\n disconnect: () => void\n toggleMute: () => void\n isMuted: boolean\n error: string | undefined\n agentName: string | undefined\n /** Render this somewhere in your tree — it's invisible but handles audio. */\n audio: ReactNode\n}\n\nexport function useThunderPhone(opts: UseThunderPhoneOptions): UseThunderPhoneReturn {\n const [state, setState] = useState<WidgetState>('idle')\n const [session, setSession] = useState<WidgetSessionResponse | null>(null)\n const [muted, setMuted] = useState(false)\n const [error, setError] = useState<string | undefined>()\n\n const handleDisconnect = useCallback(() => {\n setState('disconnected')\n setSession(null)\n setMuted(false)\n opts.onDisconnect?.()\n setTimeout(() => setState('idle'), 1500)\n }, [opts.onDisconnect])\n\n const handleAgentConnected = useCallback(() => {\n setState('connected')\n opts.onConnect?.()\n }, [opts.onConnect])\n\n const connect = useCallback(async () => {\n if (state === 'connecting' || state === 'connected') return\n setState('connecting')\n setError(undefined)\n try {\n const sess = await createWidgetSession(opts.apiKey, opts.agentId, opts.apiBase)\n setSession(sess)\n } catch (err) {\n setState('error')\n if (err instanceof WidgetAPIError) {\n setError(err.message)\n opts.onError?.({ error: err.code, message: err.message })\n } else {\n setError('Unable to connect.')\n opts.onError?.({ error: 'unknown', message: 'Unable to connect.' })\n }\n }\n }, [opts.apiKey, opts.agentId, opts.apiBase, state, opts.onError])\n\n const disconnect = useCallback(() => {\n handleDisconnect()\n }, [handleDisconnect])\n\n const toggleMute = useCallback(() => setMuted(m => !m), [])\n\n const audio: ReactNode = session\n ? createElement(\n LiveKitRoom,\n {\n token: session.token,\n serverUrl: session.server_url,\n audio: !muted,\n video: false,\n connect: true,\n },\n createElement(AudioHandler, {\n onAgentConnected: handleAgentConnected,\n onDisconnected: handleDisconnect,\n }),\n )\n : null\n\n return {\n state,\n connect,\n disconnect,\n toggleMute,\n isMuted: muted,\n error,\n agentName: session?.agent_name,\n audio,\n }\n}\n","import { useEffect, useRef } from 'react'\nimport { useRoomContext } from '@livekit/components-react'\nimport { RoomEvent, Track, type RemoteTrackPublication, type RemoteParticipant } from 'livekit-client'\n\ninterface AudioHandlerProps {\n onAgentConnected: () => void\n onDisconnected: () => void\n}\n\nexport function AudioHandler({ onAgentConnected, onDisconnected }: AudioHandlerProps) {\n const room = useRoomContext()\n const audioRef = useRef<HTMLAudioElement>(null)\n\n useEffect(() => {\n if (room.remoteParticipants.size > 0) {\n onAgentConnected()\n }\n\n const handleParticipantConnected = () => onAgentConnected()\n const handleDisconnect = () => onDisconnected()\n\n const attachTrack = (\n track: { kind: Track.Kind; mediaStreamTrack: MediaStreamTrack },\n _pub: RemoteTrackPublication,\n _participant: RemoteParticipant,\n ) => {\n if (track.kind === Track.Kind.Audio && audioRef.current) {\n const stream = new MediaStream([track.mediaStreamTrack])\n audioRef.current.srcObject = stream\n audioRef.current.play().catch(() => {})\n }\n }\n\n room.on(RoomEvent.ParticipantConnected, handleParticipantConnected)\n room.on(RoomEvent.Disconnected, handleDisconnect)\n room.on(RoomEvent.TrackSubscribed, attachTrack)\n\n for (const participant of room.remoteParticipants.values()) {\n for (const pub of participant.trackPublications.values()) {\n if (pub.track && pub.isSubscribed && pub.track.kind === Track.Kind.Audio && audioRef.current) {\n const stream = new MediaStream([pub.track.mediaStreamTrack])\n audioRef.current.srcObject = stream\n audioRef.current.play().catch(() => {})\n }\n }\n }\n\n return () => {\n room.off(RoomEvent.ParticipantConnected, handleParticipantConnected)\n room.off(RoomEvent.Disconnected, handleDisconnect)\n room.off(RoomEvent.TrackSubscribed, attachTrack)\n }\n }, [room, onAgentConnected, onDisconnected])\n\n return <audio ref={audioRef} autoPlay />\n}\n","import type { WidgetSessionResponse, WidgetError } from './types'\n\nconst DEFAULT_API_BASE = 'https://api.thunderphone.com/v1'\n\nexport class WidgetAPIError extends Error {\n constructor(public code: string, message: string) {\n super(message)\n this.name = 'WidgetAPIError'\n }\n}\n\nexport async function createWidgetSession(\n apiKey: string,\n agentId: number,\n apiBase?: string,\n): Promise<WidgetSessionResponse> {\n const base = apiBase || DEFAULT_API_BASE\n const response = await fetch(`${base}/widget/session`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-API-Key': apiKey,\n },\n body: JSON.stringify({ agent_id: agentId }),\n })\n\n if (!response.ok) {\n const data: WidgetError = await response.json().catch(() => ({\n error: 'unknown',\n message: 'Unable to connect.',\n }))\n throw new WidgetAPIError(data.error, data.message)\n }\n\n return response.json()\n}\n","import { WidgetButton } from './WidgetButton'\nimport { WidgetStatus } from './WidgetStatus'\nimport { useThunderPhone } from './useThunderPhone'\nimport type { ThunderPhoneWidgetProps } from './types'\n\nexport function ThunderPhoneWidget({\n apiKey,\n agentId,\n apiBase,\n onConnect,\n onDisconnect,\n onError,\n className,\n}: ThunderPhoneWidgetProps) {\n const phone = useThunderPhone({ apiKey, agentId, apiBase, onConnect, onDisconnect, onError })\n\n const handleClick = () => {\n if (phone.state === 'connected') {\n phone.disconnect()\n } else if (phone.state === 'idle' || phone.state === 'error' || phone.state === 'disconnected') {\n phone.connect()\n }\n }\n\n return (\n <div className={`tp-widget ${className || ''}`}>\n <WidgetStatus\n state={phone.state}\n agentName={phone.agentName || null}\n errorMessage={phone.error}\n />\n <WidgetButton\n state={phone.state}\n muted={phone.isMuted}\n onClick={handleClick}\n onMuteToggle={phone.toggleMute}\n />\n {phone.audio}\n </div>\n )\n}\n"],"mappings":"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","names":["import_jsx_runtime","import_react","import_components_react","import_react","import_jsx_runtime","import_jsx_runtime"]}