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