@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.
package/dist/index.js CHANGED
@@ -3946,7 +3946,7 @@ var HttpAdapter = class {
3946
3946
  };
3947
3947
 
3948
3948
  // src/app.ts
3949
- var SDK_VERSION = true ? "0.13.0" : "dev";
3949
+ var SDK_VERSION = true ? "0.13.1" : "dev";
3950
3950
  var FAB_SIZE = 46;
3951
3951
  var FAB_DRAG_THRESHOLD = 6;
3952
3952
  var LAUNCHER_SHORTCUT = "Alt+Shift+L";
@@ -4010,6 +4010,7 @@ var BUILTIN_TABS = [
4010
4010
  var DOCK_MODES = ["left", "right", "bottom", "float"];
4011
4011
  var RECORD_MAX_MS = 2e4;
4012
4012
  var MAX_FILES = 10;
4013
+ var SYNC_POLL_MS = 1e4;
4013
4014
  var MAX_IMAGE_BYTES = 10 * 1024 * 1024;
4014
4015
  var MAX_VIDEO_BYTES = 25 * 1024 * 1024;
4015
4016
  var uid2 = () => crypto.randomUUID ? crypto.randomUUID() : "c_" + Math.abs(hash(String(performance.now()))).toString(36);
@@ -4133,6 +4134,7 @@ var LoupeApp = class {
4133
4134
  this.activitySince = "";
4134
4135
  /** The backend answered that it keeps no feed, so the panel stops asking. */
4135
4136
  this.activityNone = false;
4137
+ this.syncBusy = false;
4136
4138
  /** Iteration history per thread — generating a change is a stack, not a one-shot. */
4137
4139
  this.iterations = /* @__PURE__ */ new Map();
4138
4140
  /** The thread whose generate pane is open, if any. */
@@ -4153,6 +4155,9 @@ var LoupeApp = class {
4153
4155
  this.floatDrag = null;
4154
4156
  this.floatResize = null;
4155
4157
  this.raf = 0;
4158
+ this.onSyncVisible = () => {
4159
+ if (document.visibilityState === "visible") void this.syncTick();
4160
+ };
4156
4161
  /** Tab id → its hint slot (custom tabs only; the built-ins have named fields). */
4157
4162
  this.customHintSlots = /* @__PURE__ */ new Map();
4158
4163
  /** The tab strip, in order. */
@@ -4517,6 +4522,7 @@ var LoupeApp = class {
4517
4522
  void this.loadOrg();
4518
4523
  void this.loadActivity();
4519
4524
  this.syncActivityPoll();
4525
+ this.startSyncPoll();
4520
4526
  void this.loadAllComments();
4521
4527
  this.observe();
4522
4528
  this.watchNavigation();
@@ -4558,6 +4564,61 @@ var LoupeApp = class {
4558
4564
  } catch {
4559
4565
  }
4560
4566
  }
4567
+ /**
4568
+ * Keep the panel current without a reload.
4569
+ *
4570
+ * A status moved in another app (CRM approving a ticket), a reply relayed through Hub,
4571
+ * or a teammate's new pin otherwise appears only after the page reloads. The bridge's
4572
+ * SSE covers threads when one is configured; this covers everything else, for every
4573
+ * host. It pauses while the tab is hidden and catches up the moment it is shown again.
4574
+ */
4575
+ startSyncPoll() {
4576
+ if (this.syncPoll) return;
4577
+ this.syncPoll = setInterval(() => void this.syncTick(), SYNC_POLL_MS);
4578
+ document.addEventListener("visibilitychange", this.onSyncVisible);
4579
+ }
4580
+ async syncTick() {
4581
+ if (this.syncBusy || document.visibilityState === "hidden") return;
4582
+ const active = this.shadow?.activeElement;
4583
+ if (active && (active.tagName === "TEXTAREA" || active.tagName === "INPUT")) return;
4584
+ this.syncBusy = true;
4585
+ try {
4586
+ const url = this.url;
4587
+ const threads = [...this.messages.keys()];
4588
+ const [page, all, ...replies] = await Promise.all([
4589
+ this.store.list(this.cfg.projectKey, url).catch(() => null),
4590
+ this.allLoaded || this.scope === "all" ? this.store.listAll(this.cfg.projectKey).catch(() => null) : null,
4591
+ ...threads.map((id) => this.store.listMessages(id).catch(() => null))
4592
+ ]);
4593
+ if (url !== this.url) return;
4594
+ let changed = false;
4595
+ if (Array.isArray(page) && JSON.stringify(page) !== JSON.stringify(this.comments)) {
4596
+ this.comments = page;
4597
+ changed = true;
4598
+ }
4599
+ if (Array.isArray(all) && JSON.stringify(all) !== JSON.stringify(this.allComments)) {
4600
+ this.allComments = all;
4601
+ changed = true;
4602
+ }
4603
+ threads.forEach((id, i) => {
4604
+ const list = replies[i];
4605
+ if (!Array.isArray(list)) return;
4606
+ const local = (this.messages.get(id) ?? []).filter((m) => this.msgPending.has(m.id) || this.msgFailed.has(m.id));
4607
+ const next = [...list, ...local];
4608
+ if (JSON.stringify(next) === JSON.stringify(this.messages.get(id))) return;
4609
+ this.messages.set(id, next);
4610
+ changed = true;
4611
+ });
4612
+ if (changed) {
4613
+ this.renderPins();
4614
+ this.renderList();
4615
+ this.renderHome();
4616
+ }
4617
+ } finally {
4618
+ this.syncBusy = false;
4619
+ }
4620
+ void this.loadNotifications();
4621
+ }
4561
4622
  // ---- DOM construction -----------------------------------------------------
4562
4623
  buildDom() {
4563
4624
  this.root = document.createElement("div");
@@ -7874,6 +7935,9 @@ ${c.body}` : c.body)}</div>` + (c.context?.html ? `<pre class="or-code">${escape
7874
7935
  if (this.companionPoll) clearInterval(this.companionPoll);
7875
7936
  if (this.activityPoll) clearInterval(this.activityPoll);
7876
7937
  this.activityPoll = void 0;
7938
+ if (this.syncPoll) clearInterval(this.syncPoll);
7939
+ this.syncPoll = void 0;
7940
+ document.removeEventListener("visibilitychange", this.onSyncVisible);
7877
7941
  this.stopVoice();
7878
7942
  this.stopPresence();
7879
7943
  this.stopRecording?.();