@ringg/react-native 0.0.1-alpha.9 → 0.2.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.
Files changed (118) hide show
  1. package/dist/index.d.mts +964 -0
  2. package/dist/index.d.ts +964 -0
  3. package/dist/index.js +1164 -0
  4. package/dist/index.js.map +1 -0
  5. package/dist/index.mjs +1129 -0
  6. package/dist/index.mjs.map +1 -0
  7. package/package.json +24 -66
  8. package/LICENSE +0 -21
  9. package/README.md +0 -250
  10. package/app.plugin.js +0 -51
  11. package/lib/module/adapters/index.js +0 -4
  12. package/lib/module/adapters/index.js.map +0 -1
  13. package/lib/module/adapters/livekit-rn-adapter.js +0 -216
  14. package/lib/module/adapters/livekit-rn-adapter.js.map +0 -1
  15. package/lib/module/components/RinggWidget.js +0 -554
  16. package/lib/module/components/RinggWidget.js.map +0 -1
  17. package/lib/module/components/steps/ButtonsStep.js +0 -146
  18. package/lib/module/components/steps/ButtonsStep.js.map +0 -1
  19. package/lib/module/components/steps/CalendarStep.js +0 -190
  20. package/lib/module/components/steps/CalendarStep.js.map +0 -1
  21. package/lib/module/components/steps/ConfirmationStep.js +0 -175
  22. package/lib/module/components/steps/ConfirmationStep.js.map +0 -1
  23. package/lib/module/components/steps/FormStep.js +0 -346
  24. package/lib/module/components/steps/FormStep.js.map +0 -1
  25. package/lib/module/components/steps/InteractiveFlow.js +0 -249
  26. package/lib/module/components/steps/InteractiveFlow.js.map +0 -1
  27. package/lib/module/components/ui/ActionButton.js +0 -92
  28. package/lib/module/components/ui/ActionButton.js.map +0 -1
  29. package/lib/module/components/ui/CallControls.js +0 -168
  30. package/lib/module/components/ui/CallControls.js.map +0 -1
  31. package/lib/module/components/ui/FeedbackScreen.js +0 -180
  32. package/lib/module/components/ui/FeedbackScreen.js.map +0 -1
  33. package/lib/module/components/ui/Icons.js +0 -294
  34. package/lib/module/components/ui/Icons.js.map +0 -1
  35. package/lib/module/components/ui/MessageBubble.js +0 -80
  36. package/lib/module/components/ui/MessageBubble.js.map +0 -1
  37. package/lib/module/components/ui/TabSelector.js +0 -80
  38. package/lib/module/components/ui/TabSelector.js.map +0 -1
  39. package/lib/module/components/ui/WidgetHeader.js +0 -168
  40. package/lib/module/components/ui/WidgetHeader.js.map +0 -1
  41. package/lib/module/context/RinggProvider.js +0 -134
  42. package/lib/module/context/RinggProvider.js.map +0 -1
  43. package/lib/module/context/index.js +0 -4
  44. package/lib/module/context/index.js.map +0 -1
  45. package/lib/module/globals.d.js +0 -2
  46. package/lib/module/globals.d.js.map +0 -1
  47. package/lib/module/hooks/index.js +0 -10
  48. package/lib/module/hooks/index.js.map +0 -1
  49. package/lib/module/hooks/useRinggCall.js +0 -259
  50. package/lib/module/hooks/useRinggCall.js.map +0 -1
  51. package/lib/module/hooks/useRinggChat.js +0 -141
  52. package/lib/module/hooks/useRinggChat.js.map +0 -1
  53. package/lib/module/hooks/useRinggComponents.js +0 -236
  54. package/lib/module/hooks/useRinggComponents.js.map +0 -1
  55. package/lib/module/hooks/useRinggFeedback.js +0 -66
  56. package/lib/module/hooks/useRinggFeedback.js.map +0 -1
  57. package/lib/module/hooks/useRinggTheme.js +0 -39
  58. package/lib/module/hooks/useRinggTheme.js.map +0 -1
  59. package/lib/module/hooks/useRinggTranscripts.js +0 -70
  60. package/lib/module/hooks/useRinggTranscripts.js.map +0 -1
  61. package/lib/module/hooks/useRinggWidgetState.js +0 -51
  62. package/lib/module/hooks/useRinggWidgetState.js.map +0 -1
  63. package/lib/module/index.js +0 -56
  64. package/lib/module/index.js.map +0 -1
  65. package/lib/module/package.json +0 -1
  66. package/lib/typescript/adapters/index.d.ts +0 -2
  67. package/lib/typescript/adapters/index.d.ts.map +0 -1
  68. package/lib/typescript/adapters/livekit-rn-adapter.d.ts +0 -38
  69. package/lib/typescript/adapters/livekit-rn-adapter.d.ts.map +0 -1
  70. package/lib/typescript/components/RinggWidget.d.ts +0 -44
  71. package/lib/typescript/components/RinggWidget.d.ts.map +0 -1
  72. package/lib/typescript/components/steps/ButtonsStep.d.ts +0 -17
  73. package/lib/typescript/components/steps/ButtonsStep.d.ts.map +0 -1
  74. package/lib/typescript/components/steps/CalendarStep.d.ts +0 -16
  75. package/lib/typescript/components/steps/CalendarStep.d.ts.map +0 -1
  76. package/lib/typescript/components/steps/ConfirmationStep.d.ts +0 -16
  77. package/lib/typescript/components/steps/ConfirmationStep.d.ts.map +0 -1
  78. package/lib/typescript/components/steps/FormStep.d.ts +0 -17
  79. package/lib/typescript/components/steps/FormStep.d.ts.map +0 -1
  80. package/lib/typescript/components/steps/InteractiveFlow.d.ts +0 -23
  81. package/lib/typescript/components/steps/InteractiveFlow.d.ts.map +0 -1
  82. package/lib/typescript/components/ui/ActionButton.d.ts +0 -22
  83. package/lib/typescript/components/ui/ActionButton.d.ts.map +0 -1
  84. package/lib/typescript/components/ui/CallControls.d.ts +0 -22
  85. package/lib/typescript/components/ui/CallControls.d.ts.map +0 -1
  86. package/lib/typescript/components/ui/FeedbackScreen.d.ts +0 -24
  87. package/lib/typescript/components/ui/FeedbackScreen.d.ts.map +0 -1
  88. package/lib/typescript/components/ui/Icons.d.ts +0 -28
  89. package/lib/typescript/components/ui/Icons.d.ts.map +0 -1
  90. package/lib/typescript/components/ui/MessageBubble.d.ts +0 -17
  91. package/lib/typescript/components/ui/MessageBubble.d.ts.map +0 -1
  92. package/lib/typescript/components/ui/TabSelector.d.ts +0 -21
  93. package/lib/typescript/components/ui/TabSelector.d.ts.map +0 -1
  94. package/lib/typescript/components/ui/WidgetHeader.d.ts +0 -20
  95. package/lib/typescript/components/ui/WidgetHeader.d.ts.map +0 -1
  96. package/lib/typescript/context/RinggProvider.d.ts +0 -53
  97. package/lib/typescript/context/RinggProvider.d.ts.map +0 -1
  98. package/lib/typescript/context/index.d.ts +0 -3
  99. package/lib/typescript/context/index.d.ts.map +0 -1
  100. package/lib/typescript/hooks/index.d.ts +0 -15
  101. package/lib/typescript/hooks/index.d.ts.map +0 -1
  102. package/lib/typescript/hooks/useRinggCall.d.ts +0 -41
  103. package/lib/typescript/hooks/useRinggCall.d.ts.map +0 -1
  104. package/lib/typescript/hooks/useRinggChat.d.ts +0 -29
  105. package/lib/typescript/hooks/useRinggChat.d.ts.map +0 -1
  106. package/lib/typescript/hooks/useRinggComponents.d.ts +0 -52
  107. package/lib/typescript/hooks/useRinggComponents.d.ts.map +0 -1
  108. package/lib/typescript/hooks/useRinggFeedback.d.ts +0 -23
  109. package/lib/typescript/hooks/useRinggFeedback.d.ts.map +0 -1
  110. package/lib/typescript/hooks/useRinggTheme.d.ts +0 -40
  111. package/lib/typescript/hooks/useRinggTheme.d.ts.map +0 -1
  112. package/lib/typescript/hooks/useRinggTranscripts.d.ts +0 -30
  113. package/lib/typescript/hooks/useRinggTranscripts.d.ts.map +0 -1
  114. package/lib/typescript/hooks/useRinggWidgetState.d.ts +0 -38
  115. package/lib/typescript/hooks/useRinggWidgetState.d.ts.map +0 -1
  116. package/lib/typescript/index.d.ts +0 -44
  117. package/lib/typescript/index.d.ts.map +0 -1
  118. package/lib/typescript/package.json +0 -1
package/dist/index.js ADDED
@@ -0,0 +1,1164 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ createCallbackEventBus: () => createCallbackEventBus,
24
+ createRinggWidgetController: () => createRinggWidgetController,
25
+ useRinggComponents: () => useRinggComponents,
26
+ useRinggMessages: () => useRinggMessages,
27
+ useRinggSession: () => useRinggSession,
28
+ useRinggShell: () => useRinggShell,
29
+ useRinggSlashCommands: () => useRinggSlashCommands,
30
+ useRinggTyping: () => useRinggTyping,
31
+ useStoreSnapshot: () => useStoreSnapshot
32
+ });
33
+ module.exports = __toCommonJS(src_exports);
34
+
35
+ // ../core/dist/generated/tokens.js
36
+ var DEFAULT_WIDGET_THEME = {
37
+ "primaryColor": "#0a0a0b",
38
+ "primaryTextColor": "#ffffff",
39
+ "backgroundColor": "#ffffff",
40
+ "surfaceColor": "#f3f4f6",
41
+ "agentBubbleColor": "",
42
+ "textColor": "#0a0a0b",
43
+ "mutedTextColor": "#6b7280",
44
+ "borderColor": "#e5e7eb",
45
+ "errorColor": "#ef4444",
46
+ "successColor": "#16a34a",
47
+ "buttonStyle": "pill",
48
+ "borderRadius": "16px",
49
+ "fontFamily": "inherit"
50
+ };
51
+ var TIMING = {
52
+ "chatWidgetGraceMs": 800,
53
+ "chatWidgetAfterTextMs": 120,
54
+ "minTypingIndicatorMs": 500
55
+ };
56
+ var WIDGET_DEFAULTS = {
57
+ "mode": "prod",
58
+ "title": "Ringg AI Support",
59
+ "description": "Ringg AI offers 24/7 voice support to handle your business calls efficiently and professionally",
60
+ "defaultTab": "audio",
61
+ "hideTabSelector": false,
62
+ "defaultExpanded": false,
63
+ "bypassStartScreen": false,
64
+ "bypassFeedbackScreen": false,
65
+ "voiceCallShowAnimation": false,
66
+ "voiceCallShowTranscript": true,
67
+ "notificationTuneUrl": "https://assets.ringg.ai/audios/misc/widget_notification.mp3"
68
+ };
69
+
70
+ // ../core/dist/ports/event-bus.js
71
+ function createCallbackEventBus() {
72
+ const listeners = /* @__PURE__ */ new Map();
73
+ return {
74
+ emit(event, payload) {
75
+ listeners.get(event)?.forEach((handler) => handler(payload));
76
+ },
77
+ on(event, handler) {
78
+ let set = listeners.get(event);
79
+ if (!set) {
80
+ set = /* @__PURE__ */ new Set();
81
+ listeners.set(event, set);
82
+ }
83
+ const typed = handler;
84
+ set.add(typed);
85
+ return () => {
86
+ listeners.get(event)?.delete(typed);
87
+ };
88
+ },
89
+ off(event) {
90
+ if (event) {
91
+ listeners.delete(event);
92
+ } else {
93
+ listeners.clear();
94
+ }
95
+ }
96
+ };
97
+ }
98
+
99
+ // ../core/dist/ports/clock.js
100
+ function createSystemClock() {
101
+ return {
102
+ now() {
103
+ return Date.now();
104
+ },
105
+ setTimeout(fn, ms) {
106
+ return setTimeout(fn, ms);
107
+ },
108
+ clearTimeout(handle) {
109
+ clearTimeout(handle);
110
+ }
111
+ };
112
+ }
113
+
114
+ // ../core/dist/ports/platform.js
115
+ var grantedMicPermission = {
116
+ isGranted: async () => true,
117
+ request: async () => true
118
+ };
119
+ var silentNotificationPlayer = {
120
+ play: () => {
121
+ }
122
+ };
123
+
124
+ // ../core/dist/api/client.js
125
+ function createRinggApiClient(config) {
126
+ const { backendUrl, livekitUrl } = config.urlResolver.resolve(config.mode);
127
+ const authHeaders = config.authorization ? { Authorization: config.authorization } : config.xApiKey ? { "X-API-KEY": config.xApiKey } : {};
128
+ const headers = () => ({ "Content-Type": "application/json", ...authHeaders });
129
+ return {
130
+ backendUrl,
131
+ livekitUrl,
132
+ async startWebcall(params) {
133
+ const response = await fetch(`${backendUrl}/calling/webcall`, {
134
+ method: "POST",
135
+ body: JSON.stringify({
136
+ agent_id: params.agentId,
137
+ custom_args_values: params.variables,
138
+ media_type: params.mediaType,
139
+ // Marks this call as coming from the v2 embed so the backend can use
140
+ // the newer endpoint; absent on older embeds, which fall back.
141
+ embed_version: "v2"
142
+ }),
143
+ headers: headers()
144
+ });
145
+ const responseText = await response.text();
146
+ let data;
147
+ try {
148
+ data = JSON.parse(responseText);
149
+ } catch {
150
+ throw new Error(responseText.slice(0, 200) || `Request failed with status ${response.status}`);
151
+ }
152
+ if (!response.ok) {
153
+ throw new Error(data?.message || "Failed to make call");
154
+ }
155
+ return {
156
+ call_id: data.call_id,
157
+ user_token: data.user_token,
158
+ enabled_slash_commands: data.enabled_slash_commands
159
+ };
160
+ },
161
+ async submitFeedback(callId, feedback) {
162
+ await fetch(`${backendUrl}/calling/${callId}/feedback`, {
163
+ method: "POST",
164
+ body: JSON.stringify(feedback),
165
+ headers: headers()
166
+ });
167
+ },
168
+ async callComponentApi(method, endpoint, payload) {
169
+ const url = endpoint.startsWith("http") ? endpoint : `${backendUrl}${endpoint}`;
170
+ const response = await fetch(url, {
171
+ method,
172
+ body: method === "GET" ? void 0 : JSON.stringify(payload),
173
+ headers: headers()
174
+ });
175
+ return await response.json();
176
+ }
177
+ };
178
+ }
179
+
180
+ // ../core/dist/theme/theme-engine.js
181
+ function mergeWidgetTheme(theme) {
182
+ return { ...DEFAULT_WIDGET_THEME, ...theme };
183
+ }
184
+
185
+ // ../core/dist/rpc/formatter.js
186
+ function transformRpcToComponent(componentName, rpcPayload) {
187
+ const componentId = `${componentName}_${Date.now()}`;
188
+ const config = rpcPayload.component_config ?? rpcPayload.component_data;
189
+ switch (componentName) {
190
+ case "calendar":
191
+ return transformCalendar(componentId, config, rpcPayload);
192
+ case "form":
193
+ return transformForm(componentId, config);
194
+ case "buttons":
195
+ return transformButtons(componentId, config, rpcPayload);
196
+ case "quick_replay":
197
+ return transformQuickReplay(componentId, config);
198
+ case "confirmation":
199
+ return transformConfirmation(componentId, rpcPayload);
200
+ default:
201
+ if (rpcPayload.component_type && rpcPayload.component_id) {
202
+ return rpcPayload;
203
+ }
204
+ return null;
205
+ }
206
+ }
207
+ function transformCalendar(componentId, config, rpcPayload) {
208
+ const configObj = config;
209
+ const slots = configObj?.slots || [];
210
+ const timezone = configObj?.timezone || "UTC";
211
+ return {
212
+ component_type: "calendar_booking",
213
+ component_id: componentId,
214
+ data: {
215
+ available_slots: slots.map((slot) => ({ id: slot.id, datetime: slot.datetime })),
216
+ timezone,
217
+ title: rpcPayload.title || "Select a time slot"
218
+ }
219
+ };
220
+ }
221
+ function transformForm(componentId, config) {
222
+ const configObj = config;
223
+ const transformedFields = (configObj?.fields || []).map((field) => ({
224
+ name: field.key || field.name || "",
225
+ type: mapFieldType(field.type),
226
+ label: field.description || field.label || field.key || field.name || "",
227
+ required: field.required,
228
+ placeholder: field.placeholder,
229
+ options: field.options
230
+ }));
231
+ return {
232
+ component_type: "form",
233
+ component_id: componentId,
234
+ data: {
235
+ title: configObj?.title || "Form",
236
+ description: configObj?.description,
237
+ fields: transformedFields,
238
+ submit_label: configObj?.cta || configObj?.submit_label || "Submit"
239
+ }
240
+ };
241
+ }
242
+ function transformButtons(componentId, config, rpcPayload) {
243
+ return {
244
+ component_type: "buttons",
245
+ component_id: componentId,
246
+ data: {
247
+ title: rpcPayload.title,
248
+ buttons: Array.isArray(config) ? config : []
249
+ }
250
+ };
251
+ }
252
+ function transformQuickReplay(componentId, config) {
253
+ const configObj = config;
254
+ const options = Array.isArray(configObj?.options) ? configObj.options.filter((option) => typeof option === "string" && option.trim().length > 0) : [];
255
+ return {
256
+ component_type: "buttons",
257
+ component_id: componentId,
258
+ data: {
259
+ presentation: "free",
260
+ completionDisplay: "selected_item",
261
+ buttons: options.map((option) => ({
262
+ id: option,
263
+ label: option,
264
+ action: { type: "trigger_component" }
265
+ }))
266
+ }
267
+ };
268
+ }
269
+ function transformConfirmation(componentId, rpcPayload) {
270
+ return {
271
+ component_type: "confirmation",
272
+ component_id: componentId,
273
+ data: {
274
+ title: rpcPayload.title || "Confirmed",
275
+ message: rpcPayload.message || "",
276
+ icon: rpcPayload.icon || "success",
277
+ details: rpcPayload.details
278
+ }
279
+ };
280
+ }
281
+ function mapFieldType(backendType) {
282
+ const typeMap = {
283
+ text: "text",
284
+ string: "text",
285
+ number: "number",
286
+ email: "email",
287
+ phone_number: "tel",
288
+ single_select: "select",
289
+ multiselect: "multiselect",
290
+ checkbox: "boolean",
291
+ date: "date",
292
+ textarea: "textarea"
293
+ };
294
+ return typeMap[backendType] || "text";
295
+ }
296
+ var RECEIVE_RPC_METHOD = "receive_dynamic_data";
297
+ function formatComponentResponse(componentName, componentId, responseData, originalConfig) {
298
+ let componentData;
299
+ const stepData = responseData.step_data;
300
+ if (componentName === "calendar") {
301
+ const slotId = stepData?.slot_id;
302
+ const configObj = originalConfig;
303
+ const slots = configObj?.slots || [];
304
+ const timezone = configObj?.timezone || "UTC";
305
+ const selectedSlot = slots.find((slot) => slot.id === slotId);
306
+ componentData = {
307
+ id: selectedSlot?.id || slotId,
308
+ datetime: selectedSlot?.datetime || stepData?.slot_datetime,
309
+ timezone,
310
+ slot_time: stepData?.slot_time,
311
+ slot_date: stepData?.slot_date
312
+ };
313
+ } else if (componentName === "form") {
314
+ componentData = responseData.step_data || responseData.all_data;
315
+ } else if (componentName === "buttons") {
316
+ const buttonId = stepData?.selected_button;
317
+ const buttons = Array.isArray(originalConfig) ? originalConfig : [];
318
+ const selectedButton = buttons.find((btn) => btn.id === buttonId);
319
+ componentData = selectedButton || { id: buttonId };
320
+ } else if (componentName === "quick_replay") {
321
+ componentData = { text: stepData?.selected_button };
322
+ } else {
323
+ componentData = responseData;
324
+ }
325
+ const toolId = originalConfig?.tool_id;
326
+ return {
327
+ method: RECEIVE_RPC_METHOD,
328
+ payload: JSON.stringify({
329
+ component_type: `receive_component_${componentName}`,
330
+ component_id: componentId,
331
+ component_data: componentData,
332
+ ...toolId && { tool_id: toolId }
333
+ })
334
+ };
335
+ }
336
+ function formatSlashCommand(command) {
337
+ return {
338
+ method: RECEIVE_RPC_METHOD,
339
+ payload: JSON.stringify({
340
+ component_type: "receive_component_slash_command",
341
+ command: command.replace(/^\//, "")
342
+ })
343
+ };
344
+ }
345
+ function formatBlocksAction(toolId, componentId, action) {
346
+ return {
347
+ method: RECEIVE_RPC_METHOD,
348
+ payload: JSON.stringify({
349
+ component_type: "receive_component_blocks",
350
+ component_id: componentId,
351
+ ...toolId && { tool_id: toolId },
352
+ component_data: {
353
+ action_id: action.action_id,
354
+ ...action.value !== void 0 && { value: action.value },
355
+ values: action.values
356
+ }
357
+ })
358
+ };
359
+ }
360
+ function formatDynamicData(payload) {
361
+ return {
362
+ method: RECEIVE_RPC_METHOD,
363
+ payload: JSON.stringify(payload)
364
+ };
365
+ }
366
+
367
+ // ../core/dist/stores/store.js
368
+ function createStore(build) {
369
+ const listeners = /* @__PURE__ */ new Set();
370
+ let snapshot = build();
371
+ return {
372
+ getSnapshot: () => snapshot,
373
+ subscribe(listener) {
374
+ listeners.add(listener);
375
+ return () => {
376
+ listeners.delete(listener);
377
+ };
378
+ },
379
+ commit() {
380
+ snapshot = build();
381
+ listeners.forEach((listener) => listener(snapshot));
382
+ },
383
+ dispose() {
384
+ listeners.clear();
385
+ }
386
+ };
387
+ }
388
+
389
+ // ../core/dist/stores/shell-store.js
390
+ function createShellStore(eventBus, initialCallMode = "audio", defaultExpanded = false) {
391
+ let isOpen = defaultExpanded;
392
+ let feedbackPending = false;
393
+ let currentCallId = null;
394
+ let callMode = initialCallMode;
395
+ const buildViewState = () => {
396
+ if (!isOpen)
397
+ return "closed";
398
+ return feedbackPending ? "feedback" : "open";
399
+ };
400
+ const build = () => ({ viewState: buildViewState(), currentCallId, callMode });
401
+ const handle = createStore(build);
402
+ const store = {
403
+ getSnapshot: handle.getSnapshot,
404
+ subscribe: handle.subscribe,
405
+ dispose: handle.dispose,
406
+ get isOpen() {
407
+ return isOpen;
408
+ },
409
+ get isShowingFeedback() {
410
+ return feedbackPending;
411
+ },
412
+ open() {
413
+ if (isOpen)
414
+ return;
415
+ isOpen = true;
416
+ eventBus.emit("ringg:widget_status", { status: "maximised", mode: callMode });
417
+ handle.commit();
418
+ },
419
+ minimize() {
420
+ if (defaultExpanded)
421
+ return;
422
+ if (!isOpen)
423
+ return;
424
+ isOpen = false;
425
+ eventBus.emit("ringg:widget_status", { status: "minimised", mode: callMode });
426
+ handle.commit();
427
+ },
428
+ close() {
429
+ feedbackPending = false;
430
+ currentCallId = null;
431
+ if (!defaultExpanded) {
432
+ isOpen = false;
433
+ eventBus.emit("ringg:widget_status", { status: "minimised", mode: callMode });
434
+ }
435
+ handle.commit();
436
+ },
437
+ toggle() {
438
+ if (isOpen) {
439
+ store.minimize();
440
+ } else {
441
+ store.open();
442
+ }
443
+ },
444
+ showFeedback() {
445
+ feedbackPending = true;
446
+ handle.commit();
447
+ },
448
+ setCallMode(mode) {
449
+ if (callMode === mode)
450
+ return;
451
+ callMode = mode;
452
+ handle.commit();
453
+ },
454
+ setCurrentCallId(callId) {
455
+ currentCallId = callId;
456
+ handle.commit();
457
+ }
458
+ };
459
+ return store;
460
+ }
461
+
462
+ // ../core/dist/stores/session-store.js
463
+ var NO_ERROR = { hasError: false, message: "" };
464
+ var MIC_DENIED_MESSAGE = "Microphone access was denied. Please enable it in your browser settings.";
465
+ var MEDIA_DEVICES_ERROR_MESSAGE = "Error acquiring microphone permission. Please make sure you grant the necessary permissions in your browser and reload the tab";
466
+ function createSessionStore(api, transport, mic) {
467
+ let phase = "idle";
468
+ let isLoading = false;
469
+ let connectionState = "disconnected";
470
+ let error = NO_ERROR;
471
+ const computeIsSessionLive = () => phase === "live-optimistic" || phase === "connected";
472
+ const build = () => ({
473
+ phase,
474
+ isLoading,
475
+ isSessionLive: computeIsSessionLive(),
476
+ connectionState,
477
+ error
478
+ });
479
+ const handle = createStore(build);
480
+ transport.onConnectionStateChange((state) => {
481
+ connectionState = state;
482
+ if (state === "connected" && phase !== "idle")
483
+ phase = "connected";
484
+ handle.commit();
485
+ });
486
+ transport.onMediaDevicesError(() => {
487
+ error = { hasError: true, message: MEDIA_DEVICES_ERROR_MESSAGE };
488
+ handle.commit();
489
+ });
490
+ return {
491
+ getSnapshot: handle.getSnapshot,
492
+ subscribe: handle.subscribe,
493
+ dispose: handle.dispose,
494
+ get isSessionLive() {
495
+ return computeIsSessionLive();
496
+ },
497
+ prewarm() {
498
+ void transport.prepareConnection(api.livekitUrl).catch(() => {
499
+ });
500
+ },
501
+ async start(params) {
502
+ if (params.mediaType === "audio" && !await mic.isGranted()) {
503
+ if (!await mic.request()) {
504
+ error = { hasError: true, message: MIC_DENIED_MESSAGE };
505
+ handle.commit();
506
+ return null;
507
+ }
508
+ }
509
+ isLoading = true;
510
+ phase = "starting";
511
+ handle.commit();
512
+ try {
513
+ const response = await api.startWebcall({
514
+ agentId: params.agentId,
515
+ variables: params.variables,
516
+ mediaType: params.mediaType
517
+ });
518
+ phase = "live-optimistic";
519
+ error = NO_ERROR;
520
+ handle.commit();
521
+ await transport.connect(api.livekitUrl, response.user_token);
522
+ if (params.mediaType === "audio")
523
+ await transport.setMicrophoneEnabled(true);
524
+ return {
525
+ callId: response.call_id,
526
+ slashCommands: response.enabled_slash_commands ?? []
527
+ };
528
+ } catch (err) {
529
+ phase = "idle";
530
+ error = { hasError: true, message: err.message };
531
+ handle.commit();
532
+ return null;
533
+ } finally {
534
+ isLoading = false;
535
+ handle.commit();
536
+ }
537
+ },
538
+ async end() {
539
+ isLoading = true;
540
+ handle.commit();
541
+ try {
542
+ await transport.disconnect();
543
+ } catch {
544
+ } finally {
545
+ phase = "idle";
546
+ isLoading = false;
547
+ handle.commit();
548
+ }
549
+ },
550
+ clearError() {
551
+ error = NO_ERROR;
552
+ handle.commit();
553
+ }
554
+ };
555
+ }
556
+
557
+ // ../core/dist/stores/message-store.js
558
+ var MONOTONIC_BUMP = 1e-3;
559
+ function createMessageStore(clock) {
560
+ let entries = [];
561
+ const transcripts = /* @__PURE__ */ new Map();
562
+ let pendingSourceUrl = "";
563
+ let lastTimestamp = 0;
564
+ const build = () => {
565
+ const merged = [...transcripts.values(), ...entries].sort((a, b) => a.timestamp - b.timestamp);
566
+ return { messages: merged };
567
+ };
568
+ const handle = createStore(build);
569
+ const store = {
570
+ getSnapshot: handle.getSnapshot,
571
+ subscribe: handle.subscribe,
572
+ dispose: handle.dispose,
573
+ nextTimestamp() {
574
+ const now = clock.now();
575
+ lastTimestamp = now > lastTimestamp ? now : lastTimestamp + MONOTONIC_BUMP;
576
+ return lastTimestamp;
577
+ },
578
+ addChatMessage(params) {
579
+ const sourceUrl = !params.isSelf && pendingSourceUrl ? pendingSourceUrl : void 0;
580
+ if (sourceUrl)
581
+ pendingSourceUrl = "";
582
+ const msg = {
583
+ name: params.name ?? (params.isSelf ? "You" : "Agent"),
584
+ message: params.message,
585
+ isSelf: params.isSelf,
586
+ timestamp: params.timestamp ?? store.nextTimestamp(),
587
+ ...sourceUrl && { sourceUrl }
588
+ };
589
+ entries.push(msg);
590
+ handle.commit();
591
+ return msg;
592
+ },
593
+ addComponent(component) {
594
+ const msg = {
595
+ name: "Agent",
596
+ message: "",
597
+ isSelf: false,
598
+ timestamp: store.nextTimestamp(),
599
+ componentType: component.component_type,
600
+ componentData: component
601
+ };
602
+ entries.push(msg);
603
+ handle.commit();
604
+ return msg;
605
+ },
606
+ addSystemLog(params) {
607
+ entries.push({
608
+ name: "system",
609
+ message: params.label,
610
+ isSelf: false,
611
+ timestamp: store.nextTimestamp(),
612
+ kind: "system",
613
+ ...params.meta && { systemMeta: params.meta },
614
+ systemLevel: params.level
615
+ });
616
+ handle.commit();
617
+ },
618
+ addLocalSelection(componentId, label) {
619
+ const prompt = entries.find((m) => m.componentData?.component_id === componentId);
620
+ const anchor = prompt ? prompt.timestamp + MONOTONIC_BUMP / 2 : store.nextTimestamp();
621
+ entries.push({ name: "You", isSelf: true, message: label, timestamp: anchor });
622
+ handle.commit();
623
+ },
624
+ upsertTranscription(segment) {
625
+ const existing = transcripts.get(segment.id);
626
+ transcripts.set(segment.id, {
627
+ name: segment.participantName ?? (segment.isLocal ? "You" : "Agent"),
628
+ message: segment.text,
629
+ isSelf: segment.isLocal,
630
+ timestamp: existing?.timestamp ?? store.nextTimestamp(),
631
+ ...existing?.sourceUrl && { sourceUrl: existing.sourceUrl }
632
+ });
633
+ if (pendingSourceUrl) {
634
+ let newestId;
635
+ let newest;
636
+ for (const [id, msg] of transcripts) {
637
+ if (!newest || msg.timestamp >= newest.timestamp) {
638
+ newest = msg;
639
+ newestId = id;
640
+ }
641
+ }
642
+ if (newest && newestId !== void 0 && !newest.isSelf && !newest.sourceUrl) {
643
+ transcripts.set(newestId, { ...newest, sourceUrl: pendingSourceUrl });
644
+ }
645
+ pendingSourceUrl = "";
646
+ }
647
+ handle.commit();
648
+ },
649
+ setPendingSourceUrl(url) {
650
+ pendingSourceUrl = url;
651
+ },
652
+ clearPendingSourceUrl() {
653
+ pendingSourceUrl = "";
654
+ },
655
+ reset() {
656
+ entries = [];
657
+ transcripts.clear();
658
+ pendingSourceUrl = "";
659
+ handle.commit();
660
+ }
661
+ };
662
+ return store;
663
+ }
664
+
665
+ // ../core/dist/constants.js
666
+ var CHAT_WIDGET_GRACE_MS = TIMING.chatWidgetGraceMs;
667
+ var CHAT_WIDGET_AFTER_TEXT_MS = TIMING.chatWidgetAfterTextMs;
668
+ var MIN_TYPING_INDICATOR_MS = TIMING.minTypingIndicatorMs;
669
+
670
+ // ../core/dist/stores/typing-store.js
671
+ function createTypingStore(clock) {
672
+ let shownAt = null;
673
+ let isTyping = false;
674
+ const build = () => ({ isTyping });
675
+ const handle = createStore(build);
676
+ const set = (next) => {
677
+ if (isTyping === next)
678
+ return;
679
+ isTyping = next;
680
+ handle.commit();
681
+ };
682
+ return {
683
+ getSnapshot: handle.getSnapshot,
684
+ subscribe: handle.subscribe,
685
+ dispose: handle.dispose,
686
+ showOnUserSend() {
687
+ if (shownAt === null)
688
+ shownAt = clock.now();
689
+ set(true);
690
+ },
691
+ clear() {
692
+ shownAt = null;
693
+ set(false);
694
+ },
695
+ waitMinDuration() {
696
+ if (shownAt === null)
697
+ return Promise.resolve();
698
+ const remaining = MIN_TYPING_INDICATOR_MS - (clock.now() - shownAt);
699
+ if (remaining <= 0)
700
+ return Promise.resolve();
701
+ return new Promise((resolve) => clock.setTimeout(resolve, remaining));
702
+ }
703
+ };
704
+ }
705
+
706
+ // ../core/dist/stores/component-store.js
707
+ function createComponentStore(clock, commitComponent) {
708
+ let pendingChatWidgets = [];
709
+ let graceTimer = null;
710
+ const pendingResponses = /* @__PURE__ */ new Map();
711
+ const completedFlowIds = /* @__PURE__ */ new Set();
712
+ let extensions = [];
713
+ const build = () => ({ completedFlowIds: new Set(completedFlowIds) });
714
+ const handle = createStore(build);
715
+ const store = {
716
+ getSnapshot: handle.getSnapshot,
717
+ subscribe: handle.subscribe,
718
+ dispose: handle.dispose,
719
+ add(component, mode) {
720
+ if (mode !== "text") {
721
+ commitComponent(component);
722
+ return;
723
+ }
724
+ pendingChatWidgets.push(component);
725
+ if (!graceTimer) {
726
+ graceTimer = clock.setTimeout(() => store.flush(), CHAT_WIDGET_GRACE_MS);
727
+ }
728
+ },
729
+ onAgentTextLanded() {
730
+ if (pendingChatWidgets.length > 0) {
731
+ clock.setTimeout(() => store.flush(), CHAT_WIDGET_AFTER_TEXT_MS);
732
+ }
733
+ },
734
+ flush() {
735
+ if (graceTimer) {
736
+ clock.clearTimeout(graceTimer);
737
+ graceTimer = null;
738
+ }
739
+ const queued = pendingChatWidgets;
740
+ pendingChatWidgets = [];
741
+ queued.forEach((component) => commitComponent(component));
742
+ },
743
+ // ─── Response tracking ─────────────────────────────────────────────────────
744
+ trackPendingResponse(componentId, pending) {
745
+ pendingResponses.set(componentId, pending);
746
+ },
747
+ takePendingResponse(componentId) {
748
+ const pending = pendingResponses.get(componentId);
749
+ if (pending)
750
+ pendingResponses.delete(componentId);
751
+ return pending;
752
+ },
753
+ markFlowComplete(componentId) {
754
+ completedFlowIds.add(componentId);
755
+ handle.commit();
756
+ },
757
+ // ─── Extensions ────────────────────────────────────────────────────────────
758
+ registerExtension(extension) {
759
+ extensions.push(extension);
760
+ return () => {
761
+ extensions = extensions.filter((e) => e !== extension);
762
+ };
763
+ },
764
+ runExtensions(componentName, parsed) {
765
+ return extensions.some((extension) => extension(componentName, parsed));
766
+ },
767
+ // ─── Lifecycle ─────────────────────────────────────────────────────────────
768
+ reset() {
769
+ if (graceTimer) {
770
+ clock.clearTimeout(graceTimer);
771
+ graceTimer = null;
772
+ }
773
+ pendingChatWidgets = [];
774
+ pendingResponses.clear();
775
+ completedFlowIds.clear();
776
+ handle.commit();
777
+ }
778
+ };
779
+ return store;
780
+ }
781
+
782
+ // ../core/dist/stores/slash-command-store.js
783
+ function createSlashCommandStore(configCommands = []) {
784
+ let runtimeCommands = [];
785
+ const build = () => {
786
+ const seen = new Set(configCommands.map((c) => c.command));
787
+ const merged = [...configCommands];
788
+ for (const cmd of runtimeCommands) {
789
+ if (!seen.has(cmd.command))
790
+ merged.push(cmd);
791
+ }
792
+ return { commands: merged };
793
+ };
794
+ const handle = createStore(build);
795
+ return {
796
+ getSnapshot: handle.getSnapshot,
797
+ subscribe: handle.subscribe,
798
+ dispose: handle.dispose,
799
+ setRuntimeCommands(commands) {
800
+ runtimeCommands = commands;
801
+ handle.commit();
802
+ },
803
+ reset() {
804
+ runtimeCommands = [];
805
+ handle.commit();
806
+ }
807
+ };
808
+ }
809
+
810
+ // ../core/dist/controller.js
811
+ function createRinggWidgetController(config, ports) {
812
+ const theme = mergeWidgetTheme(config.theme);
813
+ const eventBus = ports.eventBus ?? createCallbackEventBus();
814
+ const clock = ports.clock ?? createSystemClock();
815
+ const notification = ports.notification ?? silentNotificationPlayer;
816
+ const onDomAction = ports.onDomAction;
817
+ const transport = ports.transport;
818
+ const api = createRinggApiClient({
819
+ urlResolver: ports.urlResolver,
820
+ mode: config.mode ?? WIDGET_DEFAULTS.mode,
821
+ xApiKey: config.xApiKey,
822
+ authorization: config.authorization
823
+ });
824
+ const shell = createShellStore(eventBus, config.defaultTab ?? WIDGET_DEFAULTS.defaultTab, config.defaultExpanded ?? WIDGET_DEFAULTS.defaultExpanded);
825
+ const session = createSessionStore(api, transport, ports.micPermission ?? grantedMicPermission);
826
+ const messages = createMessageStore(clock);
827
+ const typing = createTypingStore(clock);
828
+ const slashCommands = createSlashCommandStore(config.enabledSlashCommands);
829
+ const components = createComponentStore(clock, (component) => commitComponent(component));
830
+ const unsubscribers = [];
831
+ let conversationEndedDispatched = false;
832
+ const eventLogsEnabled = config.eventLogs?.enabled === true;
833
+ const eventLogsShowIds = config.eventLogs?.showIds === true;
834
+ const pushSystemLog = (entry) => {
835
+ if (!eventLogsEnabled)
836
+ return;
837
+ const meta = eventLogsShowIds && entry.id ? entry.meta ? `${entry.id} \xB7 ${entry.meta}` : entry.id : entry.meta;
838
+ messages.addSystemLog({ label: entry.label, meta, level: entry.level });
839
+ };
840
+ function resetConversation() {
841
+ messages.reset();
842
+ components.reset();
843
+ }
844
+ function commitComponent(component) {
845
+ messages.addComponent(component);
846
+ eventBus.emit("ringg:calendar_booking", { status: "shown", componentId: component.component_id });
847
+ typing.clear();
848
+ if (!shell.isOpen)
849
+ notification.play();
850
+ shell.open();
851
+ }
852
+ async function handleAgentTextStream(stream) {
853
+ if (shell.getSnapshot().callMode !== "text" || !session.isSessionLive)
854
+ return;
855
+ try {
856
+ const message = await stream.readAll();
857
+ if (!stream.isAgent) {
858
+ messages.addChatMessage({ message, isSelf: true, name: "You" });
859
+ messages.clearPendingSourceUrl();
860
+ return;
861
+ }
862
+ await typing.waitMinDuration();
863
+ if (!shell.isOpen)
864
+ notification.play();
865
+ shell.open();
866
+ messages.addChatMessage({ message, isSelf: false, name: "Agent" });
867
+ typing.clear();
868
+ components.onAgentTextLanded();
869
+ } catch {
870
+ if (stream.isAgent)
871
+ typing.clear();
872
+ }
873
+ }
874
+ async function handleBlocksStream(stream) {
875
+ try {
876
+ const raw = await stream.readAll();
877
+ typing.clear();
878
+ const parsed = JSON.parse(raw);
879
+ if (parsed.component_type !== "render_component_blocks" || !Array.isArray(parsed.component_config?.blocks))
880
+ return;
881
+ components.add({
882
+ component_type: "blocks",
883
+ component_id: `blocks_${clock.now()}`,
884
+ data: parsed.component_config
885
+ }, shell.getSnapshot().callMode);
886
+ } catch {
887
+ }
888
+ }
889
+ async function handleDynamicData(invocation) {
890
+ typing.clear();
891
+ let parsed;
892
+ try {
893
+ parsed = JSON.parse(invocation.payload);
894
+ } catch {
895
+ throw new Error("Invalid send_dynamic_data payload");
896
+ }
897
+ const componentType = parsed.component_type;
898
+ if (!componentType) {
899
+ return JSON.stringify({ status: "error", message: "No component_type provided" });
900
+ }
901
+ if (componentType === "execute_dom_action") {
902
+ const cfg = parsed.component_config ?? parsed.component_data ?? {};
903
+ const action = {
904
+ id: cfg.id ?? cfg.action_id ?? "",
905
+ kind: cfg.kind ?? "trigger_event",
906
+ event_name: cfg.event_name,
907
+ default_payload: cfg.default_payload ?? cfg.detail
908
+ };
909
+ onDomAction?.(action, pushSystemLog);
910
+ return JSON.stringify({ status: "received" });
911
+ }
912
+ const componentName = componentType.replace("render_component_", "");
913
+ if (components.runExtensions(componentName, parsed)) {
914
+ return JSON.stringify({ status: "received" });
915
+ }
916
+ const component = transformRpcToComponent(componentName, parsed);
917
+ if (!component) {
918
+ return JSON.stringify({ status: "error", message: `Unknown component type: ${componentName}` });
919
+ }
920
+ components.trackPendingResponse(component.component_id, {
921
+ name: componentName,
922
+ originalConfig: parsed.component_config ?? parsed.component_data
923
+ });
924
+ components.add(component, shell.getSnapshot().callMode);
925
+ return JSON.stringify({ status: "received", component_id: component.component_id });
926
+ }
927
+ async function handleRenderComponent(invocation) {
928
+ typing.clear();
929
+ let parsed;
930
+ try {
931
+ parsed = JSON.parse(invocation.payload);
932
+ } catch {
933
+ throw new Error("Invalid component payload");
934
+ }
935
+ if (parsed.component_type && parsed.component_id) {
936
+ components.add(parsed, shell.getSnapshot().callMode);
937
+ }
938
+ return JSON.stringify({ status: "received" });
939
+ }
940
+ function wireTransport() {
941
+ transport.registerRpcMethod("send_dynamic_data", (invocation) => handleDynamicData(invocation));
942
+ transport.registerRpcMethod("render_component", (invocation) => handleRenderComponent(invocation));
943
+ transport.registerRpcMethod("send_source_urls", async (invocation) => {
944
+ let parsed;
945
+ try {
946
+ parsed = JSON.parse(invocation.payload);
947
+ } catch {
948
+ throw new Error("Invalid payload for send_source_urls");
949
+ }
950
+ if (Array.isArray(parsed.source_urls))
951
+ messages.setPendingSourceUrl(parsed.source_urls.join(", "));
952
+ else if (parsed.source_url)
953
+ messages.setPendingSourceUrl(String(parsed.source_url));
954
+ return JSON.stringify({ status: "received" });
955
+ });
956
+ transport.registerTextStreamHandler("lk.transcription", (stream) => {
957
+ void handleAgentTextStream(stream);
958
+ });
959
+ transport.registerTextStreamHandler("ringg.blocks", (stream) => {
960
+ void handleBlocksStream(stream);
961
+ });
962
+ unsubscribers.push(transport.onChatMessage((message) => {
963
+ if (message.isSelf)
964
+ return;
965
+ if (shell.getSnapshot().callMode !== "text" || !session.isSessionLive)
966
+ return;
967
+ messages.addChatMessage({ message: message.message, isSelf: false, name: message.senderName });
968
+ if (message.isAgent)
969
+ typing.clear();
970
+ }), transport.onTranscription((segment) => {
971
+ const { callMode } = shell.getSnapshot();
972
+ if (callMode !== "audio" || session.getSnapshot().connectionState !== "connected")
973
+ return;
974
+ messages.upsertTranscription(segment);
975
+ }), transport.onSessionEnded(() => {
976
+ if (session.isSessionLive)
977
+ void controller.endCall();
978
+ }));
979
+ }
980
+ const controller = {
981
+ config,
982
+ theme,
983
+ eventBus,
984
+ shell,
985
+ session,
986
+ messages,
987
+ typing,
988
+ components,
989
+ slashCommands,
990
+ // ─── Widget shell ──────────────────────────────────────────────────────────
991
+ openWidget() {
992
+ shell.open();
993
+ if (!session.isSessionLive)
994
+ session.prewarm();
995
+ },
996
+ minimizeWidget() {
997
+ shell.minimize();
998
+ },
999
+ handleTriggerClick() {
1000
+ if (config.defaultExpanded)
1001
+ return;
1002
+ const { isLoading, connectionState } = session.getSnapshot();
1003
+ if (config.bypassStartScreen && !isLoading && connectionState === "disconnected" && !session.isSessionLive) {
1004
+ controller.openWidget();
1005
+ void controller.startCall(shell.getSnapshot().callMode);
1006
+ } else if (shell.isOpen) {
1007
+ shell.minimize();
1008
+ } else {
1009
+ controller.openWidget();
1010
+ }
1011
+ },
1012
+ // ─── Call lifecycle ────────────────────────────────────────────────────────
1013
+ async startCall(mediaType) {
1014
+ shell.setCallMode(mediaType);
1015
+ resetConversation();
1016
+ const result = await session.start({
1017
+ agentId: config.agentId,
1018
+ variables: config.variables ?? {},
1019
+ mediaType
1020
+ });
1021
+ if (!result) {
1022
+ typing.clear();
1023
+ return;
1024
+ }
1025
+ shell.setCurrentCallId(result.callId);
1026
+ slashCommands.setRuntimeCommands(result.slashCommands);
1027
+ if (mediaType === "text")
1028
+ typing.showOnUserSend();
1029
+ conversationEndedDispatched = false;
1030
+ eventBus.emit("ringg:conversation_status", { status: "started", mode: mediaType, callId: result.callId });
1031
+ },
1032
+ async endCall() {
1033
+ const { currentCallId, callMode } = shell.getSnapshot();
1034
+ if (currentCallId && !conversationEndedDispatched) {
1035
+ conversationEndedDispatched = true;
1036
+ eventBus.emit("ringg:conversation_status", { status: "ended", mode: callMode, callId: currentCallId });
1037
+ }
1038
+ slashCommands.reset();
1039
+ typing.clear();
1040
+ components.reset();
1041
+ await session.end();
1042
+ if (config.bypassFeedbackScreen) {
1043
+ resetConversation();
1044
+ shell.close();
1045
+ } else {
1046
+ shell.showFeedback();
1047
+ shell.open();
1048
+ }
1049
+ },
1050
+ // ─── Chat ──────────────────────────────────────────────────────────────────
1051
+ async sendMessage(text) {
1052
+ messages.addChatMessage({ message: text, isSelf: true });
1053
+ typing.showOnUserSend();
1054
+ await transport.sendChatMessage(text);
1055
+ },
1056
+ // ─── Components ────────────────────────────────────────────────────────────
1057
+ async sendComponentResponse(componentId, responseData) {
1058
+ const pending = components.takePendingResponse(componentId);
1059
+ if (!pending)
1060
+ return;
1061
+ const { method, payload } = formatComponentResponse(pending.name, componentId, responseData, pending.originalConfig);
1062
+ await transport.performRpcToAgent(method, payload);
1063
+ eventBus.emit("ringg:component_acknowledgement", {
1064
+ componentName: pending.name,
1065
+ componentId,
1066
+ status: "responded"
1067
+ });
1068
+ },
1069
+ async sendBlocksAction(toolId, componentId, action) {
1070
+ const { method, payload } = formatBlocksAction(toolId, componentId, action);
1071
+ try {
1072
+ await transport.performRpcToAgent(method, payload);
1073
+ } catch {
1074
+ }
1075
+ },
1076
+ callComponentApi(method, endpoint, payload) {
1077
+ return api.callComponentApi(method, endpoint, payload);
1078
+ },
1079
+ async sendDynamicData(payload) {
1080
+ const { method, payload: rpcPayload } = formatDynamicData(payload);
1081
+ await transport.performRpcToAgent(method, rpcPayload);
1082
+ },
1083
+ addLocalComponent(component) {
1084
+ messages.addComponent(component);
1085
+ },
1086
+ displaySelection(componentId, label) {
1087
+ messages.addLocalSelection(componentId, label);
1088
+ },
1089
+ markFlowComplete(componentId) {
1090
+ components.markFlowComplete(componentId);
1091
+ eventBus.emit("ringg:calendar_booking", { status: "confirmed", componentId, message: "Flow completed" });
1092
+ },
1093
+ registerDynamicDataExtension(extension) {
1094
+ return components.registerExtension(extension);
1095
+ },
1096
+ // ─── Slash commands ────────────────────────────────────────────────────────
1097
+ async sendSlashCommand(command) {
1098
+ if (components.runExtensions("slash_command", { command: command.command, tool_type: command.tool_type }))
1099
+ return;
1100
+ const { method, payload } = formatSlashCommand(command.command);
1101
+ await transport.performRpcToAgent(method, payload);
1102
+ },
1103
+ // ─── Feedback ──────────────────────────────────────────────────────────────
1104
+ async submitFeedback(rating, comment) {
1105
+ const { currentCallId } = shell.getSnapshot();
1106
+ if (currentCallId) {
1107
+ try {
1108
+ await api.submitFeedback(currentCallId, { rating, comment });
1109
+ eventBus.emit("ringg:feedback_status", { status: "submitted", callId: currentCallId, rating });
1110
+ } catch {
1111
+ }
1112
+ }
1113
+ resetConversation();
1114
+ shell.close();
1115
+ },
1116
+ skipFeedback() {
1117
+ const { currentCallId } = shell.getSnapshot();
1118
+ if (currentCallId) {
1119
+ eventBus.emit("ringg:feedback_status", { status: "skipped", callId: currentCallId });
1120
+ }
1121
+ resetConversation();
1122
+ shell.close();
1123
+ },
1124
+ // ─── Teardown ──────────────────────────────────────────────────────────────
1125
+ destroy() {
1126
+ unsubscribers.forEach((unsubscribe) => unsubscribe());
1127
+ transport.unregisterRpcMethod("send_dynamic_data");
1128
+ transport.unregisterRpcMethod("render_component");
1129
+ transport.unregisterRpcMethod("send_source_urls");
1130
+ transport.unregisterTextStreamHandler("lk.transcription");
1131
+ transport.unregisterTextStreamHandler("ringg.blocks");
1132
+ void transport.disconnect();
1133
+ [shell, session, messages, typing, components, slashCommands].forEach((store) => store.dispose());
1134
+ eventBus.off();
1135
+ }
1136
+ };
1137
+ wireTransport();
1138
+ return controller;
1139
+ }
1140
+
1141
+ // ../core/dist/react/hooks.js
1142
+ var import_react = require("react");
1143
+ function useStoreSnapshot(store) {
1144
+ return (0, import_react.useSyncExternalStore)(store.subscribe, store.getSnapshot, store.getSnapshot);
1145
+ }
1146
+ var useRinggMessages = (controller) => useStoreSnapshot(controller.messages);
1147
+ var useRinggTyping = (controller) => useStoreSnapshot(controller.typing);
1148
+ var useRinggSession = (controller) => useStoreSnapshot(controller.session);
1149
+ var useRinggShell = (controller) => useStoreSnapshot(controller.shell);
1150
+ var useRinggComponents = (controller) => useStoreSnapshot(controller.components);
1151
+ var useRinggSlashCommands = (controller) => useStoreSnapshot(controller.slashCommands);
1152
+ // Annotate the CommonJS export names for ESM import in node:
1153
+ 0 && (module.exports = {
1154
+ createCallbackEventBus,
1155
+ createRinggWidgetController,
1156
+ useRinggComponents,
1157
+ useRinggMessages,
1158
+ useRinggSession,
1159
+ useRinggShell,
1160
+ useRinggSlashCommands,
1161
+ useRinggTyping,
1162
+ useStoreSnapshot
1163
+ });
1164
+ //# sourceMappingURL=index.js.map