obi-sdk 0.3.13 → 0.4.1

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.
@@ -15466,23 +15466,23 @@ class RTCEngine extends eventsExports.EventEmitter {
15466
15466
  this.close();
15467
15467
  };
15468
15468
  const duration2 = Date.now() - this.reconnectStart;
15469
- let delay = this.getNextRetryDelay({
15469
+ let delay2 = this.getNextRetryDelay({
15470
15470
  elapsedMs: duration2,
15471
15471
  retryCount: this.reconnectAttempts
15472
15472
  });
15473
- if (delay === null) {
15473
+ if (delay2 === null) {
15474
15474
  disconnect(duration2);
15475
15475
  return;
15476
15476
  }
15477
15477
  if (connection === leaveReconnect) {
15478
- delay = 0;
15478
+ delay2 = 0;
15479
15479
  }
15480
- this.log.debug("reconnecting in ".concat(delay, "ms"), this.logContext);
15480
+ this.log.debug("reconnecting in ".concat(delay2, "ms"), this.logContext);
15481
15481
  this.clearReconnectTimeout();
15482
15482
  if (this.token && this.regionUrlProvider) {
15483
15483
  this.regionUrlProvider.updateToken(this.token);
15484
15484
  }
15485
- this.reconnectTimeout = CriticalTimers.setTimeout(() => this.attemptReconnect(disconnectReason).finally(() => this.reconnectTimeout = void 0), delay);
15485
+ this.reconnectTimeout = CriticalTimers.setTimeout(() => this.attemptReconnect(disconnectReason).finally(() => this.reconnectTimeout = void 0), delay2);
15486
15486
  };
15487
15487
  this.waitForRestarted = () => {
15488
15488
  return new Promise((resolve, reject) => {
@@ -22954,6 +22954,9 @@ class ObiSession {
22954
22954
  this._userAudioTimer = null;
22955
22955
  this.sessionId = sessionId;
22956
22956
  this.apiBaseUrl = apiBaseUrl || DEFAULT_API_BASE_URL;
22957
+ this.client = new ObiClient({
22958
+ baseUrl: this.apiBaseUrl
22959
+ });
22957
22960
  this.emitter = new EventEmitter();
22958
22961
  }
22959
22962
  emit(event, data) {
@@ -23010,7 +23013,7 @@ class ObiSession {
23010
23013
  if (this.currentState === SDKState.RESEARCHING || this.currentState === SDKState.PAUSED)
23011
23014
  return;
23012
23015
  const state = attributes["lk.agent.state"];
23013
- const newState = z$2(state).with("listening", () => SDKState.USER_SPEAKING).with("speaking", () => SDKState.AGENT_SPEAKING).with("thinking", () => SDKState.AGENT_SPEAKING).otherwise(() => void 0);
23016
+ const newState = z$2(state).with("listening", () => SDKState.USER_SPEAKING).with("speaking", () => SDKState.AGENT_SPEAKING).with("thinking", () => SDKState.THINKING).otherwise(() => void 0);
23014
23017
  if (!newState)
23015
23018
  return;
23016
23019
  this.setState(newState);
@@ -23080,10 +23083,8 @@ class ObiSession {
23080
23083
  dynacast: true
23081
23084
  });
23082
23085
  this.setupRoomEventListeners();
23083
- const params = new URLSearchParams({ token: this.sessionId, skip_intro: "true" });
23084
- const joinEndpoint = `${this.apiBaseUrl}/join-token?${params.toString()}`;
23085
- const joinToken = await fetch(joinEndpoint).then((res) => res.json());
23086
- await this.room.connect(joinToken.url, joinToken.token);
23086
+ const joinToken = await this.client.getJoinToken(this.sessionId, { skipIntro: true });
23087
+ await this.room.connect(joinToken.data.url, joinToken.data.token);
23087
23088
  if (this.microphoneStream) {
23088
23089
  const micTrack = this.microphoneStream.getAudioTracks()[0];
23089
23090
  await this.room.localParticipant.publishTrack(micTrack, {
@@ -23092,8 +23093,8 @@ class ObiSession {
23092
23093
  });
23093
23094
  }
23094
23095
  return {
23095
- url: joinToken.url,
23096
- token: joinToken.token
23096
+ url: joinToken.data.url,
23097
+ token: joinToken.data.token
23097
23098
  };
23098
23099
  } catch (error) {
23099
23100
  console.error("Failed to connect to LiveKit:", error);
@@ -23138,6 +23139,9 @@ class ObiSession {
23138
23139
  this.currentState = newState;
23139
23140
  this.emitter.emit("stateChanged", newState);
23140
23141
  }
23142
+ getCurrentState() {
23143
+ return this.currentState;
23144
+ }
23141
23145
  async requestMicrophone() {
23142
23146
  try {
23143
23147
  const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
@@ -23294,7 +23298,6 @@ class ObiSession {
23294
23298
  }
23295
23299
  }
23296
23300
  const SESSION_URL_PARAM = "49206C6F7665204F6269_session";
23297
- const API_KEY_URL_PARAM = "49206C6F7665204F6269_client";
23298
23301
  var extendStatics = function(d2, b2) {
23299
23302
  extendStatics = Object.setPrototypeOf || { __proto__: [] } instanceof Array && function(d3, b3) {
23300
23303
  d3.__proto__ = b3;
@@ -33389,7 +33392,7 @@ class DotLoader extends LitElement {
33389
33392
  animateSequence() {
33390
33393
  this.activeDots = Array(this.numDots).fill(false);
33391
33394
  for (let i2 = 0; i2 < this.numDots; i2++) {
33392
- const delay = i2 * (this.animationDuration * this.overlapFactor);
33395
+ const delay2 = i2 * (this.animationDuration * this.overlapFactor);
33393
33396
  const bounceStartId = window.setTimeout(() => {
33394
33397
  this.activeDots = [...this.activeDots];
33395
33398
  this.activeDots[i2] = true;
@@ -33398,7 +33401,7 @@ class DotLoader extends LitElement {
33398
33401
  this.activeDots[i2] = false;
33399
33402
  }, this.animationDuration);
33400
33403
  this.timeoutIds.push(bounceEndId);
33401
- }, delay);
33404
+ }, delay2);
33402
33405
  this.timeoutIds.push(bounceStartId);
33403
33406
  }
33404
33407
  const nextSequenceId = window.setTimeout(
@@ -33507,14 +33510,9 @@ class SessionStartModal extends LitElement {
33507
33510
  this.onClose();
33508
33511
  }
33509
33512
  }
33510
- handleBackdropClick(e2) {
33511
- if (e2.target === e2.currentTarget) {
33512
- this.handleClose();
33513
- }
33514
- }
33515
33513
  render() {
33516
33514
  return html`
33517
- <div class="backdrop" @click=${this.handleBackdropClick}></div>
33515
+ <div class="backdrop"></div>
33518
33516
  <div class="container">
33519
33517
  <button class="close-button" @click=${this.handleClose}>×</button>
33520
33518
 
@@ -33551,11 +33549,12 @@ SessionStartModal.styles = css`
33551
33549
  left: 50%;
33552
33550
  transform: translate(-50%, -50%);
33553
33551
  z-index: 50;
33552
+ gap: 32px;
33554
33553
 
33555
33554
  /* Layout from user specifications */
33556
33555
  display: flex;
33557
33556
  width: 640px;
33558
- height: 380px;
33557
+ min-height: 380px;
33559
33558
  padding: 48px 48px 32px 48px;
33560
33559
  flex-direction: column;
33561
33560
  justify-content: space-between;
@@ -33587,7 +33586,7 @@ SessionStartModal.styles = css`
33587
33586
  align-items: center;
33588
33587
  gap: 8px;
33589
33588
  aspect-ratio: 1/1;
33590
- border-radius: var(--border-radius-lg, 8px);
33589
+ border-radius: var(--border-radius-lg, 12px);
33591
33590
  background: var(--tailwind-colors-violet-600, #7c3aed);
33592
33591
  box-shadow:
33593
33592
  0px 0px 8px 0px rgba(168, 85, 247, 0.12),
@@ -33609,15 +33608,15 @@ SessionStartModal.styles = css`
33609
33608
  font-family: "Syne", sans-serif;
33610
33609
  font-size: 32px;
33611
33610
  font-weight: 700;
33612
- margin: 32px 0 0 0;
33613
33611
  color: #111827;
33612
+ margin: 0;
33614
33613
  }
33615
33614
 
33616
33615
  .subtitle {
33617
33616
  font-size: 16px;
33618
33617
  color: #6b7280;
33619
- margin: 16px 0 0 0;
33620
33618
  line-height: 1.5;
33619
+ margin: 0;
33621
33620
  }
33622
33621
 
33623
33622
  .button {
@@ -33695,11 +33694,13 @@ var __decorateClass = (decorators, target, key, kind) => {
33695
33694
  __defProp(target, key, result);
33696
33695
  return result;
33697
33696
  };
33697
+ const WIDGET_PARAMS_KEY = "io.obi.widget-parameters";
33698
33698
  class ObiWidget extends LitElement {
33699
33699
  constructor() {
33700
33700
  super();
33701
33701
  this.apiKey = "";
33702
33702
  this.isActive = true;
33703
+ this.linkOnlyAccess = false;
33703
33704
  this.position = "bottom-right";
33704
33705
  this.user = null;
33705
33706
  this.state = SDKState.READY;
@@ -33721,6 +33722,7 @@ class ObiWidget extends LitElement {
33721
33722
  this.boundSaveSessionData = null;
33722
33723
  this.obiClient = null;
33723
33724
  this.closeNavTimeoutRef = null;
33725
+ this.researchingTimeoutRef = null;
33724
33726
  this.handleCourseSelectEvent = (event) => {
33725
33727
  const customEvent = event;
33726
33728
  this.selectedCourse = customEvent.detail;
@@ -33741,10 +33743,13 @@ class ObiWidget extends LitElement {
33741
33743
  const sessionWithPlan = matchingSession;
33742
33744
  this.selectedCourse = {
33743
33745
  id: sessionToken,
33744
- name: sessionWithPlan.onboarding_plan?.product?.name || "Session from URL",
33745
- description: sessionWithPlan.onboarding_plan?.product?.description || "Continue your session"
33746
+ name: sessionWithPlan.onboarding_plan?.name || "",
33747
+ description: sessionWithPlan.onboarding_plan?.description || ""
33746
33748
  };
33749
+ this.state = SDKState.LOADING;
33747
33750
  this.showSessionStartModal = true;
33751
+ } else {
33752
+ console.log("No session found with token:", sessionToken);
33748
33753
  }
33749
33754
  }
33750
33755
  } catch (error) {
@@ -33771,15 +33776,22 @@ class ObiWidget extends LitElement {
33771
33776
  if (window.obiWidgetConfig.isActive !== void 0) {
33772
33777
  this.isActive = window.obiWidgetConfig.isActive;
33773
33778
  }
33779
+ if (window.obiWidgetConfig.linkOnlyAccess !== void 0) {
33780
+ this.linkOnlyAccess = window.obiWidgetConfig.linkOnlyAccess;
33781
+ }
33774
33782
  this.style.setProperty("--obi-primary", window.obiWidgetConfig?.primaryColor || "#9500ff");
33775
33783
  this.style.setProperty("--obi-secondary", window.obiWidgetConfig?.secondaryColor || "#c4b5fd");
33776
33784
  }
33777
33785
  }
33778
- removeSessionFromUrl() {
33786
+ removeSessionUrlParams() {
33779
33787
  const url = new URL(window.location.href);
33780
33788
  url.searchParams.delete(SESSION_URL_PARAM);
33781
- url.searchParams.delete(API_KEY_URL_PARAM);
33782
33789
  window.history.replaceState({}, "", url.toString());
33790
+ try {
33791
+ localStorage.removeItem(WIDGET_PARAMS_KEY);
33792
+ } catch (error) {
33793
+ console.warn("Failed to remove widget parameters from localStorage:", error);
33794
+ }
33783
33795
  }
33784
33796
  /**
33785
33797
  * Create a new ObiSession instance with common configuration
@@ -33805,9 +33817,27 @@ class ObiWidget extends LitElement {
33805
33817
  */
33806
33818
  setupSessionEventListeners(session, onError) {
33807
33819
  session.on("stateChanged", (newState) => {
33808
- this.state = newState;
33809
- if (newState !== SDKState.READY) {
33820
+ if (newState === SDKState.RESEARCHING) {
33821
+ if (this.researchingTimeoutRef) {
33822
+ window.clearTimeout(this.researchingTimeoutRef);
33823
+ }
33824
+ this.state = newState;
33825
+ this.researchingTimeoutRef = window.setTimeout(() => {
33826
+ this.researchingTimeoutRef = null;
33827
+ const currentSessionState = session.getCurrentState();
33828
+ this.state = currentSessionState;
33829
+ if (currentSessionState !== SDKState.READY) {
33830
+ this.storedActiveState = currentSessionState;
33831
+ }
33832
+ }, 1500);
33810
33833
  this.storedActiveState = newState;
33834
+ return;
33835
+ }
33836
+ if (this.researchingTimeoutRef === null) {
33837
+ this.state = newState;
33838
+ if (newState !== SDKState.READY) {
33839
+ this.storedActiveState = newState;
33840
+ }
33811
33841
  }
33812
33842
  });
33813
33843
  session.on("volume", ({ speaker, spectrum, volume }) => {
@@ -33840,10 +33870,10 @@ class ObiWidget extends LitElement {
33840
33870
  try {
33841
33871
  const session = this.createSession(sessionToken);
33842
33872
  if (!session) {
33843
- this.handleSessionCreationFailure(() => this.removeSessionFromUrl());
33873
+ this.handleSessionCreationFailure(() => this.removeSessionUrlParams());
33844
33874
  return;
33845
33875
  }
33846
- this.setupSessionEventListeners(session, () => this.removeSessionFromUrl());
33876
+ this.setupSessionEventListeners(session, () => this.removeSessionUrlParams());
33847
33877
  session.on("screenCaptureRequested", async () => {
33848
33878
  try {
33849
33879
  const canvas = await html2canvas(document.documentElement, {
@@ -33863,12 +33893,12 @@ class ObiWidget extends LitElement {
33863
33893
  this.sessionToken = sessionToken;
33864
33894
  this.roomToken = connectionInfo.token;
33865
33895
  this.roomUrl = connectionInfo.url;
33866
- this.removeSessionFromUrl();
33896
+ this.removeSessionUrlParams();
33867
33897
  }
33868
33898
  this.activeSession = session;
33869
33899
  } catch (error) {
33870
33900
  console.error("Failed to start session:", error);
33871
- this.handleSessionCreationFailure(() => this.removeSessionFromUrl());
33901
+ this.handleSessionCreationFailure(() => this.removeSessionUrlParams());
33872
33902
  }
33873
33903
  }
33874
33904
  async handleSessionStart(sessionToken) {
@@ -33949,11 +33979,26 @@ class ObiWidget extends LitElement {
33949
33979
  async sessionConnectionCheck() {
33950
33980
  await this.checkExistingSession();
33951
33981
  if (!this.activeSession) {
33952
- const urlParams = new URLSearchParams(window.location.search);
33953
- const sessionId = urlParams.get(SESSION_URL_PARAM);
33954
- this.apiKey = urlParams.get(API_KEY_URL_PARAM) || this.apiKey;
33982
+ let storedParams = {};
33983
+ try {
33984
+ const storedParamsJson = localStorage.getItem(WIDGET_PARAMS_KEY);
33985
+ if (storedParamsJson) {
33986
+ storedParams = JSON.parse(storedParamsJson);
33987
+ }
33988
+ } catch (error) {
33989
+ console.warn("Failed to parse stored widget parameters:", error);
33990
+ }
33991
+ if (Object.keys(storedParams).length === 0) {
33992
+ const urlParams = new URLSearchParams(window.location.search);
33993
+ urlParams.forEach((value, key) => {
33994
+ storedParams[key] = value;
33995
+ });
33996
+ }
33997
+ const sessionId = storedParams[SESSION_URL_PARAM];
33955
33998
  if (sessionId && this.apiKey) {
33956
33999
  await this.handleUrlSessionEvent(sessionId);
34000
+ } else {
34001
+ console.log("No session ID found or API key is not set");
33957
34002
  }
33958
34003
  }
33959
34004
  }
@@ -33970,11 +34015,15 @@ class ObiWidget extends LitElement {
33970
34015
  if (this.closeNavTimeoutRef !== null) {
33971
34016
  window.clearTimeout(this.closeNavTimeoutRef);
33972
34017
  }
34018
+ if (this.researchingTimeoutRef !== null) {
34019
+ window.clearTimeout(this.researchingTimeoutRef);
34020
+ this.researchingTimeoutRef = null;
34021
+ }
33973
34022
  if (this.boundSaveSessionData) {
33974
34023
  window.removeEventListener("beforeunload", this.boundSaveSessionData);
33975
34024
  window.removeEventListener("pagehide", this.boundSaveSessionData);
33976
34025
  }
33977
- this.removeSessionFromUrl();
34026
+ this.removeSessionUrlParams();
33978
34027
  super.disconnectedCallback();
33979
34028
  }
33980
34029
  handleMouseEnter() {
@@ -34002,6 +34051,10 @@ class ObiWidget extends LitElement {
34002
34051
  this.sessionToken = null;
34003
34052
  this.roomToken = null;
34004
34053
  this.roomUrl = null;
34054
+ if (this.researchingTimeoutRef !== null) {
34055
+ window.clearTimeout(this.researchingTimeoutRef);
34056
+ this.researchingTimeoutRef = null;
34057
+ }
34005
34058
  if (this.activeSession) {
34006
34059
  this.activeSession.disconnect();
34007
34060
  this.activeSession = null;
@@ -34026,13 +34079,26 @@ class ObiWidget extends LitElement {
34026
34079
  render() {
34027
34080
  if (!this.isActive)
34028
34081
  return nothing;
34082
+ if (this.linkOnlyAccess && this.state === SDKState.READY)
34083
+ return nothing;
34029
34084
  const stateRender = z$2(this.state).with(SDKState.LOADING, () => html`<obi-dot-loader></obi-dot-loader>`).with(SDKState.RESEARCHING, () => html`<obi-searching-loader></obi-searching-loader>`).with(
34030
34085
  N$1.union(SDKState.USER_SPEAKING, SDKState.AGENT_SPEAKING),
34031
34086
  () => html`<obi-audio-equalizer .volume=${this.volume}></obi-audio-equalizer>`
34032
- ).with(SDKState.PAUSED, () => obiIcon).otherwise(() => obiIcon);
34087
+ ).with(SDKState.THINKING, () => html`<obi-dot-loader></obi-dot-loader>`).with(SDKState.PAUSED, () => obiIcon).otherwise(() => obiIcon);
34088
+ const isPulseState = this.state === SDKState.USER_SPEAKING || this.state === SDKState.AGENT_SPEAKING;
34089
+ const isResearching = this.state === SDKState.RESEARCHING;
34090
+ const isUserSpeaking = this.state === SDKState.USER_SPEAKING;
34091
+ const isRotated = this.state !== SDKState.READY || this.navVisible;
34092
+ const containerClasses = [
34093
+ "widget-container",
34094
+ isRotated ? "rotated" : "",
34095
+ isPulseState ? "pulse" : "",
34096
+ isResearching ? "researching" : "",
34097
+ isUserSpeaking ? "user-speaking" : ""
34098
+ ].filter(Boolean).join(" ");
34033
34099
  return html`
34034
34100
  <div
34035
- class="widget-container ${this.state === SDKState.USER_SPEAKING || this.state === SDKState.AGENT_SPEAKING ? "pulse" : ""} ${this.state !== SDKState.READY || this.navVisible ? "rounded" : ""} ${this.state === SDKState.RESEARCHING ? "researching" : ""} ${this.state === SDKState.USER_SPEAKING ? "user-speaking" : ""}"
34101
+ class="${containerClasses}"
34036
34102
  @mouseenter=${this.handleMouseEnter}
34037
34103
  @mouseleave=${this.handleMouseLeave}
34038
34104
  >
@@ -34058,6 +34124,8 @@ class ObiWidget extends LitElement {
34058
34124
  .onClose=${() => {
34059
34125
  this.showSessionStartModal = false;
34060
34126
  this.selectedCourse = null;
34127
+ this.state = SDKState.READY;
34128
+ this.removeSessionUrlParams();
34061
34129
  }}
34062
34130
  ></obi-session-start-modal>` : nothing}
34063
34131
  `;
@@ -34140,7 +34208,7 @@ ObiWidget.styles = css`
34140
34208
  position: fixed;
34141
34209
  width: 56px;
34142
34210
  height: 56px;
34143
- border-radius: 28px;
34211
+ border-radius: 12px;
34144
34212
  border-color: transparent;
34145
34213
  background-color: var(--obi-primary);
34146
34214
  display: flex;
@@ -34156,17 +34224,8 @@ ObiWidget.styles = css`
34156
34224
  linear-gradient(195.84deg, var(--obi-secondary) 00 11.05%, var(--obi-secondary) 117.01%);
34157
34225
  }
34158
34226
 
34159
- .widget-container:hover {
34160
- border-radius: 12px;
34161
- }
34162
-
34163
- .widget-container.rounded {
34164
- border-radius: 12px;
34165
- }
34166
-
34167
34227
  .widget-container.researching {
34168
34228
  width: 273px;
34169
- border-radius: 12px;
34170
34229
  }
34171
34230
 
34172
34231
  .widget-icon {
@@ -34175,7 +34234,7 @@ ObiWidget.styles = css`
34175
34234
  transition: transform 0.5s ease-in-out;
34176
34235
  }
34177
34236
 
34178
- .widget-container.rounded .widget-icon {
34237
+ .widget-container.rotated .widget-icon {
34179
34238
  transform: rotate(90deg);
34180
34239
  }
34181
34240
 
@@ -34201,6 +34260,9 @@ __decorateClass([
34201
34260
  __decorateClass([
34202
34261
  r$2()
34203
34262
  ], ObiWidget.prototype, "isActive", 2);
34263
+ __decorateClass([
34264
+ r$2()
34265
+ ], ObiWidget.prototype, "linkOnlyAccess", 2);
34204
34266
  __decorateClass([
34205
34267
  r$2()
34206
34268
  ], ObiWidget.prototype, "position", 2);
@@ -34240,6 +34302,36 @@ if (!customElements.get("obi-widget")) {
34240
34302
  if (!customElements.get("obi-widget")) {
34241
34303
  customElements.define("obi-widget", ObiWidget);
34242
34304
  }
34305
+ const RETRY_CONFIG = {
34306
+ maxAttempts: 3,
34307
+ baseDelay: 200,
34308
+ // ms
34309
+ maxDelay: 2e3
34310
+ // ms
34311
+ };
34312
+ function delay(ms) {
34313
+ return new Promise((resolve) => setTimeout(resolve, ms));
34314
+ }
34315
+ function getRetryDelay(attempt) {
34316
+ const exponentialDelay = RETRY_CONFIG.baseDelay * Math.pow(2, attempt);
34317
+ return Math.min(exponentialDelay, RETRY_CONFIG.maxDelay);
34318
+ }
34319
+ async function retryOperation(operation, operationName, maxAttempts = RETRY_CONFIG.maxAttempts) {
34320
+ let lastError = null;
34321
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
34322
+ try {
34323
+ return await operation();
34324
+ } catch (error) {
34325
+ lastError = error;
34326
+ console.warn(`${operationName} failed (attempt ${attempt + 1}/${maxAttempts}):`, error);
34327
+ if (attempt < maxAttempts - 1) {
34328
+ const delayMs = getRetryDelay(attempt);
34329
+ await delay(delayMs);
34330
+ }
34331
+ }
34332
+ }
34333
+ throw new Error(`${operationName} failed after ${maxAttempts} attempts: ${lastError?.message}`);
34334
+ }
34243
34335
  function mountSDK() {
34244
34336
  const w2 = window;
34245
34337
  if (typeof w2.ObiSDK === "function" || typeof w2.ObiSDK === "object") {
@@ -34255,12 +34347,18 @@ function mountSDK() {
34255
34347
  };
34256
34348
  w2.ObiSDK.q = [];
34257
34349
  }
34258
- function mountWidget() {
34259
- if (!document.querySelector("obi-widget")) {
34350
+ async function mountWidget() {
34351
+ return retryOperation(async () => {
34352
+ if (document.querySelector("obi-widget")) {
34353
+ return;
34354
+ }
34355
+ if (!document.body) {
34356
+ throw new Error("document.body not available");
34357
+ }
34260
34358
  const widget = document.createElement("obi-widget");
34261
34359
  document.body.appendChild(widget);
34262
34360
  console.log("Obi Widget mounted");
34263
- }
34361
+ }, "Widget mounting");
34264
34362
  }
34265
34363
  function processQueue() {
34266
34364
  const w2 = window;
@@ -34277,17 +34375,62 @@ function processQueue() {
34277
34375
  w2.ObiSDK.q = [];
34278
34376
  }
34279
34377
  }
34280
- function loadFonts() {
34281
- const link = document.createElement("link");
34282
- link.href = "https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&family=Syne:wght@400..800&display=swap";
34283
- link.rel = "stylesheet";
34284
- document.head.appendChild(link);
34378
+ async function loadFonts() {
34379
+ return retryOperation(async () => {
34380
+ if (!document.head) {
34381
+ throw new Error("document.head not available");
34382
+ }
34383
+ const existingLink = document.head.querySelector('link[href*="fonts.googleapis.com"]');
34384
+ if (existingLink) {
34385
+ return;
34386
+ }
34387
+ const link = document.createElement("link");
34388
+ document.head.querySelectorAll("link[data-obi-font]").forEach((node) => node.remove());
34389
+ link.setAttribute("data-obi-font", "true");
34390
+ link.href = "https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&family=Syne:wght@400..800&display=swap";
34391
+ link.rel = "stylesheet";
34392
+ return new Promise((resolve, reject) => {
34393
+ const timeout = setTimeout(() => {
34394
+ reject(new Error("Font loading timeout"));
34395
+ }, 5e3);
34396
+ link.onload = () => {
34397
+ clearTimeout(timeout);
34398
+ resolve();
34399
+ };
34400
+ link.onerror = () => {
34401
+ clearTimeout(timeout);
34402
+ reject(new Error("Font loading failed"));
34403
+ };
34404
+ document.head.appendChild(link);
34405
+ });
34406
+ }, "Font loading");
34407
+ }
34408
+ async function safeInitialize() {
34409
+ try {
34410
+ mountSDK();
34411
+ await loadFonts();
34412
+ await mountWidget();
34413
+ processQueue();
34414
+ console.log("Obi Widget initialized successfully");
34415
+ } catch (error) {
34416
+ console.error("Obi Widget initialization failed:", error);
34417
+ try {
34418
+ mountSDK();
34419
+ processQueue();
34420
+ console.log("Obi Widget fallback initialization completed");
34421
+ } catch (fallbackError) {
34422
+ console.error("Obi Widget fallback initialization failed:", fallbackError);
34423
+ }
34424
+ }
34285
34425
  }
34286
34426
  function initializeObiWidget() {
34287
- loadFonts();
34288
- mountSDK();
34289
- mountWidget();
34290
- processQueue();
34427
+ if (document.readyState === "loading") {
34428
+ document.addEventListener("DOMContentLoaded", () => {
34429
+ safeInitialize();
34430
+ });
34431
+ } else {
34432
+ safeInitialize();
34433
+ }
34291
34434
  }
34292
34435
  initializeObiWidget();
34293
34436
  export {