@mrrocket/sdk 0.22.0 → 0.23.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.
@@ -6,12 +6,14 @@ import {
6
6
  CHAT_FILE_TYPES,
7
7
  createApi,
8
8
  inkOn,
9
- replyLine
9
+ replyLine,
10
+ stillThere
10
11
  } from "./api-client.js";
11
12
  import { currentLocale, onWords, t, tf, wordsLang, wordsVersion } from "./i18n.js";
12
13
  import { injectStyles } from "./styles-css.js";
13
14
  var UPDATES_SHOWN = 20;
14
15
  var POLL_OPEN_MS = 4e3;
16
+ var READ_PATIENCE_MS = 5e3;
15
17
  var POLL_WAITING_MS = 8e3;
16
18
  var POLL_IDLE_MS = 3e4;
17
19
  var POLL_QUIET_MS = 6e4;
@@ -188,6 +190,23 @@ var writeCachedLook = (apiKey, look) => {
188
190
  } catch {
189
191
  }
190
192
  };
193
+ var CHATS_KEY = "ckw_msgr_chats:";
194
+ var PRODUCT_KEY = "ckw_msgr_product:";
195
+ var KEPT = 20;
196
+ var kept = (key) => {
197
+ try {
198
+ return JSON.parse(window.localStorage.getItem(key) ?? "null");
199
+ } catch {
200
+ return null;
201
+ }
202
+ };
203
+ var keep = (key, value) => {
204
+ try {
205
+ if (value) window.localStorage.setItem(key, JSON.stringify(value));
206
+ else window.localStorage.removeItem(key);
207
+ } catch {
208
+ }
209
+ };
191
210
  var mountMessenger = (o) => {
192
211
  let real = null;
193
212
  let shown = "";
@@ -233,6 +252,7 @@ var mountMessenger = (o) => {
233
252
  open: (v) => later((h) => h.open(v)),
234
253
  close: () => real?.close(),
235
254
  toggle: () => later((h) => h.toggle()),
255
+ refresh: () => real?.refresh(),
236
256
  destroy: () => {
237
257
  gone = true;
238
258
  offWords();
@@ -252,7 +272,7 @@ var build = (o) => {
252
272
  const label = o.label ?? "";
253
273
  const reply = replyLine(o.replyTime);
254
274
  const initial = (title.trim()[0] ?? "?").toUpperCase();
255
- const whoLabel = { visitor: t("You"), team: t("Team"), ai: o.agentName?.trim() || t("AI agent"), auto: t("Team") };
275
+ const whoLabel = { visitor: t("You"), team: t("Team"), ai: o.agentName?.trim() ? `${o.agentName.trim()} \xB7 ${t("AI agent")}` : t("AI agent"), auto: t("Team") };
256
276
  const launcherOn = o.launcher ?? true;
257
277
  const helpOn = o.help ?? true;
258
278
  const updatesOn = o.updates ?? true;
@@ -302,10 +322,40 @@ var build = (o) => {
302
322
  let query = "";
303
323
  let rated = /* @__PURE__ */ new Set();
304
324
  let hasArticles = null;
325
+ let homeArticles = [];
326
+ let misses = 0;
305
327
  let updates = null;
328
+ let hasUpdates = false;
306
329
  let updatesNode = null;
307
330
  let collection = null;
308
331
  let wide = false;
332
+ const remembers = !o.client && !!o.apiKey;
333
+ const chatsKey = CHATS_KEY + o.apiKey;
334
+ const productKey = PRODUCT_KEY + o.apiKey;
335
+ let visitor = o.distinctId();
336
+ let person = o.user()?.id;
337
+ const recall = () => {
338
+ const m = remembers ? kept(chatsKey) : null;
339
+ chats = m?.who === visitor && Array.isArray(m.chats) ? m.chats : [];
340
+ };
341
+ recall();
342
+ if (remembers) {
343
+ const m = kept(productKey);
344
+ if (m?.articles?.length) {
345
+ homeArticles = m.articles;
346
+ articles = m.articles;
347
+ hasArticles = true;
348
+ }
349
+ hasUpdates = !!m?.updates;
350
+ }
351
+ const rememberChats = (who) => {
352
+ if (!remembers || o.distinctId() !== who) return;
353
+ if (chats.length) keep(chatsKey, { who, chats: chats.slice(0, KEPT) });
354
+ else if (kept(chatsKey)?.who === who) keep(chatsKey, null);
355
+ };
356
+ const rememberProduct = () => {
357
+ if (remembers) keep(productKey, homeArticles.length || hasUpdates ? { articles: homeArticles.slice(0, KEPT), updates: hasUpdates } : null);
358
+ };
309
359
  const isAnswer = (m) => m.from === "team" || m.from === "ai";
310
360
  const watching = () => open && view === "messages" && document.visibilityState !== "hidden";
311
361
  const shownChat = () => listing || draft !== null ? null : chatId ?? thread?.conversation?.id ?? null;
@@ -342,6 +392,7 @@ var build = (o) => {
342
392
  if (c.id !== read?.id) learn(c.id, c.last && isAnswer(c.last) ? [c.last] : [], c.last?.at, c.status === "solved" && i > 0);
343
393
  });
344
394
  if (read) learn(read.id, t2.messages.filter(isAnswer), last?.at, read.status === "solved" && chats[0]?.id !== read.id);
395
+ unseen = unseen.filter(stillThere(t2, chats));
345
396
  if (chatId === null || read?.id === chatId) thread = t2;
346
397
  if (watching()) markSeen();
347
398
  };
@@ -351,8 +402,8 @@ var build = (o) => {
351
402
  unseen = unseen.filter((m) => m.chat !== chat);
352
403
  const at = known.get(chat);
353
404
  if (!at || seen.chats[chat] === at) return;
354
- const kept = Object.fromEntries(Object.entries(seen.chats).filter(([c]) => chats.some((x) => x.id === c)));
355
- seen = { ...seen, chats: { ...kept, [chat]: at } };
405
+ const kept2 = Object.fromEntries(Object.entries(seen.chats).filter(([c]) => chats.some((x) => x.id === c)));
406
+ seen = { ...seen, chats: { ...kept2, [chat]: at } };
356
407
  writeSeen(seen.chats);
357
408
  };
358
409
  const enterChat = (id, d, list2 = false) => {
@@ -555,7 +606,7 @@ var build = (o) => {
555
606
  else if (view === "help") mid.replaceChildren(el("b", {}, t("Help")));
556
607
  else if (view === "updates") mid.replaceChildren(el("b", {}, t("Updates")));
557
608
  else mid.replaceChildren();
558
- const items = [homeTab, chatTab, ...helpOn && hasArticles !== false ? [helpTab] : [], ...updatesOn && updates?.length ? [updatesTab] : []];
609
+ const items = [homeTab, chatTab, ...helpOn && hasArticles ? [helpTab] : [], ...updatesOn && (updates ? updates.length : hasUpdates) ? [updatesTab] : []];
559
610
  if (tabs.childNodes.length !== items.length || items.some((t2, i) => tabs.childNodes[i] !== t2)) tabs.replaceChildren(...items);
560
611
  tabs.toggleAttribute("data-four", items.length > 3);
561
612
  homeTab.setAttribute("aria-current", view === "home" ? "page" : "false");
@@ -827,7 +878,11 @@ var build = (o) => {
827
878
  const r = await api().articles(q || void 0);
828
879
  if (q !== query.trim()) return;
829
880
  articles = r.articles;
830
- if (!q) hasArticles = r.articles.length > 0;
881
+ if (!q) {
882
+ homeArticles = r.articles;
883
+ hasArticles = r.articles.length > 0;
884
+ rememberProduct();
885
+ }
831
886
  } catch {
832
887
  articles = articles ?? [];
833
888
  } finally {
@@ -838,6 +893,8 @@ var build = (o) => {
838
893
  const loadUpdates = async () => {
839
894
  try {
840
895
  updates = (await api().changelog()).entries.slice(0, UPDATES_SHOWN);
896
+ hasUpdates = updates.length > 0;
897
+ rememberProduct();
841
898
  } catch {
842
899
  updates = [];
843
900
  }
@@ -862,6 +919,25 @@ var build = (o) => {
862
919
  go("messages");
863
920
  if (canSend()) submit();
864
921
  });
922
+ const shapes = (rows, card = false) => el(
923
+ "div",
924
+ { class: "ckw-msgr-shapes", "aria-hidden": "true" },
925
+ ...Array.from(
926
+ { length: rows },
927
+ () => el(
928
+ "div",
929
+ { class: card ? "ckw-msgr-card ckw-msgr-recent" : "ckw-msgr-shape" },
930
+ el("span", { class: "ckw-msgr-recent-row" }, card ? el("span", { class: "ckw-msgr-av ckw-msgr-av-sm ckw-msgr-sk" }) : null, el("span", { class: "ckw-msgr-card-main" }, el("span", { class: "ckw-msgr-sk" }), el("span", { class: "ckw-msgr-sk" })))
931
+ )
932
+ )
933
+ );
934
+ const still = (node) => {
935
+ if (node.isConnected) node.setAttribute("data-still", "");
936
+ return node;
937
+ };
938
+ const chatShape = shapes(1, true);
939
+ const listShape = shapes(3);
940
+ let homeSeen = null;
865
941
  const chatRow = (c) => {
866
942
  const m = c.last;
867
943
  const n = unseenIn(c.id);
@@ -911,9 +987,16 @@ var build = (o) => {
911
987
  el("span", { class: "ckw-msgr-hello-sub" }, rest)
912
988
  );
913
989
  const parts = [hero, askForm];
990
+ const shown = /* @__PURE__ */ new Set();
991
+ const arriving = (node, key) => {
992
+ shown.add(key);
993
+ if (homeSeen && !homeSeen.has(key)) node.setAttribute("data-arrive", "");
994
+ return node;
995
+ };
914
996
  const running = chats.filter((c) => c.status !== "solved");
915
997
  const rows = (running.length ? running : chats).slice(0, 2);
916
- parts.push(...rows.map(chatRow));
998
+ parts.push(...rows.map((c) => arriving(chatRow(c), c.id)));
999
+ if (!rows.length && !loaded && misses < 3 && o.user()?.id) parts.push(still(chatShape));
917
1000
  if (chats.length > rows.length) {
918
1001
  const all = el("button", { type: "button", class: "ckw-msgr-all" }, tf("All conversations ({n})", { n: chats.length }), icon(I.next, 14));
919
1002
  all.addEventListener("click", () => {
@@ -922,13 +1005,9 @@ var build = (o) => {
922
1005
  });
923
1006
  parts.push(all);
924
1007
  }
925
- if (helpOn && hasArticles !== false) {
926
- const helpCard = el("div", { class: "ckw-msgr-card ckw-msgr-card-col" });
927
- const top = (articles ?? []).slice(0, 3);
928
- if (top.length) helpCard.append(...top.map(articleRow));
929
- else if (articles === null) helpCard.append(el("div", { class: "ckw-skel ckw-msgr-skel" }));
930
- parts.push(helpCard);
931
- }
1008
+ const top = helpOn ? homeArticles.slice(0, 3) : [];
1009
+ if (top.length) parts.push(arriving(el("div", { class: "ckw-msgr-card ckw-msgr-card-col" }, ...top.map(articleRow)), "help"));
1010
+ homeSeen = shown;
932
1011
  body.replaceChildren(...parts);
933
1012
  };
934
1013
  const drawHelp = () => {
@@ -940,7 +1019,7 @@ var build = (o) => {
940
1019
  const inside = !q && collection !== null ? groups.get(collection) ?? [] : null;
941
1020
  const heading = q ? t("Search results") : inside ? collection ?? "" : t("Most read articles");
942
1021
  const parts = [el("h2", { class: "ckw-msgr-help-h" }, heading), helpSearch];
943
- if (articles === null) parts.push(el("div", { class: "ckw-skel" }));
1022
+ if (articles === null) parts.push(still(listShape));
944
1023
  else if (!all.length) {
945
1024
  const ask = el("button", { type: "button", class: "ckw-btn ckw-btn-primary" }, t("Send us a message"));
946
1025
  ask.addEventListener("click", askAnew);
@@ -975,7 +1054,7 @@ var build = (o) => {
975
1054
  }
976
1055
  };
977
1056
  const drawUpdates = () => {
978
- updatesNode ??= !updates ? el("div", { class: "ckw-skel" }) : !updates.length ? el("div", { class: "ckw-msgr-empty" }, el("p", {}, t("No updates yet."))) : el(
1057
+ updatesNode ??= !updates ? shapes(3) : !updates.length ? el("div", { class: "ckw-msgr-empty" }, el("p", {}, t("No updates yet."))) : el(
979
1058
  "div",
980
1059
  { class: "ckw-msgr-updates" },
981
1060
  ...updates.map((e) => el("article", { class: "ckw-msgr-update" }, el("p", { class: "ckw-msgr-doc-c" }, timeAgo(e.publishedAt)), el("h2", { class: "ckw-msgr-update-t" }, e.title), e.body ? el("p", { class: "ckw-msgr-update-b" }, e.body) : null))
@@ -985,7 +1064,7 @@ var build = (o) => {
985
1064
  let articleShown = null;
986
1065
  const drawArticle = (id) => {
987
1066
  if (articleShown?.id === id) return body.replaceChildren(articleShown.node);
988
- const node = el("article", { class: "ckw-msgr-doc" }, el("div", { class: "ckw-skel" }));
1067
+ const node = el("article", { class: "ckw-msgr-doc" }, shapes(4));
989
1068
  articleShown = { id, node };
990
1069
  body.replaceChildren(node);
991
1070
  api().article(id).then((a) => {
@@ -1091,27 +1170,59 @@ var build = (o) => {
1091
1170
  const tick = async () => {
1092
1171
  if (ctrl) return;
1093
1172
  const mine = ctrl = new AbortController();
1173
+ const asked = o.distinctId();
1174
+ let late = false;
1175
+ const patience = setTimeout(() => {
1176
+ late = true;
1177
+ mine.abort();
1178
+ }, Math.min(READ_PATIENCE_MS * 2 ** misses, POLL_IDLE_MS));
1179
+ let again;
1094
1180
  try {
1095
1181
  take(await api().chat(mine.signal, chatId ?? void 0));
1096
1182
  error = "";
1097
1183
  refused = false;
1184
+ misses = 0;
1185
+ rememberChats(asked);
1098
1186
  } catch (e) {
1099
- if (e.name === "AbortError") return;
1187
+ if (e.name === "AbortError" && !late) return;
1100
1188
  const status = e.status;
1101
1189
  refused = status === 401 || status === 403;
1102
1190
  if (status === 404 && chatId) enterMessages();
1103
- else if (open && view === "messages") error = sayable(e);
1191
+ else if (open && view === "messages" && !late) error = sayable(e);
1192
+ if (late || status === void 0 || status >= 500) {
1193
+ misses += 1;
1194
+ again = late ? 0 : Math.min(1e3 * 2 ** (misses - 1), POLL_IDLE_MS);
1195
+ }
1104
1196
  } finally {
1197
+ clearTimeout(patience);
1105
1198
  if (ctrl === mine) ctrl = null;
1106
1199
  }
1107
- if (!loaded) {
1200
+ if (!loaded && again === void 0) {
1108
1201
  listCount = -1;
1109
1202
  if (view === "messages" && chatId === null && draft === null && !text.value.trim() && !outbox.length && !picked.length) enterMessages();
1203
+ loaded = true;
1110
1204
  }
1111
- loaded = true;
1112
1205
  draw();
1113
1206
  drawFab();
1114
- schedule();
1207
+ schedule(open && document.visibilityState !== "hidden" ? again : void 0);
1208
+ };
1209
+ const refresh = () => {
1210
+ const who = o.distinctId();
1211
+ if (who === visitor && !refused) return;
1212
+ if (who !== visitor) {
1213
+ if (person && !o.user()?.id && remembers) keep(chatsKey, null);
1214
+ visitor = who;
1215
+ ctrl?.abort();
1216
+ ctrl = null;
1217
+ unseen = [];
1218
+ loaded = false;
1219
+ misses = 0;
1220
+ recall();
1221
+ }
1222
+ person = o.user()?.id;
1223
+ draw();
1224
+ drawFab();
1225
+ schedule(0);
1115
1226
  };
1116
1227
  function schedule(now) {
1117
1228
  clearTimeout(poll);
@@ -1239,16 +1350,17 @@ var build = (o) => {
1239
1350
  addFiles([...e.dataTransfer?.files ?? []]);
1240
1351
  });
1241
1352
  email.addEventListener("input", () => draw());
1242
- function show(v = "home", still = false) {
1353
+ function show(v = "home", still2 = false) {
1243
1354
  const was = open;
1244
1355
  open = true;
1245
1356
  view = v === "help" && !helpOn || v === "updates" && !updatesOn ? "home" : v;
1246
1357
  drawn = -1;
1247
1358
  previewGone = true;
1248
- pop.toggleAttribute("data-still", still);
1359
+ homeSeen = null;
1360
+ pop.toggleAttribute("data-still", still2);
1249
1361
  enter(pop);
1250
1362
  if (was) replay(body, "ckw-msgr-enter");
1251
- if (articles === null || view === "help" && query) void loadArticles();
1363
+ if (articles === null || hasArticles === null || view === "help" && query) void loadArticles();
1252
1364
  draw();
1253
1365
  drawFab();
1254
1366
  if (view === "messages") {
@@ -1293,6 +1405,7 @@ var build = (o) => {
1293
1405
  },
1294
1406
  close,
1295
1407
  toggle: () => open ? close() : show(),
1408
+ refresh,
1296
1409
  isOpen: () => open,
1297
1410
  onClose: (fn) => {
1298
1411
  afterClose = fn;