@alxxsck/ai-assistant 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +345 -0
- package/dist/App.d.ts +26 -0
- package/dist/Avatar-XISRbl7X.js +29627 -0
- package/dist/OrbitControls-XelJ40xM.js +458 -0
- package/dist/api/messages.api.d.ts +20 -0
- package/dist/api/messages.api.types.d.ts +88 -0
- package/dist/api/messages.api.utils.d.ts +2 -0
- package/dist/api/voice.api.d.ts +65 -0
- package/dist/api/voice.api.types.d.ts +37 -0
- package/dist/avatar/AnimationManager.d.ts +39 -0
- package/dist/avatar/Avatar.d.ts +69 -0
- package/dist/avatar/BlendShapeScenario.d.ts +19 -0
- package/dist/bridge-ai-chat-widget.es.js +10194 -0
- package/dist/constants.d.ts +2 -0
- package/dist/context/ChatWidgetContext.d.ts +32 -0
- package/dist/domain/command/CommandRuntime.d.ts +50 -0
- package/dist/domain/command/command.types.d.ts +70 -0
- package/dist/domain/message/cable/AnyCable.service.d.ts +34 -0
- package/dist/domain/message/cable/CableContext.d.ts +17 -0
- package/dist/domain/message/cable/CableContext.types.d.ts +19 -0
- package/dist/domain/message/cable/WidgetChat.channel.d.ts +4 -0
- package/dist/domain/message/cable/widget-message.d.ts +3 -0
- package/dist/domain/message/message.store.d.ts +51 -0
- package/dist/domain/scenario/scenario-choice.d.ts +17 -0
- package/dist/domain/ui/ui.service.d.ts +7 -0
- package/dist/domain/voice/voice.store.d.ts +69 -0
- package/dist/hooks/useDeviceManager.d.ts +25 -0
- package/dist/hooks/useSessionExpiryChecks.d.ts +3 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.types.d.ts +50 -0
- package/dist/lib/isSessionExpired.d.ts +2 -0
- package/dist/lib/logger.d.ts +12 -0
- package/dist/lib/retrieveContent.d.ts +6 -0
- package/dist/lib/utils.d.ts +2 -0
- package/dist/lib//321/201ontentRenderer.d.ts +6 -0
- package/dist/lil-gui.esm-BicvE3D7.js +1263 -0
- package/dist/react/mount.d.ts +3 -0
- package/dist/types/index.d.ts +9 -0
- package/dist/ui/chat-routes.d.ts +11 -0
- package/dist/ui/components/ChatFooter.d.ts +2 -0
- package/dist/ui/components/ChatListItem.d.ts +10 -0
- package/dist/ui/components/ChatListPage.d.ts +1 -0
- package/dist/ui/components/ChatMessage.d.ts +8 -0
- package/dist/ui/components/ChatTextMode.d.ts +1 -0
- package/dist/ui/components/ChatTopControls.d.ts +1 -0
- package/dist/ui/components/ChatTypingIndicator.d.ts +4 -0
- package/dist/ui/components/ChatUnreadIndicator.d.ts +1 -0
- package/dist/ui/components/ChatVoiceMode.d.ts +1 -0
- package/dist/ui/components/ChatWidget.d.ts +4 -0
- package/dist/ui/components/ErrorMessage.d.ts +7 -0
- package/dist/ui/components/VoiceModeMessage.d.ts +12 -0
- package/dist/ui/components/overlays/ChatBlurOverlayBottom.d.ts +1 -0
- package/dist/ui/components/overlays/ChatBlurOverlayTop.d.ts +1 -0
- package/dist/ui/components/overlays/ChatDarkOverlay.d.ts +1 -0
- package/dist/ui/error-messages.d.ts +7 -0
- package/package.json +102 -0
package/README.md
ADDED
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
# Bridge AI Chat Widget
|
|
2
|
+
|
|
3
|
+
Embeddable React + TypeScript chat widget for Bridge AI agents, with:
|
|
4
|
+
|
|
5
|
+
- text chat
|
|
6
|
+
- optional real-time voice mode (WebRTC)
|
|
7
|
+
- optional animated 3D avatar (three.js)
|
|
8
|
+
- WebSocket live message updates (AnyCable)
|
|
9
|
+
- Shadow DOM isolation for host-page style safety
|
|
10
|
+
|
|
11
|
+
## What This Project Is
|
|
12
|
+
|
|
13
|
+
`bridgeapp-ai-chat-widget` is an ESM library bundle that host applications instantiate via:
|
|
14
|
+
|
|
15
|
+
- `new ChatWidgetInstance(config)`
|
|
16
|
+
|
|
17
|
+
The instance mounts a React app into a host element's Shadow DOM, fetches static asset mappings from a remote manifest, initializes chat/voice/avatar modules, and exposes a small integration API (`setOpen`, `destroy`, `setCustomerInteractionSession`, events).
|
|
18
|
+
|
|
19
|
+
## Tech Stack
|
|
20
|
+
|
|
21
|
+
- React 19
|
|
22
|
+
- TypeScript
|
|
23
|
+
- Vite (dev app + library build)
|
|
24
|
+
- Tailwind CSS 4
|
|
25
|
+
- Zustand (state stores)
|
|
26
|
+
- three.js + FBX assets (avatar rendering/animation)
|
|
27
|
+
- AnyCable Web client (live chat transport)
|
|
28
|
+
- WebRTC + data channels (voice mode)
|
|
29
|
+
|
|
30
|
+
## Repository Layout
|
|
31
|
+
|
|
32
|
+
High-level structure:
|
|
33
|
+
|
|
34
|
+
- `src/index.ts` - public library entry (`ChatWidgetInstance`)
|
|
35
|
+
- `src/index.types.ts` - public config/types surface
|
|
36
|
+
- `src/App.tsx` - provider composition root
|
|
37
|
+
- `src/react/mount.tsx` - React root mount helper
|
|
38
|
+
- `src/ui/components` - UI components (chat widget, routes, controls)
|
|
39
|
+
- `src/domain/message` - message store + websocket channel integration
|
|
40
|
+
- `src/domain/voice` - voice lifecycle + WebRTC state
|
|
41
|
+
- `src/avatar` - three.js avatar scene, animation manager, blend shapes
|
|
42
|
+
- `src/api` - HTTP clients for messages/voice/session-related calls
|
|
43
|
+
- `src/context/ChatWidgetContext.tsx` - widget-level UI state/context
|
|
44
|
+
- `src/dev` - local/dev/demo stand bootstrap (non-library integration path)
|
|
45
|
+
- `src/assets` - local assets used by the widget and avatar (not used directly, rather this whole folder is getting pushed to assets repo)
|
|
46
|
+
- `scripts` - utility scripts (for example asset sync to remote static repo)
|
|
47
|
+
|
|
48
|
+
## Runtime Architecture
|
|
49
|
+
|
|
50
|
+
### 1) Host Integration Layer
|
|
51
|
+
|
|
52
|
+
`ChatWidgetInstance` (`src/index.ts`) is the host-facing API:
|
|
53
|
+
|
|
54
|
+
- validates `mountElementId`
|
|
55
|
+
- creates `shadowRoot` on mount element
|
|
56
|
+
- injects bundled CSS into shadow root
|
|
57
|
+
- fetches `${ASSETS_URL}/manifest.json`
|
|
58
|
+
- resolves hashed asset paths via `resolveAssetPath(path)`
|
|
59
|
+
- optionally merges remote config (`remoteConfig`)
|
|
60
|
+
- mounts React app and wires `AppApi` callbacks/events
|
|
61
|
+
|
|
62
|
+
### 2) React App + Providers
|
|
63
|
+
|
|
64
|
+
`App` composes:
|
|
65
|
+
|
|
66
|
+
- `ChatWidgetContextProvider` (widget open state, sizing, config, loading progress)
|
|
67
|
+
- `CableProvider` (AnyCable websocket service + network status)
|
|
68
|
+
- `HashRouter` (voice/text/list routes)
|
|
69
|
+
|
|
70
|
+
### 3) Messaging Domain
|
|
71
|
+
|
|
72
|
+
`useMessageStore` handles:
|
|
73
|
+
|
|
74
|
+
- initial thread bootstrap (`ListMessages`, `ListMessagesForThreads`)
|
|
75
|
+
- optimistic and server-driven message updates
|
|
76
|
+
- thread map and per-thread message derivation
|
|
77
|
+
- message send (`CreateMessage`)
|
|
78
|
+
- agentic steps merge/update and rendering support
|
|
79
|
+
|
|
80
|
+
Live updates arrive via AnyCable channel events and are pushed into the store.
|
|
81
|
+
|
|
82
|
+
### 4) Voice Domain
|
|
83
|
+
|
|
84
|
+
`useVoiceStore` manages full voice call lifecycle:
|
|
85
|
+
|
|
86
|
+
- starts voice session via API (`StartVoiceSession`)
|
|
87
|
+
- builds RTCPeerConnection using TURN credentials from session payload
|
|
88
|
+
- sends SDP offer / receives answer
|
|
89
|
+
- buffers + sends ICE candidates
|
|
90
|
+
- consumes voice data channels (`voice-events`, `voice-input`)
|
|
91
|
+
- streams remote audio to an `Audio` element
|
|
92
|
+
- handles mic enable/disable, device switching, and teardown (`EndSession` + `EndVoiceSession`)
|
|
93
|
+
|
|
94
|
+
### 5) Avatar Domain
|
|
95
|
+
|
|
96
|
+
`Avatar` + `AnimationManager`:
|
|
97
|
+
|
|
98
|
+
- initializes three.js renderer/camera/lights/materials
|
|
99
|
+
- loads model + texture + animation assets from resolved remote paths
|
|
100
|
+
- emits `loading_progress`
|
|
101
|
+
- starts animation mixer loops
|
|
102
|
+
- maps viseme timeline to morph target animation during speech
|
|
103
|
+
- exposes external animation actions (`excited`, `fix_hair`, `spin`, `kiss`)
|
|
104
|
+
|
|
105
|
+
When all assets load, widget emits `avatar_ready` through `ChatWidgetInstance`.
|
|
106
|
+
|
|
107
|
+
## Public API
|
|
108
|
+
|
|
109
|
+
Import:
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
import {
|
|
113
|
+
ChatWidgetInstance,
|
|
114
|
+
type WidgetConfig,
|
|
115
|
+
} from "bridgeapp-ai-chat-widget";
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### `ExternalWidgetConfig`
|
|
119
|
+
|
|
120
|
+
- `mountElementId: string`
|
|
121
|
+
- - `configName: string` - remote config, lives at the same place as other static assets
|
|
122
|
+
- `customerInteractionSession?: { accessToken; chatId; expiresAt }` - if there's no session (separate case for dev/demo mode) - we look for customerId in remote config and create session ourselves.
|
|
123
|
+
|
|
124
|
+
### `RemoteWidgetConfig`
|
|
125
|
+
|
|
126
|
+
Actual widget config
|
|
127
|
+
|
|
128
|
+
- `agentId: string`
|
|
129
|
+
- `customerId?: string`
|
|
130
|
+
- `wsUrl: string`
|
|
131
|
+
- `apiUrl: string`
|
|
132
|
+
- `AIAvatar?: boolean` - is 3D avatar be used
|
|
133
|
+
- `voiceEnabled?: boolean` - is voice mode enebled
|
|
134
|
+
- `background?: boolean` - clear or visible background
|
|
135
|
+
- `footer?: boolean` - is footer rendered
|
|
136
|
+
- `debug?: boolean` (avatar debug GUI)
|
|
137
|
+
- `manualOpen?: boolean` - is widget trigger rendered or should it be open only programatically
|
|
138
|
+
|
|
139
|
+
### Instance Methods
|
|
140
|
+
|
|
141
|
+
- `setOpen(open: boolean): void` - open/close widget
|
|
142
|
+
- `setCustomerInteractionSession(session): void` - update session after initialization (on session expire)
|
|
143
|
+
- `playAnimation(name): void`
|
|
144
|
+
- `destroy(): void`
|
|
145
|
+
|
|
146
|
+
### Events
|
|
147
|
+
|
|
148
|
+
- `avatar_ready` - emitted once avatar assets are fully loaded
|
|
149
|
+
- `renew_session` - emitted when session is expired or near expiry
|
|
150
|
+
|
|
151
|
+
## Host Usage Example
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
import { ChatWidgetInstance } from "bridgeapp-ai-chat-widget";
|
|
155
|
+
|
|
156
|
+
const widget = new ChatWidgetInstance({
|
|
157
|
+
mountElementId: "chat-widget-root",
|
|
158
|
+
customerInteractionSession: CustomerInteractionSession,
|
|
159
|
+
AIAvatar: true,
|
|
160
|
+
background: false,
|
|
161
|
+
voiceEnabled: true,
|
|
162
|
+
footer: false,
|
|
163
|
+
manualOpen: true,
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
widget.addEventListener("avatar_ready", () => {
|
|
167
|
+
widget.setOpen(true);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
widget.addEventListener("renew_session", async () => {
|
|
171
|
+
const newSession = await fetchNewSessionSomehow();
|
|
172
|
+
widget.setCustomerInteractionSession(newSession);
|
|
173
|
+
});
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Teardown:
|
|
177
|
+
|
|
178
|
+
```ts
|
|
179
|
+
widget.destroy();
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Local Development
|
|
183
|
+
|
|
184
|
+
Install:
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
npm install
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Start dev server:
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
npm run dev
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Notes:
|
|
197
|
+
|
|
198
|
+
- Vite dev server runs on `5179` by default.
|
|
199
|
+
- `src/dev/main.tsx` is the local bootstrap path (creates session and mounts widget directly).
|
|
200
|
+
- `vite.config.ts` is for app/dev runtime.
|
|
201
|
+
- `vite.config.lib.ts` is for distributable library build.
|
|
202
|
+
|
|
203
|
+
## Build, Publish, and Deployment
|
|
204
|
+
|
|
205
|
+
### Build local dev app
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
npm run build-dev
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Build library artifacts
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
npm run build-lib
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
Outputs include:
|
|
218
|
+
|
|
219
|
+
- `dist/bridge-ai-chat-widget.es.js`
|
|
220
|
+
- `dist/index.types.d.ts`
|
|
221
|
+
|
|
222
|
+
### Publish flow
|
|
223
|
+
|
|
224
|
+
- `prepublishOnly` runs `build-lib`
|
|
225
|
+
- package exports are defined in `package.json` under `"exports"`
|
|
226
|
+
|
|
227
|
+
### Other project scripts
|
|
228
|
+
|
|
229
|
+
- `npm run lint`
|
|
230
|
+
- `npm run lint-fix`
|
|
231
|
+
- `npm run format`
|
|
232
|
+
- `npm run format:check`
|
|
233
|
+
- deployment scripts for S3 buckets (project-specific, to be reworked)
|
|
234
|
+
- `npm run push-assets-metamediastatic` (sync `src/assets` into remote static assets repo)
|
|
235
|
+
|
|
236
|
+
## Session Handling
|
|
237
|
+
|
|
238
|
+
Widget relies on `customerInteractionSession`:
|
|
239
|
+
|
|
240
|
+
- used as Bearer token for HTTP APIs
|
|
241
|
+
- used in websocket query for AnyCable auth
|
|
242
|
+
- periodically checked by `useSessionExpiryChecks`
|
|
243
|
+
|
|
244
|
+
When session is near expiry or invalid, `renew_session` event is emitted; host app must provide a fresh session via `setCustomerInteractionSession`.
|
|
245
|
+
|
|
246
|
+
## Lola Backend conversations
|
|
247
|
+
|
|
248
|
+
The widget uses Lola's canonical conversation API. Interaction `chatId` remains
|
|
249
|
+
the authenticated session identifier; chat history is keyed independently by
|
|
250
|
+
`conversationId` (`threadId` in realtime compatibility messages).
|
|
251
|
+
|
|
252
|
+
- `GET /chat/conversations` loads the open chat list.
|
|
253
|
+
- `GET /chat/conversations/:conversationId/messages` loads one history.
|
|
254
|
+
- `POST /chat/conversations` creates and selects a new chat.
|
|
255
|
+
- `POST /chat/conversations/:conversationId/select` selects an existing chat
|
|
256
|
+
for the current interaction session.
|
|
257
|
+
- `POST /chat/messages` sends `conversationId` and adopts the canonical
|
|
258
|
+
`conversationId` returned by the backend when a chat is resolved or created.
|
|
259
|
+
|
|
260
|
+
A `409` response means that the selected conversation was closed. It is shown
|
|
261
|
+
as a chat error and the open conversation list is refreshed; only `401`
|
|
262
|
+
requests trigger interaction-session renewal.
|
|
263
|
+
|
|
264
|
+
## Lola Backend command runtime
|
|
265
|
+
|
|
266
|
+
The widget consumes Lola Backend `command.created` envelopes from the AnyCable
|
|
267
|
+
`commandCreated`/`uiCommand` frame, acknowledges `received` and a final result, and
|
|
268
|
+
deduplicates `commandId` during reconnect replay. Updating the interaction session
|
|
269
|
+
now reconnects REST and AnyCable without remounting the widget.
|
|
270
|
+
|
|
271
|
+
Host-owned pages, modals, and elements are registered explicitly. Backend payloads
|
|
272
|
+
are never evaluated as JavaScript or used as arbitrary selectors:
|
|
273
|
+
|
|
274
|
+
```ts
|
|
275
|
+
const widget = new ChatWidgetInstance({
|
|
276
|
+
// Existing widget options...
|
|
277
|
+
customerInteractionSession: {
|
|
278
|
+
accessToken,
|
|
279
|
+
chatId: sessionId,
|
|
280
|
+
sessionId,
|
|
281
|
+
expiresAt,
|
|
282
|
+
},
|
|
283
|
+
uiTargets: {
|
|
284
|
+
deposit_button: {
|
|
285
|
+
kind: "button",
|
|
286
|
+
selector: '[data-lola-action="deposit"]',
|
|
287
|
+
},
|
|
288
|
+
deposit_modal: {
|
|
289
|
+
kind: "modal",
|
|
290
|
+
query: { lola_modal: "deposit" },
|
|
291
|
+
},
|
|
292
|
+
account_page: {
|
|
293
|
+
kind: "page",
|
|
294
|
+
route: "/account",
|
|
295
|
+
query: { source: "lola" },
|
|
296
|
+
},
|
|
297
|
+
},
|
|
298
|
+
handlers: {
|
|
299
|
+
openModal: ({ modalCode, payload }) =>
|
|
300
|
+
modalRegistry.open(modalCode!, payload),
|
|
301
|
+
openPage: ({ url }) => router.push(url!),
|
|
302
|
+
},
|
|
303
|
+
});
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
Targets and handlers can also be registered after construction with
|
|
307
|
+
`registerUiTarget(code, target)` and `registerCommandHandler(name, handler)`.
|
|
308
|
+
Both return an unregister callback.
|
|
309
|
+
|
|
310
|
+
Built-in handlers cover assistant/chat visibility, supported avatar animations,
|
|
311
|
+
CTA rendering inside the text chat, and element highlighting. Highlight targets
|
|
312
|
+
fall back to `[data-lola="code"]` or `[data-lola-action="code"]`. The default
|
|
313
|
+
highlight is a fixed overlay attached to `document.body`, so it is not clipped
|
|
314
|
+
by an element's `overflow` or container bounds and follows resize and scroll. A registered page
|
|
315
|
+
URL fallback uses `history.pushState` and emits `popstate`, so an explicitly
|
|
316
|
+
configured query such as `?lola_modal=deposit` can be handled without reloading the
|
|
317
|
+
host SPA. A registered modal selector can also open a native `HTMLDialogElement`.
|
|
318
|
+
Commands are normalized across the current backend payload (`modalId`, `pageId`,
|
|
319
|
+
`target`) and the canonical Lola contract (`data.target.code`, `modalCode`,
|
|
320
|
+
`pageCode`). If no safe handler/fallback exists, the widget sends
|
|
321
|
+
`unsupported` and emits `command_unsupported`; other lifecycle events are
|
|
322
|
+
`command_received`, `command_succeeded`, `command_failed`, and `command_expired`.
|
|
323
|
+
|
|
324
|
+
## Asset Pipeline
|
|
325
|
+
|
|
326
|
+
Runtime asset resolution:
|
|
327
|
+
|
|
328
|
+
1. fetch remote manifest from `https://metamediastatic.com/manifest.json`
|
|
329
|
+
2. map logical path (for example `animations/idle_1.fbx`) to hashed URL
|
|
330
|
+
3. load via `resolveAssetPath`
|
|
331
|
+
|
|
332
|
+
This allows cache-friendly hashed asset delivery without changing code references.
|
|
333
|
+
|
|
334
|
+
## Troubleshooting
|
|
335
|
+
|
|
336
|
+
- Widget does not mount:
|
|
337
|
+
- verify mount element exists and `mountElementId` is correct
|
|
338
|
+
- No messages or send failures:
|
|
339
|
+
- verify `apiUrl`, `accessToken`, and `chatId`
|
|
340
|
+
- No live updates:
|
|
341
|
+
- verify `wsUrl`, websocket reachability, and token validity
|
|
342
|
+
- Voice fails to start:
|
|
343
|
+
- verify mic permission, TURN reachability, and voice API endpoints
|
|
344
|
+
- Avatar never becomes ready:
|
|
345
|
+
- verify manifest URL + asset availability under static host
|
package/dist/App.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { OnAvatarReady } from "./types";
|
|
2
|
+
import type { ExternallyAvailableActions } from "./avatar/Avatar";
|
|
3
|
+
import type { WidgetConfig } from "./index.types";
|
|
4
|
+
import type { CustomerInteractionSession } from "./types";
|
|
5
|
+
import type { CommandRuntime } from "./domain/command/CommandRuntime";
|
|
6
|
+
export type AppApi = {
|
|
7
|
+
onAvatarReady: OnAvatarReady;
|
|
8
|
+
onSessionExpired: () => void;
|
|
9
|
+
registerSetOpen: (fn: ((open: boolean) => void) | null) => void;
|
|
10
|
+
registerExecuteReaction: (fn: ((params: {
|
|
11
|
+
animation: string;
|
|
12
|
+
voice: string;
|
|
13
|
+
text: string;
|
|
14
|
+
}) => void) | null) => void;
|
|
15
|
+
resolveAssetPath: (path: string) => string;
|
|
16
|
+
registerSendServiceMessage: (fn: ((message: string, animationName: ExternallyAvailableActions) => void) | null) => void;
|
|
17
|
+
registerSetLocale: (fn: ((locale: string) => void) | null) => void;
|
|
18
|
+
registerSetSession: (fn: ((session: CustomerInteractionSession) => void) | null) => void;
|
|
19
|
+
commandRuntime: CommandRuntime;
|
|
20
|
+
};
|
|
21
|
+
type AppProps = {
|
|
22
|
+
config: WidgetConfig;
|
|
23
|
+
api: AppApi;
|
|
24
|
+
};
|
|
25
|
+
export declare function App({ config, api }: AppProps): import("react/jsx-runtime").JSX.Element;
|
|
26
|
+
export default App;
|