@loupekit/sdk 0.13.0 → 0.13.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.
@@ -3983,7 +3983,7 @@ a.fwdchip { text-decoration: none; cursor: pointer; }
3983
3983
  };
3984
3984
 
3985
3985
  // src/app.ts
3986
- var SDK_VERSION = true ? "0.13.0" : "dev";
3986
+ var SDK_VERSION = true ? "0.13.1" : "dev";
3987
3987
  var FAB_SIZE = 46;
3988
3988
  var FAB_DRAG_THRESHOLD = 6;
3989
3989
  var LAUNCHER_SHORTCUT = "Alt+Shift+L";
@@ -4047,6 +4047,7 @@ a.fwdchip { text-decoration: none; cursor: pointer; }
4047
4047
  var DOCK_MODES = ["left", "right", "bottom", "float"];
4048
4048
  var RECORD_MAX_MS = 2e4;
4049
4049
  var MAX_FILES = 10;
4050
+ var SYNC_POLL_MS = 1e4;
4050
4051
  var MAX_IMAGE_BYTES = 10 * 1024 * 1024;
4051
4052
  var MAX_VIDEO_BYTES = 25 * 1024 * 1024;
4052
4053
  var uid2 = () => crypto.randomUUID ? crypto.randomUUID() : "c_" + Math.abs(hash(String(performance.now()))).toString(36);
@@ -4170,6 +4171,7 @@ a.fwdchip { text-decoration: none; cursor: pointer; }
4170
4171
  this.activitySince = "";
4171
4172
  /** The backend answered that it keeps no feed, so the panel stops asking. */
4172
4173
  this.activityNone = false;
4174
+ this.syncBusy = false;
4173
4175
  /** Iteration history per thread — generating a change is a stack, not a one-shot. */
4174
4176
  this.iterations = /* @__PURE__ */ new Map();
4175
4177
  /** The thread whose generate pane is open, if any. */
@@ -4190,6 +4192,9 @@ a.fwdchip { text-decoration: none; cursor: pointer; }
4190
4192
  this.floatDrag = null;
4191
4193
  this.floatResize = null;
4192
4194
  this.raf = 0;
4195
+ this.onSyncVisible = () => {
4196
+ if (document.visibilityState === "visible") void this.syncTick();
4197
+ };
4193
4198
  /** Tab id → its hint slot (custom tabs only; the built-ins have named fields). */
4194
4199
  this.customHintSlots = /* @__PURE__ */ new Map();
4195
4200
  /** The tab strip, in order. */
@@ -4554,6 +4559,7 @@ a.fwdchip { text-decoration: none; cursor: pointer; }
4554
4559
  void this.loadOrg();
4555
4560
  void this.loadActivity();
4556
4561
  this.syncActivityPoll();
4562
+ this.startSyncPoll();
4557
4563
  void this.loadAllComments();
4558
4564
  this.observe();
4559
4565
  this.watchNavigation();
@@ -4595,6 +4601,61 @@ a.fwdchip { text-decoration: none; cursor: pointer; }
4595
4601
  } catch {
4596
4602
  }
4597
4603
  }
4604
+ /**
4605
+ * Keep the panel current without a reload.
4606
+ *
4607
+ * A status moved in another app (CRM approving a ticket), a reply relayed through Hub,
4608
+ * or a teammate's new pin otherwise appears only after the page reloads. The bridge's
4609
+ * SSE covers threads when one is configured; this covers everything else, for every
4610
+ * host. It pauses while the tab is hidden and catches up the moment it is shown again.
4611
+ */
4612
+ startSyncPoll() {
4613
+ if (this.syncPoll) return;
4614
+ this.syncPoll = setInterval(() => void this.syncTick(), SYNC_POLL_MS);
4615
+ document.addEventListener("visibilitychange", this.onSyncVisible);
4616
+ }
4617
+ async syncTick() {
4618
+ if (this.syncBusy || document.visibilityState === "hidden") return;
4619
+ const active = this.shadow?.activeElement;
4620
+ if (active && (active.tagName === "TEXTAREA" || active.tagName === "INPUT")) return;
4621
+ this.syncBusy = true;
4622
+ try {
4623
+ const url = this.url;
4624
+ const threads = [...this.messages.keys()];
4625
+ const [page, all, ...replies] = await Promise.all([
4626
+ this.store.list(this.cfg.projectKey, url).catch(() => null),
4627
+ this.allLoaded || this.scope === "all" ? this.store.listAll(this.cfg.projectKey).catch(() => null) : null,
4628
+ ...threads.map((id) => this.store.listMessages(id).catch(() => null))
4629
+ ]);
4630
+ if (url !== this.url) return;
4631
+ let changed = false;
4632
+ if (Array.isArray(page) && JSON.stringify(page) !== JSON.stringify(this.comments)) {
4633
+ this.comments = page;
4634
+ changed = true;
4635
+ }
4636
+ if (Array.isArray(all) && JSON.stringify(all) !== JSON.stringify(this.allComments)) {
4637
+ this.allComments = all;
4638
+ changed = true;
4639
+ }
4640
+ threads.forEach((id, i) => {
4641
+ const list = replies[i];
4642
+ if (!Array.isArray(list)) return;
4643
+ const local = (this.messages.get(id) ?? []).filter((m) => this.msgPending.has(m.id) || this.msgFailed.has(m.id));
4644
+ const next = [...list, ...local];
4645
+ if (JSON.stringify(next) === JSON.stringify(this.messages.get(id))) return;
4646
+ this.messages.set(id, next);
4647
+ changed = true;
4648
+ });
4649
+ if (changed) {
4650
+ this.renderPins();
4651
+ this.renderList();
4652
+ this.renderHome();
4653
+ }
4654
+ } finally {
4655
+ this.syncBusy = false;
4656
+ }
4657
+ void this.loadNotifications();
4658
+ }
4598
4659
  // ---- DOM construction -----------------------------------------------------
4599
4660
  buildDom() {
4600
4661
  this.root = document.createElement("div");
@@ -7911,6 +7972,9 @@ ${c.body}` : c.body)}</div>` + (c.context?.html ? `<pre class="or-code">${escape
7911
7972
  if (this.companionPoll) clearInterval(this.companionPoll);
7912
7973
  if (this.activityPoll) clearInterval(this.activityPoll);
7913
7974
  this.activityPoll = void 0;
7975
+ if (this.syncPoll) clearInterval(this.syncPoll);
7976
+ this.syncPoll = void 0;
7977
+ document.removeEventListener("visibilitychange", this.onSyncVisible);
7914
7978
  this.stopVoice();
7915
7979
  this.stopPresence();
7916
7980
  this.stopRecording?.();