obi-sdk 0.4.3 → 0.5.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.
@@ -524,7 +524,7 @@ class ObiClient {
524
524
  return this.client;
525
525
  }
526
526
  }
527
- const DEFAULT_API_BASE_URL = "https://obi.getcor.io/api";
527
+ const DEFAULT_API_BASE_URL = "https://app.coragents.ai/api";
528
528
  class ObiSession {
529
529
  constructor({ sessionId, apiBaseUrl }) {
530
530
  this.currentState = SDKState.READY;
@@ -10045,6 +10045,96 @@ const STORAGE_KEYS = {
10045
10045
  SESSION_DATA: "session_data"
10046
10046
  };
10047
10047
  const storage = new StorageManager("io.obi.widget");
10048
+ const ORIGINAL_PRIMARY_COLOR = "#a10fff";
10049
+ function hexToHsl(hex) {
10050
+ hex = hex.replace(/^#/, "");
10051
+ if (!/^[0-9A-Fa-f]{3}$|^[0-9A-Fa-f]{6}$/.test(hex)) {
10052
+ throw new Error(`Invalid hex color format: ${hex}`);
10053
+ }
10054
+ if (hex.length === 3) {
10055
+ hex = hex.split("").map((char) => char + char).join("");
10056
+ }
10057
+ const r2 = parseInt(hex.slice(0, 2), 16) / 255;
10058
+ const g2 = parseInt(hex.slice(2, 4), 16) / 255;
10059
+ const b2 = parseInt(hex.slice(4, 6), 16) / 255;
10060
+ const max = Math.max(r2, g2, b2);
10061
+ const min = Math.min(r2, g2, b2);
10062
+ let h2, s2, l2 = (max + min) / 2;
10063
+ if (max === min) {
10064
+ h2 = s2 = 0;
10065
+ } else {
10066
+ const d2 = max - min;
10067
+ s2 = l2 > 0.5 ? d2 / (2 - max - min) : d2 / (max + min);
10068
+ switch (max) {
10069
+ case r2:
10070
+ h2 = (g2 - b2) / d2 + (g2 < b2 ? 6 : 0);
10071
+ break;
10072
+ case g2:
10073
+ h2 = (b2 - r2) / d2 + 2;
10074
+ break;
10075
+ case b2:
10076
+ h2 = (r2 - g2) / d2 + 4;
10077
+ break;
10078
+ default:
10079
+ h2 = 0;
10080
+ }
10081
+ h2 /= 6;
10082
+ }
10083
+ return { h: h2 * 360, s: s2 * 100, l: l2 * 100 };
10084
+ }
10085
+ function hslToHex(h2, s2, l2) {
10086
+ h2 = h2 / 360;
10087
+ s2 = s2 / 100;
10088
+ l2 = l2 / 100;
10089
+ const hue2rgb2 = (p2, q, t2) => {
10090
+ if (t2 < 0)
10091
+ t2 += 1;
10092
+ if (t2 > 1)
10093
+ t2 -= 1;
10094
+ if (t2 < 1 / 6)
10095
+ return p2 + (q - p2) * 6 * t2;
10096
+ if (t2 < 1 / 2)
10097
+ return q;
10098
+ if (t2 < 2 / 3)
10099
+ return p2 + (q - p2) * (2 / 3 - t2) * 6;
10100
+ return p2;
10101
+ };
10102
+ let r2, g2, b2;
10103
+ if (s2 === 0) {
10104
+ r2 = g2 = b2 = l2;
10105
+ } else {
10106
+ const q = l2 < 0.5 ? l2 * (1 + s2) : l2 + s2 - l2 * s2;
10107
+ const p2 = 2 * l2 - q;
10108
+ r2 = hue2rgb2(p2, q, h2 + 1 / 3);
10109
+ g2 = hue2rgb2(p2, q, h2);
10110
+ b2 = hue2rgb2(p2, q, h2 - 1 / 3);
10111
+ }
10112
+ const toHex = (c2) => {
10113
+ const hex = Math.round(c2 * 255).toString(16);
10114
+ return hex.length === 1 ? "0" + hex : hex;
10115
+ };
10116
+ return `#${toHex(r2)}${toHex(g2)}${toHex(b2)}`;
10117
+ }
10118
+ function generateSelectedColor(currentPrimaryColor, designedSelectedColor) {
10119
+ try {
10120
+ const originalPrimaryHsl = hexToHsl(ORIGINAL_PRIMARY_COLOR);
10121
+ const designedSelectedHsl = hexToHsl(designedSelectedColor);
10122
+ const hueDelta = (designedSelectedHsl.h - originalPrimaryHsl.h + 360) % 360;
10123
+ const adjustedHueDelta = hueDelta > 180 ? hueDelta - 360 : hueDelta;
10124
+ const saturationDelta = designedSelectedHsl.s - originalPrimaryHsl.s;
10125
+ const lightnessDelta = designedSelectedHsl.l - originalPrimaryHsl.l;
10126
+ const currentPrimaryHsl = hexToHsl(currentPrimaryColor);
10127
+ const selectedHsl = {
10128
+ h: (currentPrimaryHsl.h + adjustedHueDelta + 360) % 360,
10129
+ s: Math.max(0, Math.min(100, currentPrimaryHsl.s + saturationDelta)),
10130
+ l: Math.max(0, Math.min(100, currentPrimaryHsl.l + lightnessDelta))
10131
+ };
10132
+ return hslToHex(selectedHsl.h, selectedHsl.s, selectedHsl.l);
10133
+ } catch (error) {
10134
+ console.warn("Failed to generate selected color, using fallback:", error);
10135
+ return designedSelectedColor;
10136
+ }
10137
+ }
10048
10138
  var __defProp$7 = Object.defineProperty;
10049
10139
  var __getOwnPropDesc$7 = Object.getOwnPropertyDescriptor;
10050
10140
  var __decorateClass$7 = (decorators, target, key, kind) => {
@@ -10266,7 +10356,7 @@ NavigationBar.styles = i$4`
10266
10356
  :host {
10267
10357
  display: flex;
10268
10358
  position: absolute;
10269
- z-index: 10000;
10359
+ z-index: 2147483647;
10270
10360
  width: 40px;
10271
10361
  transition: all 0.2s ease-in-out;
10272
10362
  opacity: 1;
@@ -10325,12 +10415,18 @@ class Course extends i$1 {
10325
10415
  this.id = "";
10326
10416
  this.name = "";
10327
10417
  this.description = "";
10328
- this.imageSrc = "";
10418
+ this.duration = 10;
10419
+ this.selected = false;
10329
10420
  }
10330
10421
  handleClick() {
10331
10422
  this.dispatchEvent(
10332
- new CustomEvent("course-select", {
10333
- detail: { id: this.id, name: this.name, description: this.description },
10423
+ new CustomEvent("course-card-click", {
10424
+ detail: {
10425
+ id: this.id,
10426
+ name: this.name,
10427
+ description: this.description,
10428
+ duration: this.duration
10429
+ },
10334
10430
  bubbles: true,
10335
10431
  composed: true
10336
10432
  })
@@ -10338,9 +10434,11 @@ class Course extends i$1 {
10338
10434
  }
10339
10435
  render() {
10340
10436
  return x`
10341
- <div class="course-container" @click=${this.handleClick}>
10342
- <img src=${this.imageSrc} alt="Course Preview" class="course-image" />
10343
- <h2 class="course-title">${this.name}</h2>
10437
+ <div class="course-container ${this.selected ? "selected" : ""}" @click=${this.handleClick}>
10438
+ <div class="header-row">
10439
+ <h2 class="course-title">${this.name}</h2>
10440
+ <span class="duration">~${this.duration} mins</span>
10441
+ </div>
10344
10442
  ${this.description ? x`<p class="course-description">${this.description}</p>` : ""}
10345
10443
  </div>
10346
10444
  `;
@@ -10352,12 +10450,19 @@ Course.styles = i$4`
10352
10450
  }
10353
10451
 
10354
10452
  .course-container {
10355
- padding: 16px 12px;
10356
- gap: 12px;
10357
- border-radius: 4px;
10358
- border: 1px solid #e5e7eb;
10453
+ display: flex;
10454
+ padding: 20px;
10455
+ align-items: flex-start;
10456
+ gap: 8px;
10457
+ height: 180px;
10458
+ border-radius: 6px;
10459
+ border: 1px solid rgba(0, 0, 0, 0.6);
10460
+ background: #fff;
10359
10461
  cursor: pointer;
10360
10462
  transition: all 0.2s;
10463
+ flex-direction: column;
10464
+ box-sizing: border-box;
10465
+ overflow: hidden;
10361
10466
  }
10362
10467
 
10363
10468
  .course-container:hover {
@@ -10370,26 +10475,73 @@ Course.styles = i$4`
10370
10475
  transform: scale(0.98);
10371
10476
  }
10372
10477
 
10373
- .course-image {
10374
- max-height: 160px;
10375
- border-radius: 2px;
10376
- border: 1px solid #e5e7eb;
10377
- object-fit: cover;
10378
- width: 100%;
10379
- margin-bottom: 12px;
10478
+ .course-container.selected {
10479
+ border-radius: 6px;
10480
+ border: 1px solid var(--obi-course-selected, #c76cff);
10481
+ background: var(--obi-course-selected-bg, #fbf3ff);
10482
+ }
10483
+
10484
+ .course-container.selected:hover {
10485
+ background: var(--obi-course-selected-bg, #fbf3ff);
10486
+ }
10487
+
10488
+ .header-row {
10489
+ display: flex;
10490
+ justify-content: space-between;
10491
+ align-items: flex-start;
10492
+ align-self: stretch;
10493
+ flex-shrink: 0;
10494
+ text-align: left;
10380
10495
  }
10381
10496
 
10382
10497
  .course-title {
10383
- font-size: 14px;
10384
- font-weight: bold;
10498
+ color: #000;
10499
+ font-family: Satoshi;
10500
+ font-size: 20px;
10501
+ font-style: normal;
10502
+ font-weight: 500;
10503
+ line-height: 100%; /* 20px */
10504
+ letter-spacing: -0.2px;
10385
10505
  margin: 0;
10386
- margin-bottom: 12px;
10387
10506
  }
10388
10507
 
10389
- .course-description {
10508
+ .duration {
10509
+ display: flex;
10510
+ padding: 4px 12px;
10511
+ justify-content: center;
10512
+ align-items: center;
10513
+ gap: 8px;
10514
+ border-radius: 9999px;
10515
+ background: #f5f3ff;
10516
+ color: #000;
10517
+ font-family: Satoshi;
10390
10518
  font-size: 14px;
10391
- color: #71717a;
10519
+ font-style: normal;
10520
+ font-weight: 500;
10521
+ line-height: 100%; /* 14px */
10522
+ letter-spacing: -0.14px;
10523
+ flex-shrink: 0;
10524
+ }
10525
+
10526
+ .course-container.selected .duration {
10527
+ background: #fff;
10528
+ }
10529
+
10530
+ .course-description {
10531
+ flex: 1 0 0;
10532
+ color: rgba(0, 0, 0, 0.6);
10533
+ font-family: Satoshi;
10534
+ font-size: 16px;
10535
+ font-style: normal;
10536
+ font-weight: 400;
10537
+ line-height: 140%; /* 22.4px */
10538
+ letter-spacing: -0.16px;
10392
10539
  margin: 0;
10540
+ overflow: hidden;
10541
+ display: -webkit-box;
10542
+ -webkit-line-clamp: 4;
10543
+ -webkit-box-orient: vertical;
10544
+ text-align: left;
10393
10545
  }
10394
10546
  `;
10395
10547
  __decorateClass$5([
@@ -10402,12 +10554,16 @@ __decorateClass$5([
10402
10554
  n$2({ type: String })
10403
10555
  ], Course.prototype, "description", 2);
10404
10556
  __decorateClass$5([
10405
- n$2({ type: String })
10406
- ], Course.prototype, "imageSrc", 2);
10557
+ n$2({ type: Number })
10558
+ ], Course.prototype, "duration", 2);
10559
+ __decorateClass$5([
10560
+ n$2({ type: Boolean })
10561
+ ], Course.prototype, "selected", 2);
10407
10562
  class CourseList extends i$1 {
10408
10563
  constructor() {
10409
10564
  super(...arguments);
10410
10565
  this.courses = [];
10566
+ this.selectedCourseId = null;
10411
10567
  this.loading = false;
10412
10568
  this.error = "";
10413
10569
  }
@@ -10421,19 +10577,20 @@ class CourseList extends i$1 {
10421
10577
  if (!this.courses || this.courses.length === 0) {
10422
10578
  return x`<div class="empty">No courses available</div>`;
10423
10579
  }
10424
- const gridClass = this.courses.length === 1 ? "grid-cols-1" : this.courses.length === 2 ? "grid-cols-2" : "grid-cols-3";
10425
10580
  return x`
10426
- <div class="course-grid ${gridClass}">
10427
- ${this.courses.map(
10428
- (course) => x`
10581
+ <div class="course-grid">
10582
+ ${this.courses.map((course) => {
10583
+ const isSelected = course.id === this.selectedCourseId;
10584
+ return x`
10429
10585
  <obi-course
10430
10586
  id=${course.id}
10431
10587
  name=${course.name}
10432
10588
  description=${course.description || ""}
10433
- imageSrc=${course.imageSrc}
10589
+ duration=${course.duration}
10590
+ ?selected=${isSelected}
10434
10591
  ></obi-course>
10435
- `
10436
- )}
10592
+ `;
10593
+ })}
10437
10594
  </div>
10438
10595
  `;
10439
10596
  }
@@ -10445,20 +10602,17 @@ CourseList.styles = i$4`
10445
10602
 
10446
10603
  .course-grid {
10447
10604
  display: grid;
10448
- gap: 12px;
10449
- margin-top: 24px;
10450
- }
10451
-
10452
- .course-grid.grid-cols-1 {
10453
- grid-template-columns: 1fr;
10454
- }
10455
-
10456
- .course-grid.grid-cols-2 {
10457
- grid-template-columns: 1fr 1fr;
10605
+ grid-template-columns: 1fr 1fr 1fr;
10606
+ gap: 4px;
10607
+ padding: 12px 0px;
10608
+ flex: 1 0 0;
10609
+ align-self: stretch;
10458
10610
  }
10459
10611
 
10460
- .course-grid.grid-cols-3 {
10461
- grid-template-columns: 1fr 1fr 1fr;
10612
+ @media (max-width: 767px) {
10613
+ .course-grid {
10614
+ grid-template-columns: 1fr;
10615
+ }
10462
10616
  }
10463
10617
 
10464
10618
  .loading,
@@ -10477,6 +10631,9 @@ CourseList.styles = i$4`
10477
10631
  __decorateClass$5([
10478
10632
  n$2({ type: Array })
10479
10633
  ], CourseList.prototype, "courses", 2);
10634
+ __decorateClass$5([
10635
+ n$2({ type: String })
10636
+ ], CourseList.prototype, "selectedCourseId", 2);
10480
10637
  __decorateClass$5([
10481
10638
  n$2({ type: Boolean })
10482
10639
  ], CourseList.prototype, "loading", 2);
@@ -10509,12 +10666,25 @@ class CourseModal extends i$1 {
10509
10666
  this.error = "";
10510
10667
  this.apiKey = "";
10511
10668
  this.apiBaseUrl = "";
10669
+ this.selectedCourse = null;
10512
10670
  }
10513
10671
  handleClose() {
10514
10672
  if (this.onClose) {
10515
10673
  this.onClose();
10516
10674
  }
10517
10675
  }
10676
+ handleGetStarted() {
10677
+ if (this.selectedCourse) {
10678
+ this.dispatchEvent(
10679
+ new CustomEvent("course-select", {
10680
+ detail: this.selectedCourse,
10681
+ bubbles: true,
10682
+ composed: true
10683
+ })
10684
+ );
10685
+ this.onClose?.();
10686
+ }
10687
+ }
10518
10688
  async fetchCourses() {
10519
10689
  try {
10520
10690
  this.loading = true;
@@ -10524,7 +10694,7 @@ class CourseModal extends i$1 {
10524
10694
  id: session.uuid,
10525
10695
  name: session.onboarding_plan.name,
10526
10696
  description: session.onboarding_plan.description,
10527
- imageSrc: session.onboarding_plan.screen_url || "https://corproductionsydney-storagebucket5cb7c8ea-atg4gmftc6sk.s3.amazonaws.com/static/generic-course.png"
10697
+ duration: session.onboarding_plan.duration
10528
10698
  }));
10529
10699
  const filteredCourses = mappedCourses.filter((course) => !!course.name);
10530
10700
  this.courses = [...filteredCourses];
@@ -10537,6 +10707,10 @@ class CourseModal extends i$1 {
10537
10707
  this.requestUpdate();
10538
10708
  }
10539
10709
  }
10710
+ handleCourseCardClick(event) {
10711
+ const { id, name, description, duration: duration2 } = event.detail;
10712
+ this.selectedCourse = { id, name, description, duration: duration2 };
10713
+ }
10540
10714
  connectedCallback() {
10541
10715
  super.connectedCallback();
10542
10716
  if (window.obiWidgetConfig?.apiKey) {
@@ -10552,19 +10726,70 @@ class CourseModal extends i$1 {
10552
10726
  }
10553
10727
  render() {
10554
10728
  return x`
10555
- <div class="backdrop" @click=${this.handleClose}></div>
10729
+ <div class="backdrop"></div>
10556
10730
  <div class="container">
10557
- <button class="close-button" @click=${this.handleClose}>×</button>
10558
10731
  <div class="header">
10559
- <h1>Give Obi a try!</h1>
10560
- <p class="subtitle">Pick a tour, share your screen, and Obi will take it from there.</p>
10732
+ <div class="icon">${obiIcon}</div>
10733
+ <div class="title-section">
10734
+ <h1>Give Obi a try!</h1>
10735
+ <p class="subtitle">Pick a tour, share your screen, and Obi will take it from there.</p>
10736
+ </div>
10737
+ <button class="close-button" aria-label="Close course modal" @click=${this.handleClose}>
10738
+ <svg
10739
+ xmlns="http://www.w3.org/2000/svg"
10740
+ width="24"
10741
+ height="24"
10742
+ viewBox="0 0 24 24"
10743
+ fill="none"
10744
+ >
10745
+ <path
10746
+ d="M18 6L6 18M6 6L18 18"
10747
+ stroke="black"
10748
+ stroke-width="2"
10749
+ stroke-linecap="round"
10750
+ stroke-linejoin="round"
10751
+ />
10752
+ </svg>
10753
+ </button>
10754
+ </div>
10755
+
10756
+ <div class="course-list-wrapper">
10757
+ <obi-course-list
10758
+ .courses=${this.courses}
10759
+ .selectedCourseId=${this.selectedCourse?.id || ""}
10760
+ .loading=${this.loading}
10761
+ .error=${this.error}
10762
+ @course-card-click=${this.handleCourseCardClick}
10763
+ ></obi-course-list>
10561
10764
  </div>
10562
10765
 
10563
- <obi-course-list
10564
- .courses=${this.courses}
10565
- .loading=${this.loading}
10566
- .error=${this.error}
10567
- ></obi-course-list>
10766
+ <div class="footer">
10767
+ <button
10768
+ class="footer-button"
10769
+ @click=${this.handleGetStarted}
10770
+ ?disabled=${!this.selectedCourse}
10771
+ >
10772
+ <span>Get started</span>
10773
+ <svg
10774
+ xmlns="http://www.w3.org/2000/svg"
10775
+ width="17"
10776
+ height="16"
10777
+ viewBox="0 0 17 16"
10778
+ fill="none"
10779
+ >
10780
+ <path
10781
+ d="M3.83325 8.00065H13.1666M13.1666 8.00065L8.49992 3.33398M13.1666 8.00065L8.49992 12.6673"
10782
+ stroke=${this.selectedCourse ? "#FAFAFA" : "#999"}
10783
+ stroke-width="1.33"
10784
+ stroke-linecap="round"
10785
+ stroke-linejoin="round"
10786
+ />
10787
+ </svg>
10788
+ </button>
10789
+ <p class="footer-text">
10790
+ Sessions are securely recorded for quality improvement purposes.
10791
+ </p>
10792
+ </div>
10568
10793
  </div>
10569
10794
  `;
10570
10795
  }
@@ -10587,23 +10812,42 @@ CourseModal.styles = i$4`
10587
10812
  }
10588
10813
 
10589
10814
  .container {
10815
+ /* Positioning & Sizing */
10590
10816
  position: fixed;
10591
- background-color: white;
10592
- color: black;
10593
- border: 1px solid #e5e7eb;
10594
- width: 640px;
10595
- max-height: 80vh;
10596
- overflow-y: auto;
10597
- padding: 48px;
10598
- gap: 12px;
10599
10817
  top: 50%;
10600
10818
  left: 50%;
10601
10819
  transform: translate(-50%, -50%);
10602
- border-radius: 4px;
10820
+ width: 90%;
10821
+ max-width: 1200px;
10822
+ max-height: 80vh;
10603
10823
  z-index: 50;
10824
+ text-align: center;
10825
+ color: black;
10826
+
10827
+ /* Layout styles */
10604
10828
  display: flex;
10829
+ padding: 32px;
10605
10830
  flex-direction: column;
10606
- text-align: center;
10831
+ align-items: center;
10832
+ gap: 24px;
10833
+ flex: 1 0 0;
10834
+ align-self: stretch;
10835
+
10836
+ /* Appearance styles */
10837
+ border-radius: 12px;
10838
+ background: #fafafa;
10839
+
10840
+ /* shadow/lg */
10841
+ box-shadow:
10842
+ 0px 10px 15px -3px rgba(0, 0, 0, 0.1),
10843
+ 0px 4px 6px -2px rgba(0, 0, 0, 0.05);
10844
+ }
10845
+
10846
+ .course-list-wrapper {
10847
+ flex: 1;
10848
+ overflow-y: auto;
10849
+ width: 100%;
10850
+ min-height: 0;
10607
10851
  }
10608
10852
 
10609
10853
  .container:hover {
@@ -10625,32 +10869,122 @@ CourseModal.styles = i$4`
10625
10869
  }
10626
10870
 
10627
10871
  .header {
10628
- margin-bottom: 24px;
10629
- text-align: center;
10872
+ display: flex;
10873
+ align-items: flex-start;
10874
+ gap: 16px;
10875
+ width: 100%;
10876
+ text-align: left;
10877
+ }
10878
+
10879
+ .icon {
10880
+ display: flex;
10881
+ width: 56px;
10882
+ height: 56px;
10883
+ padding: 12px;
10884
+ justify-content: center;
10885
+ align-items: center;
10886
+ border-radius: 12px;
10887
+ border: 1px solid var(--obi-primary, #a10fff);
10888
+ background: var(--obi-primary, #a10fff);
10889
+ box-sizing: border-box;
10890
+ }
10891
+
10892
+ .icon img {
10893
+ width: 32px;
10894
+ height: 32px;
10895
+ color: white;
10896
+ fill: #fff;
10897
+ }
10898
+
10899
+ .title-section {
10900
+ align-self: stretch;
10901
+ flex: 1;
10630
10902
  }
10631
10903
 
10632
10904
  h1 {
10905
+ color: #18181b;
10906
+ font-family: Satoshi;
10633
10907
  font-size: 32px;
10908
+ font-style: normal;
10634
10909
  font-weight: 700;
10910
+ line-height: 90%; /* 28.8px */
10911
+ letter-spacing: -0.96px;
10635
10912
  margin: 0;
10636
10913
  margin-bottom: 8px;
10637
10914
  }
10638
10915
 
10639
10916
  .subtitle {
10917
+ color: #18181b;
10918
+ font-family: Satoshi;
10640
10919
  font-size: 20px;
10641
- color: rgb(113 113 122 / 1);
10920
+ font-style: normal;
10921
+ font-weight: 500;
10922
+ line-height: 90%; /* 18px */
10923
+ letter-spacing: -0.6px;
10642
10924
  margin: 0;
10643
10925
  }
10644
10926
 
10645
10927
  .close-button {
10646
- position: absolute;
10647
- top: 16px;
10648
- right: 16px;
10928
+ width: 24px;
10929
+ height: 24px;
10649
10930
  background: none;
10650
10931
  border: none;
10651
10932
  cursor: pointer;
10652
- font-size: 24px;
10653
- color: #6b7280;
10933
+ padding: 0;
10934
+ display: flex;
10935
+ align-items: center;
10936
+ justify-content: center;
10937
+ }
10938
+
10939
+ .footer {
10940
+ display: flex;
10941
+ width: 360px;
10942
+ flex-direction: column;
10943
+ align-items: center;
10944
+ gap: 12px;
10945
+ }
10946
+
10947
+ .footer-button {
10948
+ display: flex;
10949
+ height: 48px;
10950
+ padding: 16px;
10951
+ justify-content: center;
10952
+ align-items: center;
10953
+ gap: 8px;
10954
+ align-self: stretch;
10955
+ border-radius: 6px;
10956
+ background: #18181b;
10957
+ border: none;
10958
+ cursor: pointer;
10959
+ color: #fafafa;
10960
+ font-family: Satoshi;
10961
+ font-weight: 500;
10962
+ transition: opacity 0.2s ease;
10963
+ }
10964
+
10965
+ .footer-button:disabled {
10966
+ opacity: 0.5;
10967
+ cursor: not-allowed;
10968
+ }
10969
+
10970
+ .footer-button:not(:disabled) {
10971
+ opacity: 1;
10972
+ }
10973
+
10974
+ .footer-button:not(:disabled):hover {
10975
+ background: #27272a;
10976
+ }
10977
+
10978
+ .footer-text {
10979
+ color: #18181b;
10980
+ font-family: Satoshi;
10981
+ font-size: 12px;
10982
+ font-style: normal;
10983
+ font-weight: 400;
10984
+ line-height: 100%; /* 12px */
10985
+ letter-spacing: -0.12px;
10986
+ margin: 0;
10987
+ text-align: center;
10654
10988
  }
10655
10989
  `;
10656
10990
  __decorateClass$4([
@@ -10671,6 +11005,9 @@ __decorateClass$4([
10671
11005
  __decorateClass$4([
10672
11006
  r$1()
10673
11007
  ], CourseModal.prototype, "apiBaseUrl", 2);
11008
+ __decorateClass$4([
11009
+ r$1()
11010
+ ], CourseModal.prototype, "selectedCourse", 2);
10674
11011
  if (!customElements.get("obi-course-modal")) {
10675
11012
  customElements.define("obi-course-modal", CourseModal);
10676
11013
  }
@@ -10805,11 +11142,11 @@ class AudioEqualizer extends i$1 {
10805
11142
  this.canvasRef = e();
10806
11143
  this.barCount = 8;
10807
11144
  this.animationFrame = null;
10808
- this.primaryColor = "#9500ff";
11145
+ this.primaryColor = ORIGINAL_PRIMARY_COLOR;
10809
11146
  }
10810
11147
  connectedCallback() {
10811
11148
  super.connectedCallback();
10812
- this.primaryColor = getComputedStyle(this).getPropertyValue("--obi-primary").trim() || "#9500ff";
11149
+ this.primaryColor = getComputedStyle(this).getPropertyValue("--obi-primary").trim() || ORIGINAL_PRIMARY_COLOR;
10813
11150
  this.startAnimation();
10814
11151
  }
10815
11152
  disconnectedCallback() {
@@ -11105,7 +11442,7 @@ SearchingLoader.styles = i$4`
11105
11442
  display: flex;
11106
11443
  align-items: center;
11107
11444
  gap: 8px;
11108
- background-color: var(--obi-primary);
11445
+ background-color: var(--obi-primary, #a10fff);
11109
11446
  border-radius: 12px;
11110
11447
  padding: 8px 12px;
11111
11448
  }
@@ -11146,7 +11483,13 @@ class SessionStartModal extends i$1 {
11146
11483
  return x`
11147
11484
  <div class="backdrop"></div>
11148
11485
  <div class="container">
11149
- <button class="close-button" @click=${this.handleClose}>×</button>
11486
+ <button
11487
+ class="close-button"
11488
+ aria-label="Close session start modal"
11489
+ @click=${this.handleClose}
11490
+ >
11491
+ ×
11492
+ </button>
11150
11493
 
11151
11494
  <div class="header">
11152
11495
  <div class="logo">${obiIcon}</div>
@@ -11251,6 +11594,7 @@ SessionStartModal.styles = i$4`
11251
11594
  color: #18181b;
11252
11595
  line-height: 1.4;
11253
11596
  margin: 0;
11597
+ margin-bottom: 32px;
11254
11598
  }
11255
11599
 
11256
11600
  .button {
@@ -11270,21 +11614,21 @@ SessionStartModal.styles = i$4`
11270
11614
  .button-primary {
11271
11615
  display: flex;
11272
11616
  width: 100%;
11273
- height: var(--height-h-11, 44px);
11274
- padding: var(--spacing-2, 8px) var(--spacing-4, 16px);
11617
+ height: 44px;
11618
+ padding: 8px 16px;
11275
11619
  justify-content: center;
11276
11620
  align-items: center;
11277
- gap: var(--spacing-2, 8px);
11621
+ gap: 8px;
11278
11622
  flex-shrink: 0;
11279
11623
  align-self: stretch;
11280
- border-radius: var(--border-radius-default, 6px);
11281
- background: var(--base-primary, #18181b);
11624
+ border-radius: 6px;
11625
+ background: #18181b;
11282
11626
  color: white;
11283
11627
  margin-top: 32px;
11284
11628
  }
11285
11629
 
11286
11630
  .button-primary:hover {
11287
- background: color-mix(in srgb, var(--base-primary, #18181b) 90%, white);
11631
+ background: color-mix(in srgb, #18181b 90%, white);
11288
11632
  }
11289
11633
 
11290
11634
  .close-button {
@@ -11370,6 +11714,7 @@ class ObiWidget extends i$1 {
11370
11714
  const sessionsResponse = await this.obiClient.listSessions(this.apiKey);
11371
11715
  if (sessionsResponse.data) {
11372
11716
  const sessions = sessionsResponse.data.sessions;
11717
+ console.log("[obi-sdk] sessions:", sessions);
11373
11718
  const matchingSession = sessions?.find((session) => session.uuid === sessionToken);
11374
11719
  if (matchingSession) {
11375
11720
  const sessionWithPlan = matchingSession;
@@ -11381,7 +11726,7 @@ class ObiWidget extends i$1 {
11381
11726
  this.state = SDKState.LOADING;
11382
11727
  this.showSessionStartModal = true;
11383
11728
  } else {
11384
- console.log("No session found with token:", sessionToken);
11729
+ console.log("[obi-sdk] no session found with token:", sessionToken);
11385
11730
  }
11386
11731
  }
11387
11732
  } catch (error) {
@@ -11411,22 +11756,37 @@ class ObiWidget extends i$1 {
11411
11756
  if (window.obiWidgetConfig.linkOnlyAccess !== void 0) {
11412
11757
  this.linkOnlyAccess = window.obiWidgetConfig.linkOnlyAccess;
11413
11758
  }
11414
- this.style.setProperty("--obi-primary", window.obiWidgetConfig?.primaryColor || "#9500ff");
11415
- this.style.setProperty("--obi-secondary", window.obiWidgetConfig?.secondaryColor || "#c4b5fd");
11759
+ const primaryColor = window.obiWidgetConfig?.primaryColor || ORIGINAL_PRIMARY_COLOR;
11760
+ this.style.setProperty("--obi-primary", primaryColor);
11761
+ this.generateColorVariables(primaryColor);
11762
+ }
11763
+ }
11764
+ /**
11765
+ * Generate additional color variables based on the primary color
11766
+ */
11767
+ generateColorVariables(primaryColor) {
11768
+ try {
11769
+ const secondaryColor = generateSelectedColor(primaryColor, "#c4b5fd");
11770
+ const courseSelected = generateSelectedColor(primaryColor, "#c76cff");
11771
+ const courseSelectedBackground = generateSelectedColor(primaryColor, "#fbf3ff");
11772
+ const pausedPulseColor = generateSelectedColor(primaryColor, "#A06DF9");
11773
+ this.style.setProperty("--obi-secondary", secondaryColor);
11774
+ this.style.setProperty("--obi-course-selected", courseSelected);
11775
+ this.style.setProperty("--obi-course-selected-bg", courseSelectedBackground);
11776
+ this.style.setProperty("--obi-paused-pulse", pausedPulseColor);
11777
+ } catch (error) {
11778
+ console.warn("Failed to generate color variables:", error);
11779
+ this.style.setProperty("--obi-secondary", "#c4b5fd");
11780
+ this.style.setProperty("--obi-course-selected", "#c76cff");
11781
+ this.style.setProperty("--obi-course-selected-bg", "#fbf3ff");
11782
+ this.style.setProperty("--obi-paused-pulse", "#A06DF9");
11416
11783
  }
11417
11784
  }
11418
11785
  removeSessionUrlParams() {
11419
11786
  const url = new URL(window.location.href);
11420
11787
  url.searchParams.delete(SESSION_URL_PARAM);
11421
11788
  window.history.replaceState({}, "", url.toString());
11422
- try {
11423
- if (window.__obiUrlParams) {
11424
- ;
11425
- window.__obiUrlParams = null;
11426
- }
11427
- } catch (error) {
11428
- console.warn("Failed to clean up window URL parameters:", error);
11429
- }
11789
+ localStorage.removeItem("obi-url-params");
11430
11790
  }
11431
11791
  /**
11432
11792
  * Create a new ObiSession instance with common configuration
@@ -11558,6 +11918,7 @@ class ObiWidget extends i$1 {
11558
11918
  if (!sessionToken || !roomToken || !roomUrl) {
11559
11919
  return;
11560
11920
  }
11921
+ console.log("[obi-sdk] using existing session");
11561
11922
  if (sessionExpiry) {
11562
11923
  const expiryDate = new Date(sessionExpiry);
11563
11924
  if (expiryDate < /* @__PURE__ */ new Date()) {
@@ -11612,51 +11973,50 @@ class ObiWidget extends i$1 {
11612
11973
  }
11613
11974
  }
11614
11975
  async sessionConnectionCheck() {
11615
- await this.checkExistingSession();
11616
- if (!this.activeSession) {
11976
+ const sessionId = function() {
11617
11977
  let storedParams = {};
11618
- if (window.__obiUrlParams) {
11619
- storedParams = window.__obiUrlParams;
11620
- }
11978
+ storedParams = JSON.parse(localStorage.getItem("obi-url-params") || "{}");
11621
11979
  if (Object.keys(storedParams).length === 0) {
11622
11980
  const urlParams = new URLSearchParams(window.location.search);
11623
11981
  urlParams.forEach((value, key) => {
11624
11982
  storedParams[key] = value;
11625
11983
  });
11626
11984
  }
11627
- const sessionId = storedParams[SESSION_URL_PARAM];
11628
- if (sessionId && this.apiKey) {
11629
- await this.handleUrlSessionEvent(sessionId);
11630
- } else if (sessionId && !this.apiKey) {
11631
- console.log("Session ID found but API key not ready, retrying...");
11632
- setTimeout(() => {
11633
- if (this.apiKey) {
11634
- this.handleUrlSessionEvent(sessionId);
11635
- }
11636
- }, 100);
11637
- }
11985
+ localStorage.removeItem("obi-url-params");
11986
+ return storedParams[SESSION_URL_PARAM];
11987
+ }();
11988
+ if (!sessionId) {
11989
+ await this.checkExistingSession();
11990
+ return;
11991
+ }
11992
+ console.log("[obi-sdk] using magic link");
11993
+ if (this.apiKey) {
11994
+ await this.handleUrlSessionEvent(sessionId);
11995
+ } else {
11996
+ console.log("[obi-sdk] API key not ready, retrying...");
11997
+ setTimeout(() => {
11998
+ if (this.apiKey) {
11999
+ this.handleUrlSessionEvent(sessionId);
12000
+ }
12001
+ }, 100);
11638
12002
  }
11639
12003
  }
11640
12004
  connectedCallback() {
11641
12005
  super.connectedCallback();
11642
12006
  this.updateFromConfig();
12007
+ this.addEventListener("obi-config-updated", this.handleConfigUpdate.bind(this));
11643
12008
  this.setAttribute("position", this.position);
11644
12009
  this.boundSaveSessionData = this.saveSessionData.bind(this);
11645
12010
  window.addEventListener("beforeunload", this.boundSaveSessionData);
11646
12011
  window.addEventListener("pagehide", this.boundSaveSessionData);
11647
- if (document.readyState === "complete") {
11648
- Promise.resolve().then(() => this.sessionConnectionCheck());
11649
- } else {
11650
- window.addEventListener(
11651
- "load",
11652
- () => {
11653
- Promise.resolve().then(() => this.sessionConnectionCheck());
11654
- },
11655
- { once: true }
11656
- );
12012
+ this.addEventListener("course-select", this.handleCourseSelectEvent);
12013
+ if (this.isActive) {
12014
+ this.sessionConnectionCheck();
11657
12015
  }
11658
12016
  }
11659
12017
  disconnectedCallback() {
12018
+ super.disconnectedCallback();
12019
+ this.removeEventListener("obi-config-updated", this.handleConfigUpdate.bind(this));
11660
12020
  if (this.closeNavTimeoutRef !== null) {
11661
12021
  window.clearTimeout(this.closeNavTimeoutRef);
11662
12022
  }
@@ -11669,7 +12029,19 @@ class ObiWidget extends i$1 {
11669
12029
  window.removeEventListener("pagehide", this.boundSaveSessionData);
11670
12030
  }
11671
12031
  this.removeSessionUrlParams();
11672
- super.disconnectedCallback();
12032
+ }
12033
+ handleConfigUpdate(event) {
12034
+ const customEvent = event;
12035
+ const updatedConfig = customEvent.detail;
12036
+ const needsInit = updatedConfig.isActive && !this.isActive;
12037
+ this.updateFromConfig();
12038
+ if (this.position) {
12039
+ this.setAttribute("position", this.position);
12040
+ }
12041
+ if (needsInit) {
12042
+ this.sessionConnectionCheck();
12043
+ }
12044
+ this.requestUpdate();
11673
12045
  }
11674
12046
  handleMouseEnter() {
11675
12047
  this.isHovering = true;
@@ -11730,7 +12102,8 @@ class ObiWidget extends i$1 {
11730
12102
  N$1.union(SDKState.USER_SPEAKING, SDKState.AGENT_SPEAKING),
11731
12103
  () => x`<obi-audio-equalizer .volume=${this.volume}></obi-audio-equalizer>`
11732
12104
  ).with(SDKState.THINKING, () => x`<obi-dot-loader></obi-dot-loader>`).with(SDKState.PAUSED, () => obiIcon).otherwise(() => obiIcon);
11733
- const isPulseState = this.state === SDKState.USER_SPEAKING || this.state === SDKState.AGENT_SPEAKING;
12105
+ const isSpeakingPulseState = this.state === SDKState.USER_SPEAKING || this.state === SDKState.AGENT_SPEAKING;
12106
+ const isPausedPulse = this.state === SDKState.PAUSED;
11734
12107
  const isResearching = this.state === SDKState.RESEARCHING;
11735
12108
  const isUserSpeaking = this.state === SDKState.USER_SPEAKING;
11736
12109
  const isRotated = this.state !== SDKState.READY || this.navVisible;
@@ -11739,7 +12112,8 @@ class ObiWidget extends i$1 {
11739
12112
  const containerClasses = [
11740
12113
  "widget-container",
11741
12114
  isRotated ? "rotated" : "",
11742
- isPulseState ? "pulse" : "",
12115
+ isSpeakingPulseState ? "pulse" : "",
12116
+ isPausedPulse ? "paused-pulse" : "",
11743
12117
  isResearching ? "researching" : "",
11744
12118
  isUserSpeaking ? "user-speaking" : ""
11745
12119
  ].filter(Boolean).join(" ");
@@ -11762,8 +12136,8 @@ class ObiWidget extends i$1 {
11762
12136
  </div>
11763
12137
  ${this.showCourseModal ? x`<obi-course-modal
11764
12138
  .onClose=${() => this.showCourseModal = false}
11765
- @course-select=${this.handleCourseSelectEvent}
11766
12139
  .apiKey=${this.apiKey}
12140
+ @course-select=${this.handleCourseSelectEvent}
11767
12141
  ></obi-course-modal>` : E}
11768
12142
  ${this.showSessionStartModal && this.selectedCourse ? x`<obi-session-start-modal
11769
12143
  .session=${this.selectedCourse}
@@ -11782,11 +12156,17 @@ ObiWidget.styles = i$4`
11782
12156
  :host {
11783
12157
  display: block;
11784
12158
  position: fixed;
11785
- z-index: 9999;
12159
+ z-index: 2147483600;
11786
12160
  font-family: "Inter", sans-serif;
11787
- --obi-primary: #9500ff;
12161
+ pointer-events: auto;
12162
+ --obi-primary: #a10fff;
11788
12163
  --obi-secondary: #c4b5fd;
11789
12164
  --obi-danger: #ef4444;
12165
+
12166
+ /* Generated color variables (defaults based on original design) */
12167
+ --obi-course-selected: #c76cff;
12168
+ --obi-course-selected-bg: #fbf3ff;
12169
+ --obi-paused-pulse: #a06df9;
11790
12170
  }
11791
12171
 
11792
12172
  h1 {
@@ -11853,12 +12233,13 @@ ObiWidget.styles = i$4`
11853
12233
  height: 56px;
11854
12234
  border-radius: 12px;
11855
12235
  border-color: transparent;
11856
- background-color: var(--obi-primary);
12236
+ background-color: var(--obi-primary, #a10fff);
11857
12237
  display: flex;
11858
12238
  align-items: center;
11859
12239
  justify-content: center;
11860
12240
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
11861
12241
  transition: all 0.2s ease-out;
12242
+ pointer-events: auto;
11862
12243
  }
11863
12244
 
11864
12245
  /* Apply transforms to widget-container based on host position */
@@ -11899,19 +12280,26 @@ ObiWidget.styles = i$4`
11899
12280
  }
11900
12281
 
11901
12282
  @keyframes pulse-shadow {
11902
- 0% {
11903
- box-shadow: 0 0 0 0 color-mix(in srgb, var(--obi-primary) 40%, transparent);
11904
- }
11905
- 70% {
11906
- box-shadow: 0 0 0 10px color-mix(in srgb, var(--obi-primary) 0%, transparent);
11907
- }
12283
+ 0%,
11908
12284
  100% {
11909
- box-shadow: 0 0 0 0 color-mix(in srgb, var(--obi-primary) 0%, transparent);
12285
+ box-shadow: 0px 0px 15px 2px
12286
+ color-mix(in srgb, var(--obi-primary, #a10fff) 75%, transparent);
12287
+ }
12288
+
12289
+ 50% {
12290
+ box-shadow: 0px 0px 25px 5px
12291
+ color-mix(in srgb, var(--obi-primary, #a10fff) 90%, transparent);
11910
12292
  }
11911
12293
  }
11912
12294
 
11913
12295
  .pulse {
11914
12296
  animation: pulse-shadow 2s ease-in-out infinite;
12297
+ box-shadow: none;
12298
+ }
12299
+
12300
+ .paused-pulse {
12301
+ box-shadow: 0px 0px 18px 6px
12302
+ color-mix(in srgb, var(--obi-paused-pulse, #a06df9) 75%, transparent);
11915
12303
  }
11916
12304
  `;
11917
12305
  __decorateClass([
@@ -11959,7 +12347,6 @@ __decorateClass([
11959
12347
  if (!customElements.get("obi-widget")) {
11960
12348
  customElements.define("obi-widget", ObiWidget);
11961
12349
  }
11962
- const obiWidget = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, ObiWidget }, Symbol.toStringTag, { value: "Module" }));
11963
12350
  export {
11964
12351
  AudioEqualizer as A,
11965
12352
  Course as C,
@@ -11979,8 +12366,7 @@ export {
11979
12366
  audioEqualizer as j,
11980
12367
  dotLoader as k,
11981
12368
  n$2 as n,
11982
- obiWidget as o,
11983
12369
  searchingLoader as s,
11984
12370
  x
11985
12371
  };
11986
- //# sourceMappingURL=obi-widget-ad91b91a.js.map
12372
+ //# sourceMappingURL=obi-widget-3bb30929.js.map