@ringg/react-native 0.3.0 → 0.4.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 +90 -80
- package/app.plugin.js +73 -0
- package/dist/index.d.mts +267 -2
- package/dist/index.d.ts +267 -2
- package/dist/index.js +825 -412
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +797 -431
- package/dist/index.mjs.map +1 -1
- package/package.json +12 -3
package/dist/index.js
CHANGED
|
@@ -30,26 +30,73 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var src_exports = {};
|
|
32
32
|
__export(src_exports, {
|
|
33
|
+
CHAT_WIDGET_AFTER_TEXT_MS: () => CHAT_WIDGET_AFTER_TEXT_MS,
|
|
34
|
+
CHAT_WIDGET_GRACE_MS: () => CHAT_WIDGET_GRACE_MS,
|
|
35
|
+
DEFAULT_CONFIG: () => WIDGET_DEFAULTS,
|
|
36
|
+
DEFAULT_ENVIRONMENT_URLS: () => DEFAULT_ENVIRONMENT_URLS,
|
|
37
|
+
DEFAULT_WIDGET_THEME: () => DEFAULT_WIDGET_THEME,
|
|
33
38
|
GradientFill: () => GradientFill,
|
|
39
|
+
MIN_TYPING_INDICATOR_MS: () => MIN_TYPING_INDICATOR_MS,
|
|
34
40
|
Markdown: () => Markdown,
|
|
35
41
|
RinggWidget: () => RinggWidget,
|
|
42
|
+
WIDGET_PRESET_THEMES: () => WIDGET_PRESET_THEMES,
|
|
36
43
|
WidgetThemeProvider: () => WidgetThemeProvider,
|
|
37
44
|
appOrigin: () => appOrigin,
|
|
45
|
+
buildPayload: () => buildPayload,
|
|
46
|
+
colorToBackground: () => colorToBackground,
|
|
47
|
+
colorToBorder: () => colorToBorder,
|
|
48
|
+
colorToSolid: () => colorToSolid,
|
|
38
49
|
createAudioSession: () => createAudioSession,
|
|
39
50
|
createCallbackEventBus: () => createCallbackEventBus,
|
|
51
|
+
createComponentStore: () => createComponentStore,
|
|
52
|
+
createDomEventBus: () => createDomEventBus,
|
|
40
53
|
createHostActionDispatcher: () => createHostActionDispatcher,
|
|
41
54
|
createLiveKitTransport: () => createLiveKitTransport,
|
|
55
|
+
createMessageStore: () => createMessageStore,
|
|
42
56
|
createNativeMicPermission: () => createNativeMicPermission,
|
|
43
57
|
createNotificationPlayer: () => createNotificationPlayer,
|
|
58
|
+
createRinggApiClient: () => createRinggApiClient,
|
|
44
59
|
createRinggWidgetController: () => createRinggWidgetController,
|
|
60
|
+
createSessionStore: () => createSessionStore,
|
|
61
|
+
createShellStore: () => createShellStore,
|
|
45
62
|
createSilentNotificationPlayer: () => createSilentNotificationPlayer,
|
|
63
|
+
createSlashCommandStore: () => createSlashCommandStore,
|
|
64
|
+
createStaticUrlResolver: () => createStaticUrlResolver,
|
|
65
|
+
createStore: () => createStore,
|
|
66
|
+
createSystemClock: () => createSystemClock,
|
|
67
|
+
createTypingStore: () => createTypingStore,
|
|
68
|
+
defaultUrlResolver: () => defaultUrlResolver,
|
|
69
|
+
extractColorsFromGradient: () => extractColorsFromGradient,
|
|
70
|
+
formatBlocksAction: () => formatBlocksAction,
|
|
71
|
+
formatComponentResponse: () => formatComponentResponse,
|
|
72
|
+
formatDynamicData: () => formatDynamicData,
|
|
73
|
+
formatSlashCommand: () => formatSlashCommand,
|
|
74
|
+
formatSlotDate: () => formatSlotDate,
|
|
75
|
+
formatSlotTime: () => formatSlotTime,
|
|
76
|
+
getButtonRadius: () => getButtonRadius,
|
|
77
|
+
getContrastingTextColor: () => getContrastingTextColor,
|
|
78
|
+
getDominantColor: () => getDominantColor,
|
|
79
|
+
grantedMicPermission: () => grantedMicPermission,
|
|
80
|
+
groupSlotsByDate: () => groupSlotsByDate,
|
|
81
|
+
isBlocks: () => isBlocks,
|
|
82
|
+
isButtons: () => isButtons,
|
|
83
|
+
isCalendarBooking: () => isCalendarBooking,
|
|
84
|
+
isConfirmation: () => isConfirmation,
|
|
85
|
+
isForm: () => isForm,
|
|
86
|
+
isGradient: () => isGradient,
|
|
87
|
+
isInteractiveFlow: () => isInteractiveFlow,
|
|
88
|
+
isLightColor: () => isLightColor,
|
|
89
|
+
mergeComponentTheme: () => mergeComponentTheme,
|
|
90
|
+
mergeWidgetTheme: () => mergeWidgetTheme,
|
|
46
91
|
resolveButtonRadius: () => resolveButtonRadius,
|
|
47
92
|
resolveFontFamily: () => resolveFontFamily,
|
|
48
93
|
resolveRadius: () => resolveRadius,
|
|
49
94
|
ringgId: () => ringgId,
|
|
95
|
+
silentNotificationPlayer: () => silentNotificationPlayer,
|
|
50
96
|
solidColor: () => solidColor,
|
|
51
97
|
toSize: () => toSize,
|
|
52
98
|
toViewStyle: () => toViewStyle,
|
|
99
|
+
transformRpcToComponent: () => transformRpcToComponent,
|
|
53
100
|
useRinggComponents: () => useRinggComponents,
|
|
54
101
|
useRinggMessages: () => useRinggMessages,
|
|
55
102
|
useRinggSession: () => useRinggSession,
|
|
@@ -77,6 +124,117 @@ var DEFAULT_WIDGET_THEME = {
|
|
|
77
124
|
"borderRadius": "16px",
|
|
78
125
|
"fontFamily": "inherit"
|
|
79
126
|
};
|
|
127
|
+
var WIDGET_PRESET_THEMES = {
|
|
128
|
+
"light": {
|
|
129
|
+
"primaryColor": "#18181b",
|
|
130
|
+
"primaryTextColor": "#ffffff",
|
|
131
|
+
"backgroundColor": "#ffffff",
|
|
132
|
+
"surfaceColor": "#f4f4f5",
|
|
133
|
+
"textColor": "#18181b",
|
|
134
|
+
"mutedTextColor": "#71717a",
|
|
135
|
+
"borderColor": "#e4e4e7"
|
|
136
|
+
},
|
|
137
|
+
"warm": {
|
|
138
|
+
"primaryColor": "#57534e",
|
|
139
|
+
"primaryTextColor": "#ffffff",
|
|
140
|
+
"backgroundColor": "#fefefe",
|
|
141
|
+
"surfaceColor": "#fafaf9",
|
|
142
|
+
"textColor": "#1c1917",
|
|
143
|
+
"mutedTextColor": "#78716c",
|
|
144
|
+
"borderColor": "#e7e5e4"
|
|
145
|
+
},
|
|
146
|
+
"blue": {
|
|
147
|
+
"primaryColor": "#2563eb",
|
|
148
|
+
"primaryTextColor": "#ffffff",
|
|
149
|
+
"backgroundColor": "#fafcff",
|
|
150
|
+
"surfaceColor": "#f0f7ff",
|
|
151
|
+
"textColor": "#1e3a8a",
|
|
152
|
+
"mutedTextColor": "#64748b",
|
|
153
|
+
"borderColor": "#dbeafe"
|
|
154
|
+
},
|
|
155
|
+
"indigo": {
|
|
156
|
+
"primaryColor": "#6366f1",
|
|
157
|
+
"primaryTextColor": "#ffffff",
|
|
158
|
+
"backgroundColor": "#fbfbff",
|
|
159
|
+
"surfaceColor": "#f5f5ff",
|
|
160
|
+
"textColor": "#312e81",
|
|
161
|
+
"mutedTextColor": "#6b7280",
|
|
162
|
+
"borderColor": "#e0e7ff"
|
|
163
|
+
},
|
|
164
|
+
"green": {
|
|
165
|
+
"primaryColor": "#16a34a",
|
|
166
|
+
"primaryTextColor": "#ffffff",
|
|
167
|
+
"backgroundColor": "#fbfefb",
|
|
168
|
+
"surfaceColor": "#f0fdf4",
|
|
169
|
+
"textColor": "#14532d",
|
|
170
|
+
"mutedTextColor": "#6b7280",
|
|
171
|
+
"borderColor": "#dcfce7"
|
|
172
|
+
},
|
|
173
|
+
"orange": {
|
|
174
|
+
"primaryColor": "#ea580c",
|
|
175
|
+
"primaryTextColor": "#ffffff",
|
|
176
|
+
"backgroundColor": "#fffdfb",
|
|
177
|
+
"surfaceColor": "#fff7ed",
|
|
178
|
+
"textColor": "#7c2d12",
|
|
179
|
+
"mutedTextColor": "#78716c",
|
|
180
|
+
"borderColor": "#ffedd5"
|
|
181
|
+
},
|
|
182
|
+
"dark": {
|
|
183
|
+
"primaryColor": "#fafafa",
|
|
184
|
+
"primaryTextColor": "#18181b",
|
|
185
|
+
"backgroundColor": "#18181b",
|
|
186
|
+
"surfaceColor": "#27272a",
|
|
187
|
+
"textColor": "#fafafa",
|
|
188
|
+
"mutedTextColor": "#a1a1aa",
|
|
189
|
+
"borderColor": "#3f3f46"
|
|
190
|
+
},
|
|
191
|
+
"sunset": {
|
|
192
|
+
"primaryColor": "linear-gradient(135deg, #f97316, #ef4444)",
|
|
193
|
+
"primaryTextColor": "#ffffff",
|
|
194
|
+
"backgroundColor": "#fffbf7",
|
|
195
|
+
"surfaceColor": "#fff3e8",
|
|
196
|
+
"textColor": "#7c2d12",
|
|
197
|
+
"mutedTextColor": "#9a3412",
|
|
198
|
+
"borderColor": "#fed7aa"
|
|
199
|
+
},
|
|
200
|
+
"ocean": {
|
|
201
|
+
"primaryColor": "linear-gradient(135deg, #667eea, #764ba2)",
|
|
202
|
+
"primaryTextColor": "#ffffff",
|
|
203
|
+
"backgroundColor": "#faf9ff",
|
|
204
|
+
"surfaceColor": "#f0edff",
|
|
205
|
+
"textColor": "#312e81",
|
|
206
|
+
"mutedTextColor": "#6366f1",
|
|
207
|
+
"borderColor": "#ddd6fe"
|
|
208
|
+
},
|
|
209
|
+
"emerald": {
|
|
210
|
+
"primaryColor": "linear-gradient(135deg, #10b981, #0ea5e9)",
|
|
211
|
+
"primaryTextColor": "#ffffff",
|
|
212
|
+
"backgroundColor": "#f7fdfb",
|
|
213
|
+
"surfaceColor": "#ecfdf5",
|
|
214
|
+
"textColor": "#064e3b",
|
|
215
|
+
"mutedTextColor": "#047857",
|
|
216
|
+
"borderColor": "#a7f3d0"
|
|
217
|
+
},
|
|
218
|
+
"midnight": {
|
|
219
|
+
"primaryColor": "linear-gradient(135deg, #6366f1, #8b5cf6)",
|
|
220
|
+
"primaryTextColor": "#ffffff",
|
|
221
|
+
"backgroundColor": "#18181b",
|
|
222
|
+
"surfaceColor": "#27272a",
|
|
223
|
+
"textColor": "#fafafa",
|
|
224
|
+
"mutedTextColor": "#a1a1aa",
|
|
225
|
+
"borderColor": "#3f3f46"
|
|
226
|
+
},
|
|
227
|
+
"policybazaar": {
|
|
228
|
+
"primaryColor": "linear-gradient(88deg, #9403fd, #00adfe)",
|
|
229
|
+
"primaryTextColor": "#ffffff",
|
|
230
|
+
"backgroundColor": "#ffffff",
|
|
231
|
+
"surfaceColor": "#f4f2ff",
|
|
232
|
+
"agentBubbleColor": "linear-gradient(107deg, #f0fff9, #f7eeff)",
|
|
233
|
+
"textColor": "#18181b",
|
|
234
|
+
"mutedTextColor": "#64748b",
|
|
235
|
+
"borderColor": "#e6e2ff"
|
|
236
|
+
}
|
|
237
|
+
};
|
|
80
238
|
var TIMING = {
|
|
81
239
|
"chatWidgetGraceMs": 800,
|
|
82
240
|
"chatWidgetAfterTextMs": 120,
|
|
@@ -97,6 +255,47 @@ var WIDGET_DEFAULTS = {
|
|
|
97
255
|
};
|
|
98
256
|
|
|
99
257
|
// ../core/dist/ports/event-bus.js
|
|
258
|
+
function createDomEventBus() {
|
|
259
|
+
const listeners = /* @__PURE__ */ new Map();
|
|
260
|
+
return {
|
|
261
|
+
emit(event, payload) {
|
|
262
|
+
if (typeof window !== "undefined") {
|
|
263
|
+
window.dispatchEvent(new CustomEvent(event, { detail: payload }));
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
on(event, handler) {
|
|
267
|
+
if (typeof window === "undefined")
|
|
268
|
+
return () => {
|
|
269
|
+
};
|
|
270
|
+
const wrapped = ((e) => handler(e.detail));
|
|
271
|
+
let set = listeners.get(event);
|
|
272
|
+
if (!set) {
|
|
273
|
+
set = /* @__PURE__ */ new Set();
|
|
274
|
+
listeners.set(event, set);
|
|
275
|
+
}
|
|
276
|
+
set.add(wrapped);
|
|
277
|
+
window.addEventListener(event, wrapped);
|
|
278
|
+
return () => {
|
|
279
|
+
window.removeEventListener(event, wrapped);
|
|
280
|
+
listeners.get(event)?.delete(wrapped);
|
|
281
|
+
};
|
|
282
|
+
},
|
|
283
|
+
off(event) {
|
|
284
|
+
const remove = (name, handlers) => {
|
|
285
|
+
handlers.forEach((h) => window.removeEventListener(name, h));
|
|
286
|
+
};
|
|
287
|
+
if (event) {
|
|
288
|
+
const handlers = listeners.get(event);
|
|
289
|
+
if (handlers)
|
|
290
|
+
remove(event, handlers);
|
|
291
|
+
listeners.delete(event);
|
|
292
|
+
} else {
|
|
293
|
+
listeners.forEach((handlers, name) => remove(name, handlers));
|
|
294
|
+
listeners.clear();
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
}
|
|
100
299
|
function createCallbackEventBus() {
|
|
101
300
|
const listeners = /* @__PURE__ */ new Map();
|
|
102
301
|
return {
|
|
@@ -151,6 +350,13 @@ var silentNotificationPlayer = {
|
|
|
151
350
|
};
|
|
152
351
|
|
|
153
352
|
// ../core/dist/api/client.js
|
|
353
|
+
function createStaticUrlResolver(urls) {
|
|
354
|
+
return {
|
|
355
|
+
resolve(mode) {
|
|
356
|
+
return urls[mode];
|
|
357
|
+
}
|
|
358
|
+
};
|
|
359
|
+
}
|
|
154
360
|
function createRinggApiClient(config) {
|
|
155
361
|
const { backendUrl, livekitUrl } = config.urlResolver.resolve(config.mode);
|
|
156
362
|
const authHeaders = config.authorization ? { Authorization: config.authorization } : config.xApiKey ? { "X-API-KEY": config.xApiKey } : {};
|
|
@@ -275,11 +481,47 @@ function getDominantColor(color) {
|
|
|
275
481
|
return color;
|
|
276
482
|
return extractColorsFromGradient(color)[0] ?? color;
|
|
277
483
|
}
|
|
484
|
+
function colorToBackground(color) {
|
|
485
|
+
if (isGradient(color)) {
|
|
486
|
+
return { background: color };
|
|
487
|
+
}
|
|
488
|
+
return { backgroundColor: color };
|
|
489
|
+
}
|
|
490
|
+
function colorToSolid(color) {
|
|
491
|
+
return getDominantColor(color);
|
|
492
|
+
}
|
|
493
|
+
function colorToBorder(color) {
|
|
494
|
+
if (isGradient(color)) {
|
|
495
|
+
return {
|
|
496
|
+
borderImage: `${color} 1`,
|
|
497
|
+
borderStyle: "solid"
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
return { borderColor: color };
|
|
501
|
+
}
|
|
278
502
|
|
|
279
503
|
// ../core/dist/theme/theme-engine.js
|
|
280
504
|
function mergeWidgetTheme(theme) {
|
|
281
505
|
return { ...DEFAULT_WIDGET_THEME, ...theme };
|
|
282
506
|
}
|
|
507
|
+
function mergeComponentTheme(componentTheme, widgetTheme) {
|
|
508
|
+
const base = widgetTheme ?? DEFAULT_WIDGET_THEME;
|
|
509
|
+
return {
|
|
510
|
+
primaryColor: base.primaryColor,
|
|
511
|
+
primaryTextColor: base.primaryTextColor,
|
|
512
|
+
backgroundColor: base.backgroundColor,
|
|
513
|
+
surfaceColor: base.surfaceColor,
|
|
514
|
+
textColor: base.textColor,
|
|
515
|
+
mutedTextColor: base.mutedTextColor,
|
|
516
|
+
borderColor: base.borderColor,
|
|
517
|
+
errorColor: base.errorColor,
|
|
518
|
+
successColor: base.successColor,
|
|
519
|
+
fontFamily: base.fontFamily,
|
|
520
|
+
borderRadius: base.borderRadius,
|
|
521
|
+
buttonStyle: base.buttonStyle,
|
|
522
|
+
...componentTheme
|
|
523
|
+
};
|
|
524
|
+
}
|
|
283
525
|
function getButtonRadius(style) {
|
|
284
526
|
switch (style) {
|
|
285
527
|
case "pill":
|
|
@@ -291,6 +533,25 @@ function getButtonRadius(style) {
|
|
|
291
533
|
return "12px";
|
|
292
534
|
}
|
|
293
535
|
}
|
|
536
|
+
function getLuminance(hexColor) {
|
|
537
|
+
const solid = getDominantColor(hexColor);
|
|
538
|
+
const hex = solid.replace("#", "");
|
|
539
|
+
const r = parseInt(hex.slice(0, 2), 16) / 255;
|
|
540
|
+
const g = parseInt(hex.slice(2, 4), 16) / 255;
|
|
541
|
+
const b = parseInt(hex.slice(4, 6), 16) / 255;
|
|
542
|
+
const toLinear = (c) => c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
|
|
543
|
+
return 0.2126 * toLinear(r) + 0.7152 * toLinear(g) + 0.0722 * toLinear(b);
|
|
544
|
+
}
|
|
545
|
+
function isLightColor(hexColor) {
|
|
546
|
+
try {
|
|
547
|
+
return getLuminance(hexColor) > 0.5;
|
|
548
|
+
} catch {
|
|
549
|
+
return true;
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
function getContrastingTextColor(bgColor) {
|
|
553
|
+
return isLightColor(bgColor) ? "#212121" : "#ffffff";
|
|
554
|
+
}
|
|
294
555
|
|
|
295
556
|
// ../core/dist/rpc/formatter.js
|
|
296
557
|
function transformRpcToComponent(componentName, rpcPayload) {
|
|
@@ -474,6 +735,68 @@ function formatDynamicData(payload) {
|
|
|
474
735
|
};
|
|
475
736
|
}
|
|
476
737
|
|
|
738
|
+
// ../core/dist/components/helpers.js
|
|
739
|
+
function groupSlotsByDate(slots) {
|
|
740
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
741
|
+
slots.forEach((slot) => {
|
|
742
|
+
const dateKey = new Date(slot.datetime).toISOString().split("T")[0];
|
|
743
|
+
if (!grouped.has(dateKey)) {
|
|
744
|
+
grouped.set(dateKey, []);
|
|
745
|
+
}
|
|
746
|
+
grouped.get(dateKey).push(slot);
|
|
747
|
+
});
|
|
748
|
+
return Array.from(grouped.entries()).sort(([a], [b]) => a.localeCompare(b)).map(([dateKey, dateSlots]) => ({
|
|
749
|
+
date: dateKey,
|
|
750
|
+
dateLabel: new Date(dateKey).toLocaleDateString("en-US", {
|
|
751
|
+
weekday: "short",
|
|
752
|
+
month: "short",
|
|
753
|
+
day: "numeric"
|
|
754
|
+
}),
|
|
755
|
+
slots: dateSlots.sort((a, b) => new Date(a.datetime).getTime() - new Date(b.datetime).getTime())
|
|
756
|
+
}));
|
|
757
|
+
}
|
|
758
|
+
function formatSlotTime(datetime, timezone) {
|
|
759
|
+
return new Date(datetime).toLocaleTimeString("en-US", {
|
|
760
|
+
hour: "numeric",
|
|
761
|
+
minute: "2-digit",
|
|
762
|
+
hour12: true,
|
|
763
|
+
timeZone: timezone
|
|
764
|
+
});
|
|
765
|
+
}
|
|
766
|
+
function formatSlotDate(datetime, timezone) {
|
|
767
|
+
return new Date(datetime).toLocaleDateString("en-US", {
|
|
768
|
+
weekday: "long",
|
|
769
|
+
month: "long",
|
|
770
|
+
day: "numeric",
|
|
771
|
+
timeZone: timezone
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
function buildPayload(template, values) {
|
|
775
|
+
const result = {};
|
|
776
|
+
for (const [key, raw] of Object.entries(template)) {
|
|
777
|
+
result[key] = raw.replace(/\{\{(\w+)\}\}/g, (_, name) => values[name] ?? "");
|
|
778
|
+
}
|
|
779
|
+
return result;
|
|
780
|
+
}
|
|
781
|
+
function isCalendarBooking(payload) {
|
|
782
|
+
return payload.component_type === "calendar_booking";
|
|
783
|
+
}
|
|
784
|
+
function isForm(payload) {
|
|
785
|
+
return payload.component_type === "form";
|
|
786
|
+
}
|
|
787
|
+
function isButtons(payload) {
|
|
788
|
+
return payload.component_type === "buttons";
|
|
789
|
+
}
|
|
790
|
+
function isConfirmation(payload) {
|
|
791
|
+
return payload.component_type === "confirmation";
|
|
792
|
+
}
|
|
793
|
+
function isInteractiveFlow(payload) {
|
|
794
|
+
return payload.component_type === "interactive_flow";
|
|
795
|
+
}
|
|
796
|
+
function isBlocks(payload) {
|
|
797
|
+
return payload.component_type === "blocks";
|
|
798
|
+
}
|
|
799
|
+
|
|
477
800
|
// ../core/dist/stores/store.js
|
|
478
801
|
function createStore(build) {
|
|
479
802
|
const listeners = /* @__PURE__ */ new Set();
|
|
@@ -1263,7 +1586,7 @@ var useRinggSlashCommands = (controller) => useStoreSnapshot(controller.slashCom
|
|
|
1263
1586
|
|
|
1264
1587
|
// src/RinggWidget.tsx
|
|
1265
1588
|
var import_react24 = require("react");
|
|
1266
|
-
var
|
|
1589
|
+
var import_react_native31 = require("react-native");
|
|
1267
1590
|
var import_lucide_react_native10 = require("lucide-react-native");
|
|
1268
1591
|
|
|
1269
1592
|
// src/components/action-button.tsx
|
|
@@ -1871,7 +2194,7 @@ var import_react_native13 = require("react-native");
|
|
|
1871
2194
|
var import_lucide_react_native8 = require("lucide-react-native");
|
|
1872
2195
|
|
|
1873
2196
|
// src/lib/component-types.ts
|
|
1874
|
-
function
|
|
2197
|
+
function groupSlotsByDate2(slots) {
|
|
1875
2198
|
const grouped = /* @__PURE__ */ new Map();
|
|
1876
2199
|
slots.forEach((slot) => {
|
|
1877
2200
|
const date = new Date(slot.datetime);
|
|
@@ -1890,7 +2213,7 @@ function groupSlotsByDate(slots) {
|
|
|
1890
2213
|
};
|
|
1891
2214
|
});
|
|
1892
2215
|
}
|
|
1893
|
-
function
|
|
2216
|
+
function formatSlotTime2(datetime, timezone) {
|
|
1894
2217
|
const date = new Date(datetime);
|
|
1895
2218
|
return date.toLocaleTimeString("en-US", {
|
|
1896
2219
|
hour: "numeric",
|
|
@@ -1899,7 +2222,7 @@ function formatSlotTime(datetime, timezone) {
|
|
|
1899
2222
|
timeZone: timezone
|
|
1900
2223
|
});
|
|
1901
2224
|
}
|
|
1902
|
-
function
|
|
2225
|
+
function formatSlotDate2(datetime, timezone) {
|
|
1903
2226
|
const date = new Date(datetime);
|
|
1904
2227
|
return date.toLocaleDateString("en-US", {
|
|
1905
2228
|
weekday: "short",
|
|
@@ -1908,7 +2231,7 @@ function formatSlotDate(datetime, timezone) {
|
|
|
1908
2231
|
timeZone: timezone
|
|
1909
2232
|
});
|
|
1910
2233
|
}
|
|
1911
|
-
function
|
|
2234
|
+
function buildPayload2(template, values) {
|
|
1912
2235
|
const result = {};
|
|
1913
2236
|
for (const [key, value] of Object.entries(template)) {
|
|
1914
2237
|
if (typeof value === "string" && value.startsWith("{") && value.endsWith("}")) {
|
|
@@ -1920,13 +2243,13 @@ function buildPayload(template, values) {
|
|
|
1920
2243
|
}
|
|
1921
2244
|
return result;
|
|
1922
2245
|
}
|
|
1923
|
-
function
|
|
2246
|
+
function isButtons2(payload) {
|
|
1924
2247
|
return payload.component_type === "buttons";
|
|
1925
2248
|
}
|
|
1926
|
-
function
|
|
2249
|
+
function isInteractiveFlow2(payload) {
|
|
1927
2250
|
return payload.component_type === "interactive_flow";
|
|
1928
2251
|
}
|
|
1929
|
-
function
|
|
2252
|
+
function isBlocks2(payload) {
|
|
1930
2253
|
return payload.component_type === "blocks";
|
|
1931
2254
|
}
|
|
1932
2255
|
|
|
@@ -2138,7 +2461,7 @@ var CalendarStep = ({ payload, isProcessing, onComplete }) => {
|
|
|
2138
2461
|
const buttonRadius = getButtonRadius2(theme.buttonStyle);
|
|
2139
2462
|
const fontFamily = resolveFontFamily(theme.fontFamily);
|
|
2140
2463
|
const { data } = payload;
|
|
2141
|
-
const groupedSlots = (0, import_react8.useMemo)(() =>
|
|
2464
|
+
const groupedSlots = (0, import_react8.useMemo)(() => groupSlotsByDate2(data.available_slots), [data.available_slots]);
|
|
2142
2465
|
const [selectedDate, setSelectedDate] = (0, import_react8.useState)(groupedSlots[0]?.date || "");
|
|
2143
2466
|
const [selectedSlot, setSelectedSlot] = (0, import_react8.useState)(null);
|
|
2144
2467
|
const selectedDateGroup = groupedSlots.find((g) => g.date === selectedDate);
|
|
@@ -2202,7 +2525,7 @@ var CalendarStep = ({ payload, isProcessing, onComplete }) => {
|
|
|
2202
2525
|
{
|
|
2203
2526
|
color: isSelected ? theme.primaryColor : theme.backgroundColor,
|
|
2204
2527
|
style: [styles9.slot, { borderRadius: buttonRadius, borderColor: isSelected ? solidColor(theme.primaryColor) : theme.borderColor }, isSelected ? styles9.selectedShadow : null],
|
|
2205
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react_native9.Text, { style: [styles9.slotLabel, { color: isSelected ? theme.primaryTextColor : theme.textColor, fontFamily }], numberOfLines: 1, children:
|
|
2528
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react_native9.Text, { style: [styles9.slotLabel, { color: isSelected ? theme.primaryTextColor : theme.textColor, fontFamily }], numberOfLines: 1, children: formatSlotTime2(slot.datetime, data.timezone) })
|
|
2206
2529
|
}
|
|
2207
2530
|
) }) }, slot.id);
|
|
2208
2531
|
}) })
|
|
@@ -2744,8 +3067,8 @@ var EnterFade = ({ translateX = 0, translateY = 0, durationMs = ENTER_DURATION_M
|
|
|
2744
3067
|
);
|
|
2745
3068
|
};
|
|
2746
3069
|
var InteractiveFlow = ({ initialComponent, onApiCall, onComplete, onSendResponse, onSelectionDisplayed, quickReplyAlign = "end" }) => {
|
|
2747
|
-
const isMultiStepFlow =
|
|
2748
|
-
const isFreeButtonGroup = !isMultiStepFlow &&
|
|
3070
|
+
const isMultiStepFlow = isInteractiveFlow2(initialComponent);
|
|
3071
|
+
const isFreeButtonGroup = !isMultiStepFlow && isButtons2(initialComponent) && initialComponent.data.presentation === "free";
|
|
2749
3072
|
const widgetTheme = useWidgetTheme();
|
|
2750
3073
|
const theme = (0, import_react12.useMemo)(
|
|
2751
3074
|
() => mergeTheme({
|
|
@@ -2791,7 +3114,7 @@ var InteractiveFlow = ({ initialComponent, onApiCall, onComplete, onSendResponse
|
|
|
2791
3114
|
try {
|
|
2792
3115
|
const isLastStep = currentStepIndex === totalSteps - 1;
|
|
2793
3116
|
const selectedButtonId = typeof stepData.selected_button === "string" ? stepData.selected_button : null;
|
|
2794
|
-
const buttonsComponent = currentComponent !== void 0 &&
|
|
3117
|
+
const buttonsComponent = currentComponent !== void 0 && isButtons2(currentComponent) ? currentComponent : null;
|
|
2795
3118
|
const submittedButton = buttonsComponent && selectedButtonId ? buttonsComponent.data.buttons.find((button) => button.id === selectedButtonId) ?? { id: selectedButtonId, label: selectedButtonId, action: { type: "trigger_component" } } : null;
|
|
2796
3119
|
const shouldShowSubmittedSelection = buttonsComponent?.data.completionDisplay === "selected_item" && !!submittedButton;
|
|
2797
3120
|
if (onSendResponse) {
|
|
@@ -2830,7 +3153,7 @@ var InteractiveFlow = ({ initialComponent, onApiCall, onComplete, onSendResponse
|
|
|
2830
3153
|
for (const [key, value] of Object.entries(newAllData)) {
|
|
2831
3154
|
stringifiedData[key] = Array.isArray(value) ? JSON.stringify(value) : String(value);
|
|
2832
3155
|
}
|
|
2833
|
-
const apiPayload =
|
|
3156
|
+
const apiPayload = buildPayload2(on_complete.payload, {
|
|
2834
3157
|
...stringifiedData,
|
|
2835
3158
|
all_data: JSON.stringify(newAllData)
|
|
2836
3159
|
});
|
|
@@ -2909,8 +3232,8 @@ var InteractiveFlow = ({ initialComponent, onApiCall, onComplete, onSendResponse
|
|
|
2909
3232
|
handleStepComplete({
|
|
2910
3233
|
slot_id: slotId,
|
|
2911
3234
|
slot_datetime: slotDatetime,
|
|
2912
|
-
slot_time:
|
|
2913
|
-
slot_date:
|
|
3235
|
+
slot_time: formatSlotTime2(slotDatetime, timezone),
|
|
3236
|
+
slot_date: formatSlotDate2(slotDatetime, timezone)
|
|
2914
3237
|
});
|
|
2915
3238
|
}
|
|
2916
3239
|
}
|
|
@@ -4245,7 +4568,7 @@ var FeedbackScreen = ({ callMode, isSubmitting, feedbackScreenConfig, messages,
|
|
|
4245
4568
|
transcript.map((message, index) => {
|
|
4246
4569
|
if (message.componentType && message.componentData) {
|
|
4247
4570
|
const componentData = message.componentData;
|
|
4248
|
-
if (
|
|
4571
|
+
if (isBlocks2(componentData)) {
|
|
4249
4572
|
return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react_native17.View, { testID: ringgId("blocks-widget"), style: styles16.replayed, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(blocks_step_default, { config: componentData.data, disabled: true, onAction: async () => {
|
|
4250
4573
|
} }) }, `c_${message.timestamp}__${index}`);
|
|
4251
4574
|
}
|
|
@@ -4941,370 +5264,83 @@ var renderTextWithLinks = (text, links, linkStyle) => {
|
|
|
4941
5264
|
return parts;
|
|
4942
5265
|
};
|
|
4943
5266
|
|
|
4944
|
-
// src/
|
|
4945
|
-
var
|
|
4946
|
-
|
|
4947
|
-
|
|
4948
|
-
|
|
4949
|
-
|
|
4950
|
-
|
|
4951
|
-
|
|
4952
|
-
|
|
4953
|
-
|
|
4954
|
-
|
|
4955
|
-
|
|
4956
|
-
|
|
4957
|
-
|
|
4958
|
-
var isFreeButtonsMessage = (message) => {
|
|
4959
|
-
const componentData = message?.componentData;
|
|
4960
|
-
return !!componentData && isButtons(componentData) && componentData.data.presentation === "free";
|
|
5267
|
+
// src/config/default-urls.ts
|
|
5268
|
+
var DEFAULT_ENVIRONMENT_URLS = {
|
|
5269
|
+
dev: {
|
|
5270
|
+
backendUrl: "https://calling-dev.ringg.ai/ca/api/v0",
|
|
5271
|
+
livekitUrl: "wss://ringg-ai-dev-92tubwpz.livekit.cloud"
|
|
5272
|
+
},
|
|
5273
|
+
stage: {
|
|
5274
|
+
backendUrl: "https://stage-api.ringg.ai/ca/api/v0",
|
|
5275
|
+
livekitUrl: "wss://mercury.webrtc-stage.ringg.ai"
|
|
5276
|
+
},
|
|
5277
|
+
prod: {
|
|
5278
|
+
backendUrl: "https://prod-api.ringg.ai/ca/api/v0",
|
|
5279
|
+
livekitUrl: "wss://mercury.webrtc.ringg.ai"
|
|
5280
|
+
}
|
|
4961
5281
|
};
|
|
4962
|
-
var
|
|
4963
|
-
|
|
4964
|
-
|
|
4965
|
-
|
|
4966
|
-
|
|
4967
|
-
|
|
4968
|
-
|
|
4969
|
-
|
|
4970
|
-
|
|
4971
|
-
|
|
4972
|
-
|
|
4973
|
-
|
|
4974
|
-
|
|
4975
|
-
|
|
4976
|
-
|
|
4977
|
-
|
|
4978
|
-
|
|
4979
|
-
|
|
4980
|
-
|
|
4981
|
-
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
|
|
4988
|
-
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
4996
|
-
|
|
4997
|
-
|
|
4998
|
-
|
|
4999
|
-
|
|
5000
|
-
|
|
5001
|
-
|
|
5002
|
-
|
|
5003
|
-
|
|
5004
|
-
|
|
5005
|
-
|
|
5006
|
-
|
|
5007
|
-
|
|
5008
|
-
|
|
5009
|
-
|
|
5010
|
-
|
|
5011
|
-
|
|
5012
|
-
|
|
5013
|
-
|
|
5014
|
-
|
|
5015
|
-
|
|
5016
|
-
|
|
5017
|
-
|
|
5018
|
-
|
|
5019
|
-
|
|
5020
|
-
|
|
5021
|
-
|
|
5022
|
-
|
|
5023
|
-
|
|
5024
|
-
!defaultExpanded ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
5025
|
-
Touchable,
|
|
5026
|
-
{
|
|
5027
|
-
testID: ringgId("close-button"),
|
|
5028
|
-
accessibilityLabel: isSessionLive ? "End call" : "Close widget",
|
|
5029
|
-
onPress: () => isSessionLive ? setAlertEndCall(true) : showFeedback ? handleFeedbackSkip() : controller.minimizeWidget(),
|
|
5030
|
-
style: [styles24.chromeButton, styles24.closeButton],
|
|
5031
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_lucide_react_native10.X, { size: 16, color: solidColor(theme.mutedTextColor) })
|
|
5032
|
-
}
|
|
5033
|
-
) : null
|
|
5034
|
-
] });
|
|
5035
|
-
const transcript = /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
|
|
5036
|
-
import_react_native28.ScrollView,
|
|
5037
|
-
{
|
|
5038
|
-
testID: ringgId("transcript"),
|
|
5039
|
-
ref: transcriptRef,
|
|
5040
|
-
style: styles24.transcript,
|
|
5041
|
-
contentContainerStyle: [styles24.transcriptList, !showVoiceAnimation && styles24.transcriptTopClearance, !isAudioMode && styles24.transcriptBottomClearance],
|
|
5042
|
-
keyboardShouldPersistTaps: "handled",
|
|
5043
|
-
showsVerticalScrollIndicator: false,
|
|
5044
|
-
children: [
|
|
5045
|
-
sortedMessages.map((segment, index) => {
|
|
5046
|
-
if (segment.kind === "system") return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(system_log_default, { label: segment.message, meta: segment.systemMeta, level: segment.systemLevel }, `sys_${segment.timestamp}_${index}`);
|
|
5047
|
-
const previousSegment = sortedMessages[index - 1];
|
|
5048
|
-
const nextSegment = sortedMessages[index + 1];
|
|
5049
|
-
const attachFreeButtonsToPrevious = isFreeButtonsMessage(segment) && isAttachableAgentMessage(previousSegment);
|
|
5050
|
-
const skipStandaloneRender = isAttachableAgentMessage(segment) && isFreeButtonsMessage(nextSegment);
|
|
5051
|
-
if (skipStandaloneRender) return null;
|
|
5052
|
-
if (attachFreeButtonsToPrevious && previousSegment && segment.componentData) {
|
|
5053
|
-
const componentData = segment.componentData;
|
|
5054
|
-
const isCompletedSelectedItem = componentData.component_type === "buttons" && componentData.data.completionDisplay === "selected_item" && completedFlowIds.has(componentData.component_id);
|
|
5055
|
-
if (isCompletedSelectedItem) return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(message_item_default, { itemIndex: index, message: previousSegment }, `message_with_buttons_${segment.componentData.component_id}_${index}`);
|
|
5056
|
-
return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_react24.Fragment, { children: [
|
|
5057
|
-
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(message_item_default, { itemIndex: index, message: previousSegment }),
|
|
5058
|
-
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_react_native28.View, { testID: ringgId("attached-buttons"), style: styles24.fullWidth, children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
5059
|
-
interactive_flow_default,
|
|
5060
|
-
{
|
|
5061
|
-
initialComponent: segment.componentData,
|
|
5062
|
-
onApiCall: callComponentApi,
|
|
5063
|
-
onComplete: () => handleFlowComplete(componentData.component_id),
|
|
5064
|
-
onSendResponse: sendComponentResponse,
|
|
5065
|
-
onSelectionDisplayed: handleSelectionDisplayed,
|
|
5066
|
-
quickReplyAlign: isAudioMode ? "center" : "end"
|
|
5067
|
-
}
|
|
5068
|
-
) })
|
|
5069
|
-
] }, `message_with_buttons_${segment.componentData.component_id}_${index}`);
|
|
5070
|
-
}
|
|
5071
|
-
if (segment.componentType === "blocks" && segment.componentData) {
|
|
5072
|
-
const blocksPayload = segment.componentData;
|
|
5073
|
-
if (isBlocks(blocksPayload)) {
|
|
5074
|
-
const blocksConfig = blocksPayload.data;
|
|
5075
|
-
const blockId = blocksPayload.component_id;
|
|
5076
|
-
return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_react_native28.View, { testID: ringgId("blocks-widget"), style: styles24.fullWidth, children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
5077
|
-
blocks_step_default,
|
|
5078
|
-
{
|
|
5079
|
-
config: blocksConfig,
|
|
5080
|
-
disabled: completedFlowIds.has(blockId),
|
|
5081
|
-
onAction: (action) => {
|
|
5082
|
-
handleUserMessageSent();
|
|
5083
|
-
handleFlowComplete(blockId);
|
|
5084
|
-
const selection = typeof action.value === "string" || typeof action.value === "number" ? String(action.value) : action.label;
|
|
5085
|
-
if (selection) handleSelectionDisplayed(blockId, selection);
|
|
5086
|
-
return controller.sendBlocksAction(blocksConfig.tool_id, blockId, { action_id: action.action_id, value: action.value, values: action.values });
|
|
5087
|
-
}
|
|
5088
|
-
}
|
|
5089
|
-
) }, `blocks_${blockId}`);
|
|
5090
|
-
}
|
|
5091
|
-
}
|
|
5092
|
-
if (segment.componentType && segment.componentData) {
|
|
5093
|
-
const componentData = segment.componentData;
|
|
5094
|
-
const isCompletedSelectedItem = componentData.component_type === "buttons" && componentData.data.completionDisplay === "selected_item" && completedFlowIds.has(componentData.component_id);
|
|
5095
|
-
if (isCompletedSelectedItem) return null;
|
|
5096
|
-
return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
5097
|
-
interactive_flow_default,
|
|
5098
|
-
{
|
|
5099
|
-
initialComponent: segment.componentData,
|
|
5100
|
-
onApiCall: callComponentApi,
|
|
5101
|
-
onComplete: () => handleFlowComplete(componentData.component_id),
|
|
5102
|
-
onSendResponse: sendComponentResponse,
|
|
5103
|
-
onSelectionDisplayed: handleSelectionDisplayed,
|
|
5104
|
-
quickReplyAlign: isAudioMode ? "center" : "end"
|
|
5105
|
-
},
|
|
5106
|
-
`flow_${segment.componentData.component_id}_${index}`
|
|
5107
|
-
);
|
|
5108
|
-
}
|
|
5109
|
-
return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(message_item_default, { itemIndex: index, message: segment }, `${segment.timestamp}__${index}`);
|
|
5110
|
-
}),
|
|
5111
|
-
isAgentTyping ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(typing_indicator_default, { words: typingWords }) : null
|
|
5112
|
-
]
|
|
5113
|
-
}
|
|
5114
|
-
);
|
|
5115
|
-
const controls = /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
5116
|
-
call_controls_default,
|
|
5117
|
-
{
|
|
5118
|
-
buttonConfig: buttons,
|
|
5119
|
-
callMode,
|
|
5120
|
-
isCalling: isSessionLive,
|
|
5121
|
-
enabledSlashCommands: mergedSlashCommands,
|
|
5122
|
-
micEnabled: microphone?.enabled ?? true,
|
|
5123
|
-
onToggleMic: microphone?.toggle,
|
|
5124
|
-
onSlashCommand: sendSlashCommand,
|
|
5125
|
-
onUserMessage: handleUserMessageSent,
|
|
5126
|
-
onCallEnd: handleCallEnd,
|
|
5127
|
-
onSendMessage: (message) => controller.sendMessage(message)
|
|
5128
|
-
}
|
|
5129
|
-
);
|
|
5130
|
-
return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
|
|
5131
|
-
import_react_native28.View,
|
|
5132
|
-
{
|
|
5133
|
-
testID: ringgId("widget-root"),
|
|
5134
|
-
style: [styles24.panel, { width, height, borderRadius: panelRadius, backgroundColor: solidColor(theme.backgroundColor), borderColor: solidColor(theme.borderColor) }],
|
|
5135
|
-
children: [
|
|
5136
|
-
chrome,
|
|
5137
|
-
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
5138
|
-
ConfirmDialog,
|
|
5139
|
-
{
|
|
5140
|
-
open: alertEndCall,
|
|
5141
|
-
title: `Are you sure you want to end ${callMode === "text" ? "chat" : "call"}?`,
|
|
5142
|
-
description: "Your current conversation will be closed and cannot be accessed later.",
|
|
5143
|
-
confirmLabel: `End ${callMode === "text" ? "Chat" : "Call"}`,
|
|
5144
|
-
cancelLabel: "Cancel",
|
|
5145
|
-
onConfirm: handleCallEnd,
|
|
5146
|
-
onCancel: () => setAlertEndCall(false)
|
|
5147
|
-
}
|
|
5148
|
-
),
|
|
5149
|
-
showFeedback ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
5150
|
-
feedback_screen_default,
|
|
5151
|
-
{
|
|
5152
|
-
callMode,
|
|
5153
|
-
isSubmitting: isLoading,
|
|
5154
|
-
feedbackScreenConfig: feedbackScreen,
|
|
5155
|
-
messages,
|
|
5156
|
-
title,
|
|
5157
|
-
logoUrl,
|
|
5158
|
-
logoStyles,
|
|
5159
|
-
onSubmit: handleFeedbackSubmit,
|
|
5160
|
-
onSkip: handleFeedbackSkip
|
|
5161
|
-
}
|
|
5162
|
-
) : /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_react_native28.View, { testID: ringgId("widget-body"), style: [styles24.body, isSessionLive ? styles24.bodyLive : styles24.bodyIdle], children: [
|
|
5163
|
-
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(header_default, { isCalling: isSessionLive, isLoading, title, description, logoUrl, logoStyles }),
|
|
5164
|
-
showVoiceAnimation ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_react_native28.View, { testID: ringgId("voice-animation-wrapper"), style: [styles24.voiceWrapper, showVoiceTranscript && messages.length > 0 ? styles24.voiceWrapperCompact : styles24.voiceWrapperFull], children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(VoiceAnimationLeaf, { room }) }) : null,
|
|
5165
|
-
isSessionLive && !showVoiceAnimation && (messages.length === 0 && !isAgentTyping || !showVoiceTranscript) ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_react_native28.View, { testID: ringgId("body-spacer"), style: styles24.bodySpacer }) : null,
|
|
5166
|
-
(messages.length > 0 || isAgentTyping) && isSessionLive && showVoiceTranscript ? transcript : null,
|
|
5167
|
-
isSessionLive && callMode === "text" ? (
|
|
5168
|
-
// Transparent bottom bar: it only positions the composer over the
|
|
5169
|
-
// transcript, so the gutters stay clear while messages scroll under.
|
|
5170
|
-
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_react_native28.View, { testID: ringgId("controls-bar"), style: styles24.controlsBar, children: [
|
|
5171
|
-
error.hasError ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_react_native28.Text, { testID: ringgId("error-message"), style: [styles24.error, { color: solidColor(theme.errorColor), fontFamily }], children: error.message }) : null,
|
|
5172
|
-
controls
|
|
5173
|
-
] })
|
|
5174
|
-
) : /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_jsx_runtime28.Fragment, { children: [
|
|
5175
|
-
controls,
|
|
5176
|
-
error.hasError ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_react_native28.Text, { testID: ringgId("error-message"), style: [styles24.error, { color: solidColor(theme.errorColor), fontFamily }], children: error.message }) : null
|
|
5177
|
-
] }),
|
|
5178
|
-
!isSessionLive && isLoading ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_react_native28.View, { testID: ringgId("connecting-status"), children: [
|
|
5179
|
-
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_react_native28.Text, { testID: ringgId("connecting-label"), style: [styles24.connecting, { color: theme.mutedTextColor, fontFamily }], children: "Connecting..." }),
|
|
5180
|
-
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(loading_bar_default, { ariaLabel: callMode === "text" ? "Starting chat" : "Connecting" })
|
|
5181
|
-
] }) : null,
|
|
5182
|
-
!isLoading && connectionState === "disconnected" && (!bypassStartScreen || error.hasError) ? hideTabSelector ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(action_button_default, { callMode, buttonConfig: buttons, isCalling, isLoading, onCallTrigger: handleCallStart }) : /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_react_native28.View, { testID: ringgId("start-actions"), style: styles24.startActions, children: [
|
|
5183
|
-
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(action_button_default, { dual: true, callMode: "text", buttonConfig: buttons, isCalling, isLoading, onCallTrigger: handleCallStart }),
|
|
5184
|
-
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(action_button_default, { dual: true, callMode: "audio", buttonConfig: buttons, isCalling, isLoading, onCallTrigger: handleCallStart })
|
|
5185
|
-
] }) : null,
|
|
5186
|
-
!isSessionLive ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_react_native28.Text, { testID: ringgId("legal-disclaimer"), style: [styles24.legal, { color: theme.mutedTextColor, fontFamily }], children: legalDisclaimer ? renderTextWithLinks(legalDisclaimer.text, legalDisclaimer.links) : renderTextWithLinks(LEGAL_TEXT, LEGAL_LINKS, { color: solidColor(theme.primaryColor), textDecorationLine: "underline" }) }) : null
|
|
5187
|
-
] })
|
|
5188
|
-
]
|
|
5189
|
-
}
|
|
5190
|
-
);
|
|
5191
|
-
};
|
|
5192
|
-
var RinggWidget = ({ controller, room }) => {
|
|
5193
|
-
const shell = useRinggShell(controller);
|
|
5194
|
-
const { width: screenWidth, height: screenHeight } = (0, import_react_native28.useWindowDimensions)();
|
|
5195
|
-
const { buttons, defaultExpanded = WIDGET_DEFAULTS.defaultExpanded, widgetPosition } = controller.config;
|
|
5196
|
-
const isOpen = shell.viewState === "open" || shell.viewState === "feedback";
|
|
5197
|
-
const hideTriggerOnExpand = widgetPosition?.hideTriggerOnExpand ?? true;
|
|
5198
|
-
const showTrigger = !defaultExpanded && (!hideTriggerOnExpand || !isOpen);
|
|
5199
|
-
const panel = usePresence(isOpen, PANEL_SPRING);
|
|
5200
|
-
const trigger = usePresence(showTrigger, TRIGGER_SPRING);
|
|
5201
|
-
const panelWidth = Math.min(screenWidth - EDGE_INSET * 2, PANEL_MAX_WIDTH);
|
|
5202
|
-
const panelHeight = Math.min(screenHeight * PANEL_HEIGHT_RATIO, PANEL_HEIGHT);
|
|
5203
|
-
const triggerIconSize = toSize(buttons?.modalTrigger?.icon?.size, DEFAULT_TRIGGER_ICON_SIZE);
|
|
5204
|
-
return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(WidgetThemeProvider, { theme: controller.theme, children: /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_react_native28.View, { pointerEvents: "box-none", style: import_react_native28.StyleSheet.absoluteFill, children: [
|
|
5205
|
-
panel.mounted ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_react_native28.KeyboardAvoidingView, { pointerEvents: "box-none", behavior: import_react_native28.Platform.OS === "ios" ? "padding" : void 0, style: styles24.overlay, children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
5206
|
-
import_react_native28.Animated.View,
|
|
5207
|
-
{
|
|
5208
|
-
renderToHardwareTextureAndroid: true,
|
|
5209
|
-
shouldRasterizeIOS: true,
|
|
5210
|
-
style: [
|
|
5211
|
-
styles24.panelAnchor,
|
|
5212
|
-
{
|
|
5213
|
-
opacity: panel.progress,
|
|
5214
|
-
transform: [{ translateY: panel.progress.interpolate({ inputRange: [0, 1], outputRange: [24, 0] }) }, { scale: panel.progress.interpolate({ inputRange: [0, 1], outputRange: [0.96, 1] }) }]
|
|
5215
|
-
}
|
|
5216
|
-
],
|
|
5217
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(WidgetPanel, { controller, room, width: panelWidth, height: panelHeight })
|
|
5218
|
-
}
|
|
5219
|
-
) }) : null,
|
|
5220
|
-
trigger.mounted ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(WidgetTrigger, { controller, iconSize: triggerIconSize, progress: trigger.progress }) : null
|
|
5221
|
-
] }) });
|
|
5222
|
-
};
|
|
5223
|
-
var WidgetTrigger = ({ controller, iconSize, progress }) => {
|
|
5224
|
-
const theme = useWidgetTheme();
|
|
5225
|
-
const { buttons } = controller.config;
|
|
5226
|
-
return (
|
|
5227
|
-
// Web pops the trigger with a spring and fades it out again when the panel
|
|
5228
|
-
// takes over; both ends of that are visible on every open/close cycle.
|
|
5229
|
-
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_react_native28.Animated.View, { style: [styles24.trigger, { opacity: progress, transform: [{ scale: progress.interpolate({ inputRange: [0, 1], outputRange: [0.8, 1] }) }] }], children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(Touchable, { testID: ringgId("trigger-button"), accessibilityLabel: "Open call widget", onPress: () => controller.handleTriggerClick(), children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(GradientFill, { color: theme.primaryColor, style: [styles24.triggerSurface, toViewStyle(buttons?.modalTrigger?.styles)], children: buttons?.modalTrigger?.icon?.url ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
5230
|
-
import_react_native28.Image,
|
|
5231
|
-
{
|
|
5232
|
-
testID: ringgId("trigger-icon"),
|
|
5233
|
-
accessibilityLabel: "Widget trigger",
|
|
5234
|
-
source: { uri: buttons.modalTrigger.icon.url },
|
|
5235
|
-
resizeMode: "contain",
|
|
5236
|
-
style: { width: iconSize, height: iconSize }
|
|
5237
|
-
}
|
|
5238
|
-
) : /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(PhoneCallRingIcon, { testID: ringgId("trigger-icon"), size: iconSize, fill: solidColor(theme.primaryTextColor) }) }) }) })
|
|
5239
|
-
);
|
|
5240
|
-
};
|
|
5241
|
-
var styles24 = import_react_native28.StyleSheet.create({
|
|
5242
|
-
overlay: { ...import_react_native28.StyleSheet.absoluteFill, justifyContent: "flex-end", alignItems: "flex-end" },
|
|
5243
|
-
panelAnchor: { marginHorizontal: EDGE_INSET, marginTop: EDGE_INSET, marginBottom: BOTTOM_SAFE_INSET },
|
|
5244
|
-
panel: {
|
|
5245
|
-
borderWidth: import_react_native28.StyleSheet.hairlineWidth,
|
|
5246
|
-
// `overflow: hidden` is what keeps the transcript, the header bar and the
|
|
5247
|
-
// rounded corners agreeing with each other.
|
|
5248
|
-
overflow: "hidden",
|
|
5249
|
-
// Web stacks three shadows at 1-2% alpha — the panel sits just off the page
|
|
5250
|
-
// rather than floating above it. 12% read as a hard slab, and on Android
|
|
5251
|
-
// `elevation` is the only one of these that applies, so it was the entire
|
|
5252
|
-
// effect instead of a supporting layer.
|
|
5253
|
-
shadowColor: "#000000",
|
|
5254
|
-
shadowOpacity: 0.05,
|
|
5255
|
-
shadowRadius: 20,
|
|
5256
|
-
shadowOffset: { width: 0, height: 8 },
|
|
5257
|
-
elevation: 4
|
|
5258
|
-
},
|
|
5259
|
-
trigger: { position: "absolute", right: EDGE_INSET, bottom: BOTTOM_SAFE_INSET },
|
|
5260
|
-
triggerSurface: { width: TRIGGER_SIZE, height: TRIGGER_SIZE, borderRadius: TRIGGER_RADIUS, alignItems: "center", justifyContent: "center" },
|
|
5261
|
-
chromeButton: { position: "absolute", top: 10, zIndex: 20, width: 28, height: 28, borderRadius: 14, alignItems: "center", justifyContent: "center" },
|
|
5262
|
-
minimizeButton: { right: 42 },
|
|
5263
|
-
closeButton: { right: 10 },
|
|
5264
|
-
body: { flex: 1 },
|
|
5265
|
-
bodyLive: { paddingHorizontal: 20, paddingVertical: 8, gap: 10 },
|
|
5266
|
-
bodyIdle: { padding: 24, gap: 12 },
|
|
5267
|
-
bodySpacer: { flex: 1, marginTop: 56 },
|
|
5268
|
-
voiceWrapper: { width: "100%", alignItems: "stretch", justifyContent: "center", marginTop: 56 },
|
|
5269
|
-
voiceWrapperCompact: { height: 56, flexShrink: 0 },
|
|
5270
|
-
voiceWrapperFull: { flex: 1, minHeight: 0 },
|
|
5271
|
-
transcript: { flex: 1 },
|
|
5272
|
-
// `justifyContent: flex-end` is web's `mt-auto`: a short conversation sits at
|
|
5273
|
-
// the bottom of the panel rather than floating at the top.
|
|
5274
|
-
transcriptList: { flexGrow: 1, justifyContent: "flex-end", gap: 12 },
|
|
5275
|
-
transcriptTopClearance: { paddingTop: 64 },
|
|
5276
|
-
transcriptBottomClearance: { paddingBottom: 80 },
|
|
5277
|
-
fullWidth: { width: "100%" },
|
|
5278
|
-
controlsBar: { position: "absolute", zIndex: 10, bottom: 0, left: 0, right: 0, paddingHorizontal: 20, paddingVertical: 12 },
|
|
5279
|
-
error: { textAlign: "center", fontSize: 14, marginBottom: 8 },
|
|
5280
|
-
connecting: { fontSize: 14, textAlign: "center" },
|
|
5281
|
-
startActions: { flexDirection: "row", alignItems: "center", gap: 8, width: "100%" },
|
|
5282
|
-
legal: { fontSize: 12, lineHeight: 20, textAlign: "center" }
|
|
5283
|
-
});
|
|
5284
|
-
|
|
5285
|
-
// src/transport/livekit-transport.ts
|
|
5286
|
-
var import_react_native30 = require("@livekit/react-native");
|
|
5287
|
-
var import_livekit_client5 = require("livekit-client");
|
|
5288
|
-
|
|
5289
|
-
// src/platform/audio-session.ts
|
|
5290
|
-
var import_react_native29 = require("@livekit/react-native");
|
|
5291
|
-
var PREFERRED_OUTPUTS = ["bluetooth", "headset", "speaker", "earpiece"];
|
|
5292
|
-
var createAudioSession = () => {
|
|
5293
|
-
let active = false;
|
|
5294
|
-
const start = async () => {
|
|
5295
|
-
if (active) return;
|
|
5296
|
-
active = true;
|
|
5297
|
-
try {
|
|
5298
|
-
await import_react_native29.AudioSession.configureAudio({
|
|
5299
|
-
android: {
|
|
5300
|
-
audioTypeOptions: import_react_native29.AndroidAudioTypePresets.communication,
|
|
5301
|
-
preferredOutputList: [...PREFERRED_OUTPUTS]
|
|
5302
|
-
},
|
|
5303
|
-
ios: { defaultOutput: "speaker" }
|
|
5304
|
-
});
|
|
5305
|
-
await import_react_native29.AudioSession.startAudioSession();
|
|
5306
|
-
} catch {
|
|
5307
|
-
}
|
|
5282
|
+
var defaultUrlResolver = createStaticUrlResolver(DEFAULT_ENVIRONMENT_URLS);
|
|
5283
|
+
|
|
5284
|
+
// src/platform/mic-permission.ts
|
|
5285
|
+
var import_react_native28 = require("react-native");
|
|
5286
|
+
var import_react_native_webrtc = require("@livekit/react-native-webrtc");
|
|
5287
|
+
var stopTracks = (stream) => {
|
|
5288
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
5289
|
+
};
|
|
5290
|
+
var probeMicrophone = async () => {
|
|
5291
|
+
try {
|
|
5292
|
+
const stream = await import_react_native_webrtc.mediaDevices.getUserMedia({ audio: true });
|
|
5293
|
+
stopTracks(stream);
|
|
5294
|
+
return true;
|
|
5295
|
+
} catch {
|
|
5296
|
+
return false;
|
|
5297
|
+
}
|
|
5298
|
+
};
|
|
5299
|
+
var ANDROID_RATIONALE = {
|
|
5300
|
+
title: "Microphone access",
|
|
5301
|
+
message: "Voice calls need the microphone to hear you.",
|
|
5302
|
+
buttonPositive: "Allow",
|
|
5303
|
+
buttonNegative: "Not now"
|
|
5304
|
+
};
|
|
5305
|
+
var createNativeMicPermission = () => ({
|
|
5306
|
+
async isGranted() {
|
|
5307
|
+
if (import_react_native28.Platform.OS === "android") {
|
|
5308
|
+
return import_react_native28.PermissionsAndroid.check(import_react_native28.PermissionsAndroid.PERMISSIONS.RECORD_AUDIO);
|
|
5309
|
+
}
|
|
5310
|
+
return probeMicrophone();
|
|
5311
|
+
},
|
|
5312
|
+
async request() {
|
|
5313
|
+
if (import_react_native28.Platform.OS === "android") {
|
|
5314
|
+
const result = await import_react_native28.PermissionsAndroid.request(import_react_native28.PermissionsAndroid.PERMISSIONS.RECORD_AUDIO, ANDROID_RATIONALE);
|
|
5315
|
+
return result === import_react_native28.PermissionsAndroid.RESULTS.GRANTED;
|
|
5316
|
+
}
|
|
5317
|
+
return probeMicrophone();
|
|
5318
|
+
}
|
|
5319
|
+
});
|
|
5320
|
+
|
|
5321
|
+
// src/transport/livekit-transport.ts
|
|
5322
|
+
var import_react_native30 = require("@livekit/react-native");
|
|
5323
|
+
var import_livekit_client5 = require("livekit-client");
|
|
5324
|
+
|
|
5325
|
+
// src/platform/audio-session.ts
|
|
5326
|
+
var import_react_native29 = require("@livekit/react-native");
|
|
5327
|
+
var PREFERRED_OUTPUTS = ["bluetooth", "headset", "speaker", "earpiece"];
|
|
5328
|
+
var createAudioSession = () => {
|
|
5329
|
+
let active = false;
|
|
5330
|
+
const start = async () => {
|
|
5331
|
+
if (active) return;
|
|
5332
|
+
active = true;
|
|
5333
|
+
try {
|
|
5334
|
+
await import_react_native29.AudioSession.configureAudio({
|
|
5335
|
+
android: {
|
|
5336
|
+
audioTypeOptions: import_react_native29.AndroidAudioTypePresets.communication,
|
|
5337
|
+
preferredOutputList: [...PREFERRED_OUTPUTS]
|
|
5338
|
+
},
|
|
5339
|
+
ios: { defaultOutput: "speaker" }
|
|
5340
|
+
});
|
|
5341
|
+
await import_react_native29.AudioSession.startAudioSession();
|
|
5342
|
+
} catch {
|
|
5343
|
+
}
|
|
5308
5344
|
};
|
|
5309
5345
|
const stop = async () => {
|
|
5310
5346
|
if (!active) return;
|
|
@@ -5630,41 +5666,371 @@ var createLiveKitTransport = (options = {}) => {
|
|
|
5630
5666
|
return { transport, room, dispose };
|
|
5631
5667
|
};
|
|
5632
5668
|
|
|
5633
|
-
// src/
|
|
5634
|
-
var
|
|
5635
|
-
var
|
|
5636
|
-
var
|
|
5637
|
-
|
|
5638
|
-
|
|
5639
|
-
var
|
|
5640
|
-
|
|
5641
|
-
|
|
5642
|
-
|
|
5643
|
-
|
|
5644
|
-
|
|
5645
|
-
|
|
5646
|
-
|
|
5647
|
-
|
|
5648
|
-
|
|
5649
|
-
|
|
5650
|
-
message: "Voice calls need the microphone to hear you.",
|
|
5651
|
-
buttonPositive: "Allow",
|
|
5652
|
-
buttonNegative: "Not now"
|
|
5669
|
+
// src/RinggWidget.tsx
|
|
5670
|
+
var import_jsx_runtime28 = require("react/jsx-runtime");
|
|
5671
|
+
var PANEL_MAX_WIDTH = 400;
|
|
5672
|
+
var PANEL_HEIGHT = 456;
|
|
5673
|
+
var PANEL_HEIGHT_RATIO = 0.9;
|
|
5674
|
+
var EDGE_INSET = 16;
|
|
5675
|
+
var BOTTOM_SAFE_INSET = 24;
|
|
5676
|
+
var TRIGGER_SIZE = 52;
|
|
5677
|
+
var TRIGGER_RADIUS = 20;
|
|
5678
|
+
var DEFAULT_TRIGGER_ICON_SIZE = 24;
|
|
5679
|
+
var PANEL_SPRING = { stiffness: 380, damping: 30 };
|
|
5680
|
+
var TRIGGER_SPRING = { stiffness: 400, damping: 25 };
|
|
5681
|
+
var LEGAL_LINKS = { "Privacy Policy": "https://www.ringg.ai/privacy", "T&C": "https://www.ringg.ai/terms" };
|
|
5682
|
+
var LEGAL_TEXT = "By continuing, you agree to our\nPrivacy Policy and T&C.";
|
|
5683
|
+
var isFreeButtonsMessage = (message) => {
|
|
5684
|
+
const componentData = message?.componentData;
|
|
5685
|
+
return !!componentData && isButtons2(componentData) && componentData.data.presentation === "free";
|
|
5653
5686
|
};
|
|
5654
|
-
var
|
|
5655
|
-
|
|
5656
|
-
|
|
5657
|
-
|
|
5687
|
+
var isAttachableAgentMessage = (message) => !!message && !message.componentType && !message.isSelf && Boolean(message.message.trim());
|
|
5688
|
+
var WidgetPanel = ({ controller, room, width, height }) => {
|
|
5689
|
+
const theme = useWidgetTheme();
|
|
5690
|
+
const { config } = controller;
|
|
5691
|
+
const {
|
|
5692
|
+
title = WIDGET_DEFAULTS.title,
|
|
5693
|
+
description = WIDGET_DEFAULTS.description,
|
|
5694
|
+
buttons,
|
|
5695
|
+
defaultExpanded = WIDGET_DEFAULTS.defaultExpanded,
|
|
5696
|
+
hideTabSelector = WIDGET_DEFAULTS.hideTabSelector,
|
|
5697
|
+
bypassStartScreen = WIDGET_DEFAULTS.bypassStartScreen,
|
|
5698
|
+
logoUrl,
|
|
5699
|
+
logoStyles,
|
|
5700
|
+
typingWords,
|
|
5701
|
+
legalDisclaimer,
|
|
5702
|
+
feedbackScreen,
|
|
5703
|
+
voiceCall
|
|
5704
|
+
} = config;
|
|
5705
|
+
const shell = useRinggShell(controller);
|
|
5706
|
+
const session = useRinggSession(controller);
|
|
5707
|
+
const { messages } = useRinggMessages(controller);
|
|
5708
|
+
const { isTyping: isAgentTyping } = useRinggTyping(controller);
|
|
5709
|
+
const { completedFlowIds } = useRinggComponents(controller);
|
|
5710
|
+
const { commands } = useRinggSlashCommands(controller);
|
|
5711
|
+
const [alertEndCall, setAlertEndCall] = (0, import_react24.useState)(false);
|
|
5712
|
+
const microphone = useMicrophone(room);
|
|
5713
|
+
const callMode = shell.callMode;
|
|
5714
|
+
const showFeedback = shell.viewState === "feedback";
|
|
5715
|
+
const isLoading = session.isLoading;
|
|
5716
|
+
const error = session.error;
|
|
5717
|
+
const connectionState = session.connectionState;
|
|
5718
|
+
const isCalling = connectionState === "connected";
|
|
5719
|
+
const isSessionLive = session.isSessionLive;
|
|
5720
|
+
const isAudioMode = callMode === "audio";
|
|
5721
|
+
const showVoiceAnimation = isAudioMode && isCalling && (voiceCall?.showAnimation ?? WIDGET_DEFAULTS.voiceCallShowAnimation);
|
|
5722
|
+
const showVoiceTranscript = !isAudioMode || (voiceCall?.showTranscript ?? WIDGET_DEFAULTS.voiceCallShowTranscript);
|
|
5723
|
+
const mergedSlashCommands = (0, import_react24.useMemo)(() => [...commands], [commands]);
|
|
5724
|
+
const sortedMessages = (0, import_react24.useMemo)(() => [...messages].sort((a, b) => a.timestamp - b.timestamp), [messages]);
|
|
5725
|
+
const transcriptRef = (0, import_react24.useRef)(null);
|
|
5726
|
+
const previousMessagesCountRef = (0, import_react24.useRef)(0);
|
|
5727
|
+
(0, import_react24.useEffect)(() => {
|
|
5728
|
+
const newMessageAdded = messages.length > previousMessagesCountRef.current;
|
|
5729
|
+
previousMessagesCountRef.current = messages.length;
|
|
5730
|
+
transcriptRef.current?.scrollToEnd({ animated: newMessageAdded });
|
|
5731
|
+
}, [messages, isAgentTyping]);
|
|
5732
|
+
const handleCallStart = (mediaType) => void controller.startCall(mediaType);
|
|
5733
|
+
const handleCallEnd = () => {
|
|
5734
|
+
setAlertEndCall(false);
|
|
5735
|
+
void controller.endCall();
|
|
5736
|
+
};
|
|
5737
|
+
const handleFeedbackSubmit = (rating, comment) => void controller.submitFeedback(rating, comment);
|
|
5738
|
+
const handleFeedbackSkip = () => controller.skipFeedback();
|
|
5739
|
+
const handleUserMessageSent = () => controller.typing.showOnUserSend();
|
|
5740
|
+
const handleFlowComplete = (componentId) => controller.markFlowComplete(componentId);
|
|
5741
|
+
const handleSelectionDisplayed = (componentId, label) => controller.displaySelection(componentId, label);
|
|
5742
|
+
const sendComponentResponse = (componentId, responseData) => controller.sendComponentResponse(componentId, responseData);
|
|
5743
|
+
const sendSlashCommand = (command) => void controller.sendSlashCommand(command);
|
|
5744
|
+
const callComponentApi = controller.callComponentApi;
|
|
5745
|
+
const panelRadius = resolveRadius(theme.borderRadius, 16);
|
|
5746
|
+
const fontFamily = resolveFontFamily(theme.fontFamily);
|
|
5747
|
+
const chrome = /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_jsx_runtime28.Fragment, { children: [
|
|
5748
|
+
!defaultExpanded && (isSessionLive || showFeedback) ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(Touchable, { testID: ringgId("minimize-button"), accessibilityLabel: "Minimize widget", onPress: () => controller.minimizeWidget(), style: [styles24.chromeButton, styles24.minimizeButton], children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_lucide_react_native10.Minus, { size: 16, color: solidColor(theme.mutedTextColor) }) }) : null,
|
|
5749
|
+
!defaultExpanded ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
5750
|
+
Touchable,
|
|
5751
|
+
{
|
|
5752
|
+
testID: ringgId("close-button"),
|
|
5753
|
+
accessibilityLabel: isSessionLive ? "End call" : "Close widget",
|
|
5754
|
+
onPress: () => isSessionLive ? setAlertEndCall(true) : showFeedback ? handleFeedbackSkip() : controller.minimizeWidget(),
|
|
5755
|
+
style: [styles24.chromeButton, styles24.closeButton],
|
|
5756
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_lucide_react_native10.X, { size: 16, color: solidColor(theme.mutedTextColor) })
|
|
5757
|
+
}
|
|
5758
|
+
) : null
|
|
5759
|
+
] });
|
|
5760
|
+
const transcript = /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
|
|
5761
|
+
import_react_native31.ScrollView,
|
|
5762
|
+
{
|
|
5763
|
+
testID: ringgId("transcript"),
|
|
5764
|
+
ref: transcriptRef,
|
|
5765
|
+
style: styles24.transcript,
|
|
5766
|
+
contentContainerStyle: [styles24.transcriptList, !showVoiceAnimation && styles24.transcriptTopClearance, !isAudioMode && styles24.transcriptBottomClearance],
|
|
5767
|
+
keyboardShouldPersistTaps: "handled",
|
|
5768
|
+
showsVerticalScrollIndicator: false,
|
|
5769
|
+
children: [
|
|
5770
|
+
sortedMessages.map((segment, index) => {
|
|
5771
|
+
if (segment.kind === "system") return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(system_log_default, { label: segment.message, meta: segment.systemMeta, level: segment.systemLevel }, `sys_${segment.timestamp}_${index}`);
|
|
5772
|
+
const previousSegment = sortedMessages[index - 1];
|
|
5773
|
+
const nextSegment = sortedMessages[index + 1];
|
|
5774
|
+
const attachFreeButtonsToPrevious = isFreeButtonsMessage(segment) && isAttachableAgentMessage(previousSegment);
|
|
5775
|
+
const skipStandaloneRender = isAttachableAgentMessage(segment) && isFreeButtonsMessage(nextSegment);
|
|
5776
|
+
if (skipStandaloneRender) return null;
|
|
5777
|
+
if (attachFreeButtonsToPrevious && previousSegment && segment.componentData) {
|
|
5778
|
+
const componentData = segment.componentData;
|
|
5779
|
+
const isCompletedSelectedItem = componentData.component_type === "buttons" && componentData.data.completionDisplay === "selected_item" && completedFlowIds.has(componentData.component_id);
|
|
5780
|
+
if (isCompletedSelectedItem) return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(message_item_default, { itemIndex: index, message: previousSegment }, `message_with_buttons_${segment.componentData.component_id}_${index}`);
|
|
5781
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_react24.Fragment, { children: [
|
|
5782
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(message_item_default, { itemIndex: index, message: previousSegment }),
|
|
5783
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_react_native31.View, { testID: ringgId("attached-buttons"), style: styles24.fullWidth, children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
5784
|
+
interactive_flow_default,
|
|
5785
|
+
{
|
|
5786
|
+
initialComponent: segment.componentData,
|
|
5787
|
+
onApiCall: callComponentApi,
|
|
5788
|
+
onComplete: () => handleFlowComplete(componentData.component_id),
|
|
5789
|
+
onSendResponse: sendComponentResponse,
|
|
5790
|
+
onSelectionDisplayed: handleSelectionDisplayed,
|
|
5791
|
+
quickReplyAlign: isAudioMode ? "center" : "end"
|
|
5792
|
+
}
|
|
5793
|
+
) })
|
|
5794
|
+
] }, `message_with_buttons_${segment.componentData.component_id}_${index}`);
|
|
5795
|
+
}
|
|
5796
|
+
if (segment.componentType === "blocks" && segment.componentData) {
|
|
5797
|
+
const blocksPayload = segment.componentData;
|
|
5798
|
+
if (isBlocks2(blocksPayload)) {
|
|
5799
|
+
const blocksConfig = blocksPayload.data;
|
|
5800
|
+
const blockId = blocksPayload.component_id;
|
|
5801
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_react_native31.View, { testID: ringgId("blocks-widget"), style: styles24.fullWidth, children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
5802
|
+
blocks_step_default,
|
|
5803
|
+
{
|
|
5804
|
+
config: blocksConfig,
|
|
5805
|
+
disabled: completedFlowIds.has(blockId),
|
|
5806
|
+
onAction: (action) => {
|
|
5807
|
+
handleUserMessageSent();
|
|
5808
|
+
handleFlowComplete(blockId);
|
|
5809
|
+
const selection = typeof action.value === "string" || typeof action.value === "number" ? String(action.value) : action.label;
|
|
5810
|
+
if (selection) handleSelectionDisplayed(blockId, selection);
|
|
5811
|
+
return controller.sendBlocksAction(blocksConfig.tool_id, blockId, { action_id: action.action_id, value: action.value, values: action.values });
|
|
5812
|
+
}
|
|
5813
|
+
}
|
|
5814
|
+
) }, `blocks_${blockId}`);
|
|
5815
|
+
}
|
|
5816
|
+
}
|
|
5817
|
+
if (segment.componentType && segment.componentData) {
|
|
5818
|
+
const componentData = segment.componentData;
|
|
5819
|
+
const isCompletedSelectedItem = componentData.component_type === "buttons" && componentData.data.completionDisplay === "selected_item" && completedFlowIds.has(componentData.component_id);
|
|
5820
|
+
if (isCompletedSelectedItem) return null;
|
|
5821
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
5822
|
+
interactive_flow_default,
|
|
5823
|
+
{
|
|
5824
|
+
initialComponent: segment.componentData,
|
|
5825
|
+
onApiCall: callComponentApi,
|
|
5826
|
+
onComplete: () => handleFlowComplete(componentData.component_id),
|
|
5827
|
+
onSendResponse: sendComponentResponse,
|
|
5828
|
+
onSelectionDisplayed: handleSelectionDisplayed,
|
|
5829
|
+
quickReplyAlign: isAudioMode ? "center" : "end"
|
|
5830
|
+
},
|
|
5831
|
+
`flow_${segment.componentData.component_id}_${index}`
|
|
5832
|
+
);
|
|
5833
|
+
}
|
|
5834
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(message_item_default, { itemIndex: index, message: segment }, `${segment.timestamp}__${index}`);
|
|
5835
|
+
}),
|
|
5836
|
+
isAgentTyping ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(typing_indicator_default, { words: typingWords }) : null
|
|
5837
|
+
]
|
|
5658
5838
|
}
|
|
5659
|
-
|
|
5660
|
-
|
|
5661
|
-
|
|
5662
|
-
|
|
5663
|
-
|
|
5664
|
-
|
|
5839
|
+
);
|
|
5840
|
+
const controls = /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
5841
|
+
call_controls_default,
|
|
5842
|
+
{
|
|
5843
|
+
buttonConfig: buttons,
|
|
5844
|
+
callMode,
|
|
5845
|
+
isCalling: isSessionLive,
|
|
5846
|
+
enabledSlashCommands: mergedSlashCommands,
|
|
5847
|
+
micEnabled: microphone?.enabled ?? true,
|
|
5848
|
+
onToggleMic: microphone?.toggle,
|
|
5849
|
+
onSlashCommand: sendSlashCommand,
|
|
5850
|
+
onUserMessage: handleUserMessageSent,
|
|
5851
|
+
onCallEnd: handleCallEnd,
|
|
5852
|
+
onSendMessage: (message) => controller.sendMessage(message)
|
|
5665
5853
|
}
|
|
5666
|
-
|
|
5667
|
-
|
|
5854
|
+
);
|
|
5855
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
|
|
5856
|
+
import_react_native31.View,
|
|
5857
|
+
{
|
|
5858
|
+
testID: ringgId("widget-root"),
|
|
5859
|
+
style: [styles24.panel, { width, height, borderRadius: panelRadius, backgroundColor: solidColor(theme.backgroundColor), borderColor: solidColor(theme.borderColor) }],
|
|
5860
|
+
children: [
|
|
5861
|
+
chrome,
|
|
5862
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
5863
|
+
ConfirmDialog,
|
|
5864
|
+
{
|
|
5865
|
+
open: alertEndCall,
|
|
5866
|
+
title: `Are you sure you want to end ${callMode === "text" ? "chat" : "call"}?`,
|
|
5867
|
+
description: "Your current conversation will be closed and cannot be accessed later.",
|
|
5868
|
+
confirmLabel: `End ${callMode === "text" ? "Chat" : "Call"}`,
|
|
5869
|
+
cancelLabel: "Cancel",
|
|
5870
|
+
onConfirm: handleCallEnd,
|
|
5871
|
+
onCancel: () => setAlertEndCall(false)
|
|
5872
|
+
}
|
|
5873
|
+
),
|
|
5874
|
+
showFeedback ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
5875
|
+
feedback_screen_default,
|
|
5876
|
+
{
|
|
5877
|
+
callMode,
|
|
5878
|
+
isSubmitting: isLoading,
|
|
5879
|
+
feedbackScreenConfig: feedbackScreen,
|
|
5880
|
+
messages,
|
|
5881
|
+
title,
|
|
5882
|
+
logoUrl,
|
|
5883
|
+
logoStyles,
|
|
5884
|
+
onSubmit: handleFeedbackSubmit,
|
|
5885
|
+
onSkip: handleFeedbackSkip
|
|
5886
|
+
}
|
|
5887
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_react_native31.View, { testID: ringgId("widget-body"), style: [styles24.body, isSessionLive ? styles24.bodyLive : styles24.bodyIdle], children: [
|
|
5888
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(header_default, { isCalling: isSessionLive, isLoading, title, description, logoUrl, logoStyles }),
|
|
5889
|
+
showVoiceAnimation ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_react_native31.View, { testID: ringgId("voice-animation-wrapper"), style: [styles24.voiceWrapper, showVoiceTranscript && messages.length > 0 ? styles24.voiceWrapperCompact : styles24.voiceWrapperFull], children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(VoiceAnimationLeaf, { room }) }) : null,
|
|
5890
|
+
isSessionLive && !showVoiceAnimation && (messages.length === 0 && !isAgentTyping || !showVoiceTranscript) ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_react_native31.View, { testID: ringgId("body-spacer"), style: styles24.bodySpacer }) : null,
|
|
5891
|
+
(messages.length > 0 || isAgentTyping) && isSessionLive && showVoiceTranscript ? transcript : null,
|
|
5892
|
+
isSessionLive && callMode === "text" ? (
|
|
5893
|
+
// Transparent bottom bar: it only positions the composer over the
|
|
5894
|
+
// transcript, so the gutters stay clear while messages scroll under.
|
|
5895
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_react_native31.View, { testID: ringgId("controls-bar"), style: styles24.controlsBar, children: [
|
|
5896
|
+
error.hasError ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_react_native31.Text, { testID: ringgId("error-message"), style: [styles24.error, { color: solidColor(theme.errorColor), fontFamily }], children: error.message }) : null,
|
|
5897
|
+
controls
|
|
5898
|
+
] })
|
|
5899
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_jsx_runtime28.Fragment, { children: [
|
|
5900
|
+
controls,
|
|
5901
|
+
error.hasError ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_react_native31.Text, { testID: ringgId("error-message"), style: [styles24.error, { color: solidColor(theme.errorColor), fontFamily }], children: error.message }) : null
|
|
5902
|
+
] }),
|
|
5903
|
+
!isSessionLive && isLoading ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_react_native31.View, { testID: ringgId("connecting-status"), children: [
|
|
5904
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_react_native31.Text, { testID: ringgId("connecting-label"), style: [styles24.connecting, { color: theme.mutedTextColor, fontFamily }], children: "Connecting..." }),
|
|
5905
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(loading_bar_default, { ariaLabel: callMode === "text" ? "Starting chat" : "Connecting" })
|
|
5906
|
+
] }) : null,
|
|
5907
|
+
!isLoading && connectionState === "disconnected" && (!bypassStartScreen || error.hasError) ? hideTabSelector ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(action_button_default, { callMode, buttonConfig: buttons, isCalling, isLoading, onCallTrigger: handleCallStart }) : /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_react_native31.View, { testID: ringgId("start-actions"), style: styles24.startActions, children: [
|
|
5908
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(action_button_default, { dual: true, callMode: "text", buttonConfig: buttons, isCalling, isLoading, onCallTrigger: handleCallStart }),
|
|
5909
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(action_button_default, { dual: true, callMode: "audio", buttonConfig: buttons, isCalling, isLoading, onCallTrigger: handleCallStart })
|
|
5910
|
+
] }) : null,
|
|
5911
|
+
!isSessionLive ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_react_native31.Text, { testID: ringgId("legal-disclaimer"), style: [styles24.legal, { color: theme.mutedTextColor, fontFamily }], children: legalDisclaimer ? renderTextWithLinks(legalDisclaimer.text, legalDisclaimer.links) : renderTextWithLinks(LEGAL_TEXT, LEGAL_LINKS, { color: solidColor(theme.primaryColor), textDecorationLine: "underline" }) }) : null
|
|
5912
|
+
] })
|
|
5913
|
+
]
|
|
5914
|
+
}
|
|
5915
|
+
);
|
|
5916
|
+
};
|
|
5917
|
+
var ControlledRinggWidget = ({ controller, room }) => {
|
|
5918
|
+
const shell = useRinggShell(controller);
|
|
5919
|
+
const { width: screenWidth, height: screenHeight } = (0, import_react_native31.useWindowDimensions)();
|
|
5920
|
+
const { buttons, defaultExpanded = WIDGET_DEFAULTS.defaultExpanded, widgetPosition } = controller.config;
|
|
5921
|
+
const isOpen = shell.viewState === "open" || shell.viewState === "feedback";
|
|
5922
|
+
const hideTriggerOnExpand = widgetPosition?.hideTriggerOnExpand ?? true;
|
|
5923
|
+
const showTrigger = !defaultExpanded && (!hideTriggerOnExpand || !isOpen);
|
|
5924
|
+
const panel = usePresence(isOpen, PANEL_SPRING);
|
|
5925
|
+
const trigger = usePresence(showTrigger, TRIGGER_SPRING);
|
|
5926
|
+
const panelWidth = Math.min(screenWidth - EDGE_INSET * 2, PANEL_MAX_WIDTH);
|
|
5927
|
+
const panelHeight = Math.min(screenHeight * PANEL_HEIGHT_RATIO, PANEL_HEIGHT);
|
|
5928
|
+
const triggerIconSize = toSize(buttons?.modalTrigger?.icon?.size, DEFAULT_TRIGGER_ICON_SIZE);
|
|
5929
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(WidgetThemeProvider, { theme: controller.theme, children: /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_react_native31.View, { pointerEvents: "box-none", style: import_react_native31.StyleSheet.absoluteFill, children: [
|
|
5930
|
+
panel.mounted ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_react_native31.KeyboardAvoidingView, { pointerEvents: "box-none", behavior: import_react_native31.Platform.OS === "ios" ? "padding" : void 0, style: styles24.overlay, children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
5931
|
+
import_react_native31.Animated.View,
|
|
5932
|
+
{
|
|
5933
|
+
renderToHardwareTextureAndroid: true,
|
|
5934
|
+
shouldRasterizeIOS: true,
|
|
5935
|
+
style: [
|
|
5936
|
+
styles24.panelAnchor,
|
|
5937
|
+
{
|
|
5938
|
+
opacity: panel.progress,
|
|
5939
|
+
transform: [{ translateY: panel.progress.interpolate({ inputRange: [0, 1], outputRange: [24, 0] }) }, { scale: panel.progress.interpolate({ inputRange: [0, 1], outputRange: [0.96, 1] }) }]
|
|
5940
|
+
}
|
|
5941
|
+
],
|
|
5942
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(WidgetPanel, { controller, room, width: panelWidth, height: panelHeight })
|
|
5943
|
+
}
|
|
5944
|
+
) }) : null,
|
|
5945
|
+
trigger.mounted ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(WidgetTrigger, { controller, iconSize: triggerIconSize, progress: trigger.progress }) : null
|
|
5946
|
+
] }) });
|
|
5947
|
+
};
|
|
5948
|
+
var WidgetTrigger = ({ controller, iconSize, progress }) => {
|
|
5949
|
+
const theme = useWidgetTheme();
|
|
5950
|
+
const { buttons } = controller.config;
|
|
5951
|
+
return (
|
|
5952
|
+
// Web pops the trigger with a spring and fades it out again when the panel
|
|
5953
|
+
// takes over; both ends of that are visible on every open/close cycle.
|
|
5954
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_react_native31.Animated.View, { style: [styles24.trigger, { opacity: progress, transform: [{ scale: progress.interpolate({ inputRange: [0, 1], outputRange: [0.8, 1] }) }] }], children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(Touchable, { testID: ringgId("trigger-button"), accessibilityLabel: "Open call widget", onPress: () => controller.handleTriggerClick(), children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(GradientFill, { color: theme.primaryColor, style: [styles24.triggerSurface, toViewStyle(buttons?.modalTrigger?.styles)], children: buttons?.modalTrigger?.icon?.url ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
5955
|
+
import_react_native31.Image,
|
|
5956
|
+
{
|
|
5957
|
+
testID: ringgId("trigger-icon"),
|
|
5958
|
+
accessibilityLabel: "Widget trigger",
|
|
5959
|
+
source: { uri: buttons.modalTrigger.icon.url },
|
|
5960
|
+
resizeMode: "contain",
|
|
5961
|
+
style: { width: iconSize, height: iconSize }
|
|
5962
|
+
}
|
|
5963
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(PhoneCallRingIcon, { testID: ringgId("trigger-icon"), size: iconSize, fill: solidColor(theme.primaryTextColor) }) }) }) })
|
|
5964
|
+
);
|
|
5965
|
+
};
|
|
5966
|
+
var ManagedRinggWidget = ({ config, ports, onReady }) => {
|
|
5967
|
+
const stack = (0, import_react24.useMemo)(() => {
|
|
5968
|
+
const livekit = ports?.transport ? null : createLiveKitTransport();
|
|
5969
|
+
const controller = createRinggWidgetController(config, {
|
|
5970
|
+
urlResolver: defaultUrlResolver,
|
|
5971
|
+
micPermission: createNativeMicPermission(),
|
|
5972
|
+
...ports,
|
|
5973
|
+
transport: ports?.transport ?? livekit.transport
|
|
5974
|
+
});
|
|
5975
|
+
return { controller, livekit };
|
|
5976
|
+
}, [config]);
|
|
5977
|
+
(0, import_react24.useEffect)(
|
|
5978
|
+
() => () => {
|
|
5979
|
+
stack.controller.destroy();
|
|
5980
|
+
stack.livekit?.dispose();
|
|
5981
|
+
},
|
|
5982
|
+
[stack]
|
|
5983
|
+
);
|
|
5984
|
+
const onReadyRef = (0, import_react24.useRef)(onReady);
|
|
5985
|
+
onReadyRef.current = onReady;
|
|
5986
|
+
(0, import_react24.useEffect)(() => {
|
|
5987
|
+
onReadyRef.current?.(stack.controller);
|
|
5988
|
+
}, [stack]);
|
|
5989
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ControlledRinggWidget, { controller: stack.controller, room: stack.livekit?.room });
|
|
5990
|
+
};
|
|
5991
|
+
var RinggWidget = (props) => props.config ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ManagedRinggWidget, { config: props.config, ports: props.ports, onReady: props.onReady }) : /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ControlledRinggWidget, { controller: props.controller, room: props.room });
|
|
5992
|
+
var styles24 = import_react_native31.StyleSheet.create({
|
|
5993
|
+
overlay: { ...import_react_native31.StyleSheet.absoluteFill, justifyContent: "flex-end", alignItems: "flex-end" },
|
|
5994
|
+
panelAnchor: { marginHorizontal: EDGE_INSET, marginTop: EDGE_INSET, marginBottom: BOTTOM_SAFE_INSET },
|
|
5995
|
+
panel: {
|
|
5996
|
+
borderWidth: import_react_native31.StyleSheet.hairlineWidth,
|
|
5997
|
+
// `overflow: hidden` is what keeps the transcript, the header bar and the
|
|
5998
|
+
// rounded corners agreeing with each other.
|
|
5999
|
+
overflow: "hidden",
|
|
6000
|
+
// Web stacks three shadows at 1-2% alpha — the panel sits just off the page
|
|
6001
|
+
// rather than floating above it. 12% read as a hard slab, and on Android
|
|
6002
|
+
// `elevation` is the only one of these that applies, so it was the entire
|
|
6003
|
+
// effect instead of a supporting layer.
|
|
6004
|
+
shadowColor: "#000000",
|
|
6005
|
+
shadowOpacity: 0.05,
|
|
6006
|
+
shadowRadius: 20,
|
|
6007
|
+
shadowOffset: { width: 0, height: 8 },
|
|
6008
|
+
elevation: 4
|
|
6009
|
+
},
|
|
6010
|
+
trigger: { position: "absolute", right: EDGE_INSET, bottom: BOTTOM_SAFE_INSET },
|
|
6011
|
+
triggerSurface: { width: TRIGGER_SIZE, height: TRIGGER_SIZE, borderRadius: TRIGGER_RADIUS, alignItems: "center", justifyContent: "center" },
|
|
6012
|
+
chromeButton: { position: "absolute", top: 10, zIndex: 20, width: 28, height: 28, borderRadius: 14, alignItems: "center", justifyContent: "center" },
|
|
6013
|
+
minimizeButton: { right: 42 },
|
|
6014
|
+
closeButton: { right: 10 },
|
|
6015
|
+
body: { flex: 1 },
|
|
6016
|
+
bodyLive: { paddingHorizontal: 20, paddingVertical: 8, gap: 10 },
|
|
6017
|
+
bodyIdle: { padding: 24, gap: 12 },
|
|
6018
|
+
bodySpacer: { flex: 1, marginTop: 56 },
|
|
6019
|
+
voiceWrapper: { width: "100%", alignItems: "stretch", justifyContent: "center", marginTop: 56 },
|
|
6020
|
+
voiceWrapperCompact: { height: 56, flexShrink: 0 },
|
|
6021
|
+
voiceWrapperFull: { flex: 1, minHeight: 0 },
|
|
6022
|
+
transcript: { flex: 1 },
|
|
6023
|
+
// `justifyContent: flex-end` is web's `mt-auto`: a short conversation sits at
|
|
6024
|
+
// the bottom of the panel rather than floating at the top.
|
|
6025
|
+
transcriptList: { flexGrow: 1, justifyContent: "flex-end", gap: 12 },
|
|
6026
|
+
transcriptTopClearance: { paddingTop: 64 },
|
|
6027
|
+
transcriptBottomClearance: { paddingBottom: 80 },
|
|
6028
|
+
fullWidth: { width: "100%" },
|
|
6029
|
+
controlsBar: { position: "absolute", zIndex: 10, bottom: 0, left: 0, right: 0, paddingHorizontal: 20, paddingVertical: 12 },
|
|
6030
|
+
error: { textAlign: "center", fontSize: 14, marginBottom: 8 },
|
|
6031
|
+
connecting: { fontSize: 14, textAlign: "center" },
|
|
6032
|
+
startActions: { flexDirection: "row", alignItems: "center", gap: 8, width: "100%" },
|
|
6033
|
+
legal: { fontSize: 12, lineHeight: 20, textAlign: "center" }
|
|
5668
6034
|
});
|
|
5669
6035
|
|
|
5670
6036
|
// src/platform/app-origin.ts
|
|
@@ -5733,26 +6099,73 @@ var createHostActionDispatcher = (onAction) => (action, onLog) => {
|
|
|
5733
6099
|
};
|
|
5734
6100
|
// Annotate the CommonJS export names for ESM import in node:
|
|
5735
6101
|
0 && (module.exports = {
|
|
6102
|
+
CHAT_WIDGET_AFTER_TEXT_MS,
|
|
6103
|
+
CHAT_WIDGET_GRACE_MS,
|
|
6104
|
+
DEFAULT_CONFIG,
|
|
6105
|
+
DEFAULT_ENVIRONMENT_URLS,
|
|
6106
|
+
DEFAULT_WIDGET_THEME,
|
|
5736
6107
|
GradientFill,
|
|
6108
|
+
MIN_TYPING_INDICATOR_MS,
|
|
5737
6109
|
Markdown,
|
|
5738
6110
|
RinggWidget,
|
|
6111
|
+
WIDGET_PRESET_THEMES,
|
|
5739
6112
|
WidgetThemeProvider,
|
|
5740
6113
|
appOrigin,
|
|
6114
|
+
buildPayload,
|
|
6115
|
+
colorToBackground,
|
|
6116
|
+
colorToBorder,
|
|
6117
|
+
colorToSolid,
|
|
5741
6118
|
createAudioSession,
|
|
5742
6119
|
createCallbackEventBus,
|
|
6120
|
+
createComponentStore,
|
|
6121
|
+
createDomEventBus,
|
|
5743
6122
|
createHostActionDispatcher,
|
|
5744
6123
|
createLiveKitTransport,
|
|
6124
|
+
createMessageStore,
|
|
5745
6125
|
createNativeMicPermission,
|
|
5746
6126
|
createNotificationPlayer,
|
|
6127
|
+
createRinggApiClient,
|
|
5747
6128
|
createRinggWidgetController,
|
|
6129
|
+
createSessionStore,
|
|
6130
|
+
createShellStore,
|
|
5748
6131
|
createSilentNotificationPlayer,
|
|
6132
|
+
createSlashCommandStore,
|
|
6133
|
+
createStaticUrlResolver,
|
|
6134
|
+
createStore,
|
|
6135
|
+
createSystemClock,
|
|
6136
|
+
createTypingStore,
|
|
6137
|
+
defaultUrlResolver,
|
|
6138
|
+
extractColorsFromGradient,
|
|
6139
|
+
formatBlocksAction,
|
|
6140
|
+
formatComponentResponse,
|
|
6141
|
+
formatDynamicData,
|
|
6142
|
+
formatSlashCommand,
|
|
6143
|
+
formatSlotDate,
|
|
6144
|
+
formatSlotTime,
|
|
6145
|
+
getButtonRadius,
|
|
6146
|
+
getContrastingTextColor,
|
|
6147
|
+
getDominantColor,
|
|
6148
|
+
grantedMicPermission,
|
|
6149
|
+
groupSlotsByDate,
|
|
6150
|
+
isBlocks,
|
|
6151
|
+
isButtons,
|
|
6152
|
+
isCalendarBooking,
|
|
6153
|
+
isConfirmation,
|
|
6154
|
+
isForm,
|
|
6155
|
+
isGradient,
|
|
6156
|
+
isInteractiveFlow,
|
|
6157
|
+
isLightColor,
|
|
6158
|
+
mergeComponentTheme,
|
|
6159
|
+
mergeWidgetTheme,
|
|
5749
6160
|
resolveButtonRadius,
|
|
5750
6161
|
resolveFontFamily,
|
|
5751
6162
|
resolveRadius,
|
|
5752
6163
|
ringgId,
|
|
6164
|
+
silentNotificationPlayer,
|
|
5753
6165
|
solidColor,
|
|
5754
6166
|
toSize,
|
|
5755
6167
|
toViewStyle,
|
|
6168
|
+
transformRpcToComponent,
|
|
5756
6169
|
useRinggComponents,
|
|
5757
6170
|
useRinggMessages,
|
|
5758
6171
|
useRinggSession,
|