@thunderphone/widget 0.1.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.css +112 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.mts +20 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +270 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +243 -0
- package/dist/index.mjs.map +1 -0
- package/dist/mount.css +112 -0
- package/dist/mount.css.map +1 -0
- package/dist/mount.global.js +57719 -0
- package/dist/mount.global.js.map +1 -0
- package/package.json +38 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
// src/ThunderPhoneWidget.tsx
|
|
2
|
+
import { useCallback, useState as useState2 } from "react";
|
|
3
|
+
import { LiveKitRoom } from "@livekit/components-react";
|
|
4
|
+
|
|
5
|
+
// src/AudioHandler.tsx
|
|
6
|
+
import { useEffect, useRef } from "react";
|
|
7
|
+
import { useRoomContext } from "@livekit/components-react";
|
|
8
|
+
import { RoomEvent, Track } from "livekit-client";
|
|
9
|
+
import { jsx } from "react/jsx-runtime";
|
|
10
|
+
function AudioHandler({ onAgentConnected, onDisconnected }) {
|
|
11
|
+
const room = useRoomContext();
|
|
12
|
+
const audioRef = useRef(null);
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
if (room.remoteParticipants.size > 0) {
|
|
15
|
+
onAgentConnected();
|
|
16
|
+
}
|
|
17
|
+
const handleParticipantConnected = () => onAgentConnected();
|
|
18
|
+
const handleDisconnect = () => onDisconnected();
|
|
19
|
+
const attachTrack = (track, _pub, _participant) => {
|
|
20
|
+
if (track.kind === Track.Kind.Audio && audioRef.current) {
|
|
21
|
+
const stream = new MediaStream([track.mediaStreamTrack]);
|
|
22
|
+
audioRef.current.srcObject = stream;
|
|
23
|
+
audioRef.current.play().catch(() => {
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
room.on(RoomEvent.ParticipantConnected, handleParticipantConnected);
|
|
28
|
+
room.on(RoomEvent.Disconnected, handleDisconnect);
|
|
29
|
+
room.on(RoomEvent.TrackSubscribed, attachTrack);
|
|
30
|
+
for (const participant of room.remoteParticipants.values()) {
|
|
31
|
+
for (const pub of participant.trackPublications.values()) {
|
|
32
|
+
if (pub.track && pub.isSubscribed && pub.track.kind === Track.Kind.Audio && audioRef.current) {
|
|
33
|
+
const stream = new MediaStream([pub.track.mediaStreamTrack]);
|
|
34
|
+
audioRef.current.srcObject = stream;
|
|
35
|
+
audioRef.current.play().catch(() => {
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return () => {
|
|
41
|
+
room.off(RoomEvent.ParticipantConnected, handleParticipantConnected);
|
|
42
|
+
room.off(RoomEvent.Disconnected, handleDisconnect);
|
|
43
|
+
room.off(RoomEvent.TrackSubscribed, attachTrack);
|
|
44
|
+
};
|
|
45
|
+
}, [room, onAgentConnected, onDisconnected]);
|
|
46
|
+
return /* @__PURE__ */ jsx("audio", { ref: audioRef, autoPlay: true });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// src/WidgetButton.tsx
|
|
50
|
+
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
51
|
+
function WidgetButton({ state, muted, onClick, onMuteToggle }) {
|
|
52
|
+
if (state === "connected") {
|
|
53
|
+
return /* @__PURE__ */ jsxs("div", { className: "tp-button-group", children: [
|
|
54
|
+
/* @__PURE__ */ jsx2("button", { className: "tp-button tp-button--mute", onClick: onMuteToggle, type: "button", children: muted ? /* @__PURE__ */ jsxs("svg", { className: "tp-icon", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
|
|
55
|
+
/* @__PURE__ */ jsx2("line", { x1: "1", y1: "1", x2: "23", y2: "23" }),
|
|
56
|
+
/* @__PURE__ */ jsx2("path", { d: "M9 9v3a3 3 0 0 0 5.12 2.12M15 9.34V4a3 3 0 0 0-5.94-.6" }),
|
|
57
|
+
/* @__PURE__ */ jsx2("path", { d: "M17 16.95A7 7 0 0 1 5 12v-2m14 0v2c0 .76-.13 1.49-.36 2.18" }),
|
|
58
|
+
/* @__PURE__ */ jsx2("line", { x1: "12", y1: "19", x2: "12", y2: "23" }),
|
|
59
|
+
/* @__PURE__ */ jsx2("line", { x1: "8", y1: "23", x2: "16", y2: "23" })
|
|
60
|
+
] }) : /* @__PURE__ */ jsxs("svg", { className: "tp-icon", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
|
|
61
|
+
/* @__PURE__ */ jsx2("path", { d: "M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z" }),
|
|
62
|
+
/* @__PURE__ */ jsx2("path", { d: "M19 10v2a7 7 0 0 1-14 0v-2" }),
|
|
63
|
+
/* @__PURE__ */ jsx2("line", { x1: "12", y1: "19", x2: "12", y2: "23" }),
|
|
64
|
+
/* @__PURE__ */ jsx2("line", { x1: "8", y1: "23", x2: "16", y2: "23" })
|
|
65
|
+
] }) }),
|
|
66
|
+
/* @__PURE__ */ jsx2("button", { className: "tp-button tp-button--end", onClick, type: "button", children: /* @__PURE__ */ jsx2("svg", { className: "tp-icon", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx2("rect", { x: "6", y: "6", width: "12", height: "12", rx: "2" }) }) })
|
|
67
|
+
] });
|
|
68
|
+
}
|
|
69
|
+
return /* @__PURE__ */ jsx2(
|
|
70
|
+
"button",
|
|
71
|
+
{
|
|
72
|
+
className: `tp-button tp-button--start ${state === "connecting" ? "tp-button--loading" : ""}`,
|
|
73
|
+
onClick,
|
|
74
|
+
disabled: state === "connecting",
|
|
75
|
+
type: "button",
|
|
76
|
+
children: state === "connecting" ? /* @__PURE__ */ jsx2("svg", { className: "tp-icon tp-spin", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx2("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }) }) : /* @__PURE__ */ jsxs("svg", { className: "tp-icon", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
|
|
77
|
+
/* @__PURE__ */ jsx2("path", { d: "M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z" }),
|
|
78
|
+
/* @__PURE__ */ jsx2("path", { d: "M19 10v2a7 7 0 0 1-14 0v-2" }),
|
|
79
|
+
/* @__PURE__ */ jsx2("line", { x1: "12", y1: "19", x2: "12", y2: "23" }),
|
|
80
|
+
/* @__PURE__ */ jsx2("line", { x1: "8", y1: "23", x2: "16", y2: "23" })
|
|
81
|
+
] })
|
|
82
|
+
}
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// src/WidgetStatus.tsx
|
|
87
|
+
import { useEffect as useEffect2, useState } from "react";
|
|
88
|
+
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
89
|
+
function WidgetStatus({ state, agentName, errorMessage }) {
|
|
90
|
+
const [elapsed, setElapsed] = useState(0);
|
|
91
|
+
useEffect2(() => {
|
|
92
|
+
if (state !== "connected") {
|
|
93
|
+
setElapsed(0);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const interval = setInterval(() => setElapsed((s) => s + 1), 1e3);
|
|
97
|
+
return () => clearInterval(interval);
|
|
98
|
+
}, [state]);
|
|
99
|
+
const formatTime = (seconds) => {
|
|
100
|
+
const m = Math.floor(seconds / 60);
|
|
101
|
+
const s = seconds % 60;
|
|
102
|
+
return `${m}:${s.toString().padStart(2, "0")}`;
|
|
103
|
+
};
|
|
104
|
+
const statusText = {
|
|
105
|
+
idle: "Ready",
|
|
106
|
+
connecting: "Connecting...",
|
|
107
|
+
connected: formatTime(elapsed),
|
|
108
|
+
disconnected: "Disconnected",
|
|
109
|
+
error: "Unable to connect"
|
|
110
|
+
};
|
|
111
|
+
return /* @__PURE__ */ jsxs2("div", { className: "tp-status", children: [
|
|
112
|
+
agentName && /* @__PURE__ */ jsx3("div", { className: "tp-status__name", children: agentName }),
|
|
113
|
+
/* @__PURE__ */ jsxs2("div", { className: `tp-status__text tp-status--${state}`, children: [
|
|
114
|
+
state === "connected" && /* @__PURE__ */ jsx3("span", { className: "tp-status__dot" }),
|
|
115
|
+
errorMessage && state === "error" ? errorMessage : statusText[state]
|
|
116
|
+
] })
|
|
117
|
+
] });
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// src/api.ts
|
|
121
|
+
var DEFAULT_API_BASE = "https://api.thunderphone.com/v1";
|
|
122
|
+
var WidgetAPIError = class extends Error {
|
|
123
|
+
constructor(code, message) {
|
|
124
|
+
super(message);
|
|
125
|
+
this.code = code;
|
|
126
|
+
this.name = "WidgetAPIError";
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
async function createWidgetSession(apiKey, agentId, apiBase) {
|
|
130
|
+
const base = apiBase || DEFAULT_API_BASE;
|
|
131
|
+
const response = await fetch(`${base}/widget/session`, {
|
|
132
|
+
method: "POST",
|
|
133
|
+
headers: {
|
|
134
|
+
"Content-Type": "application/json",
|
|
135
|
+
"X-API-Key": apiKey
|
|
136
|
+
},
|
|
137
|
+
body: JSON.stringify({ agent_id: agentId })
|
|
138
|
+
});
|
|
139
|
+
if (!response.ok) {
|
|
140
|
+
const data = await response.json().catch(() => ({
|
|
141
|
+
error: "unknown",
|
|
142
|
+
message: "Unable to connect."
|
|
143
|
+
}));
|
|
144
|
+
throw new WidgetAPIError(data.error, data.message);
|
|
145
|
+
}
|
|
146
|
+
return response.json();
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// src/ThunderPhoneWidget.tsx
|
|
150
|
+
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
151
|
+
function ThunderPhoneWidget({
|
|
152
|
+
apiKey,
|
|
153
|
+
agentId,
|
|
154
|
+
apiBase,
|
|
155
|
+
onConnect,
|
|
156
|
+
onDisconnect,
|
|
157
|
+
onError,
|
|
158
|
+
className
|
|
159
|
+
}) {
|
|
160
|
+
const [state, setState] = useState2("idle");
|
|
161
|
+
const [session, setSession] = useState2(null);
|
|
162
|
+
const [muted, setMuted] = useState2(false);
|
|
163
|
+
const [errorMessage, setErrorMessage] = useState2();
|
|
164
|
+
const handleConnect = useCallback(async () => {
|
|
165
|
+
if (state === "connecting" || state === "connected") return;
|
|
166
|
+
setState("connecting");
|
|
167
|
+
setErrorMessage(void 0);
|
|
168
|
+
try {
|
|
169
|
+
const sess = await createWidgetSession(apiKey, agentId, apiBase);
|
|
170
|
+
setSession(sess);
|
|
171
|
+
} catch (err) {
|
|
172
|
+
setState("error");
|
|
173
|
+
if (err instanceof WidgetAPIError) {
|
|
174
|
+
setErrorMessage(err.message);
|
|
175
|
+
onError?.({ error: err.code, message: err.message });
|
|
176
|
+
} else {
|
|
177
|
+
setErrorMessage("Unable to connect.");
|
|
178
|
+
onError?.({ error: "unknown", message: "Unable to connect." });
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}, [apiKey, agentId, apiBase, state, onError]);
|
|
182
|
+
const handleDisconnect = useCallback(() => {
|
|
183
|
+
setState("disconnected");
|
|
184
|
+
setSession(null);
|
|
185
|
+
setMuted(false);
|
|
186
|
+
onDisconnect?.();
|
|
187
|
+
setTimeout(() => setState("idle"), 1500);
|
|
188
|
+
}, [onDisconnect]);
|
|
189
|
+
const handleAgentConnected = useCallback(() => {
|
|
190
|
+
setState("connected");
|
|
191
|
+
onConnect?.();
|
|
192
|
+
}, [onConnect]);
|
|
193
|
+
const handleClick = () => {
|
|
194
|
+
if (state === "connected") {
|
|
195
|
+
handleDisconnect();
|
|
196
|
+
} else if (state === "idle" || state === "error" || state === "disconnected") {
|
|
197
|
+
handleConnect();
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
const handleMuteToggle = useCallback(() => {
|
|
201
|
+
setMuted((m) => !m);
|
|
202
|
+
}, []);
|
|
203
|
+
return /* @__PURE__ */ jsxs3("div", { className: `tp-widget ${className || ""}`, children: [
|
|
204
|
+
/* @__PURE__ */ jsx4(
|
|
205
|
+
WidgetStatus,
|
|
206
|
+
{
|
|
207
|
+
state,
|
|
208
|
+
agentName: session?.agent_name || null,
|
|
209
|
+
errorMessage
|
|
210
|
+
}
|
|
211
|
+
),
|
|
212
|
+
/* @__PURE__ */ jsx4(
|
|
213
|
+
WidgetButton,
|
|
214
|
+
{
|
|
215
|
+
state,
|
|
216
|
+
muted,
|
|
217
|
+
onClick: handleClick,
|
|
218
|
+
onMuteToggle: handleMuteToggle
|
|
219
|
+
}
|
|
220
|
+
),
|
|
221
|
+
session && /* @__PURE__ */ jsx4(
|
|
222
|
+
LiveKitRoom,
|
|
223
|
+
{
|
|
224
|
+
token: session.token,
|
|
225
|
+
serverUrl: session.server_url,
|
|
226
|
+
audio: !muted,
|
|
227
|
+
video: false,
|
|
228
|
+
connect: true,
|
|
229
|
+
children: /* @__PURE__ */ jsx4(
|
|
230
|
+
AudioHandler,
|
|
231
|
+
{
|
|
232
|
+
onAgentConnected: handleAgentConnected,
|
|
233
|
+
onDisconnected: handleDisconnect
|
|
234
|
+
}
|
|
235
|
+
)
|
|
236
|
+
}
|
|
237
|
+
)
|
|
238
|
+
] });
|
|
239
|
+
}
|
|
240
|
+
export {
|
|
241
|
+
ThunderPhoneWidget
|
|
242
|
+
};
|
|
243
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/ThunderPhoneWidget.tsx","../src/AudioHandler.tsx","../src/WidgetButton.tsx","../src/WidgetStatus.tsx","../src/api.ts"],"sourcesContent":["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":["useState","jsx","useEffect","jsx","jsxs","jsx","jsxs","useState"]}
|
package/dist/mount.css
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/* src/style.css */
|
|
2
|
+
.tp-widget {
|
|
3
|
+
display: inline-flex;
|
|
4
|
+
align-items: center;
|
|
5
|
+
gap: 12px;
|
|
6
|
+
font-family:
|
|
7
|
+
-apple-system,
|
|
8
|
+
BlinkMacSystemFont,
|
|
9
|
+
"Segoe UI",
|
|
10
|
+
Roboto,
|
|
11
|
+
sans-serif;
|
|
12
|
+
}
|
|
13
|
+
.tp-status {
|
|
14
|
+
display: flex;
|
|
15
|
+
flex-direction: column;
|
|
16
|
+
gap: 2px;
|
|
17
|
+
}
|
|
18
|
+
.tp-status__name {
|
|
19
|
+
font-size: 14px;
|
|
20
|
+
font-weight: 500;
|
|
21
|
+
color: #18181b;
|
|
22
|
+
}
|
|
23
|
+
.tp-status__text {
|
|
24
|
+
font-size: 12px;
|
|
25
|
+
color: #71717a;
|
|
26
|
+
display: flex;
|
|
27
|
+
align-items: center;
|
|
28
|
+
gap: 6px;
|
|
29
|
+
}
|
|
30
|
+
.tp-status--connected {
|
|
31
|
+
color: #059669;
|
|
32
|
+
}
|
|
33
|
+
.tp-status--error {
|
|
34
|
+
color: #dc2626;
|
|
35
|
+
}
|
|
36
|
+
.tp-status__dot {
|
|
37
|
+
width: 6px;
|
|
38
|
+
height: 6px;
|
|
39
|
+
border-radius: 50%;
|
|
40
|
+
background: #059669;
|
|
41
|
+
animation: tp-pulse 2s ease-in-out infinite;
|
|
42
|
+
}
|
|
43
|
+
.tp-icon {
|
|
44
|
+
width: 20px;
|
|
45
|
+
height: 20px;
|
|
46
|
+
}
|
|
47
|
+
.tp-button-group {
|
|
48
|
+
display: inline-flex;
|
|
49
|
+
gap: 8px;
|
|
50
|
+
}
|
|
51
|
+
.tp-button {
|
|
52
|
+
display: inline-flex;
|
|
53
|
+
align-items: center;
|
|
54
|
+
justify-content: center;
|
|
55
|
+
width: 44px;
|
|
56
|
+
height: 44px;
|
|
57
|
+
border-radius: 50%;
|
|
58
|
+
border: none;
|
|
59
|
+
cursor: pointer;
|
|
60
|
+
transition: background-color 0.15s, transform 0.1s;
|
|
61
|
+
}
|
|
62
|
+
.tp-button:active {
|
|
63
|
+
transform: scale(0.95);
|
|
64
|
+
}
|
|
65
|
+
.tp-button--start {
|
|
66
|
+
background: #18181b;
|
|
67
|
+
color: white;
|
|
68
|
+
}
|
|
69
|
+
.tp-button--start:hover {
|
|
70
|
+
background: #27272a;
|
|
71
|
+
}
|
|
72
|
+
.tp-button--start:disabled {
|
|
73
|
+
opacity: 0.5;
|
|
74
|
+
cursor: not-allowed;
|
|
75
|
+
}
|
|
76
|
+
.tp-button--mute {
|
|
77
|
+
background: #f4f4f5;
|
|
78
|
+
color: #18181b;
|
|
79
|
+
}
|
|
80
|
+
.tp-button--mute:hover {
|
|
81
|
+
background: #e4e4e7;
|
|
82
|
+
}
|
|
83
|
+
.tp-button--end {
|
|
84
|
+
background: #dc2626;
|
|
85
|
+
color: white;
|
|
86
|
+
}
|
|
87
|
+
.tp-button--end:hover {
|
|
88
|
+
background: #b91c1c;
|
|
89
|
+
}
|
|
90
|
+
.tp-button--loading {
|
|
91
|
+
opacity: 0.7;
|
|
92
|
+
}
|
|
93
|
+
.tp-spin {
|
|
94
|
+
animation: tp-spin 1s linear infinite;
|
|
95
|
+
}
|
|
96
|
+
@keyframes tp-spin {
|
|
97
|
+
from {
|
|
98
|
+
transform: rotate(0deg);
|
|
99
|
+
}
|
|
100
|
+
to {
|
|
101
|
+
transform: rotate(360deg);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
@keyframes tp-pulse {
|
|
105
|
+
0%, 100% {
|
|
106
|
+
opacity: 1;
|
|
107
|
+
}
|
|
108
|
+
50% {
|
|
109
|
+
opacity: 0.5;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/*# sourceMappingURL=mount.css.map */
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/style.css"],"sourcesContent":[".tp-widget {\n display: inline-flex;\n align-items: center;\n gap: 12px;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n}\n\n.tp-status {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.tp-status__name {\n font-size: 14px;\n font-weight: 500;\n color: #18181b;\n}\n\n.tp-status__text {\n font-size: 12px;\n color: #71717a;\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.tp-status--connected {\n color: #059669;\n}\n\n.tp-status--error {\n color: #dc2626;\n}\n\n.tp-status__dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: #059669;\n animation: tp-pulse 2s ease-in-out infinite;\n}\n\n.tp-icon {\n width: 20px;\n height: 20px;\n}\n\n.tp-button-group {\n display: inline-flex;\n gap: 8px;\n}\n\n.tp-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 44px;\n height: 44px;\n border-radius: 50%;\n border: none;\n cursor: pointer;\n transition: background-color 0.15s, transform 0.1s;\n}\n\n.tp-button:active {\n transform: scale(0.95);\n}\n\n.tp-button--start {\n background: #18181b;\n color: white;\n}\n\n.tp-button--start:hover {\n background: #27272a;\n}\n\n.tp-button--start:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.tp-button--mute {\n background: #f4f4f5;\n color: #18181b;\n}\n\n.tp-button--mute:hover {\n background: #e4e4e7;\n}\n\n.tp-button--end {\n background: #dc2626;\n color: white;\n}\n\n.tp-button--end:hover {\n background: #b91c1c;\n}\n\n.tp-button--loading {\n opacity: 0.7;\n}\n\n.tp-spin {\n animation: tp-spin 1s linear infinite;\n}\n\n@keyframes tp-spin {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n}\n\n@keyframes tp-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.5; }\n}\n"],"mappings":";AAAA,CAAC;AACC,WAAS;AACT,eAAa;AACb,OAAK;AACL;AAAA,IAAa,aAAa;AAAA,IAAE,kBAAkB;AAAA,IAAE,UAAU;AAAA,IAAE,MAAM;AAAA,IAAE;AACtE;AAEA,CAAC;AACC,WAAS;AACT,kBAAgB;AAChB,OAAK;AACP;AAEA,CAAC;AACC,aAAW;AACX,eAAa;AACb,SAAO;AACT;AAEA,CAAC;AACC,aAAW;AACX,SAAO;AACP,WAAS;AACT,eAAa;AACb,OAAK;AACP;AAEA,CAAC;AACC,SAAO;AACT;AAEA,CAAC;AACC,SAAO;AACT;AAEA,CAAC;AACC,SAAO;AACP,UAAQ;AACR,iBAAe;AACf,cAAY;AACZ,aAAW,SAAS,GAAG,YAAY;AACrC;AAEA,CAAC;AACC,SAAO;AACP,UAAQ;AACV;AAEA,CAAC;AACC,WAAS;AACT,OAAK;AACP;AAEA,CAAC;AACC,WAAS;AACT,eAAa;AACb,mBAAiB;AACjB,SAAO;AACP,UAAQ;AACR,iBAAe;AACf,UAAQ;AACR,UAAQ;AACR,cAAY,iBAAiB,KAAK,EAAE,UAAU;AAChD;AAEA,CAZC,SAYS;AACR,aAAW,MAAM;AACnB;AAEA,CAAC;AACC,cAAY;AACZ,SAAO;AACT;AAEA,CALC,gBAKgB;AACf,cAAY;AACd;AAEA,CATC,gBASgB;AACf,WAAS;AACT,UAAQ;AACV;AAEA,CAAC;AACC,cAAY;AACZ,SAAO;AACT;AAEA,CALC,eAKe;AACd,cAAY;AACd;AAEA,CAAC;AACC,cAAY;AACZ,SAAO;AACT;AAEA,CALC,cAKc;AACb,cAAY;AACd;AAEA,CAAC;AACC,WAAS;AACX;AAEA,CAAC;AACC,aAAW,QAAQ,GAAG,OAAO;AAC/B;AAEA,WAJC;AAKC;AAAO,eAAW,OAAO;AAAO;AAChC;AAAK,eAAW,OAAO;AAAS;AAClC;AAEA,WA1Ea;AA2EX;AAAW,aAAS;AAAG;AACvB;AAAM,aAAS;AAAK;AACtB;","names":[]}
|