obi-sdk 0.6.6 → 0.7.0

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/loader.d.ts CHANGED
@@ -14,6 +14,7 @@ interface ObiWidgetConfig {
14
14
  } | null;
15
15
  isActive?: boolean;
16
16
  linkOnlyAccess?: boolean;
17
+ showMenu?: boolean;
17
18
  primaryColor?: string;
18
19
  }
19
20
  declare class ObiWidgetLoader {
@@ -0,0 +1,6 @@
1
+ import { O } from "./obi-widget-6550377d.js";
2
+ import "./types-e0297e7b.js";
3
+ export {
4
+ O as ObiWidget
5
+ };
6
+ //# sourceMappingURL=index-f31a59cc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-f31a59cc.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
@@ -481,6 +481,11 @@ class ObiClient {
481
481
  body: data
482
482
  });
483
483
  }
484
+ async getPlanVideoUrl(id) {
485
+ return await this.client.GET("/plans/{id}/video-url", {
486
+ params: { path: { id } }
487
+ });
488
+ }
484
489
  // Sessions
485
490
  async listSessions(token) {
486
491
  return await this.client.GET("/sessions", {
@@ -15810,13 +15815,11 @@ function getDefaultIntegrations(_options) {
15810
15815
  ];
15811
15816
  }
15812
15817
  function createSentryScope() {
15813
- const integrations = getDefaultIntegrations().filter(
15814
- (defaultIntegration) => {
15815
- return !["BrowserApiErrors", "Breadcrumbs", "GlobalHandlers", "HttpContext"].includes(
15816
- defaultIntegration.name
15817
- );
15818
- }
15819
- );
15818
+ const integrations = getDefaultIntegrations().filter((defaultIntegration) => {
15819
+ return !["BrowserApiErrors", "Breadcrumbs", "GlobalHandlers", "HttpContext"].includes(
15820
+ defaultIntegration.name
15821
+ );
15822
+ });
15820
15823
  const client = new BrowserClient({
15821
15824
  dsn: "https://ac54bd10eac67be2dafbc346e704e4d4@o4507251798835200.ingest.us.sentry.io/4509409566392320",
15822
15825
  environment: "production",
@@ -16511,6 +16514,7 @@ CourseList.styles = i$4`
16511
16514
  padding: 12px 0px;
16512
16515
  flex: 1 0 0;
16513
16516
  align-self: stretch;
16517
+ min-height: 364px; /* Ensure minimum 2 rows: 2 * 180px + 4px gap */
16514
16518
  }
16515
16519
 
16516
16520
  @media (max-width: 767px) {
@@ -16571,6 +16575,7 @@ class CourseModal extends i$1 {
16571
16575
  this.apiKey = "";
16572
16576
  this.apiBaseUrl = "";
16573
16577
  this.selectedCourse = null;
16578
+ this.agentName = "";
16574
16579
  }
16575
16580
  handleClose() {
16576
16581
  if (this.onClose) {
@@ -16586,26 +16591,49 @@ class CourseModal extends i$1 {
16586
16591
  composed: true
16587
16592
  })
16588
16593
  );
16589
- this.onClose?.();
16590
16594
  }
16591
16595
  }
16592
16596
  async fetchCourses() {
16593
16597
  try {
16594
16598
  this.loading = true;
16595
- const raw_response = await fetch(`${this.apiBaseUrl}/sessions?token=${this.apiKey}`);
16599
+ console.log("[CourseModal] Fetching sessions from API", {
16600
+ apiKey: this.apiKey ? "present" : "missing"
16601
+ });
16602
+ const raw_response = await fetch(`${this.apiBaseUrl}/sessions?token=${this.apiKey}`, {
16603
+ cache: "no-cache",
16604
+ headers: {
16605
+ "Cache-Control": "no-cache, no-store, must-revalidate",
16606
+ Pragma: "no-cache"
16607
+ }
16608
+ });
16609
+ if (!raw_response.ok) {
16610
+ throw new Error(`HTTP ${raw_response.status}: ${raw_response.statusText}`);
16611
+ }
16596
16612
  const response = await raw_response.json();
16597
- const mappedCourses = response.sessions?.map((session) => ({
16598
- id: session.uuid,
16599
- name: session.onboarding_plan.name,
16600
- description: session.onboarding_plan.description,
16601
- duration: session.onboarding_plan.duration
16602
- }));
16613
+ let agentName = "";
16614
+ const mappedCourses = response.sessions?.map((session) => {
16615
+ if (!agentName) {
16616
+ agentName = session.onboarding_plan.product.agent_name;
16617
+ }
16618
+ return {
16619
+ id: session.uuid,
16620
+ name: session.onboarding_plan.name,
16621
+ description: session.onboarding_plan.description,
16622
+ duration: session.onboarding_plan.duration
16623
+ };
16624
+ });
16603
16625
  const filteredCourses = mappedCourses.filter((course) => !!course.name);
16626
+ this.agentName = agentName || "Obi";
16604
16627
  this.courses = [...filteredCourses];
16628
+ this.error = "";
16629
+ console.log("[CourseModal] Sessions fetched successfully", {
16630
+ sessionCount: filteredCourses.length,
16631
+ sessions: filteredCourses.map((c2) => ({ id: c2.id, name: c2.name }))
16632
+ });
16605
16633
  this.requestUpdate();
16606
16634
  } catch (err) {
16607
- console.error("Fetch error:", err);
16608
- this.error = "Failed to fetch courses";
16635
+ console.error("[CourseModal] Fetch error:", err);
16636
+ this.error = `Failed to fetch courses: ${err instanceof Error ? err.message : "Unknown error"}`;
16609
16637
  } finally {
16610
16638
  this.loading = false;
16611
16639
  this.requestUpdate();
@@ -16635,8 +16663,15 @@ class CourseModal extends i$1 {
16635
16663
  <div class="header">
16636
16664
  <div class="icon">${obiIcon}</div>
16637
16665
  <div class="title-section">
16638
- <h1>Give Obi a try!</h1>
16639
- <p class="subtitle">Pick a tour, share your screen, and Obi will take it from there.</p>
16666
+ ${this.loading ? x`
16667
+ <div class="skeleton skeleton-title"></div>
16668
+ <div class="skeleton skeleton-subtitle"></div>
16669
+ ` : x`
16670
+ <h1>Give ${this.agentName} a try!</h1>
16671
+ <p class="subtitle">
16672
+ Pick a tour, share your screen, and ${this.agentName} will take it from there.
16673
+ </p>
16674
+ `}
16640
16675
  </div>
16641
16676
  <button class="close-button" aria-label="Close course modal" @click=${this.handleClose}>
16642
16677
  <svg
@@ -16701,7 +16736,7 @@ class CourseModal extends i$1 {
16701
16736
  CourseModal.styles = i$4`
16702
16737
  :host {
16703
16738
  display: block;
16704
- font-family: "Syne", sans-serif;
16739
+ font-family: "Satoshi", sans-serif;
16705
16740
  }
16706
16741
 
16707
16742
  .backdrop {
@@ -16890,6 +16925,33 @@ CourseModal.styles = i$4`
16890
16925
  margin: 0;
16891
16926
  text-align: center;
16892
16927
  }
16928
+
16929
+ .skeleton {
16930
+ background: linear-gradient(90deg, #e5e7eb 25%, #f3f4f6 50%, #e5e7eb 75%);
16931
+ background-size: 200% 100%;
16932
+ animation: loading 1.5s infinite;
16933
+ border-radius: 4px;
16934
+ }
16935
+
16936
+ @keyframes loading {
16937
+ 0% {
16938
+ background-position: 200% 0;
16939
+ }
16940
+ 100% {
16941
+ background-position: -200% 0;
16942
+ }
16943
+ }
16944
+
16945
+ .skeleton-title {
16946
+ height: 32px;
16947
+ width: 60%;
16948
+ margin-bottom: 12px;
16949
+ }
16950
+
16951
+ .skeleton-subtitle {
16952
+ height: 20px;
16953
+ width: 80%;
16954
+ }
16893
16955
  `;
16894
16956
  __decorateClass$4([
16895
16957
  n$2({ type: Array })
@@ -16912,6 +16974,9 @@ __decorateClass$4([
16912
16974
  __decorateClass$4([
16913
16975
  r$1()
16914
16976
  ], CourseModal.prototype, "selectedCourse", 2);
16977
+ __decorateClass$4([
16978
+ r$1()
16979
+ ], CourseModal.prototype, "agentName", 2);
16915
16980
  if (!customElements.get("obi-course-modal")) {
16916
16981
  customElements.define("obi-course-modal", CourseModal);
16917
16982
  }
@@ -17372,12 +17437,144 @@ var __decorateClass$1 = (decorators, target, key, kind) => {
17372
17437
  return result;
17373
17438
  };
17374
17439
  class SessionStartModal extends i$1 {
17375
- handleStart() {
17376
- if (this.onStart && this.session) {
17440
+ constructor() {
17441
+ super(...arguments);
17442
+ this.isLoading = false;
17443
+ this.error = null;
17444
+ this.startAttempts = 0;
17445
+ }
17446
+ async handleStart() {
17447
+ console.log("[SessionStartModal] Start button clicked", {
17448
+ sessionId: this.session?.id,
17449
+ hasOnStartCallback: !!this.onStart,
17450
+ hasSession: !!this.session,
17451
+ isLoading: this.isLoading,
17452
+ startAttempts: this.startAttempts,
17453
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
17454
+ });
17455
+ trackEvent(
17456
+ "session_start_modal_continue_clicked",
17457
+ {
17458
+ sessionId: this.session?.id,
17459
+ sessionName: this.session?.name
17460
+ },
17461
+ "SessionStartModal"
17462
+ );
17463
+ if (this.isLoading) {
17464
+ console.warn("[SessionStartModal] Start already in progress, ignoring click");
17465
+ return;
17466
+ }
17467
+ if (!this.session) {
17468
+ const error = "Session data is missing.";
17469
+ console.error("[SessionStartModal] Validation failed:", error);
17470
+ this.showError(error);
17471
+ return;
17472
+ }
17473
+ if (!this.session.id) {
17474
+ const error = "Session ID is missing.";
17475
+ console.error("[SessionStartModal] Validation failed:", error, { session: this.session });
17476
+ this.showError(error);
17477
+ return;
17478
+ }
17479
+ if (!this.onStart) {
17480
+ const error = "Start callback is not defined.";
17481
+ console.error("[SessionStartModal] Validation failed:", error);
17482
+ captureException(error, {
17483
+ componentName: "SessionStartModal",
17484
+ handlerName: "handleStart",
17485
+ sessionData: this.session,
17486
+ hasOnStart: !!this.onStart
17487
+ });
17488
+ this.showError(error);
17489
+ return;
17490
+ }
17491
+ this.startAttempts++;
17492
+ console.log("[SessionStartModal] Starting session attempt", {
17493
+ attempt: this.startAttempts,
17494
+ sessionId: this.session.id
17495
+ });
17496
+ this.error = null;
17497
+ this.isLoading = true;
17498
+ try {
17499
+ console.log("[SessionStartModal] Calling onStart callback", {
17500
+ sessionId: this.session.id,
17501
+ attempt: this.startAttempts
17502
+ });
17503
+ trackEvent(
17504
+ "session_start_modal_calling_onStart",
17505
+ {
17506
+ sessionId: this.session.id
17507
+ },
17508
+ "SessionStartModal"
17509
+ );
17377
17510
  this.onStart(this.session.id);
17511
+ console.log("[SessionStartModal] Session start completed successfully", {
17512
+ sessionId: this.session.id,
17513
+ attempt: this.startAttempts
17514
+ });
17515
+ trackEvent(
17516
+ "session_start_modal_onStart_called",
17517
+ {
17518
+ sessionId: this.session.id
17519
+ },
17520
+ "SessionStartModal"
17521
+ );
17522
+ setTimeout(() => {
17523
+ if (this.isConnected) {
17524
+ captureException(
17525
+ new Error("Session start modal did not close after Continue was clicked"),
17526
+ {
17527
+ componentName: "SessionStartModal",
17528
+ handlerName: "handleStart_timeout",
17529
+ sessionId: this.session?.id,
17530
+ modalStillVisible: true
17531
+ }
17532
+ );
17533
+ }
17534
+ }, 2e3);
17535
+ } catch (error) {
17536
+ console.error("[SessionStartModal] Session start failed", {
17537
+ sessionId: this.session.id,
17538
+ attempt: this.startAttempts,
17539
+ error: error instanceof Error ? error.message : String(error),
17540
+ errorStack: error instanceof Error ? error.stack : void 0
17541
+ });
17542
+ captureException(error, {
17543
+ componentName: "SessionStartModal",
17544
+ handlerName: "handleStart",
17545
+ sessionId: this.session?.id
17546
+ });
17547
+ this.isLoading = false;
17548
+ if (error instanceof Error) {
17549
+ if (error.message.includes("network") || error.message.includes("fetch")) {
17550
+ this.showError(
17551
+ "Network connection failed. Please check your internet connection, refresh the page, and try again."
17552
+ );
17553
+ } else if (error.message.includes("unauthorized") || error.message.includes("403")) {
17554
+ this.showError("Session access denied.");
17555
+ } else if (error.message.includes("session") && error.message.includes("not found")) {
17556
+ this.showError("Session not found.");
17557
+ } else {
17558
+ this.showError(`Failed to start session: ${error.message}.`);
17559
+ }
17560
+ } else {
17561
+ this.showError("An unexpected error occurred while starting the session.");
17562
+ }
17378
17563
  }
17379
17564
  }
17565
+ showError(message) {
17566
+ this.error = message;
17567
+ console.error("[SessionStartModal] Displaying error to user:", message);
17568
+ }
17380
17569
  handleClose() {
17570
+ console.log("[SessionStartModal] Close button clicked", {
17571
+ sessionId: this.session?.id,
17572
+ isLoading: this.isLoading,
17573
+ startAttempts: this.startAttempts
17574
+ });
17575
+ if (this.isLoading) {
17576
+ console.warn("[SessionStartModal] Attempted to close while loading");
17577
+ }
17381
17578
  if (this.onClose) {
17382
17579
  this.onClose();
17383
17580
  }
@@ -17390,17 +17587,33 @@ class SessionStartModal extends i$1 {
17390
17587
  class="close-button"
17391
17588
  aria-label="Close session start modal"
17392
17589
  @click=${this.handleClose}
17590
+ ?disabled=${this.isLoading}
17393
17591
  >
17394
17592
  ×
17395
17593
  </button>
17396
17594
 
17397
17595
  <div class="header">
17398
17596
  <div class="logo">${obiIcon}</div>
17399
- <h1>${this.session.name}</h1>
17400
- <p class="subtitle">${this.session.description}</p>
17597
+ <h1>${this.session?.name || "Session"}</h1>
17598
+ <p class="subtitle">${this.session?.description || "Starting your session..."}</p>
17401
17599
  </div>
17402
17600
 
17403
- <button class="button button-primary" @click=${this.handleStart}>Continue</button>
17601
+ <button
17602
+ class="button button-primary"
17603
+ @click=${this.handleStart}
17604
+ ?disabled=${this.isLoading}
17605
+ aria-label=${this.isLoading ? "Starting session..." : "Start session"}
17606
+ >
17607
+ ${this.isLoading ? x`<div class="loading-spinner"></div>
17608
+ <span class="button-text">Starting...</span>` : x`<span class="button-text">Continue</span>`}
17609
+ </button>
17610
+
17611
+ ${this.error ? x`
17612
+ <div class="error-message">
17613
+ <div>${this.error}</div>
17614
+ <div>Please refresh the page and try again.</div>
17615
+ </div>
17616
+ ` : ""}
17404
17617
  </div>
17405
17618
  `;
17406
17619
  }
@@ -17532,12 +17745,80 @@ SessionStartModal.styles = i$4`
17532
17745
  background: #18181b;
17533
17746
  color: white;
17534
17747
  margin-top: 32px;
17748
+ position: relative;
17749
+ overflow: hidden;
17535
17750
  }
17536
17751
 
17537
- .button-primary:hover {
17752
+ .button-primary:hover:not(:disabled) {
17538
17753
  background: color-mix(in srgb, #18181b 90%, white);
17539
17754
  }
17540
17755
 
17756
+ .button-primary:disabled {
17757
+ background: #6b7280;
17758
+ cursor: not-allowed;
17759
+ }
17760
+
17761
+ .button-primary:disabled .button-text {
17762
+ opacity: 0.7;
17763
+ }
17764
+
17765
+ .loading-spinner {
17766
+ width: 16px;
17767
+ height: 16px;
17768
+ border: 2px solid transparent;
17769
+ border-top: 2px solid white;
17770
+ border-radius: 50%;
17771
+ animation: spin 1s linear infinite;
17772
+ margin-right: 8px;
17773
+ }
17774
+
17775
+ @keyframes spin {
17776
+ 0% {
17777
+ transform: rotate(0deg);
17778
+ }
17779
+ 100% {
17780
+ transform: rotate(360deg);
17781
+ }
17782
+ }
17783
+
17784
+ .error-message {
17785
+ margin-top: 16px;
17786
+ padding: 12px 16px;
17787
+ background: #fef2f2;
17788
+ border: 1px solid #fecaca;
17789
+ border-radius: 6px;
17790
+ color: #dc2626;
17791
+ font-size: 14px;
17792
+ line-height: 1.4;
17793
+ text-align: center;
17794
+ max-width: 100%;
17795
+ word-wrap: break-word;
17796
+ }
17797
+
17798
+ .error-actions {
17799
+ margin-top: 12px;
17800
+ display: flex;
17801
+ gap: 8px;
17802
+ justify-content: center;
17803
+ flex-wrap: wrap;
17804
+ }
17805
+
17806
+ .error-action-button {
17807
+ padding: 6px 12px;
17808
+ background: white;
17809
+ border: 1px solid #dc2626;
17810
+ color: #dc2626;
17811
+ border-radius: 4px;
17812
+ font-size: 12px;
17813
+ cursor: pointer;
17814
+ transition: all 0.2s ease;
17815
+ }
17816
+
17817
+ .error-action-button:hover {
17818
+ background: #dc2626;
17819
+ color: white;
17820
+ }
17821
+
17541
17822
  .close-button {
17542
17823
  position: absolute;
17543
17824
  top: 12px;
@@ -17554,6 +17835,11 @@ SessionStartModal.styles = i$4`
17554
17835
  align-items: center;
17555
17836
  justify-content: center;
17556
17837
  }
17838
+
17839
+ .close-button:disabled {
17840
+ opacity: 0.5;
17841
+ cursor: not-allowed;
17842
+ }
17557
17843
  `;
17558
17844
  __decorateClass$1([
17559
17845
  n$2({ type: Object })
@@ -17564,6 +17850,15 @@ __decorateClass$1([
17564
17850
  __decorateClass$1([
17565
17851
  n$2({ type: Function })
17566
17852
  ], SessionStartModal.prototype, "onClose", 2);
17853
+ __decorateClass$1([
17854
+ r$1()
17855
+ ], SessionStartModal.prototype, "isLoading", 2);
17856
+ __decorateClass$1([
17857
+ r$1()
17858
+ ], SessionStartModal.prototype, "error", 2);
17859
+ __decorateClass$1([
17860
+ r$1()
17861
+ ], SessionStartModal.prototype, "startAttempts", 2);
17567
17862
  if (!customElements.get("obi-session-start-modal")) {
17568
17863
  customElements.define("obi-session-start-modal", SessionStartModal);
17569
17864
  }
@@ -17690,6 +17985,11 @@ class ObiWidget extends i$1 {
17690
17985
  "connectObi",
17691
17986
  "ObiWidget"
17692
17987
  );
17988
+ this.handleCloseModals = () => {
17989
+ this.state = SDKState.READY;
17990
+ this.showCourseModal = false;
17991
+ this.showSessionStartModal = false;
17992
+ };
17693
17993
  this.handleCourseSelectEvent = (event) => {
17694
17994
  const customEvent = event;
17695
17995
  this.selectedCourse = customEvent.detail;
@@ -17761,8 +18061,13 @@ class ObiWidget extends i$1 {
17761
18061
  trackEvent("session_start_requested", { sessionToken }, "ObiWidget");
17762
18062
  if (this.activeSession) {
17763
18063
  trackEvent("session_start_blocked", { sessionToken }, "ObiWidget");
17764
- console.log("Connection already in progress or active session exists");
17765
- return;
18064
+ captureException(new Error("Session start blocked"), {
18065
+ componentName: "ObiWidget",
18066
+ handlerName: "handleSessionStart",
18067
+ sessionToken
18068
+ });
18069
+ this.activeSession.disconnect();
18070
+ this.activeSession = null;
17766
18071
  }
17767
18072
  this.showSessionStartModal = false;
17768
18073
  this.state = SDKState.LOADING;
@@ -17880,6 +18185,11 @@ class ObiWidget extends i$1 {
17880
18185
  if (window.obiWidgetConfig.linkOnlyAccess !== void 0) {
17881
18186
  this.linkOnlyAccess = window.obiWidgetConfig.linkOnlyAccess;
17882
18187
  }
18188
+ if (window.obiWidgetConfig.showMenu === true) {
18189
+ this.state = SDKState.LOADING;
18190
+ this.showCourseModal = true;
18191
+ window.obiWidgetConfig.showMenu = false;
18192
+ }
17883
18193
  const primaryColor = window.obiWidgetConfig?.primaryColor || OBI_PRIMARY_COLOR;
17884
18194
  this.style.setProperty("--obi-primary", primaryColor);
17885
18195
  this.generateColorVariables(primaryColor);
@@ -18167,7 +18477,7 @@ class ObiWidget extends i$1 {
18167
18477
  ` : E}
18168
18478
  </div>
18169
18479
  ${this.showCourseModal ? x`<obi-course-modal
18170
- .onClose=${() => this.showCourseModal = false}
18480
+ .onClose=${this.handleCloseModals.bind(this)}
18171
18481
  .apiKey=${this.apiKey}
18172
18482
  @course-select=${this.handleCourseSelectEvent}
18173
18483
  ></obi-course-modal>` : E}
@@ -18175,9 +18485,8 @@ class ObiWidget extends i$1 {
18175
18485
  .session=${this.selectedCourse}
18176
18486
  .onStart=${this.handleSessionStart.bind(this)}
18177
18487
  .onClose=${() => {
18178
- this.showSessionStartModal = false;
18488
+ this.handleCloseModals();
18179
18489
  this.selectedCourse = null;
18180
- this.state = SDKState.READY;
18181
18490
  this.removeSessionUrlParams();
18182
18491
  }}
18183
18492
  ></obi-session-start-modal>` : E}
@@ -18202,7 +18511,7 @@ ObiWidget.styles = i$4`
18202
18511
  }
18203
18512
 
18204
18513
  h1 {
18205
- font-family: "Syne", sans-serif;
18514
+ font-family: "Satoshi", sans-serif;
18206
18515
  }
18207
18516
 
18208
18517
  /* Position-specific styles */
@@ -18406,4 +18715,4 @@ export {
18406
18715
  withSentryAsyncHandler as w,
18407
18716
  x
18408
18717
  };
18409
- //# sourceMappingURL=obi-widget-94e8c026.js.map
18718
+ //# sourceMappingURL=obi-widget-6550377d.js.map