@skippr/live-agent-sdk 0.85.0 → 0.87.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/README.md CHANGED
@@ -10,6 +10,7 @@ Embed product specialists that see, speak, and act in real time. Configure **mod
10
10
  - **Real-time voice** — two-way audio with live transcription
11
11
  - **Capture modes** — `screenshare` (user shares screen) or `auto` (DOM capture)
12
12
  - **Agent controls** — opt-in capabilities like element highlighting and on-screen actions, configured per module (auto mode only)
13
+ - **Bring your own button** — start, pause, and end sessions from your own UI with the `useLiveAgent` hook (see [Bring your own button](#bring-your-own-button))
13
14
  - **Chat + transcript** — text messaging with voice transcripts merged into one thread
14
15
  - **Session agenda** — structured phases with progress tracking
15
16
  - **Flexible auth** — email OTP (direct auth) or backend-signed JWT (secret mode)
@@ -195,6 +196,41 @@ function App() {
195
196
  }
196
197
  ```
197
198
 
199
+ ### Bring your own button
200
+
201
+ You can bring your own widget or button and start, pause, and end sessions from it — no need to rely on the built-in launcher:
202
+
203
+ ```tsx
204
+ import { LiveAgent, useLiveAgent } from '@skippr/live-agent-sdk';
205
+
206
+ function TalkToExpertButton() {
207
+ const { isConnected, isStarting, displayModules, selectModule, disconnect } = useLiveAgent();
208
+
209
+ if (isConnected) {
210
+ return <button onClick={() => disconnect()}>End session</button>;
211
+ }
212
+
213
+ const module = displayModules[0];
214
+ return (
215
+ <button disabled={!module || isStarting} onClick={() => module && selectModule(module.id)}>
216
+ {isStarting ? 'Connecting…' : 'Talk to an expert'}
217
+ </button>
218
+ );
219
+ }
220
+
221
+ function App() {
222
+ return (
223
+ <LiveAgent appKey="pk_live_your_key">
224
+ <TalkToExpertButton />
225
+ </LiveAgent>
226
+ );
227
+ }
228
+ ```
229
+
230
+ - **Start** — call `selectModule(id)` with your agent's id (the same id you'd pass as `agentId`). Running multiple agents? Pick one from `displayModules`. If the user has a paused session with that agent, it resumes automatically.
231
+ - **Close** — call `disconnect()` to end the current session.
232
+ - **Show the right label** — use `isConnected` and `isStarting` to switch your button between start, connecting, and end states.
233
+
198
234
  ## API Reference
199
235
 
200
236
  ### `<LiveAgent>`
@@ -231,16 +267,14 @@ Hook for accessing session state and panel controls. Must be called within `<Liv
231
267
  | `isDisconnecting` | `boolean` | Whether the session is being torn down |
232
268
  | `isPausing` | `boolean` | Whether a pause request is in flight (before `isPaused` commits) |
233
269
  | `isPaused` | `boolean` | Whether the current session is paused |
234
- | `resumableSession` | `{ id: string } \| null` | The pinned agent's most recent paused session, or `null` |
235
- | `resumableSessions` | `{ id: string; agentId: string }[]` | All of the user's paused sessions (one per agent), for module pickers |
270
+ | `resumableSession` | `{ id: string; agentId: string } \| null` | The operating module's paused session, or `null` |
236
271
  | `isPanelOpen` | `boolean` | Whether the panel is currently open |
237
272
  | `isMinimized` | `boolean` | Whether the widget is minimized |
238
273
  | `isAuthenticated` | `boolean` | Whether the user is authenticated |
239
274
  | `variant` | `'floating' \| 'sidebar'` | Current widget display mode |
240
275
  | `position` | `'left' \| 'right'` | Current widget position |
241
276
  | `error` | `string` | Error message, if any |
242
- | `hasModuleSelector` | `boolean` | Whether the widget is in picker mode |
243
- | `availableModules` | `Module[]` | Modules fetched for the picker |
277
+ | `displayModules` | `Module[]` | The user's active modules, for pickers and custom start buttons |
244
278
  | `activeModule` | `Module \| null` | The module the current session was started with |
245
279
  | `isLoadingModules` | `boolean` | Whether the picker list is being fetched |
246
280
  | `modulesError` | `string \| null` | Error from fetching the picker list, if any |
@@ -3125,6 +3125,7 @@ function DomCapture({ pushOrTapMicMode = false }) {
3125
3125
  let cancelled = false;
3126
3126
  let snapshotInFlight = false;
3127
3127
  let a11yPublishInFlight = false;
3128
+ let a11yPublishPending = false;
3128
3129
  let consecutiveCaptureFailures = 0;
3129
3130
  if (!pushOrTapMicMode && !localParticipant.isMicrophoneEnabled) {
3130
3131
  localParticipant.setMicrophoneEnabled(true).catch((error) => console.error("Failed to enable microphone:", error));
@@ -3162,8 +3163,12 @@ function DomCapture({ pushOrTapMicMode = false }) {
3162
3163
  }
3163
3164
  };
3164
3165
  const tickA11yPublish = async () => {
3165
- if (cancelled || a11yPublishInFlight || pausedRef.current)
3166
+ if (cancelled || pausedRef.current)
3166
3167
  return;
3168
+ if (a11yPublishInFlight) {
3169
+ a11yPublishPending = true;
3170
+ return;
3171
+ }
3167
3172
  a11yPublishInFlight = true;
3168
3173
  try {
3169
3174
  const frame = await buildDomSnapshotFrame();
@@ -3175,6 +3180,10 @@ function DomCapture({ pushOrTapMicMode = false }) {
3175
3180
  });
3176
3181
  } catch {} finally {
3177
3182
  a11yPublishInFlight = false;
3183
+ if (a11yPublishPending && !cancelled) {
3184
+ a11yPublishPending = false;
3185
+ tickA11yPublish();
3186
+ }
3178
3187
  }
3179
3188
  };
3180
3189
  const cleanupDomEventListeners = installDomEventListeners(localParticipant, {