@eka-care/medassist-widget-embed 0.2.13 → 0.2.15
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-helper.js +2 -75
- package/dist/iframe.js +1 -318
- package/dist/index.js +4 -456
- package/dist/src/medassist-widget.css +6 -2
- package/dist/src/medassist-widget.js +89 -29
- package/package.json +10 -15
- package/assets/bot-icon.svg +0 -7
- package/dist/src/medassist-widget.js.map +0 -1
- package/iframe-helper.ts +0 -126
- package/iframe.html +0 -61
- package/iframe.ts +0 -381
- package/index.ts +0 -603
- package/src/.gitkeep +0 -2
- package/src/medassist-widget.css +0 -2793
- package/src/medassist-widget.js +0 -43678
- package/src/medassist-widget.js.map +0 -1
- package/src/vite.svg +0 -1
- package/test.html +0 -70
package/index.ts
DELETED
|
@@ -1,603 +0,0 @@
|
|
|
1
|
-
const getCurrentScript = (): HTMLScriptElement | null => {
|
|
2
|
-
if (typeof document === "undefined") {
|
|
3
|
-
console.error("document is not defined");
|
|
4
|
-
return null;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
const { currentScript } = document;
|
|
8
|
-
if (currentScript instanceof HTMLScriptElement) {
|
|
9
|
-
return currentScript;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const scripts = document.getElementsByTagName("script");
|
|
13
|
-
return scripts.length ? scripts[scripts.length - 1] : null;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
type WidgetEnvironment = "production" | "development" | "staging";
|
|
17
|
-
|
|
18
|
-
const getEnvironment = (
|
|
19
|
-
value: string | null
|
|
20
|
-
): WidgetEnvironment | undefined => {
|
|
21
|
-
if (
|
|
22
|
-
value === "production" ||
|
|
23
|
-
value === "development" ||
|
|
24
|
-
value === "staging"
|
|
25
|
-
) {
|
|
26
|
-
return value;
|
|
27
|
-
}
|
|
28
|
-
return undefined;
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
const scriptEl = getCurrentScript();
|
|
32
|
-
|
|
33
|
-
// Detect version tag from script src (e.g., @dev, @latest)
|
|
34
|
-
const DEFAULT_WIDGET_ASSET_BASE = (() => {
|
|
35
|
-
if (scriptEl?.src?.includes("@dev")) {
|
|
36
|
-
return "https://unpkg.com/@eka-care/medassist-widget@dev/dist/";
|
|
37
|
-
}
|
|
38
|
-
return "https://unpkg.com/@eka-care/medassist-widget@latest/dist/";
|
|
39
|
-
})();
|
|
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
|
-
|
|
55
|
-
type MedAssistInitConfig = {
|
|
56
|
-
auth?: string;
|
|
57
|
-
agentId?: string;
|
|
58
|
-
context?: Record<string, unknown>;
|
|
59
|
-
title?: string;
|
|
60
|
-
iconUrl?: string;
|
|
61
|
-
baseUrl?: string;
|
|
62
|
-
theme?: {
|
|
63
|
-
background?: string;
|
|
64
|
-
backgroundImage?: string;
|
|
65
|
-
primary?: string;
|
|
66
|
-
textColor?: "black" | "white";
|
|
67
|
-
};
|
|
68
|
-
[key: string]: unknown;
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
/** Map agent-config API theme to widget theme (mode → textColor: dark→black, light→white) */
|
|
72
|
-
function mapAgentConfigThemeToWidgetTheme(apiTheme: AgentConfig["theme"] | undefined): MedAssistInitConfig["theme"] {
|
|
73
|
-
if (!apiTheme) return undefined;
|
|
74
|
-
const textColor = apiTheme.mode === "dark" ? "white" : apiTheme.mode === "light" ? "black" : undefined;
|
|
75
|
-
return {
|
|
76
|
-
...(apiTheme.background && { background: apiTheme.background }),
|
|
77
|
-
...(apiTheme.background_img && { backgroundImage: apiTheme.background_img }),
|
|
78
|
-
...(apiTheme.accent && { primary: apiTheme.accent }),
|
|
79
|
-
...(textColor && { textColor }),
|
|
80
|
-
...(apiTheme.title_img && { titleImg: apiTheme.title_img }),
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/** Preload an image by URL so it is cached and loads faster when the widget uses it. */
|
|
85
|
-
function preloadImage(url: string): void {
|
|
86
|
-
if (!url || typeof document === "undefined") return;
|
|
87
|
-
try {
|
|
88
|
-
const link = document.createElement("link");
|
|
89
|
-
link.rel = "preload";
|
|
90
|
-
link.as = "image";
|
|
91
|
-
link.href = url;
|
|
92
|
-
document.head.appendChild(link);
|
|
93
|
-
} catch {
|
|
94
|
-
// fallback: use Image to prime cache
|
|
95
|
-
const img = new Image();
|
|
96
|
-
img.src = url;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/** Fetch agent-config from API and return theme from response data */
|
|
101
|
-
async function fetchAgentConfig(baseUrl: string, agentId: string): Promise<AgentConfig | undefined> {
|
|
102
|
-
const url = `${baseUrl.replace(/\/$/, "")}/med-assist/agent-config/${agentId}`;
|
|
103
|
-
const res = await fetch(url, {
|
|
104
|
-
headers: {
|
|
105
|
-
"ngrok-skip-browser-warning": "69420",
|
|
106
|
-
},
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
if (!res.ok) return undefined;
|
|
110
|
-
const json = await res.json();
|
|
111
|
-
if (json?.success && json?.data) return json.data as AgentConfig;
|
|
112
|
-
return undefined;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
interface EkaMedAssistWindow extends Window {
|
|
116
|
-
EkaMedAssist?: {
|
|
117
|
-
init: (config: MedAssistInitConfig) => void;
|
|
118
|
-
onClose?: () => void;
|
|
119
|
-
};
|
|
120
|
-
__ekaMedAssistConfig__?: MedAssistInitConfig;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
const globalMedAssistConfig: MedAssistInitConfig = {};
|
|
124
|
-
|
|
125
|
-
const getWidgetElement = (): HTMLElement | null => {
|
|
126
|
-
if (typeof document === "undefined") {
|
|
127
|
-
return null;
|
|
128
|
-
}
|
|
129
|
-
return document.querySelector("eka-medassist-widget");
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
if (typeof window !== "undefined") {
|
|
133
|
-
const w = window as EkaMedAssistWindow;
|
|
134
|
-
w.__ekaMedAssistConfig__ = globalMedAssistConfig;
|
|
135
|
-
|
|
136
|
-
w.EkaMedAssist = {
|
|
137
|
-
init(config: MedAssistInitConfig) {
|
|
138
|
-
Object.assign(globalMedAssistConfig, config);
|
|
139
|
-
const el = getWidgetElement();
|
|
140
|
-
if (!el) return;
|
|
141
|
-
|
|
142
|
-
if (config.agentId) el.setAttribute("agent-id", config.agentId);
|
|
143
|
-
if (config.title) el.setAttribute("title", config.title);
|
|
144
|
-
if (config.iconUrl) el.setAttribute("icon-url", config.iconUrl);
|
|
145
|
-
if (config.baseUrl) el.setAttribute("base-url", config.baseUrl);
|
|
146
|
-
if (config.context) {
|
|
147
|
-
try {
|
|
148
|
-
el.setAttribute("context", JSON.stringify(config.context));
|
|
149
|
-
} catch {
|
|
150
|
-
console.warn("Failed to stringify context passed to init");
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
if (config.theme?.backgroundImage) {
|
|
155
|
-
preloadImage(config.theme.backgroundImage);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
(el as any).openFromBridge?.();
|
|
159
|
-
},
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
const scriptBaseUrl = (() => {
|
|
164
|
-
if (!scriptEl) {
|
|
165
|
-
return "";
|
|
166
|
-
}
|
|
167
|
-
try {
|
|
168
|
-
return new URL(".", scriptEl.src || window.location.href).href;
|
|
169
|
-
} catch {
|
|
170
|
-
return "";
|
|
171
|
-
}
|
|
172
|
-
})();
|
|
173
|
-
|
|
174
|
-
const widgetAssetBaseUrl = (() => {
|
|
175
|
-
if (!scriptEl) {
|
|
176
|
-
return DEFAULT_WIDGET_ASSET_BASE;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
const dataValue = scriptEl.dataset.widgetAssets;
|
|
180
|
-
if (dataValue === "local") {
|
|
181
|
-
return `${scriptBaseUrl}src/`;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
if (dataValue && dataValue.length > 0) {
|
|
185
|
-
try {
|
|
186
|
-
return new URL(dataValue, scriptBaseUrl).href;
|
|
187
|
-
} catch {
|
|
188
|
-
return dataValue.endsWith("/") ? dataValue : `${dataValue}/`;
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
return DEFAULT_WIDGET_ASSET_BASE;
|
|
193
|
-
})();
|
|
194
|
-
|
|
195
|
-
const WIDGET_JS_URL = `${widgetAssetBaseUrl}medassist-widget.js`;
|
|
196
|
-
const WIDGET_CSS_URL = `${widgetAssetBaseUrl}medassist-widget.css`;
|
|
197
|
-
|
|
198
|
-
let widgetScriptPromise: Promise<void> | null = null;
|
|
199
|
-
let widgetCssTextPromise: Promise<string> | null = null;
|
|
200
|
-
|
|
201
|
-
/** Start fetching widget CSS (shared promise). Used for preload and by loadWidgetCss. */
|
|
202
|
-
function getWidgetCssTextPromise(): Promise<string> {
|
|
203
|
-
if (!widgetCssTextPromise) {
|
|
204
|
-
widgetCssTextPromise = fetch(WIDGET_CSS_URL).then((response) => {
|
|
205
|
-
if (!response.ok) {
|
|
206
|
-
throw new Error(
|
|
207
|
-
`Unable to fetch widget styles from ${WIDGET_CSS_URL}`
|
|
208
|
-
);
|
|
209
|
-
}
|
|
210
|
-
return response.text();
|
|
211
|
-
});
|
|
212
|
-
}
|
|
213
|
-
return widgetCssTextPromise;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
class MedAssistWidgetLoader extends HTMLElement {
|
|
217
|
-
private defaultIconUrl: string;
|
|
218
|
-
private widgetLoaded: boolean;
|
|
219
|
-
private displayMode: "full" | "widget";
|
|
220
|
-
|
|
221
|
-
constructor() {
|
|
222
|
-
super();
|
|
223
|
-
this.attachShadow({ mode: "open" });
|
|
224
|
-
this.defaultIconUrl = "https://cdn.eka.care/bot-icon.svg";
|
|
225
|
-
this.widgetLoaded = false;
|
|
226
|
-
this.displayMode = this.getAttribute("display-mode") === "full" ? "full" : "widget";
|
|
227
|
-
|
|
228
|
-
// Listen for AUTH_EXPIRED events from the widget and forward to parent
|
|
229
|
-
this.setupAuthExpirationListener();
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
static get observedAttributes(): string[] {
|
|
233
|
-
return ["icon-url", "display-mode"];
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
connectedCallback(): void {
|
|
237
|
-
if (this.displayMode === "full") {
|
|
238
|
-
this.initializeFullMode();
|
|
239
|
-
} else {
|
|
240
|
-
this.renderButton();
|
|
241
|
-
this.preloadWidgetAssets();
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
attributeChangedCallback(name: string): void {
|
|
246
|
-
if (name === "icon-url") {
|
|
247
|
-
if (this.displayMode === "widget") {
|
|
248
|
-
this.renderButton();
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
if (name === "display-mode") {
|
|
252
|
-
const newMode = this.getAttribute("display-mode") === "full" ? "full" : "widget";
|
|
253
|
-
if (newMode !== this.displayMode) {
|
|
254
|
-
this.displayMode = newMode;
|
|
255
|
-
if (newMode === "full") {
|
|
256
|
-
this.initializeFullMode();
|
|
257
|
-
} else {
|
|
258
|
-
this.renderButton();
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
/** When in widget mode, start loading JS and CSS on page load so first open is faster. */
|
|
265
|
-
private preloadWidgetAssets(): void {
|
|
266
|
-
getWidgetCssTextPromise(); // start CSS fetch (no inject until open)
|
|
267
|
-
void this.loadWidgetScript(); // start JS load
|
|
268
|
-
void this.preloadBackgroundImage();
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
/** Preload theme background image from init config, theme attribute, or agent-config API. */
|
|
272
|
-
private preloadBackgroundImage(): void {
|
|
273
|
-
const initConfig =
|
|
274
|
-
typeof window !== "undefined"
|
|
275
|
-
? (window as EkaMedAssistWindow).__ekaMedAssistConfig__
|
|
276
|
-
: undefined;
|
|
277
|
-
const attributeTheme = this.getAttribute("theme")
|
|
278
|
-
? (() => {
|
|
279
|
-
try {
|
|
280
|
-
return JSON.parse(this.getAttribute("theme") || "{}") as { backgroundImage?: string };
|
|
281
|
-
} catch {
|
|
282
|
-
return undefined;
|
|
283
|
-
}
|
|
284
|
-
})()
|
|
285
|
-
: undefined;
|
|
286
|
-
|
|
287
|
-
const fromInit = initConfig?.theme?.backgroundImage;
|
|
288
|
-
const fromAttr = attributeTheme?.backgroundImage;
|
|
289
|
-
if (fromInit) preloadImage(fromInit);
|
|
290
|
-
if (fromAttr && fromAttr !== fromInit) preloadImage(fromAttr);
|
|
291
|
-
|
|
292
|
-
const baseUrl =
|
|
293
|
-
(initConfig?.baseUrl as string) || this.getAttribute("base-url") || "";
|
|
294
|
-
const agentId =
|
|
295
|
-
(initConfig?.agentId as string) || this.getAttribute("agent-id") || "";
|
|
296
|
-
if (baseUrl && agentId) {
|
|
297
|
-
fetchAgentConfig(baseUrl, agentId)
|
|
298
|
-
.then((agentConfig) => {
|
|
299
|
-
const url = agentConfig?.theme?.background_img;
|
|
300
|
-
if (url) preloadImage(url);
|
|
301
|
-
})
|
|
302
|
-
.catch(() => {
|
|
303
|
-
// ignore; widget will load config when it opens
|
|
304
|
-
});
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
private setupAuthExpirationListener(): void {
|
|
309
|
-
if (typeof window === "undefined") return;
|
|
310
|
-
|
|
311
|
-
const messageHandler = (event: MessageEvent) => {
|
|
312
|
-
if (event.data?.type === "AUTH_EXPIRED") {
|
|
313
|
-
if (window.parent !== window) {
|
|
314
|
-
window.parent.postMessage({ type: "AUTH_EXPIRED", message: "Authentication expired" }, "*");
|
|
315
|
-
}
|
|
316
|
-
// window.webkit.messageHandlers.authTokenExpired.postMessage({});
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
window.addEventListener("message", messageHandler);
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
public openFromBridge(): void {
|
|
324
|
-
this.displayMode = this.getAttribute("display-mode") === "full" ? "full" : "widget";
|
|
325
|
-
if (this.displayMode === "full") {
|
|
326
|
-
this.initializeFullMode();
|
|
327
|
-
} else {
|
|
328
|
-
this.renderButton();
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
this.loadWidgetCss()
|
|
332
|
-
.then(() => this.loadWidgetScript())
|
|
333
|
-
.then(() => this.loadAndRender())
|
|
334
|
-
.catch((error) => {
|
|
335
|
-
console.error("Failed to open MedAssist widget from bridge", error);
|
|
336
|
-
});
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
renderButton(): void {
|
|
340
|
-
const iconUrl = this.getAttribute("icon-url") || this.defaultIconUrl;
|
|
341
|
-
const shadowRoot = this.shadowRoot;
|
|
342
|
-
|
|
343
|
-
if (!shadowRoot) {
|
|
344
|
-
console.error("Shadow root was not created");
|
|
345
|
-
return;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
if (!shadowRoot.querySelector("button")) {
|
|
349
|
-
shadowRoot.innerHTML = `
|
|
350
|
-
<style>
|
|
351
|
-
#medassist-open-btn {
|
|
352
|
-
width: 60px;
|
|
353
|
-
height: 60px;
|
|
354
|
-
border-radius: 50%;
|
|
355
|
-
border: none;
|
|
356
|
-
cursor: pointer;
|
|
357
|
-
background-color:rgb(255, 255, 255);
|
|
358
|
-
padding: 0;
|
|
359
|
-
display: flex;
|
|
360
|
-
align-items: center;
|
|
361
|
-
justify-content: center;
|
|
362
|
-
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
|
|
363
|
-
}
|
|
364
|
-
#medassist-open-btn img {
|
|
365
|
-
width: 30px;
|
|
366
|
-
height: 30px;
|
|
367
|
-
}
|
|
368
|
-
:host {
|
|
369
|
-
position: fixed;
|
|
370
|
-
bottom: 20px;
|
|
371
|
-
right: 20px;
|
|
372
|
-
z-index: 200000;
|
|
373
|
-
}
|
|
374
|
-
#medassist-open-btn:hover {
|
|
375
|
-
background-color:rgb(239, 237, 245);
|
|
376
|
-
}
|
|
377
|
-
.hidden {
|
|
378
|
-
display: none !important;
|
|
379
|
-
}
|
|
380
|
-
#medassist-open-btn.hidden {
|
|
381
|
-
display: none !important;
|
|
382
|
-
}
|
|
383
|
-
</style>
|
|
384
|
-
|
|
385
|
-
<button id="medassist-open-btn">
|
|
386
|
-
<img src="${iconUrl}" alt="MedAssist Icon">
|
|
387
|
-
</button>
|
|
388
|
-
<div id="medassist-widget-root" class="hidden"></div>
|
|
389
|
-
`;
|
|
390
|
-
|
|
391
|
-
shadowRoot
|
|
392
|
-
.getElementById("medassist-open-btn")
|
|
393
|
-
?.addEventListener("click", () => this.loadAndRender());
|
|
394
|
-
} else {
|
|
395
|
-
const img = shadowRoot.querySelector<HTMLImageElement>(
|
|
396
|
-
"#medassist-open-btn img"
|
|
397
|
-
);
|
|
398
|
-
if (img) {
|
|
399
|
-
img.src = iconUrl;
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
initializeFullMode(): void {
|
|
405
|
-
const shadowRoot = this.shadowRoot;
|
|
406
|
-
if (!shadowRoot) {
|
|
407
|
-
console.error("Shadow root is not available");
|
|
408
|
-
return;
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
if (!shadowRoot.getElementById("medassist-widget-root")) {
|
|
412
|
-
shadowRoot.innerHTML = `
|
|
413
|
-
<style>
|
|
414
|
-
:host {
|
|
415
|
-
display: block;
|
|
416
|
-
width: 100%;
|
|
417
|
-
height: 100%;
|
|
418
|
-
position: fixed;
|
|
419
|
-
top: 0;
|
|
420
|
-
left: 0;
|
|
421
|
-
z-index: 200000;
|
|
422
|
-
}
|
|
423
|
-
#medassist-widget-root {
|
|
424
|
-
width: 100%;
|
|
425
|
-
height: 100%;
|
|
426
|
-
}
|
|
427
|
-
</style>
|
|
428
|
-
<div id="medassist-widget-root"></div>
|
|
429
|
-
`;
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
async loadAndRender(): Promise<void> {
|
|
434
|
-
const shadowRoot = this.shadowRoot;
|
|
435
|
-
if (!shadowRoot) {
|
|
436
|
-
console.error("Shadow root is not available");
|
|
437
|
-
return;
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
const button = shadowRoot.getElementById("medassist-open-btn");
|
|
441
|
-
const rootElement = shadowRoot.getElementById("medassist-widget-root");
|
|
442
|
-
const agentId = this.getAttribute("agent-id");
|
|
443
|
-
let iconUrl = this.getAttribute("icon-url") || this.defaultIconUrl;
|
|
444
|
-
const environment =
|
|
445
|
-
getEnvironment(this.getAttribute("environment")) ?? "production";
|
|
446
|
-
let title = this.getAttribute("title") || "Medi Clinic";
|
|
447
|
-
const baseUrl = this.getAttribute("base-url") || "https://matrix.eka.care/reloaded";
|
|
448
|
-
const displayModeAttribute = this.getAttribute("display-mode");
|
|
449
|
-
const displayMode: "full" | "widget" = displayModeAttribute === "full" ? "full" : "widget";
|
|
450
|
-
const attributeContext = this.getAttribute("context") ? JSON.parse(this.getAttribute("context") || "{}") : undefined;
|
|
451
|
-
const attributeTheme = this.getAttribute("theme") ? (() => { try { return JSON.parse(this.getAttribute("theme") || "{}"); } catch { return undefined; } })() : undefined;
|
|
452
|
-
|
|
453
|
-
const initConfig = (typeof window !== "undefined"
|
|
454
|
-
? (window as EkaMedAssistWindow).__ekaMedAssistConfig__
|
|
455
|
-
: undefined) || {};
|
|
456
|
-
|
|
457
|
-
const mergedContext = {
|
|
458
|
-
...(attributeContext || {}),
|
|
459
|
-
...(initConfig.context || {}),
|
|
460
|
-
};
|
|
461
|
-
|
|
462
|
-
const auth = initConfig.auth || undefined;
|
|
463
|
-
const resolvedAgentId = initConfig.agentId || agentId;
|
|
464
|
-
const apiBaseUrl = (initConfig.baseUrl as string) || baseUrl;
|
|
465
|
-
|
|
466
|
-
const customOnClose = typeof window !== "undefined" ? (window as EkaMedAssistWindow).EkaMedAssist?.onClose : undefined;
|
|
467
|
-
|
|
468
|
-
if (!resolvedAgentId) {
|
|
469
|
-
console.error("Agent ID is required");
|
|
470
|
-
return;
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
if (!rootElement) {
|
|
474
|
-
console.error("Widget root element is missing");
|
|
475
|
-
return;
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
if (button) {
|
|
479
|
-
button.classList.add("hidden");
|
|
480
|
-
}
|
|
481
|
-
rootElement.classList.remove("hidden");
|
|
482
|
-
|
|
483
|
-
// Fetch agent-config when baseUrl is available; derive theme (mode → textColor: dark→black, light→white)
|
|
484
|
-
let apiTheme: MedAssistInitConfig["theme"] = undefined;
|
|
485
|
-
let allowed: ("text" | "file" | "audio")[] | undefined;
|
|
486
|
-
if (apiBaseUrl) {
|
|
487
|
-
try {
|
|
488
|
-
const agentConfig = await fetchAgentConfig(apiBaseUrl, resolvedAgentId);
|
|
489
|
-
title = agentConfig?.name || title;
|
|
490
|
-
iconUrl = agentConfig?.theme?.icon_img || iconUrl;
|
|
491
|
-
apiTheme = mapAgentConfigThemeToWidgetTheme(agentConfig?.theme || undefined);
|
|
492
|
-
allowed = agentConfig?.allowed;
|
|
493
|
-
} catch {
|
|
494
|
-
// ignore; use attribute + init theme only
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
const widgetConfig = {
|
|
499
|
-
title: (initConfig.title as string) || title,
|
|
500
|
-
iconUrl: (initConfig.iconUrl as string) || iconUrl,
|
|
501
|
-
allowed,
|
|
502
|
-
environment,
|
|
503
|
-
onClose: () => {
|
|
504
|
-
if (customOnClose && typeof customOnClose === "function") {
|
|
505
|
-
customOnClose();
|
|
506
|
-
}
|
|
507
|
-
if (button) {
|
|
508
|
-
button.classList.remove("hidden");
|
|
509
|
-
}
|
|
510
|
-
rootElement.classList.add("hidden");
|
|
511
|
-
|
|
512
|
-
},
|
|
513
|
-
baseUrl: apiBaseUrl,
|
|
514
|
-
context: mergedContext,
|
|
515
|
-
displayMode,
|
|
516
|
-
auth,
|
|
517
|
-
theme: { ...(apiTheme || {}), ...(attributeTheme || {}), ...(initConfig.theme || {}) }
|
|
518
|
-
};
|
|
519
|
-
|
|
520
|
-
if (this.widgetLoaded) {
|
|
521
|
-
window.renderMedAssist?.(rootElement, resolvedAgentId, widgetConfig);
|
|
522
|
-
return;
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
try {
|
|
526
|
-
await Promise.all([this.loadWidgetCss(), this.loadWidgetScript()]);
|
|
527
|
-
if (typeof window.renderMedAssist !== "function") {
|
|
528
|
-
throw new Error("renderMedAssist is not available on window");
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
window.renderMedAssist(rootElement, resolvedAgentId, widgetConfig);
|
|
532
|
-
this.widgetLoaded = true;
|
|
533
|
-
} catch (error) {
|
|
534
|
-
console.error("Failed to load MedAssist widget", error);
|
|
535
|
-
if (button) {
|
|
536
|
-
button.classList.remove("hidden");
|
|
537
|
-
}
|
|
538
|
-
rootElement.classList.add("hidden");
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
async loadWidgetCss(): Promise<void> {
|
|
543
|
-
const shadowRoot = this.shadowRoot;
|
|
544
|
-
if (!shadowRoot) {
|
|
545
|
-
throw new Error("Shadow root is not available");
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
if (shadowRoot.querySelector("[data-medassist-style='true']")) {
|
|
549
|
-
return;
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
const cssText = await getWidgetCssTextPromise();
|
|
553
|
-
const styleTag = document.createElement("style");
|
|
554
|
-
styleTag.setAttribute("data-medassist-style", "true");
|
|
555
|
-
styleTag.textContent = cssText;
|
|
556
|
-
shadowRoot.appendChild(styleTag);
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
loadWidgetScript(): Promise<void> {
|
|
560
|
-
if (!widgetScriptPromise) {
|
|
561
|
-
widgetScriptPromise = new Promise<void>((resolve, reject) => {
|
|
562
|
-
const existingScript = document.querySelector<HTMLScriptElement>(
|
|
563
|
-
`script[src="${WIDGET_JS_URL}"]`
|
|
564
|
-
);
|
|
565
|
-
|
|
566
|
-
if (existingScript) {
|
|
567
|
-
if (existingScript.dataset.loaded === "true") {
|
|
568
|
-
resolve();
|
|
569
|
-
return;
|
|
570
|
-
}
|
|
571
|
-
existingScript.addEventListener("load", () => resolve());
|
|
572
|
-
existingScript.addEventListener("error", (event) =>
|
|
573
|
-
reject(event.error || new Error("Unknown script loading error"))
|
|
574
|
-
);
|
|
575
|
-
return;
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
const script = document.createElement("script");
|
|
579
|
-
script.src = WIDGET_JS_URL;
|
|
580
|
-
script.async = true;
|
|
581
|
-
script.dataset.widget = "medassist";
|
|
582
|
-
|
|
583
|
-
script.onload = () => {
|
|
584
|
-
script.dataset.loaded = "true";
|
|
585
|
-
resolve();
|
|
586
|
-
};
|
|
587
|
-
script.onerror = (event) => {
|
|
588
|
-
if (event instanceof ErrorEvent && event.error) {
|
|
589
|
-
reject(event.error);
|
|
590
|
-
return;
|
|
591
|
-
}
|
|
592
|
-
reject(new Error(`Failed to load ${WIDGET_JS_URL}`));
|
|
593
|
-
};
|
|
594
|
-
|
|
595
|
-
document.head.appendChild(script);
|
|
596
|
-
});
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
return widgetScriptPromise;
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
customElements.define("eka-medassist-widget", MedAssistWidgetLoader);
|
package/src/.gitkeep
DELETED