@syntrologie/adapt-chatbot 2.52.0 → 2.53.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.
@@ -199,13 +199,41 @@ export declare class LeaveAMessageLit extends LitElement {
199
199
  /** Snapshot {state, transcript, email} so the NEXT mount (a fresh page
200
200
  * in the same session) can pick the thread back up. */
201
201
  private _persist;
202
+ /** The pending reply announcement, if the canvas gave us a handle to it. */
203
+ private _noticeHandle;
202
204
  /**
203
- * Raise the reply toast through the canvas element. Pierces shadow roots to
205
+ * Is the visitor LOOKING at this card right now? True only when the card
206
+ * has a laid-out box inside the viewport, the document is visible, and a
207
+ * hit-test at the card's center lands on the card itself — so a deck card
208
+ * stacked behind another, a card under a maximized chat, or a closed
209
+ * canvas all read as "not on screen". The hit-test descends through open
210
+ * shadow roots (`document.elementFromPoint` stops at the outermost host).
211
+ * Canvas-agnostic on purpose: the card never asks a canvas which tile is
212
+ * front. Anything this can't evaluate (jsdom, no layout) is "not on
213
+ * screen": when unsure, announce — a missed reply costs more than an
214
+ * extra toast.
215
+ */
216
+ private _isOnScreen;
217
+ /** The tile id sc-mount stamps on every widget's container, so the card can
218
+ * name itself to the canvas when the visitor taps its announcement. */
219
+ private _instanceId;
220
+ /**
221
+ * Tap on the reply announcement: open the thread and ask the canvas to
222
+ * bring this tile to the front. `tile-recall` is the same composed event
223
+ * AdaptiveChatBar already raises for its own recalls — velvet cycles the
224
+ * deck to the card and un-maximizes the chat; the host opens the canvas
225
+ * if it was closed.
226
+ */
227
+ private _recall;
228
+ /**
229
+ * Announce a reply through the canvas element. Pierces shadow roots to
204
230
  * find `<smart-canvas>` (same resolution AdaptiveChatBarMountable uses), and
205
231
  * no-ops when no canvas is present — the tile's own "You got mail!" face is
206
- * always the durable signal.
232
+ * always the durable signal. The announcement is PERSISTENT: a human's
233
+ * reply is the one thing the visitor was waiting on, so it stays until
234
+ * tapped or until the visitor opens the thread themselves (`_flip`).
207
235
  */
208
- private _toastReplyArrived;
236
+ private _announceReply;
209
237
  /** Paint our own focus ring from the theme. The border now lives on the
210
238
  * field SHELL (`data-lam-field-wrap`), not the borderless inner
211
239
  * textarea/input, so the ring targets that ancestor via `closest()`. */
@@ -1 +1 @@
1
- {"version":3,"file":"LeaveAMessageLit.d.ts","sourceRoot":"","sources":["../src/LeaveAMessageLit.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"LeaveAMessageLit.d.ts","sourceRoot":"","sources":["../src/LeaveAMessageLit.ts"],"names":[],"mappings":"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"}
package/dist/runtime.js CHANGED
@@ -2031,7 +2031,7 @@ async function pollOnce(cb, isActive) {
2031
2031
  const qs = new URLSearchParams({
2032
2032
  conversationId: id.conversationId,
2033
2033
  locationId: id.locationId,
2034
- lastMessageId: id.lastMessageId ?? "",
2034
+ lastMessageId: "",
2035
2035
  pageLimit: String(LC_PAGE_LIMIT)
2036
2036
  });
2037
2037
  try {
@@ -2051,14 +2051,15 @@ async function pollOnce(cb, isActive) {
2051
2051
  const j = await r.json();
2052
2052
  if (!isActive()) return;
2053
2053
  const messages = j.messages ?? [];
2054
- for (const m of messages) {
2054
+ const seenIdx = id.lastSeenMessageId ? messages.findIndex((m) => m.id === id.lastSeenMessageId) : -1;
2055
+ const fresh = seenIdx === -1 ? messages : messages.slice(0, seenIdx);
2056
+ for (const m of [...fresh].reverse()) {
2055
2057
  if (m.direction === "outbound" && typeof m.body === "string") cb(m.body);
2056
2058
  }
2057
- const lastMessage = messages[messages.length - 1];
2058
- const nextLastMessageId = typeof j.lastMessageId === "string" && j.lastMessageId ? j.lastMessageId : typeof lastMessage?.id === "string" && lastMessage.id ? lastMessage.id : void 0;
2059
- if (nextLastMessageId) {
2059
+ const newestId = messages[0]?.id;
2060
+ if (typeof newestId === "string" && newestId) {
2060
2061
  const current = load();
2061
- save({ ...id, ...current, lastMessageId: nextLastMessageId });
2062
+ save({ ...id, ...current, lastSeenMessageId: newestId });
2062
2063
  }
2063
2064
  pollWarned = false;
2064
2065
  } catch (err) {
@@ -2740,6 +2741,21 @@ var LeaveAMessageLit = class extends LitElement {
2740
2741
  * 'flipped' branch). Seeded with the visitor's own compose-step message
2741
2742
  * in `_submitMessage`. */
2742
2743
  this._transcript = [];
2744
+ /**
2745
+ * Tap on the reply announcement: open the thread and ask the canvas to
2746
+ * bring this tile to the front. `tile-recall` is the same composed event
2747
+ * AdaptiveChatBar already raises for its own recalls — velvet cycles the
2748
+ * deck to the card and un-maximizes the chat; the host opens the canvas
2749
+ * if it was closed.
2750
+ */
2751
+ this._recall = () => {
2752
+ this._flip();
2753
+ const instanceId = this._instanceId();
2754
+ if (!instanceId) return;
2755
+ this.dispatchEvent(
2756
+ new CustomEvent("tile-recall", { bubbles: true, composed: true, detail: { instanceId } })
2757
+ );
2758
+ };
2743
2759
  /** Paint our own focus ring from the theme. The border now lives on the
2744
2760
  * field SHELL (`data-lam-field-wrap`), not the borderless inner
2745
2761
  * textarea/input, so the ring targets that ancestor via `closest()`. */
@@ -2756,10 +2772,10 @@ var LeaveAMessageLit = class extends LitElement {
2756
2772
  };
2757
2773
  this._onReply = (text) => {
2758
2774
  this._transcript = [...this._transcript, { role: "operator", text }];
2775
+ const reading = this.state === "flipped" && this._isOnScreen();
2759
2776
  if (this.state !== "flipped") {
2760
2777
  this.state = "mail";
2761
2778
  this.runtime?.events.publish("supportchat:reply_received", { text });
2762
- this._toastReplyArrived();
2763
2779
  this.dispatchEvent(
2764
2780
  new CustomEvent("syntro:supportchat-reply", {
2765
2781
  bubbles: true,
@@ -2768,6 +2784,7 @@ var LeaveAMessageLit = class extends LitElement {
2768
2784
  })
2769
2785
  );
2770
2786
  }
2787
+ if (!reading) this._announceReply();
2771
2788
  this._persist();
2772
2789
  this.requestUpdate();
2773
2790
  };
@@ -2811,6 +2828,8 @@ var LeaveAMessageLit = class extends LitElement {
2811
2828
  this._flip = () => {
2812
2829
  this.state = "flipped";
2813
2830
  this._touched = false;
2831
+ this._noticeHandle?.dismiss();
2832
+ this._noticeHandle = void 0;
2814
2833
  this._persist();
2815
2834
  };
2816
2835
  this._sendReply = () => {
@@ -2869,6 +2888,8 @@ var LeaveAMessageLit = class extends LitElement {
2869
2888
  this.removeEventListener("keydown", this._onKeyDown);
2870
2889
  this._unsubscribe?.();
2871
2890
  this._unsubscribe = void 0;
2891
+ this._noticeHandle?.dismiss();
2892
+ this._noticeHandle = void 0;
2872
2893
  super.disconnectedCallback();
2873
2894
  }
2874
2895
  /** Read the injected persistence store, if any (I2). */
@@ -2897,12 +2918,53 @@ var LeaveAMessageLit = class extends LitElement {
2897
2918
  store.set(PERSIST_KEY, snapshot);
2898
2919
  }
2899
2920
  /**
2900
- * Raise the reply toast through the canvas element. Pierces shadow roots to
2921
+ * Is the visitor LOOKING at this card right now? True only when the card
2922
+ * has a laid-out box inside the viewport, the document is visible, and a
2923
+ * hit-test at the card's center lands on the card itself — so a deck card
2924
+ * stacked behind another, a card under a maximized chat, or a closed
2925
+ * canvas all read as "not on screen". The hit-test descends through open
2926
+ * shadow roots (`document.elementFromPoint` stops at the outermost host).
2927
+ * Canvas-agnostic on purpose: the card never asks a canvas which tile is
2928
+ * front. Anything this can't evaluate (jsdom, no layout) is "not on
2929
+ * screen": when unsure, announce — a missed reply costs more than an
2930
+ * extra toast.
2931
+ */
2932
+ _isOnScreen() {
2933
+ if (typeof document === "undefined" || typeof window === "undefined") return false;
2934
+ if (document.visibilityState !== "visible") return false;
2935
+ const r = this.getBoundingClientRect();
2936
+ if (r.width <= 0 || r.height <= 0) return false;
2937
+ const cx = r.left + r.width / 2;
2938
+ const cy = r.top + r.height / 2;
2939
+ if (cx < 0 || cy < 0 || cx > window.innerWidth || cy > window.innerHeight) return false;
2940
+ if (typeof document.elementFromPoint !== "function") return false;
2941
+ let hit = document.elementFromPoint(cx, cy);
2942
+ while (hit?.shadowRoot && typeof hit.shadowRoot.elementFromPoint === "function") {
2943
+ const inner = hit.shadowRoot.elementFromPoint(cx, cy);
2944
+ if (!inner || inner === hit) break;
2945
+ hit = inner;
2946
+ }
2947
+ let node = hit;
2948
+ while (node) {
2949
+ if (node === this) return true;
2950
+ node = node.parentNode ?? node.host ?? null;
2951
+ }
2952
+ return false;
2953
+ }
2954
+ /** The tile id sc-mount stamps on every widget's container, so the card can
2955
+ * name itself to the canvas when the visitor taps its announcement. */
2956
+ _instanceId() {
2957
+ return this.closest("[data-tile-id]")?.getAttribute("data-tile-id") ?? void 0;
2958
+ }
2959
+ /**
2960
+ * Announce a reply through the canvas element. Pierces shadow roots to
2901
2961
  * find `<smart-canvas>` (same resolution AdaptiveChatBarMountable uses), and
2902
2962
  * no-ops when no canvas is present — the tile's own "You got mail!" face is
2903
- * always the durable signal.
2963
+ * always the durable signal. The announcement is PERSISTENT: a human's
2964
+ * reply is the one thing the visitor was waiting on, so it stays until
2965
+ * tapped or until the visitor opens the thread themselves (`_flip`).
2904
2966
  */
2905
- _toastReplyArrived() {
2967
+ _announceReply() {
2906
2968
  if (typeof document === "undefined") return;
2907
2969
  const title = this.replyToast?.title?.trim();
2908
2970
  if (!title) return;
@@ -2920,7 +2982,15 @@ var LeaveAMessageLit = class extends LitElement {
2920
2982
  };
2921
2983
  try {
2922
2984
  const canvas = find(document);
2923
- canvas?.previewNotification?.({ title, body: this.replyToast?.body });
2985
+ this._noticeHandle?.dismiss();
2986
+ this._noticeHandle = void 0;
2987
+ const handle = canvas?.previewNotification?.({
2988
+ title,
2989
+ body: this.replyToast?.body,
2990
+ persistent: true,
2991
+ onTap: this._recall
2992
+ });
2993
+ if (handle && typeof handle.dismiss === "function") this._noticeHandle = handle;
2924
2994
  } catch (err) {
2925
2995
  console.warn("supportChat.widget: reply toast failed", err);
2926
2996
  }