@eka-care/medassist-widget-embed 0.2.4 → 0.2.6
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/dist/iframe.js +85 -11
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +59 -7
- package/dist/src/medassist-widget.css +756 -279
- package/dist/src/medassist-widget.js +1149 -467
- package/dist/src/medassist-widget.js.map +1 -1
- package/iframe.ts +114 -10
- package/index.ts +74 -6
- package/package.json +1 -1
- package/src/medassist-widget.css +756 -279
- package/src/medassist-widget.js +1149 -467
- package/src/medassist-widget.js.map +1 -1
- package/test.html +8 -8
package/iframe.ts
CHANGED
|
@@ -29,6 +29,78 @@
|
|
|
29
29
|
return undefined;
|
|
30
30
|
};
|
|
31
31
|
|
|
32
|
+
/** Theme from agent-config API (has mode; textColor derived: dark → black, light → white) */
|
|
33
|
+
type AgentConfig = {
|
|
34
|
+
name?: string;
|
|
35
|
+
allowed?: ("text" | "file" | "audio")[];
|
|
36
|
+
theme?: {
|
|
37
|
+
background?: string;
|
|
38
|
+
background_img?: string;
|
|
39
|
+
accent?: string;
|
|
40
|
+
mode?: "light" | "dark";
|
|
41
|
+
title_img?: string;
|
|
42
|
+
icon_img?: string;
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
type WidgetTheme = {
|
|
47
|
+
background?: string;
|
|
48
|
+
backgroundImage?: string;
|
|
49
|
+
primary?: string;
|
|
50
|
+
textColor?: "black" | "white";
|
|
51
|
+
titleImg?: string;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const API_FETCH_TIMEOUT_MS = 10000;
|
|
55
|
+
|
|
56
|
+
/** Map agent-config API theme to widget theme (mode → textColor: dark→black, light→white) */
|
|
57
|
+
function mapAgentConfigThemeToWidgetTheme(
|
|
58
|
+
apiTheme: AgentConfig["theme"] | undefined
|
|
59
|
+
): WidgetTheme | undefined {
|
|
60
|
+
if (!apiTheme) return undefined;
|
|
61
|
+
const textColor =
|
|
62
|
+
apiTheme.mode === "dark"
|
|
63
|
+
? "white"
|
|
64
|
+
: apiTheme.mode === "light"
|
|
65
|
+
? "black"
|
|
66
|
+
: undefined;
|
|
67
|
+
return {
|
|
68
|
+
...(apiTheme.background && { background: apiTheme.background }),
|
|
69
|
+
...(apiTheme.background_img && {
|
|
70
|
+
backgroundImage: apiTheme.background_img,
|
|
71
|
+
}),
|
|
72
|
+
...(apiTheme.accent && { primary: apiTheme.accent }),
|
|
73
|
+
...(textColor && { textColor }),
|
|
74
|
+
...(apiTheme.title_img && { titleImg: apiTheme.title_img }),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Fetch agent-config from API with timeout; aborts if pending > 10s */
|
|
79
|
+
async function fetchAgentConfig(
|
|
80
|
+
baseUrl: string,
|
|
81
|
+
agentId: string
|
|
82
|
+
): Promise<AgentConfig | undefined> {
|
|
83
|
+
const url = `${baseUrl.replace(/\/$/, "")}/med-assist/agent-config/${agentId}`;
|
|
84
|
+
const controller = new AbortController();
|
|
85
|
+
const timeoutId = setTimeout(() => controller.abort(), API_FETCH_TIMEOUT_MS);
|
|
86
|
+
try {
|
|
87
|
+
const res = await fetch(url, {
|
|
88
|
+
signal: controller.signal,
|
|
89
|
+
headers: {
|
|
90
|
+
"ngrok-skip-browser-warning": "69420",
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
if (!res.ok) return undefined;
|
|
94
|
+
const json = await res.json();
|
|
95
|
+
if (json?.success && json?.data) return json.data as AgentConfig;
|
|
96
|
+
return undefined;
|
|
97
|
+
} catch {
|
|
98
|
+
return undefined;
|
|
99
|
+
} finally {
|
|
100
|
+
clearTimeout(timeoutId);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
32
104
|
const DEFAULT_WIDGET_ASSET_BASE =
|
|
33
105
|
"https://unpkg.com/@eka-care/medassist-widget@latest/dist/";
|
|
34
106
|
|
|
@@ -81,35 +153,67 @@
|
|
|
81
153
|
return;
|
|
82
154
|
}
|
|
83
155
|
|
|
84
|
-
|
|
85
|
-
|
|
156
|
+
let title = urlParams.get("title") || "Medi Clinic";
|
|
157
|
+
let iconUrl = urlParams.get("iconUrl") || undefined;
|
|
86
158
|
const context = urlParams.get("context");
|
|
87
|
-
const baseUrl = urlParams.get("baseUrl") ||
|
|
159
|
+
const baseUrl = urlParams.get("baseUrl") || "https://matrix.eka.care/reloaded";
|
|
88
160
|
const environment =
|
|
89
161
|
getEnvironment(urlParams.get("environment")) ?? "production";
|
|
162
|
+
const attributeTheme = urlParams.get("theme")
|
|
163
|
+
? (() => {
|
|
164
|
+
try {
|
|
165
|
+
return JSON.parse(urlParams.get("theme") || "{}") as WidgetTheme;
|
|
166
|
+
} catch {
|
|
167
|
+
return undefined;
|
|
168
|
+
}
|
|
169
|
+
})()
|
|
170
|
+
: undefined;
|
|
90
171
|
const container = document.getElementById("root") || document.body;
|
|
91
172
|
try {
|
|
92
|
-
await Promise.all([
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
173
|
+
await Promise.all([loadWidgetCss(), loadWidgetScript()]);
|
|
174
|
+
if (
|
|
175
|
+
!window?.renderMedAssist ||
|
|
176
|
+
typeof window?.renderMedAssist !== "function"
|
|
177
|
+
) {
|
|
97
178
|
throw new Error("renderMedAssist is not available on window");
|
|
98
179
|
}
|
|
99
180
|
|
|
181
|
+
// Fetch agent-config when baseUrl is available; 10s timeout aborts pending request
|
|
182
|
+
let apiTheme: WidgetTheme | undefined;
|
|
183
|
+
let allowed: ("text" | "file" | "audio")[] | undefined;
|
|
184
|
+
console.log("base url", baseUrl);
|
|
185
|
+
|
|
186
|
+
if (baseUrl) {
|
|
187
|
+
try {
|
|
188
|
+
const agentConfig = await fetchAgentConfig(baseUrl, agentId);
|
|
189
|
+
title = agentConfig?.name ?? title;
|
|
190
|
+
iconUrl = agentConfig?.theme?.icon_img ?? iconUrl;
|
|
191
|
+
apiTheme = mapAgentConfigThemeToWidgetTheme(
|
|
192
|
+
agentConfig?.theme ?? undefined
|
|
193
|
+
);
|
|
194
|
+
allowed = agentConfig?.allowed;
|
|
195
|
+
} catch {
|
|
196
|
+
// ignore; use URL params only
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
100
200
|
const config = {
|
|
101
201
|
title,
|
|
102
202
|
iconUrl,
|
|
203
|
+
allowed,
|
|
103
204
|
environment,
|
|
104
205
|
onClose: () => {
|
|
105
|
-
// Send message to parent window to close iframe
|
|
106
206
|
if (window.parent !== window) {
|
|
107
207
|
window.parent.postMessage({ type: "WIDGET_CLOSE" }, "*");
|
|
108
208
|
}
|
|
109
209
|
},
|
|
110
210
|
context: context ? JSON.parse(context) : undefined,
|
|
111
211
|
baseUrl,
|
|
112
|
-
displayMode: "full" as "full" | "widget"
|
|
212
|
+
displayMode: "full" as "full" | "widget",
|
|
213
|
+
theme: {
|
|
214
|
+
...(apiTheme ?? {}),
|
|
215
|
+
...(attributeTheme ?? {}),
|
|
216
|
+
},
|
|
113
217
|
};
|
|
114
218
|
window.renderMedAssist(container, agentId, config);
|
|
115
219
|
} catch (error) {
|
package/index.ts
CHANGED
|
@@ -38,6 +38,20 @@ const DEFAULT_WIDGET_ASSET_BASE = (() => {
|
|
|
38
38
|
return "https://unpkg.com/@eka-care/medassist-widget@latest/dist/";
|
|
39
39
|
})();
|
|
40
40
|
|
|
41
|
+
/** Theme from agent-config API (has mode; textColor derived: dark → black, light → white) */
|
|
42
|
+
type AgentConfig = {
|
|
43
|
+
name?: string;
|
|
44
|
+
allowed?: ("text" | "file" | "audio")[];
|
|
45
|
+
theme?: {
|
|
46
|
+
background?: string;
|
|
47
|
+
background_img?: string;
|
|
48
|
+
accent?: string;
|
|
49
|
+
mode?: "light" | "dark";
|
|
50
|
+
title_img?: string;
|
|
51
|
+
icon_img?: string
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
41
55
|
type MedAssistInitConfig = {
|
|
42
56
|
auth?: string;
|
|
43
57
|
agentId?: string;
|
|
@@ -45,9 +59,44 @@ type MedAssistInitConfig = {
|
|
|
45
59
|
title?: string;
|
|
46
60
|
iconUrl?: string;
|
|
47
61
|
baseUrl?: string;
|
|
62
|
+
allowed?: ("text" | "file" | "audio")[];
|
|
63
|
+
theme?: {
|
|
64
|
+
background?: string;
|
|
65
|
+
backgroundImage?: string;
|
|
66
|
+
primary?: string;
|
|
67
|
+
textColor?: "black" | "white";
|
|
68
|
+
};
|
|
48
69
|
[key: string]: unknown;
|
|
49
70
|
};
|
|
50
71
|
|
|
72
|
+
/** Map agent-config API theme to widget theme (mode → textColor: dark→black, light→white) */
|
|
73
|
+
function mapAgentConfigThemeToWidgetTheme(apiTheme: AgentConfig["theme"] | undefined): MedAssistInitConfig["theme"] {
|
|
74
|
+
if (!apiTheme) return undefined;
|
|
75
|
+
const textColor = apiTheme.mode === "dark" ? "white" : apiTheme.mode === "light" ? "black" : undefined;
|
|
76
|
+
return {
|
|
77
|
+
...(apiTheme.background && { background: apiTheme.background }),
|
|
78
|
+
...(apiTheme.background_img && { backgroundImage: apiTheme.background_img }),
|
|
79
|
+
...(apiTheme.accent && { primary: apiTheme.accent }),
|
|
80
|
+
...(textColor && { textColor }),
|
|
81
|
+
...(apiTheme.title_img && { titleImg: apiTheme.title_img }),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Fetch agent-config from API and return theme from response data */
|
|
86
|
+
async function fetchAgentConfig(baseUrl: string, agentId: string): Promise<AgentConfig | undefined> {
|
|
87
|
+
const url = `${baseUrl.replace(/\/$/, "")}/med-assist/agent-config/${agentId}`;
|
|
88
|
+
const res = await fetch(url, {
|
|
89
|
+
headers: {
|
|
90
|
+
"ngrok-skip-browser-warning": "69420",
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
if (!res.ok) return undefined;
|
|
95
|
+
const json = await res.json();
|
|
96
|
+
if (json?.success && json?.data) return json.data as AgentConfig;
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
|
|
51
100
|
interface EkaMedAssistWindow extends Window {
|
|
52
101
|
EkaMedAssist?: {
|
|
53
102
|
init: (config: MedAssistInitConfig) => void;
|
|
@@ -312,14 +361,15 @@ class MedAssistWidgetLoader extends HTMLElement {
|
|
|
312
361
|
const button = shadowRoot.getElementById("medassist-open-btn");
|
|
313
362
|
const rootElement = shadowRoot.getElementById("medassist-widget-root");
|
|
314
363
|
const agentId = this.getAttribute("agent-id");
|
|
315
|
-
|
|
364
|
+
let iconUrl = this.getAttribute("icon-url") || this.defaultIconUrl;
|
|
316
365
|
const environment =
|
|
317
366
|
getEnvironment(this.getAttribute("environment")) ?? "production";
|
|
318
|
-
|
|
319
|
-
const baseUrl = this.getAttribute("base-url") ||
|
|
367
|
+
let title = this.getAttribute("title") || "Medi Clinic";
|
|
368
|
+
const baseUrl = this.getAttribute("base-url") || "https://matrix.eka.care/reloaded";
|
|
320
369
|
const displayModeAttribute = this.getAttribute("display-mode");
|
|
321
370
|
const displayMode: "full" | "widget" = displayModeAttribute === "full" ? "full" : "widget";
|
|
322
371
|
const attributeContext = this.getAttribute("context") ? JSON.parse(this.getAttribute("context") || "{}") : undefined;
|
|
372
|
+
const attributeTheme = this.getAttribute("theme") ? (() => { try { return JSON.parse(this.getAttribute("theme") || "{}"); } catch { return undefined; } })() : undefined;
|
|
323
373
|
|
|
324
374
|
const initConfig = (typeof window !== "undefined"
|
|
325
375
|
? (window as EkaMedAssistWindow).__ekaMedAssistConfig__
|
|
@@ -332,6 +382,7 @@ class MedAssistWidgetLoader extends HTMLElement {
|
|
|
332
382
|
|
|
333
383
|
const auth = initConfig.auth || undefined;
|
|
334
384
|
const resolvedAgentId = initConfig.agentId || agentId;
|
|
385
|
+
const apiBaseUrl = (initConfig.baseUrl as string) || baseUrl;
|
|
335
386
|
|
|
336
387
|
const customOnClose = typeof window !== "undefined" ? (window as EkaMedAssistWindow).EkaMedAssist?.onClose : undefined;
|
|
337
388
|
|
|
@@ -343,16 +394,32 @@ class MedAssistWidgetLoader extends HTMLElement {
|
|
|
343
394
|
if (!rootElement) {
|
|
344
395
|
console.error("Widget root element is missing");
|
|
345
396
|
return;
|
|
346
|
-
}
|
|
397
|
+
}
|
|
347
398
|
|
|
348
399
|
if (button) {
|
|
349
400
|
button.classList.add("hidden");
|
|
350
401
|
}
|
|
351
402
|
rootElement.classList.remove("hidden");
|
|
352
403
|
|
|
404
|
+
// Fetch agent-config when baseUrl is available; derive theme (mode → textColor: dark→black, light→white)
|
|
405
|
+
let apiTheme: MedAssistInitConfig["theme"] = undefined;
|
|
406
|
+
let allowed: ("text" | "file" | "audio")[] | undefined;
|
|
407
|
+
if (apiBaseUrl) {
|
|
408
|
+
try {
|
|
409
|
+
const agentConfig = await fetchAgentConfig(apiBaseUrl, resolvedAgentId);
|
|
410
|
+
title = agentConfig?.name || title;
|
|
411
|
+
iconUrl = agentConfig?.theme?.icon_img || iconUrl;
|
|
412
|
+
apiTheme = mapAgentConfigThemeToWidgetTheme(agentConfig?.theme || undefined);
|
|
413
|
+
allowed = agentConfig?.allowed;
|
|
414
|
+
} catch {
|
|
415
|
+
// ignore; use attribute + init theme only
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
353
419
|
const widgetConfig = {
|
|
354
420
|
title: (initConfig.title as string) || title,
|
|
355
421
|
iconUrl: (initConfig.iconUrl as string) || iconUrl,
|
|
422
|
+
allowed: (initConfig.allowed as ("text" | "file" | "audio")[] | undefined) || allowed,
|
|
356
423
|
environment,
|
|
357
424
|
onClose: () => {
|
|
358
425
|
if (customOnClose && typeof customOnClose === "function") {
|
|
@@ -364,10 +431,11 @@ class MedAssistWidgetLoader extends HTMLElement {
|
|
|
364
431
|
rootElement.classList.add("hidden");
|
|
365
432
|
|
|
366
433
|
},
|
|
367
|
-
baseUrl:
|
|
434
|
+
baseUrl: apiBaseUrl,
|
|
368
435
|
context: mergedContext,
|
|
369
436
|
displayMode,
|
|
370
|
-
auth
|
|
437
|
+
auth,
|
|
438
|
+
theme: { ...(apiTheme || {}), ...(attributeTheme || {}), ...(initConfig.theme || {}) }
|
|
371
439
|
};
|
|
372
440
|
|
|
373
441
|
if (this.widgetLoaded) {
|