@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/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
- const title = urlParams.get("title") || "Medi Clinic";
85
- const iconUrl = urlParams.get("iconUrl") || undefined;
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") || undefined;
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
- loadWidgetCss(),
94
- loadWidgetScript(),
95
- ])
96
- if (!window?.renderMedAssist || typeof window?.renderMedAssist !== "function") {
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",//for iframe default display mode is full
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
- const iconUrl = this.getAttribute("icon-url") || this.defaultIconUrl;
364
+ let iconUrl = this.getAttribute("icon-url") || this.defaultIconUrl;
316
365
  const environment =
317
366
  getEnvironment(this.getAttribute("environment")) ?? "production";
318
- const title = this.getAttribute("title") || "Medi Clinic";
319
- const baseUrl = this.getAttribute("base-url") || undefined;
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: (initConfig.baseUrl as string) || 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) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eka-care/medassist-widget-embed",
3
- "version": "0.2.4",
3
+ "version": "0.2.6",
4
4
  "description": "Embeddable MedAssist widget loader built with Web Components.",
5
5
  "author": "Geethanjali S",
6
6
  "license": "MIT",