@ringg/core 0.0.1-alpha.10
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/LICENSE +21 -0
- package/README.md +93 -0
- package/dist/index.cjs +757 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +762 -0
- package/dist/index.d.ts +762 -0
- package/dist/index.js +718 -0
- package/dist/index.js.map +1 -0
- package/package.json +53 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,757 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/types/config.ts
|
|
4
|
+
var DEFAULT_CONFIG = {
|
|
5
|
+
mode: "prod",
|
|
6
|
+
title: "Ringg AI Support",
|
|
7
|
+
description: "Ringg AI offers 24/7 voice support to handle your business calls efficiently and professionally",
|
|
8
|
+
defaultTab: "audio",
|
|
9
|
+
hideTabSelector: false,
|
|
10
|
+
notificationTuneUrl: "https://assets.ringg.ai/audios/misc/widget_notification.mp3"
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
// src/adapters/event-bus.ts
|
|
14
|
+
var DomEventBus = class {
|
|
15
|
+
constructor() {
|
|
16
|
+
this.listeners = /* @__PURE__ */ new Map();
|
|
17
|
+
}
|
|
18
|
+
emit(event, payload) {
|
|
19
|
+
if (typeof window !== "undefined") {
|
|
20
|
+
window.dispatchEvent(new CustomEvent(event, { detail: payload }));
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
on(event, handler) {
|
|
24
|
+
if (typeof window === "undefined") return () => {
|
|
25
|
+
};
|
|
26
|
+
const wrappedHandler = ((e) => handler(e.detail));
|
|
27
|
+
if (!this.listeners.has(event)) {
|
|
28
|
+
this.listeners.set(event, /* @__PURE__ */ new Set());
|
|
29
|
+
}
|
|
30
|
+
this.listeners.get(event).add(wrappedHandler);
|
|
31
|
+
window.addEventListener(event, wrappedHandler);
|
|
32
|
+
return () => {
|
|
33
|
+
window.removeEventListener(event, wrappedHandler);
|
|
34
|
+
this.listeners.get(event)?.delete(wrappedHandler);
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
off(event) {
|
|
38
|
+
if (event) {
|
|
39
|
+
this.listeners.get(event)?.forEach((handler) => {
|
|
40
|
+
window.removeEventListener(event, handler);
|
|
41
|
+
});
|
|
42
|
+
this.listeners.delete(event);
|
|
43
|
+
} else {
|
|
44
|
+
this.listeners.forEach((handlers, eventName) => {
|
|
45
|
+
handlers.forEach((handler) => {
|
|
46
|
+
window.removeEventListener(eventName, handler);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
this.listeners.clear();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
var CallbackEventBus = class {
|
|
54
|
+
constructor() {
|
|
55
|
+
this.listeners = /* @__PURE__ */ new Map();
|
|
56
|
+
}
|
|
57
|
+
emit(event, payload) {
|
|
58
|
+
const handlers = this.listeners.get(event);
|
|
59
|
+
if (handlers) {
|
|
60
|
+
handlers.forEach((handler) => handler(payload));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
on(event, handler) {
|
|
64
|
+
if (!this.listeners.has(event)) {
|
|
65
|
+
this.listeners.set(event, /* @__PURE__ */ new Set());
|
|
66
|
+
}
|
|
67
|
+
const typedHandler = handler;
|
|
68
|
+
this.listeners.get(event).add(typedHandler);
|
|
69
|
+
return () => {
|
|
70
|
+
this.listeners.get(event)?.delete(typedHandler);
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
off(event) {
|
|
74
|
+
if (event) {
|
|
75
|
+
this.listeners.delete(event);
|
|
76
|
+
} else {
|
|
77
|
+
this.listeners.clear();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// src/api/client.ts
|
|
83
|
+
var StaticUrlResolver = class {
|
|
84
|
+
constructor(urls) {
|
|
85
|
+
this.urls = urls;
|
|
86
|
+
}
|
|
87
|
+
resolve(mode) {
|
|
88
|
+
return this.urls[mode];
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
var RinggApiClient = class {
|
|
92
|
+
constructor(config) {
|
|
93
|
+
const { backendUrl } = config.urlResolver.resolve(config.mode);
|
|
94
|
+
this.backendUrl = backendUrl;
|
|
95
|
+
this.xApiKey = config.xApiKey;
|
|
96
|
+
}
|
|
97
|
+
/** Common headers for all API requests. Includes Origin for CORS. */
|
|
98
|
+
get headers() {
|
|
99
|
+
return {
|
|
100
|
+
"Content-Type": "application/json",
|
|
101
|
+
"X-API-KEY": this.xApiKey,
|
|
102
|
+
Origin: "http://localhost:3000"
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Initiate a webcall — returns the LiveKit token and call ID.
|
|
107
|
+
*/
|
|
108
|
+
async startWebcall(params) {
|
|
109
|
+
const response = await fetch(`${this.backendUrl}/calling/webcall`, {
|
|
110
|
+
method: "POST",
|
|
111
|
+
body: JSON.stringify({
|
|
112
|
+
agent_id: params.agentId,
|
|
113
|
+
custom_args_values: params.variables,
|
|
114
|
+
media_type: params.mediaType
|
|
115
|
+
}),
|
|
116
|
+
headers: this.headers
|
|
117
|
+
});
|
|
118
|
+
const responseText = await response.text();
|
|
119
|
+
let data;
|
|
120
|
+
try {
|
|
121
|
+
data = JSON.parse(responseText);
|
|
122
|
+
} catch {
|
|
123
|
+
throw new Error(responseText.slice(0, 200) || `Request failed with status ${response.status}`);
|
|
124
|
+
}
|
|
125
|
+
if (!response.ok) {
|
|
126
|
+
throw new Error(data?.message || "Failed to make call");
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
call_id: data.call_id,
|
|
130
|
+
user_token: data.user_token,
|
|
131
|
+
enabled_slash_commands: data.enabled_slash_commands
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Submit post-call feedback.
|
|
136
|
+
*/
|
|
137
|
+
async submitFeedback(callId, feedback) {
|
|
138
|
+
await fetch(`${this.backendUrl}/calling/${callId}/feedback`, {
|
|
139
|
+
method: "POST",
|
|
140
|
+
body: JSON.stringify(feedback),
|
|
141
|
+
headers: this.headers
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Call a component API endpoint (for interactive flows).
|
|
146
|
+
*/
|
|
147
|
+
async callComponentApi(method, endpoint, payload) {
|
|
148
|
+
const url = endpoint.startsWith("http") ? endpoint : `${this.backendUrl}${endpoint}`;
|
|
149
|
+
const response = await fetch(url, {
|
|
150
|
+
method,
|
|
151
|
+
body: JSON.stringify(payload),
|
|
152
|
+
headers: this.headers
|
|
153
|
+
});
|
|
154
|
+
return response.json();
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
// src/state/widget-state.ts
|
|
159
|
+
var WidgetStateController = class {
|
|
160
|
+
constructor(eventBus, initialCallMode = "audio") {
|
|
161
|
+
this.eventBus = eventBus;
|
|
162
|
+
this.viewState = "closed";
|
|
163
|
+
this.currentCallId = null;
|
|
164
|
+
this.listeners = /* @__PURE__ */ new Set();
|
|
165
|
+
this.conversationEndDispatched = false;
|
|
166
|
+
this.callMode = initialCallMode;
|
|
167
|
+
}
|
|
168
|
+
// ─── Getters ─────────────────────────────────────────────────────────────
|
|
169
|
+
getSnapshot() {
|
|
170
|
+
return {
|
|
171
|
+
viewState: this.viewState,
|
|
172
|
+
currentCallId: this.currentCallId,
|
|
173
|
+
callMode: this.callMode
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
get isOpen() {
|
|
177
|
+
return this.viewState === "open" || this.viewState === "feedback";
|
|
178
|
+
}
|
|
179
|
+
get isShowingFeedback() {
|
|
180
|
+
return this.viewState === "feedback";
|
|
181
|
+
}
|
|
182
|
+
// ─── Mutations ───────────────────────────────────────────────────────────
|
|
183
|
+
open() {
|
|
184
|
+
if (this.viewState === "open") return;
|
|
185
|
+
this.viewState = "open";
|
|
186
|
+
this.eventBus.emit("ringg:widget_status", { status: "maximised", mode: this.callMode });
|
|
187
|
+
this.notify();
|
|
188
|
+
}
|
|
189
|
+
minimize() {
|
|
190
|
+
if (this.viewState === "closed") return;
|
|
191
|
+
this.viewState = "closed";
|
|
192
|
+
this.eventBus.emit("ringg:widget_status", { status: "minimised", mode: this.callMode });
|
|
193
|
+
this.notify();
|
|
194
|
+
}
|
|
195
|
+
close() {
|
|
196
|
+
this.viewState = "closed";
|
|
197
|
+
this.currentCallId = null;
|
|
198
|
+
this.eventBus.emit("ringg:widget_status", { status: "minimised", mode: this.callMode });
|
|
199
|
+
this.notify();
|
|
200
|
+
}
|
|
201
|
+
toggle() {
|
|
202
|
+
if (this.isOpen) {
|
|
203
|
+
this.minimize();
|
|
204
|
+
} else {
|
|
205
|
+
this.open();
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
showFeedback() {
|
|
209
|
+
this.viewState = "feedback";
|
|
210
|
+
this.notify();
|
|
211
|
+
}
|
|
212
|
+
setCallMode(mode) {
|
|
213
|
+
this.callMode = mode;
|
|
214
|
+
this.notify();
|
|
215
|
+
}
|
|
216
|
+
setCurrentCallId(callId) {
|
|
217
|
+
this.currentCallId = callId;
|
|
218
|
+
this.notify();
|
|
219
|
+
}
|
|
220
|
+
// ─── Conversation lifecycle helpers ──────────────────────────────────────
|
|
221
|
+
dispatchConversationStart(mode, callId) {
|
|
222
|
+
this.conversationEndDispatched = false;
|
|
223
|
+
this.eventBus.emit("ringg:conversation_status", { status: "started", mode, callId });
|
|
224
|
+
}
|
|
225
|
+
dispatchConversationEnd(mode, callId) {
|
|
226
|
+
if (!this.conversationEndDispatched) {
|
|
227
|
+
this.conversationEndDispatched = true;
|
|
228
|
+
this.eventBus.emit("ringg:conversation_status", { status: "ended", mode, callId });
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
// ─── Subscription ────────────────────────────────────────────────────────
|
|
232
|
+
subscribe(listener) {
|
|
233
|
+
this.listeners.add(listener);
|
|
234
|
+
return () => {
|
|
235
|
+
this.listeners.delete(listener);
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
notify() {
|
|
239
|
+
const snapshot = this.getSnapshot();
|
|
240
|
+
this.listeners.forEach((listener) => listener(snapshot));
|
|
241
|
+
}
|
|
242
|
+
// ─── Cleanup ─────────────────────────────────────────────────────────────
|
|
243
|
+
dispose() {
|
|
244
|
+
this.listeners.clear();
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
// src/theme/gradient-utils.ts
|
|
249
|
+
var GRADIENT_REGEX = /^(linear-gradient|radial-gradient|conic-gradient|repeating-linear-gradient|repeating-radial-gradient|repeating-conic-gradient)\s*\(/i;
|
|
250
|
+
var HEX_COLOR_REGEX = /#([0-9a-fA-F]{3,8})\b/g;
|
|
251
|
+
var RGB_COLOR_REGEX = /rgba?\(\s*[\d.]+[\s,]+[\d.]+[\s,]+[\d.]+(?:[\s,/]+[\d.%]+)?\s*\)/g;
|
|
252
|
+
function isGradient(color) {
|
|
253
|
+
return GRADIENT_REGEX.test(color.trim());
|
|
254
|
+
}
|
|
255
|
+
function extractColorsFromGradient(gradient) {
|
|
256
|
+
const colors = [];
|
|
257
|
+
const hexMatches = gradient.match(HEX_COLOR_REGEX);
|
|
258
|
+
if (hexMatches) colors.push(...hexMatches);
|
|
259
|
+
const rgbMatches = gradient.match(RGB_COLOR_REGEX);
|
|
260
|
+
if (rgbMatches) colors.push(...rgbMatches);
|
|
261
|
+
return colors;
|
|
262
|
+
}
|
|
263
|
+
function getDominantColor(color) {
|
|
264
|
+
if (!isGradient(color)) return color;
|
|
265
|
+
const colors = extractColorsFromGradient(color);
|
|
266
|
+
return colors.length > 0 ? colors[0] : color;
|
|
267
|
+
}
|
|
268
|
+
function colorToBackground(color) {
|
|
269
|
+
if (isGradient(color)) {
|
|
270
|
+
return { background: color };
|
|
271
|
+
}
|
|
272
|
+
return { backgroundColor: color };
|
|
273
|
+
}
|
|
274
|
+
function colorToSolid(color) {
|
|
275
|
+
return getDominantColor(color);
|
|
276
|
+
}
|
|
277
|
+
function colorToBorder(color) {
|
|
278
|
+
if (isGradient(color)) {
|
|
279
|
+
return {
|
|
280
|
+
borderImage: `${color} 1`,
|
|
281
|
+
borderStyle: "solid"
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
return { borderColor: color };
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// src/theme/theme-engine.ts
|
|
288
|
+
var DEFAULT_WIDGET_THEME = {
|
|
289
|
+
primaryColor: "#313335",
|
|
290
|
+
primaryTextColor: "#ffffff",
|
|
291
|
+
backgroundColor: "#fafafa",
|
|
292
|
+
surfaceColor: "#F3F3F5",
|
|
293
|
+
textColor: "#212121",
|
|
294
|
+
mutedTextColor: "#11111180",
|
|
295
|
+
borderColor: "#e5e5e5",
|
|
296
|
+
errorColor: "#ef4444",
|
|
297
|
+
successColor: "#10b981",
|
|
298
|
+
buttonStyle: "rounded",
|
|
299
|
+
borderRadius: "20px",
|
|
300
|
+
fontFamily: "inherit"
|
|
301
|
+
};
|
|
302
|
+
function mergeWidgetTheme(theme) {
|
|
303
|
+
return { ...DEFAULT_WIDGET_THEME, ...theme };
|
|
304
|
+
}
|
|
305
|
+
function mergeComponentTheme(componentTheme, widgetTheme) {
|
|
306
|
+
const base = widgetTheme ?? DEFAULT_WIDGET_THEME;
|
|
307
|
+
return {
|
|
308
|
+
primaryColor: base.primaryColor,
|
|
309
|
+
primaryTextColor: base.primaryTextColor,
|
|
310
|
+
backgroundColor: base.backgroundColor,
|
|
311
|
+
surfaceColor: base.surfaceColor,
|
|
312
|
+
textColor: base.textColor,
|
|
313
|
+
mutedTextColor: base.mutedTextColor,
|
|
314
|
+
borderColor: base.borderColor,
|
|
315
|
+
errorColor: base.errorColor,
|
|
316
|
+
successColor: base.successColor,
|
|
317
|
+
fontFamily: base.fontFamily,
|
|
318
|
+
borderRadius: base.borderRadius,
|
|
319
|
+
buttonStyle: base.buttonStyle,
|
|
320
|
+
...componentTheme
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
function getButtonRadius(style) {
|
|
324
|
+
switch (style) {
|
|
325
|
+
case "pill":
|
|
326
|
+
return "9999px";
|
|
327
|
+
case "square":
|
|
328
|
+
return "6px";
|
|
329
|
+
case "rounded":
|
|
330
|
+
default:
|
|
331
|
+
return "12px";
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
function getLuminance(hexColor) {
|
|
335
|
+
const solid = getDominantColor(hexColor);
|
|
336
|
+
const hex = solid.replace("#", "");
|
|
337
|
+
const r = parseInt(hex.substr(0, 2), 16) / 255;
|
|
338
|
+
const g = parseInt(hex.substr(2, 2), 16) / 255;
|
|
339
|
+
const b = parseInt(hex.substr(4, 2), 16) / 255;
|
|
340
|
+
const toLinear = (c) => c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
|
|
341
|
+
return 0.2126 * toLinear(r) + 0.7152 * toLinear(g) + 0.0722 * toLinear(b);
|
|
342
|
+
}
|
|
343
|
+
function isLightColor(hexColor) {
|
|
344
|
+
try {
|
|
345
|
+
return getLuminance(hexColor) > 0.5;
|
|
346
|
+
} catch {
|
|
347
|
+
return true;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
function getContrastingTextColor(bgColor) {
|
|
351
|
+
return isLightColor(bgColor) ? "#212121" : "#ffffff";
|
|
352
|
+
}
|
|
353
|
+
var WIDGET_PRESET_THEMES = {
|
|
354
|
+
light: {
|
|
355
|
+
primaryColor: "#18181b",
|
|
356
|
+
primaryTextColor: "#ffffff",
|
|
357
|
+
backgroundColor: "#ffffff",
|
|
358
|
+
surfaceColor: "#f4f4f5",
|
|
359
|
+
textColor: "#18181b",
|
|
360
|
+
mutedTextColor: "#71717a",
|
|
361
|
+
borderColor: "#e4e4e7"
|
|
362
|
+
},
|
|
363
|
+
warm: {
|
|
364
|
+
primaryColor: "#57534e",
|
|
365
|
+
primaryTextColor: "#ffffff",
|
|
366
|
+
backgroundColor: "#fefefe",
|
|
367
|
+
surfaceColor: "#fafaf9",
|
|
368
|
+
textColor: "#1c1917",
|
|
369
|
+
mutedTextColor: "#78716c",
|
|
370
|
+
borderColor: "#e7e5e4"
|
|
371
|
+
},
|
|
372
|
+
blue: {
|
|
373
|
+
primaryColor: "#2563eb",
|
|
374
|
+
primaryTextColor: "#ffffff",
|
|
375
|
+
backgroundColor: "#fafcff",
|
|
376
|
+
surfaceColor: "#f0f7ff",
|
|
377
|
+
textColor: "#1e3a8a",
|
|
378
|
+
mutedTextColor: "#64748b",
|
|
379
|
+
borderColor: "#dbeafe"
|
|
380
|
+
},
|
|
381
|
+
indigo: {
|
|
382
|
+
primaryColor: "#6366f1",
|
|
383
|
+
primaryTextColor: "#ffffff",
|
|
384
|
+
backgroundColor: "#fbfbff",
|
|
385
|
+
surfaceColor: "#f5f5ff",
|
|
386
|
+
textColor: "#312e81",
|
|
387
|
+
mutedTextColor: "#6b7280",
|
|
388
|
+
borderColor: "#e0e7ff"
|
|
389
|
+
},
|
|
390
|
+
green: {
|
|
391
|
+
primaryColor: "#16a34a",
|
|
392
|
+
primaryTextColor: "#ffffff",
|
|
393
|
+
backgroundColor: "#fbfefb",
|
|
394
|
+
surfaceColor: "#f0fdf4",
|
|
395
|
+
textColor: "#14532d",
|
|
396
|
+
mutedTextColor: "#6b7280",
|
|
397
|
+
borderColor: "#dcfce7"
|
|
398
|
+
},
|
|
399
|
+
orange: {
|
|
400
|
+
primaryColor: "#ea580c",
|
|
401
|
+
primaryTextColor: "#ffffff",
|
|
402
|
+
backgroundColor: "#fffdfb",
|
|
403
|
+
surfaceColor: "#fff7ed",
|
|
404
|
+
textColor: "#7c2d12",
|
|
405
|
+
mutedTextColor: "#78716c",
|
|
406
|
+
borderColor: "#ffedd5"
|
|
407
|
+
},
|
|
408
|
+
dark: {
|
|
409
|
+
primaryColor: "#fafafa",
|
|
410
|
+
primaryTextColor: "#18181b",
|
|
411
|
+
backgroundColor: "#18181b",
|
|
412
|
+
surfaceColor: "#27272a",
|
|
413
|
+
textColor: "#fafafa",
|
|
414
|
+
mutedTextColor: "#a1a1aa",
|
|
415
|
+
borderColor: "#3f3f46"
|
|
416
|
+
},
|
|
417
|
+
sunset: {
|
|
418
|
+
primaryColor: "linear-gradient(135deg, #f97316, #ef4444)",
|
|
419
|
+
primaryTextColor: "#ffffff",
|
|
420
|
+
backgroundColor: "#fffbf7",
|
|
421
|
+
surfaceColor: "#fff3e8",
|
|
422
|
+
textColor: "#7c2d12",
|
|
423
|
+
mutedTextColor: "#9a3412",
|
|
424
|
+
borderColor: "#fed7aa"
|
|
425
|
+
},
|
|
426
|
+
ocean: {
|
|
427
|
+
primaryColor: "linear-gradient(135deg, #667eea, #764ba2)",
|
|
428
|
+
primaryTextColor: "#ffffff",
|
|
429
|
+
backgroundColor: "#faf9ff",
|
|
430
|
+
surfaceColor: "#f0edff",
|
|
431
|
+
textColor: "#312e81",
|
|
432
|
+
mutedTextColor: "#6366f1",
|
|
433
|
+
borderColor: "#ddd6fe"
|
|
434
|
+
},
|
|
435
|
+
emerald: {
|
|
436
|
+
primaryColor: "linear-gradient(135deg, #10b981, #0ea5e9)",
|
|
437
|
+
primaryTextColor: "#ffffff",
|
|
438
|
+
backgroundColor: "#f7fdfb",
|
|
439
|
+
surfaceColor: "#ecfdf5",
|
|
440
|
+
textColor: "#064e3b",
|
|
441
|
+
mutedTextColor: "#047857",
|
|
442
|
+
borderColor: "#a7f3d0"
|
|
443
|
+
},
|
|
444
|
+
midnight: {
|
|
445
|
+
primaryColor: "linear-gradient(135deg, #6366f1, #8b5cf6)",
|
|
446
|
+
primaryTextColor: "#ffffff",
|
|
447
|
+
backgroundColor: "#18181b",
|
|
448
|
+
surfaceColor: "#27272a",
|
|
449
|
+
textColor: "#fafafa",
|
|
450
|
+
mutedTextColor: "#a1a1aa",
|
|
451
|
+
borderColor: "#3f3f46"
|
|
452
|
+
}
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
// src/helpers/component-helpers.ts
|
|
456
|
+
function groupSlotsByDate(slots) {
|
|
457
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
458
|
+
slots.forEach((slot) => {
|
|
459
|
+
const date = new Date(slot.datetime);
|
|
460
|
+
const dateKey = date.toISOString().split("T")[0];
|
|
461
|
+
if (!grouped.has(dateKey)) {
|
|
462
|
+
grouped.set(dateKey, []);
|
|
463
|
+
}
|
|
464
|
+
grouped.get(dateKey).push(slot);
|
|
465
|
+
});
|
|
466
|
+
return Array.from(grouped.entries()).sort(([a], [b]) => a.localeCompare(b)).map(([dateKey, dateSlots]) => {
|
|
467
|
+
const date = new Date(dateKey);
|
|
468
|
+
return {
|
|
469
|
+
date: dateKey,
|
|
470
|
+
dateLabel: date.toLocaleDateString("en-US", {
|
|
471
|
+
weekday: "short",
|
|
472
|
+
month: "short",
|
|
473
|
+
day: "numeric"
|
|
474
|
+
}),
|
|
475
|
+
slots: dateSlots.sort((a, b) => new Date(a.datetime).getTime() - new Date(b.datetime).getTime())
|
|
476
|
+
};
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
function formatSlotTime(datetime, timezone) {
|
|
480
|
+
const date = new Date(datetime);
|
|
481
|
+
return date.toLocaleTimeString("en-US", {
|
|
482
|
+
hour: "numeric",
|
|
483
|
+
minute: "2-digit",
|
|
484
|
+
hour12: true,
|
|
485
|
+
timeZone: timezone
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
function formatSlotDate(datetime, timezone) {
|
|
489
|
+
const date = new Date(datetime);
|
|
490
|
+
return date.toLocaleDateString("en-US", {
|
|
491
|
+
weekday: "short",
|
|
492
|
+
month: "short",
|
|
493
|
+
day: "numeric",
|
|
494
|
+
timeZone: timezone
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
function buildPayload(template, values) {
|
|
498
|
+
const result = {};
|
|
499
|
+
for (const [key, value] of Object.entries(template)) {
|
|
500
|
+
if (typeof value === "string" && value.startsWith("{") && value.endsWith("}")) {
|
|
501
|
+
const placeholder = value.slice(1, -1);
|
|
502
|
+
result[key] = values[placeholder] ?? value;
|
|
503
|
+
} else {
|
|
504
|
+
result[key] = value;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
return result;
|
|
508
|
+
}
|
|
509
|
+
function isCalendarBooking(payload) {
|
|
510
|
+
return payload.component_type === "calendar_booking";
|
|
511
|
+
}
|
|
512
|
+
function isForm(payload) {
|
|
513
|
+
return payload.component_type === "form";
|
|
514
|
+
}
|
|
515
|
+
function isButtons(payload) {
|
|
516
|
+
return payload.component_type === "buttons";
|
|
517
|
+
}
|
|
518
|
+
function isConfirmation(payload) {
|
|
519
|
+
return payload.component_type === "confirmation";
|
|
520
|
+
}
|
|
521
|
+
function isInteractiveFlow(payload) {
|
|
522
|
+
return payload.component_type === "interactive_flow";
|
|
523
|
+
}
|
|
524
|
+
function filterSlashCommands(commands, query) {
|
|
525
|
+
if (!query.startsWith("/")) return [];
|
|
526
|
+
const search = query.slice(1).toLowerCase();
|
|
527
|
+
if (!search) return commands;
|
|
528
|
+
return commands.filter((cmd) => cmd.command.slice(1).toLowerCase().includes(search) || cmd.display_name.toLowerCase().includes(search));
|
|
529
|
+
}
|
|
530
|
+
function hasSubQuestions(disease) {
|
|
531
|
+
return disease.sub_questions.length > 0;
|
|
532
|
+
}
|
|
533
|
+
function getDiseasesWithSubQuestions(selected) {
|
|
534
|
+
return selected.filter(hasSubQuestions);
|
|
535
|
+
}
|
|
536
|
+
function formatTimestamp(dateString, mode = "fromnow") {
|
|
537
|
+
const date = new Date(dateString);
|
|
538
|
+
const now = /* @__PURE__ */ new Date();
|
|
539
|
+
if (mode === "fromnow") {
|
|
540
|
+
const diffSeconds = Math.floor((now.getTime() - date.getTime()) / 1e3);
|
|
541
|
+
if (diffSeconds < 60) return "Just now";
|
|
542
|
+
if (diffSeconds < 3600) {
|
|
543
|
+
const minutes = Math.floor(diffSeconds / 60);
|
|
544
|
+
return `${minutes} min ago`;
|
|
545
|
+
}
|
|
546
|
+
if (diffSeconds < 86400) {
|
|
547
|
+
const hours = Math.floor(diffSeconds / 3600);
|
|
548
|
+
return `${hours} hour${hours !== 1 ? "s" : ""} ago`;
|
|
549
|
+
}
|
|
550
|
+
const days = Math.floor(diffSeconds / 86400);
|
|
551
|
+
return `${days} day${days !== 1 ? "s" : ""} ago`;
|
|
552
|
+
}
|
|
553
|
+
return date.toLocaleTimeString([], {
|
|
554
|
+
hour: "2-digit",
|
|
555
|
+
minute: "2-digit",
|
|
556
|
+
hour12: true
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
function isAgentIdentity(identity) {
|
|
560
|
+
return identity.includes("agent") || identity.includes("Agent");
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
// src/rpc/rpc-formatter.ts
|
|
564
|
+
function transformRpcToComponent(componentName, rpcPayload) {
|
|
565
|
+
const componentId = `${componentName}_${Date.now()}`;
|
|
566
|
+
const config = rpcPayload.component_config;
|
|
567
|
+
if (componentName === "calendar") {
|
|
568
|
+
return transformCalendar(componentId, config, rpcPayload);
|
|
569
|
+
}
|
|
570
|
+
if (componentName === "form") {
|
|
571
|
+
return transformForm(componentId, config);
|
|
572
|
+
}
|
|
573
|
+
if (componentName === "buttons") {
|
|
574
|
+
return transformButtons(componentId, config, rpcPayload);
|
|
575
|
+
}
|
|
576
|
+
if (componentName === "confirmation") {
|
|
577
|
+
return transformConfirmation(componentId, rpcPayload);
|
|
578
|
+
}
|
|
579
|
+
if (rpcPayload.component_type && rpcPayload.component_id) {
|
|
580
|
+
return rpcPayload;
|
|
581
|
+
}
|
|
582
|
+
return null;
|
|
583
|
+
}
|
|
584
|
+
function transformCalendar(componentId, config, rpcPayload) {
|
|
585
|
+
const configObj = config;
|
|
586
|
+
const slots = configObj?.slots || [];
|
|
587
|
+
const timezone = configObj?.timezone || "UTC";
|
|
588
|
+
return {
|
|
589
|
+
component_type: "calendar_booking",
|
|
590
|
+
component_id: componentId,
|
|
591
|
+
data: {
|
|
592
|
+
available_slots: slots.map((slot) => ({ id: slot.id, datetime: slot.datetime })),
|
|
593
|
+
timezone,
|
|
594
|
+
title: rpcPayload.title || "Select a time slot"
|
|
595
|
+
}
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
function transformForm(componentId, config) {
|
|
599
|
+
const configObj = config;
|
|
600
|
+
const transformedFields = (configObj?.fields || []).map((field) => ({
|
|
601
|
+
name: field.key || field.name || "",
|
|
602
|
+
type: mapFieldType(field.type),
|
|
603
|
+
label: field.description || field.label || field.key || field.name || "",
|
|
604
|
+
required: field.required,
|
|
605
|
+
placeholder: field.placeholder,
|
|
606
|
+
options: field.options
|
|
607
|
+
}));
|
|
608
|
+
return {
|
|
609
|
+
component_type: "form",
|
|
610
|
+
component_id: componentId,
|
|
611
|
+
data: {
|
|
612
|
+
title: configObj?.title || "Form",
|
|
613
|
+
description: configObj?.description,
|
|
614
|
+
fields: transformedFields,
|
|
615
|
+
submit_label: configObj?.cta || configObj?.submit_label || "Submit"
|
|
616
|
+
}
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
function transformButtons(componentId, config, rpcPayload) {
|
|
620
|
+
return {
|
|
621
|
+
component_type: "buttons",
|
|
622
|
+
component_id: componentId,
|
|
623
|
+
data: {
|
|
624
|
+
title: rpcPayload.title,
|
|
625
|
+
buttons: Array.isArray(config) ? config : []
|
|
626
|
+
}
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
function transformConfirmation(componentId, rpcPayload) {
|
|
630
|
+
return {
|
|
631
|
+
component_type: "confirmation",
|
|
632
|
+
component_id: componentId,
|
|
633
|
+
data: {
|
|
634
|
+
title: rpcPayload.title || "Confirmed",
|
|
635
|
+
message: rpcPayload.message || "",
|
|
636
|
+
icon: rpcPayload.icon || "success",
|
|
637
|
+
details: rpcPayload.details
|
|
638
|
+
}
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
function mapFieldType(backendType) {
|
|
642
|
+
const typeMap = {
|
|
643
|
+
text: "text",
|
|
644
|
+
string: "text",
|
|
645
|
+
number: "number",
|
|
646
|
+
email: "email",
|
|
647
|
+
phone_number: "tel",
|
|
648
|
+
single_select: "select",
|
|
649
|
+
multiselect: "multiselect",
|
|
650
|
+
checkbox: "boolean",
|
|
651
|
+
date: "date",
|
|
652
|
+
textarea: "textarea"
|
|
653
|
+
};
|
|
654
|
+
return typeMap[backendType] || "text";
|
|
655
|
+
}
|
|
656
|
+
function formatComponentResponse(componentName, componentId, responseData, originalConfig) {
|
|
657
|
+
const rpcMethod = "receive_dynamic_data";
|
|
658
|
+
let componentData;
|
|
659
|
+
if (componentName === "calendar") {
|
|
660
|
+
const slotId = responseData.step_data && responseData.step_data.slot_id;
|
|
661
|
+
const configObj = originalConfig;
|
|
662
|
+
const slots = configObj?.slots || [];
|
|
663
|
+
const timezone = configObj?.timezone || "UTC";
|
|
664
|
+
const selectedSlot = slots.find((slot) => slot.id === slotId);
|
|
665
|
+
componentData = {
|
|
666
|
+
id: selectedSlot?.id || slotId,
|
|
667
|
+
datetime: selectedSlot?.datetime || responseData.step_data?.slot_datetime,
|
|
668
|
+
timezone
|
|
669
|
+
};
|
|
670
|
+
} else if (componentName === "form") {
|
|
671
|
+
componentData = responseData.step_data || responseData.all_data;
|
|
672
|
+
} else if (componentName === "buttons") {
|
|
673
|
+
const buttonId = responseData.step_data && responseData.step_data.selected_button;
|
|
674
|
+
const buttons = Array.isArray(originalConfig) ? originalConfig : [];
|
|
675
|
+
const selectedButton = buttons.find((btn) => btn.id === buttonId);
|
|
676
|
+
componentData = selectedButton || { id: buttonId };
|
|
677
|
+
} else {
|
|
678
|
+
componentData = responseData;
|
|
679
|
+
}
|
|
680
|
+
const toolId = originalConfig?.tool_id;
|
|
681
|
+
const payload = JSON.stringify({
|
|
682
|
+
component_type: `receive_component_${componentName}`,
|
|
683
|
+
component_id: componentId,
|
|
684
|
+
component_data: componentData,
|
|
685
|
+
...toolId && { tool_id: toolId }
|
|
686
|
+
});
|
|
687
|
+
return { method: rpcMethod, payload };
|
|
688
|
+
}
|
|
689
|
+
function formatDiseaseSubmission(componentId, selections) {
|
|
690
|
+
return {
|
|
691
|
+
method: "receive_dynamic_data",
|
|
692
|
+
payload: JSON.stringify({
|
|
693
|
+
component_type: "receive_component_disease_plans",
|
|
694
|
+
component_id: componentId,
|
|
695
|
+
selections
|
|
696
|
+
})
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
function formatDiseaseSearch(query) {
|
|
700
|
+
return {
|
|
701
|
+
method: "receive_dynamic_data",
|
|
702
|
+
payload: JSON.stringify({
|
|
703
|
+
component_type: "receive_component_disease_search",
|
|
704
|
+
query
|
|
705
|
+
})
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
function formatSlashCommand(command) {
|
|
709
|
+
return {
|
|
710
|
+
method: "receive_dynamic_data",
|
|
711
|
+
payload: JSON.stringify({
|
|
712
|
+
component_type: "receive_component_slash_command",
|
|
713
|
+
command: command.replace(/^\//, "")
|
|
714
|
+
})
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
exports.CallbackEventBus = CallbackEventBus;
|
|
719
|
+
exports.DEFAULT_CONFIG = DEFAULT_CONFIG;
|
|
720
|
+
exports.DEFAULT_WIDGET_THEME = DEFAULT_WIDGET_THEME;
|
|
721
|
+
exports.DomEventBus = DomEventBus;
|
|
722
|
+
exports.RinggApiClient = RinggApiClient;
|
|
723
|
+
exports.StaticUrlResolver = StaticUrlResolver;
|
|
724
|
+
exports.WIDGET_PRESET_THEMES = WIDGET_PRESET_THEMES;
|
|
725
|
+
exports.WidgetStateController = WidgetStateController;
|
|
726
|
+
exports.buildPayload = buildPayload;
|
|
727
|
+
exports.colorToBackground = colorToBackground;
|
|
728
|
+
exports.colorToBorder = colorToBorder;
|
|
729
|
+
exports.colorToSolid = colorToSolid;
|
|
730
|
+
exports.extractColorsFromGradient = extractColorsFromGradient;
|
|
731
|
+
exports.filterSlashCommands = filterSlashCommands;
|
|
732
|
+
exports.formatComponentResponse = formatComponentResponse;
|
|
733
|
+
exports.formatDiseaseSearch = formatDiseaseSearch;
|
|
734
|
+
exports.formatDiseaseSubmission = formatDiseaseSubmission;
|
|
735
|
+
exports.formatSlashCommand = formatSlashCommand;
|
|
736
|
+
exports.formatSlotDate = formatSlotDate;
|
|
737
|
+
exports.formatSlotTime = formatSlotTime;
|
|
738
|
+
exports.formatTimestamp = formatTimestamp;
|
|
739
|
+
exports.getButtonRadius = getButtonRadius;
|
|
740
|
+
exports.getContrastingTextColor = getContrastingTextColor;
|
|
741
|
+
exports.getDiseasesWithSubQuestions = getDiseasesWithSubQuestions;
|
|
742
|
+
exports.getDominantColor = getDominantColor;
|
|
743
|
+
exports.groupSlotsByDate = groupSlotsByDate;
|
|
744
|
+
exports.hasSubQuestions = hasSubQuestions;
|
|
745
|
+
exports.isAgentIdentity = isAgentIdentity;
|
|
746
|
+
exports.isButtons = isButtons;
|
|
747
|
+
exports.isCalendarBooking = isCalendarBooking;
|
|
748
|
+
exports.isConfirmation = isConfirmation;
|
|
749
|
+
exports.isForm = isForm;
|
|
750
|
+
exports.isGradient = isGradient;
|
|
751
|
+
exports.isInteractiveFlow = isInteractiveFlow;
|
|
752
|
+
exports.isLightColor = isLightColor;
|
|
753
|
+
exports.mergeComponentTheme = mergeComponentTheme;
|
|
754
|
+
exports.mergeWidgetTheme = mergeWidgetTheme;
|
|
755
|
+
exports.transformRpcToComponent = transformRpcToComponent;
|
|
756
|
+
//# sourceMappingURL=index.cjs.map
|
|
757
|
+
//# sourceMappingURL=index.cjs.map
|