@eka-care/medassist-widget-embed 0.2.3 → 0.2.5
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 +83 -11
- package/dist/index.d.ts +22 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +59 -29
- package/dist/src/medassist-widget.css +795 -277
- package/dist/src/medassist-widget.js +1074 -458
- package/dist/src/medassist-widget.js.map +1 -1
- package/iframe.ts +110 -10
- package/index.ts +70 -29
- package/package.json +1 -1
- package/src/medassist-widget.css +795 -277
- package/src/medassist-widget.js +1074 -458
- package/src/medassist-widget.js.map +1 -1
- package/test.html +8 -8
package/iframe.ts
CHANGED
|
@@ -29,6 +29,77 @@
|
|
|
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
|
+
theme?: {
|
|
36
|
+
background?: string;
|
|
37
|
+
background_img?: string;
|
|
38
|
+
accent?: string;
|
|
39
|
+
mode?: "light" | "dark";
|
|
40
|
+
title_img?: string;
|
|
41
|
+
icon_img?: string;
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
type WidgetTheme = {
|
|
46
|
+
background?: string;
|
|
47
|
+
backgroundImage?: string;
|
|
48
|
+
primary?: string;
|
|
49
|
+
textColor?: "black" | "white";
|
|
50
|
+
titleImg?: string;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const API_FETCH_TIMEOUT_MS = 10000;
|
|
54
|
+
|
|
55
|
+
/** Map agent-config API theme to widget theme (mode → textColor: dark→black, light→white) */
|
|
56
|
+
function mapAgentConfigThemeToWidgetTheme(
|
|
57
|
+
apiTheme: AgentConfig["theme"] | undefined
|
|
58
|
+
): WidgetTheme | undefined {
|
|
59
|
+
if (!apiTheme) return undefined;
|
|
60
|
+
const textColor =
|
|
61
|
+
apiTheme.mode === "dark"
|
|
62
|
+
? "white"
|
|
63
|
+
: apiTheme.mode === "light"
|
|
64
|
+
? "black"
|
|
65
|
+
: undefined;
|
|
66
|
+
return {
|
|
67
|
+
...(apiTheme.background && { background: apiTheme.background }),
|
|
68
|
+
...(apiTheme.background_img && {
|
|
69
|
+
backgroundImage: apiTheme.background_img,
|
|
70
|
+
}),
|
|
71
|
+
...(apiTheme.accent && { primary: apiTheme.accent }),
|
|
72
|
+
...(textColor && { textColor }),
|
|
73
|
+
...(apiTheme.title_img && { titleImg: apiTheme.title_img }),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Fetch agent-config from API with timeout; aborts if pending > 10s */
|
|
78
|
+
async function fetchAgentConfig(
|
|
79
|
+
baseUrl: string,
|
|
80
|
+
agentId: string
|
|
81
|
+
): Promise<AgentConfig | undefined> {
|
|
82
|
+
const url = `${baseUrl.replace(/\/$/, "")}/med-assist/agent-config/${agentId}`;
|
|
83
|
+
const controller = new AbortController();
|
|
84
|
+
const timeoutId = setTimeout(() => controller.abort(), API_FETCH_TIMEOUT_MS);
|
|
85
|
+
try {
|
|
86
|
+
const res = await fetch(url, {
|
|
87
|
+
signal: controller.signal,
|
|
88
|
+
headers: {
|
|
89
|
+
"ngrok-skip-browser-warning": "69420",
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
if (!res.ok) return undefined;
|
|
93
|
+
const json = await res.json();
|
|
94
|
+
if (json?.success && json?.data?.theme) return json.data as AgentConfig;
|
|
95
|
+
return undefined;
|
|
96
|
+
} catch {
|
|
97
|
+
return undefined;
|
|
98
|
+
} finally {
|
|
99
|
+
clearTimeout(timeoutId);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
32
103
|
const DEFAULT_WIDGET_ASSET_BASE =
|
|
33
104
|
"https://unpkg.com/@eka-care/medassist-widget@latest/dist/";
|
|
34
105
|
|
|
@@ -81,35 +152,64 @@
|
|
|
81
152
|
return;
|
|
82
153
|
}
|
|
83
154
|
|
|
84
|
-
|
|
85
|
-
|
|
155
|
+
let title = urlParams.get("title") || "Medi Clinic";
|
|
156
|
+
let iconUrl = urlParams.get("iconUrl") || undefined;
|
|
86
157
|
const context = urlParams.get("context");
|
|
87
|
-
const baseUrl = urlParams.get("baseUrl") ||
|
|
158
|
+
const baseUrl = urlParams.get("baseUrl") || "https://matrix.eka.care/reloaded";
|
|
88
159
|
const environment =
|
|
89
160
|
getEnvironment(urlParams.get("environment")) ?? "production";
|
|
161
|
+
const attributeTheme = urlParams.get("theme")
|
|
162
|
+
? (() => {
|
|
163
|
+
try {
|
|
164
|
+
return JSON.parse(urlParams.get("theme") || "{}") as WidgetTheme;
|
|
165
|
+
} catch {
|
|
166
|
+
return undefined;
|
|
167
|
+
}
|
|
168
|
+
})()
|
|
169
|
+
: undefined;
|
|
90
170
|
const container = document.getElementById("root") || document.body;
|
|
91
171
|
try {
|
|
92
|
-
await Promise.all([
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
172
|
+
await Promise.all([loadWidgetCss(), loadWidgetScript()]);
|
|
173
|
+
if (
|
|
174
|
+
!window?.renderMedAssist ||
|
|
175
|
+
typeof window?.renderMedAssist !== "function"
|
|
176
|
+
) {
|
|
97
177
|
throw new Error("renderMedAssist is not available on window");
|
|
98
178
|
}
|
|
99
179
|
|
|
180
|
+
// Fetch agent-config when baseUrl is available; 10s timeout aborts pending request
|
|
181
|
+
let apiTheme: WidgetTheme | undefined;
|
|
182
|
+
console.log("base url", baseUrl);
|
|
183
|
+
|
|
184
|
+
if (baseUrl) {
|
|
185
|
+
try {
|
|
186
|
+
const agentConfig = await fetchAgentConfig(baseUrl, agentId);
|
|
187
|
+
title = agentConfig?.name ?? title;
|
|
188
|
+
iconUrl = agentConfig?.theme?.icon_img ?? iconUrl;
|
|
189
|
+
apiTheme = mapAgentConfigThemeToWidgetTheme(
|
|
190
|
+
agentConfig?.theme ?? undefined
|
|
191
|
+
);
|
|
192
|
+
} catch {
|
|
193
|
+
// ignore; use URL params only
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
100
197
|
const config = {
|
|
101
198
|
title,
|
|
102
199
|
iconUrl,
|
|
103
200
|
environment,
|
|
104
201
|
onClose: () => {
|
|
105
|
-
// Send message to parent window to close iframe
|
|
106
202
|
if (window.parent !== window) {
|
|
107
203
|
window.parent.postMessage({ type: "WIDGET_CLOSE" }, "*");
|
|
108
204
|
}
|
|
109
205
|
},
|
|
110
206
|
context: context ? JSON.parse(context) : undefined,
|
|
111
207
|
baseUrl,
|
|
112
|
-
displayMode: "full" as "full" | "widget"
|
|
208
|
+
displayMode: "full" as "full" | "widget",
|
|
209
|
+
theme: {
|
|
210
|
+
...(apiTheme ?? {}),
|
|
211
|
+
...(attributeTheme ?? {}),
|
|
212
|
+
},
|
|
113
213
|
};
|
|
114
214
|
window.renderMedAssist(container, agentId, config);
|
|
115
215
|
} catch (error) {
|
package/index.ts
CHANGED
|
@@ -38,6 +38,19 @@ 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
|
+
theme?: {
|
|
45
|
+
background?: string;
|
|
46
|
+
background_img?: string;
|
|
47
|
+
accent?: string;
|
|
48
|
+
mode?: "light" | "dark";
|
|
49
|
+
title_img?: string;
|
|
50
|
+
icon_img?: string
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
41
54
|
type MedAssistInitConfig = {
|
|
42
55
|
auth?: string;
|
|
43
56
|
agentId?: string;
|
|
@@ -45,9 +58,43 @@ type MedAssistInitConfig = {
|
|
|
45
58
|
title?: string;
|
|
46
59
|
iconUrl?: string;
|
|
47
60
|
baseUrl?: string;
|
|
61
|
+
theme?: {
|
|
62
|
+
background?: string;
|
|
63
|
+
backgroundImage?: string;
|
|
64
|
+
primary?: string;
|
|
65
|
+
textColor?: "black" | "white";
|
|
66
|
+
};
|
|
48
67
|
[key: string]: unknown;
|
|
49
68
|
};
|
|
50
69
|
|
|
70
|
+
/** Map agent-config API theme to widget theme (mode → textColor: dark→black, light→white) */
|
|
71
|
+
function mapAgentConfigThemeToWidgetTheme(apiTheme: AgentConfig["theme"] | undefined): MedAssistInitConfig["theme"] {
|
|
72
|
+
if (!apiTheme) return undefined;
|
|
73
|
+
const textColor = apiTheme.mode === "dark" ? "white" : apiTheme.mode === "light" ? "black" : undefined;
|
|
74
|
+
return {
|
|
75
|
+
...(apiTheme.background && { background: apiTheme.background }),
|
|
76
|
+
...(apiTheme.background_img && { backgroundImage: apiTheme.background_img }),
|
|
77
|
+
...(apiTheme.accent && { primary: apiTheme.accent }),
|
|
78
|
+
...(textColor && { textColor }),
|
|
79
|
+
...(apiTheme.title_img && { titleImg: apiTheme.title_img }),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Fetch agent-config from API and return theme from response data */
|
|
84
|
+
async function fetchAgentConfig(baseUrl: string, agentId: string): Promise<AgentConfig | undefined> {
|
|
85
|
+
const url = `${baseUrl.replace(/\/$/, "")}/med-assist/agent-config/${agentId}`;
|
|
86
|
+
const res = await fetch(url, {
|
|
87
|
+
headers: {
|
|
88
|
+
"ngrok-skip-browser-warning": "69420",
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
if (!res.ok) return undefined;
|
|
93
|
+
const json = await res.json();
|
|
94
|
+
if (json?.success && json?.data?.theme) return json.data as AgentConfig;
|
|
95
|
+
return undefined;
|
|
96
|
+
}
|
|
97
|
+
|
|
51
98
|
interface EkaMedAssistWindow extends Window {
|
|
52
99
|
EkaMedAssist?: {
|
|
53
100
|
init: (config: MedAssistInitConfig) => void;
|
|
@@ -142,7 +189,6 @@ class MedAssistWidgetLoader extends HTMLElement {
|
|
|
142
189
|
this.widgetLoaded = false;
|
|
143
190
|
this.displayMode = this.getAttribute("display-mode") === "full" ? "full" : "widget";
|
|
144
191
|
|
|
145
|
-
this.preloadResources();
|
|
146
192
|
// Listen for AUTH_EXPIRED events from the widget and forward to parent
|
|
147
193
|
this.setupAuthExpirationListener();
|
|
148
194
|
}
|
|
@@ -178,28 +224,6 @@ class MedAssistWidgetLoader extends HTMLElement {
|
|
|
178
224
|
}
|
|
179
225
|
}
|
|
180
226
|
|
|
181
|
-
// Preload resources in the background
|
|
182
|
-
private preloadResources(): void {
|
|
183
|
-
// Preload CSS (browser will cache it)
|
|
184
|
-
if (!document.querySelector(`link[href="${WIDGET_CSS_URL}"]`)) {
|
|
185
|
-
const cssLink = document.createElement("link");
|
|
186
|
-
cssLink.rel = "preload";
|
|
187
|
-
cssLink.href = WIDGET_CSS_URL;
|
|
188
|
-
cssLink.as = "style";
|
|
189
|
-
document.head.appendChild(cssLink);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// Preload JS (browser will cache it)
|
|
193
|
-
if (!document.querySelector(`link[href="${WIDGET_JS_URL}"]`)) {
|
|
194
|
-
const jsLink = document.createElement("link");
|
|
195
|
-
jsLink.rel = "preload";
|
|
196
|
-
jsLink.href = WIDGET_JS_URL;
|
|
197
|
-
jsLink.as = "script";
|
|
198
|
-
jsLink.crossOrigin = "anonymous";
|
|
199
|
-
document.head.appendChild(jsLink);
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
227
|
private setupAuthExpirationListener(): void {
|
|
204
228
|
if (typeof window === "undefined") return;
|
|
205
229
|
|
|
@@ -223,7 +247,8 @@ class MedAssistWidgetLoader extends HTMLElement {
|
|
|
223
247
|
this.renderButton();
|
|
224
248
|
}
|
|
225
249
|
|
|
226
|
-
|
|
250
|
+
this.loadWidgetCss()
|
|
251
|
+
.then(() => this.loadWidgetScript())
|
|
227
252
|
.then(() => this.loadAndRender())
|
|
228
253
|
.catch((error) => {
|
|
229
254
|
console.error("Failed to open MedAssist widget from bridge", error);
|
|
@@ -334,14 +359,15 @@ class MedAssistWidgetLoader extends HTMLElement {
|
|
|
334
359
|
const button = shadowRoot.getElementById("medassist-open-btn");
|
|
335
360
|
const rootElement = shadowRoot.getElementById("medassist-widget-root");
|
|
336
361
|
const agentId = this.getAttribute("agent-id");
|
|
337
|
-
|
|
362
|
+
let iconUrl = this.getAttribute("icon-url") || this.defaultIconUrl;
|
|
338
363
|
const environment =
|
|
339
364
|
getEnvironment(this.getAttribute("environment")) ?? "production";
|
|
340
|
-
|
|
341
|
-
const baseUrl = this.getAttribute("base-url") ||
|
|
365
|
+
let title = this.getAttribute("title") || "Medi Clinic";
|
|
366
|
+
const baseUrl = this.getAttribute("base-url") || "https://matrix.eka.care/reloaded";
|
|
342
367
|
const displayModeAttribute = this.getAttribute("display-mode");
|
|
343
368
|
const displayMode: "full" | "widget" = displayModeAttribute === "full" ? "full" : "widget";
|
|
344
369
|
const attributeContext = this.getAttribute("context") ? JSON.parse(this.getAttribute("context") || "{}") : undefined;
|
|
370
|
+
const attributeTheme = this.getAttribute("theme") ? (() => { try { return JSON.parse(this.getAttribute("theme") || "{}"); } catch { return undefined; } })() : undefined;
|
|
345
371
|
|
|
346
372
|
const initConfig = (typeof window !== "undefined"
|
|
347
373
|
? (window as EkaMedAssistWindow).__ekaMedAssistConfig__
|
|
@@ -354,6 +380,7 @@ class MedAssistWidgetLoader extends HTMLElement {
|
|
|
354
380
|
|
|
355
381
|
const auth = initConfig.auth || undefined;
|
|
356
382
|
const resolvedAgentId = initConfig.agentId || agentId;
|
|
383
|
+
const apiBaseUrl = (initConfig.baseUrl as string) || baseUrl;
|
|
357
384
|
|
|
358
385
|
const customOnClose = typeof window !== "undefined" ? (window as EkaMedAssistWindow).EkaMedAssist?.onClose : undefined;
|
|
359
386
|
|
|
@@ -372,6 +399,19 @@ class MedAssistWidgetLoader extends HTMLElement {
|
|
|
372
399
|
}
|
|
373
400
|
rootElement.classList.remove("hidden");
|
|
374
401
|
|
|
402
|
+
// Fetch agent-config when baseUrl is available; derive theme (mode → textColor: dark→black, light→white)
|
|
403
|
+
let apiTheme: MedAssistInitConfig["theme"] = undefined;
|
|
404
|
+
if (apiBaseUrl) {
|
|
405
|
+
try {
|
|
406
|
+
const agentConfig = await fetchAgentConfig(apiBaseUrl, resolvedAgentId);
|
|
407
|
+
title = agentConfig?.name || title;
|
|
408
|
+
iconUrl = agentConfig?.theme?.icon_img || iconUrl;
|
|
409
|
+
apiTheme = mapAgentConfigThemeToWidgetTheme(agentConfig?.theme || undefined);
|
|
410
|
+
} catch {
|
|
411
|
+
// ignore; use attribute + init theme only
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
375
415
|
const widgetConfig = {
|
|
376
416
|
title: (initConfig.title as string) || title,
|
|
377
417
|
iconUrl: (initConfig.iconUrl as string) || iconUrl,
|
|
@@ -386,10 +426,11 @@ class MedAssistWidgetLoader extends HTMLElement {
|
|
|
386
426
|
rootElement.classList.add("hidden");
|
|
387
427
|
|
|
388
428
|
},
|
|
389
|
-
baseUrl:
|
|
429
|
+
baseUrl: apiBaseUrl,
|
|
390
430
|
context: mergedContext,
|
|
391
431
|
displayMode,
|
|
392
|
-
auth
|
|
432
|
+
auth,
|
|
433
|
+
theme: { ...(apiTheme || {}), ...(attributeTheme || {}), ...(initConfig.theme || {}) }
|
|
393
434
|
};
|
|
394
435
|
|
|
395
436
|
if (this.widgetLoaded) {
|