@decentnetwork/beagle 0.1.114 → 0.1.116

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.
Files changed (2) hide show
  1. package/dist/desktop/app.js +222 -109
  2. package/package.json +2 -2
@@ -1,4 +1,4 @@
1
- window.__DK_UI_VERSION="0.1.114";
1
+ window.__DK_UI_VERSION="0.1.116";
2
2
  "use strict";
3
3
  (() => {
4
4
  // packages/beagle-ui/dist/beagle-ui.js
@@ -163,6 +163,140 @@ window.__DK_UI_VERSION="0.1.114";
163
163
  function DkBrowserNotice(props) {
164
164
  return H.DkBrowserNotice ? /* @__PURE__ */ React.createElement(H.DkBrowserNotice, { ...props }) : null;
165
165
  }
166
+ function protocolTest(rule) {
167
+ if (typeof rule !== "function")
168
+ return () => false;
169
+ return (msg) => {
170
+ if (!msg)
171
+ return false;
172
+ try {
173
+ const v = rule(msg);
174
+ return typeof v === "string" ? v || true : !!v;
175
+ } catch (e) {
176
+ return false;
177
+ }
178
+ };
179
+ }
180
+ function stripOwnPrefix(text) {
181
+ return String(text == null ? "" : text).replace(/^you:\s*/i, "");
182
+ }
183
+ function lastMessageOf(peer) {
184
+ const raw = String(peer && peer.lastMsg || "");
185
+ return {
186
+ text: stripOwnPrefix(raw),
187
+ dir: peer && peer.lastDir || (/^you:\s*/i.test(raw) ? "out" : "in"),
188
+ ts: peer && peer.lastTs || 0
189
+ };
190
+ }
191
+ function lastIsProtocol(peer, isProto) {
192
+ const last = lastMessageOf(peer);
193
+ if (!last.text)
194
+ return false;
195
+ return isProto(last);
196
+ }
197
+ function newestHumanMessage(msgs, isProto) {
198
+ const arr = msgs || [];
199
+ for (let i = arr.length - 1; i >= 0; i--) {
200
+ const m = arr[i];
201
+ if (!m)
202
+ continue;
203
+ if (m.file)
204
+ return m;
205
+ if (!m.text)
206
+ continue;
207
+ if (isProto({ text: m.text, dir: m.dir, ts: m.ts }))
208
+ continue;
209
+ return m;
210
+ }
211
+ return null;
212
+ }
213
+ function humanUnread(msgs, unread, isProto) {
214
+ const want = Number(unread) || 0;
215
+ if (want <= 0)
216
+ return { count: 0, exact: true };
217
+ const arr = msgs || [];
218
+ const window2 = arr.slice(Math.max(0, arr.length - want));
219
+ let n = 0;
220
+ for (const m of window2) {
221
+ if (!m || m.dir !== "in")
222
+ continue;
223
+ if (m.text && !m.file && isProto({ text: m.text, dir: m.dir, ts: m.ts }))
224
+ continue;
225
+ n++;
226
+ }
227
+ return { count: n, exact: window2.length >= want };
228
+ }
229
+ function rosterRow(peer, isProto, fix) {
230
+ const hidden = lastIsProtocol(peer, isProto);
231
+ if (!hidden) {
232
+ return {
233
+ preview: peer && peer.lastMsg || "",
234
+ lastTime: peer && peer.lastTime || "",
235
+ unread: Number(peer && peer.unread || 0),
236
+ activityTs: Number(peer && peer.lastTs || 0),
237
+ hidden: false
238
+ };
239
+ }
240
+ return {
241
+ preview: fix && fix.preview || "",
242
+ lastTime: fix && fix.lastTime || "",
243
+ unread: fix && fix.unreadExact ? fix.unread : 0,
244
+ activityTs: fix && fix.ts || 0,
245
+ hidden: true,
246
+ pending: !fix
247
+ };
248
+ }
249
+ function rosterUnreadTotal(peers, isProto, fixes) {
250
+ return (peers || []).reduce((n, p) => n + rosterRow(p, isProto, fixes && fixes[p.id]).unread, 0);
251
+ }
252
+ function collapseProtocol(thread, isProto, lang) {
253
+ const out = [];
254
+ let run = null;
255
+ const flush = () => {
256
+ if (!run)
257
+ return;
258
+ out.push({
259
+ protocol: true,
260
+ id: `proto-${run.first.id || run.startIndex}`,
261
+ count: run.items.length,
262
+ label: run.label,
263
+ dir: run.first.from === "me" ? "out" : "in",
264
+ text: run.items.map((m) => m.text).join("\n"),
265
+ line: protocolLine(run, lang)
266
+ });
267
+ run = null;
268
+ };
269
+ (thread || []).forEach((m, i) => {
270
+ const hit = m && !m.day ? isProto({ text: m.text, dir: m && m.from === "me" ? "out" : "in", ts: m && m.ts }) : false;
271
+ if (!hit || m && m.file) {
272
+ flush();
273
+ out.push(m);
274
+ return;
275
+ }
276
+ if (!run)
277
+ run = { first: m, startIndex: i, items: [], label: typeof hit === "string" ? hit : "" };
278
+ if (!run.label && typeof hit === "string")
279
+ run.label = hit;
280
+ run.items.push(m);
281
+ });
282
+ flush();
283
+ return out;
284
+ }
285
+ function protocolLine(run, lang) {
286
+ const arrow = run.first.from === "me" ? "\u2192" : "\u2190";
287
+ const n = run.items.length;
288
+ if (run.label) {
289
+ return `\u2699 ${arrow} ${run.label}${n > 1 ? ` \xB7 ${n}` : ""}`;
290
+ }
291
+ if (n > 1) {
292
+ return lang === "zh" ? `\u2699 ${arrow} ${n} \u6761\u534F\u8BAE\u6D88\u606F` : `\u2699 ${arrow} ${n} protocol messages`;
293
+ }
294
+ return `\u2699 ${arrow} ${protocolSummary(run.first.text)}`;
295
+ }
296
+ function protocolSummary(text, max = 72) {
297
+ const s = String(text == null ? "" : text).replace(/\s+/g, " ").trim();
298
+ return s.length > max ? s.slice(0, max - 1) + "\u2026" : s;
299
+ }
166
300
  function dkHash(s) {
167
301
  let h = 2166136261;
168
302
  for (let i = 0; i < s.length; i++) {
@@ -394,6 +528,48 @@ window.__DK_UI_VERSION="0.1.114";
394
528
  components: m.components
395
529
  };
396
530
  }
531
+ var DK_TAIL_MIN = 30;
532
+ var DK_TAIL_MAX = 200;
533
+ function useProtocolTails(peers, isProtocol, enabled) {
534
+ const [fixes, setFixes] = React.useState({});
535
+ const cache = React.useRef(/* @__PURE__ */ new Map());
536
+ const inFlight = React.useRef(/* @__PURE__ */ new Set());
537
+ React.useEffect(() => {
538
+ if (!enabled)
539
+ return;
540
+ for (const p of peers || []) {
541
+ if (!p || !lastIsProtocol(p, isProtocol))
542
+ continue;
543
+ const unread = Number(p.unread) || 0;
544
+ const key = `${p.lastTs || 0}:${unread}`;
545
+ const had = cache.current.get(p.id);
546
+ if (had && had.key === key)
547
+ continue;
548
+ if (inFlight.current.has(p.id))
549
+ continue;
550
+ const limit = Math.min(DK_TAIL_MAX, Math.max(DK_TAIL_MIN, unread + 5));
551
+ inFlight.current.add(p.id);
552
+ dkGet("/api/chat-history?peer=" + encodeURIComponent(p.id) + "&limit=" + limit).then((d) => {
553
+ const msgs = d && d.chats && d.chats[p.id] || [];
554
+ const human = newestHumanMessage(msgs, isProtocol);
555
+ const count = humanUnread(msgs, unread, isProtocol);
556
+ const fix = {
557
+ preview: human ? (human.dir === "out" ? "you: " : "") + (human.file ? human.file.name : human.text || "") : "",
558
+ lastTime: human ? dkClock(human.ts) : "",
559
+ ts: human ? human.ts : 0,
560
+ unread: count.count,
561
+ unreadExact: count.exact
562
+ };
563
+ cache.current.set(p.id, { key, fix });
564
+ setFixes((f) => Object.assign({}, f, { [p.id]: fix }));
565
+ }).catch(() => {
566
+ }).finally(() => {
567
+ inFlight.current.delete(p.id);
568
+ });
569
+ }
570
+ }, [peers, isProtocol, enabled]);
571
+ return fixes;
572
+ }
397
573
  function useDaemonData() {
398
574
  const [snap, setSnap] = React.useState({
399
575
  me: DK_ME_FALLBACK,
@@ -1145,6 +1321,20 @@ window.__DK_UI_VERSION="0.1.114";
1145
1321
  return service_peers_default;
1146
1322
  }
1147
1323
  }
1324
+ var ASSISTANT_TS_KEY = "beagle.assistant.lastTs";
1325
+ function loadAssistantTs() {
1326
+ try {
1327
+ return Number(localStorage.getItem(ASSISTANT_TS_KEY)) || 0;
1328
+ } catch (e) {
1329
+ return 0;
1330
+ }
1331
+ }
1332
+ function saveAssistantTs(ts) {
1333
+ try {
1334
+ localStorage.setItem(ASSISTANT_TS_KEY, String(ts));
1335
+ } catch (e) {
1336
+ }
1337
+ }
1148
1338
  var ASSISTANT_PEER = {
1149
1339
  id: ASSISTANT_ID,
1150
1340
  userId: ASSISTANT_ID,
@@ -1152,7 +1342,6 @@ window.__DK_UI_VERSION="0.1.114";
1152
1342
  address: "",
1153
1343
  online: true,
1154
1344
  app: true,
1155
- pinned: true,
1156
1345
  agent: false,
1157
1346
  pending: false,
1158
1347
  via: null,
@@ -1257,9 +1446,18 @@ ${tail}`;
1257
1446
  }).finally(() => setBusy(false));
1258
1447
  } }, busy ? T.working || "Working\u2026" : label), /* @__PURE__ */ React.createElement("span", { style: { fontSize: 12, color: "var(--faint)" } }, adopt ? lang === "zh" ? "\u53D6\u6D88\u52FE\u9009\u5373\u4FDD\u6301\u4E3A\u597D\u53CB" : "Untick any you want to keep as a friend" : lang === "zh" ? "\u53D6\u6D88\u52FE\u9009\u53EF\u4FDD\u7559" : "Untick anyone you want to keep"))));
1259
1448
  }
1260
- function useAssistant({ lang, T, muted, onToggleMute, sound, flash, setAlert, onReloadFriends }) {
1449
+ function useAssistant({ lang, T, muted, onToggleMute, sound, flash, setAlert, onReloadFriends, onActivity }) {
1261
1450
  const [thread, setThread] = React.useState(() => [{ day: (/* @__PURE__ */ new Date()).toLocaleDateString() }, asMsg("them", helpText(lang))]);
1262
- const append = React.useCallback((...ms) => setThread((t) => t.concat(ms)), []);
1451
+ const bump = React.useCallback(() => {
1452
+ const ts = Date.now();
1453
+ saveAssistantTs(ts);
1454
+ if (onActivity)
1455
+ onActivity(ts);
1456
+ }, [onActivity]);
1457
+ const append = React.useCallback((...ms) => {
1458
+ bump();
1459
+ setThread((t) => t.concat(ms));
1460
+ }, [bump]);
1263
1461
  React.useEffect(() => {
1264
1462
  setThread((t) => t.length === 2 ? [t[0], asMsg("them", helpText(lang))] : t);
1265
1463
  }, [lang]);
@@ -1493,98 +1691,6 @@ ${lines}`, { assistant: { kind: "adopt", rows: fresh } }));
1493
1691
  }, [append, lang, onReloadFriends]);
1494
1692
  return { thread, handle, removeFriends, adoptServicePeers };
1495
1693
  }
1496
- function protocolTest(rule) {
1497
- if (typeof rule !== "function")
1498
- return () => false;
1499
- return (msg) => {
1500
- if (!msg)
1501
- return false;
1502
- try {
1503
- const v = rule(msg);
1504
- return typeof v === "string" ? v || true : !!v;
1505
- } catch (e) {
1506
- return false;
1507
- }
1508
- };
1509
- }
1510
- function stripOwnPrefix(text) {
1511
- return String(text == null ? "" : text).replace(/^you:\s*/i, "");
1512
- }
1513
- function lastMessageOf(peer) {
1514
- const raw = String(peer && peer.lastMsg || "");
1515
- return {
1516
- text: stripOwnPrefix(raw),
1517
- dir: peer && peer.lastDir || (/^you:\s*/i.test(raw) ? "out" : "in"),
1518
- ts: peer && peer.lastTs || 0
1519
- };
1520
- }
1521
- function lastIsProtocol(peer, isProto) {
1522
- const last = lastMessageOf(peer);
1523
- if (!last.text)
1524
- return false;
1525
- return isProto(last);
1526
- }
1527
- function rosterRow(peer, isProto) {
1528
- const hidden = lastIsProtocol(peer, isProto);
1529
- return {
1530
- preview: hidden ? "" : peer && peer.lastMsg || "",
1531
- lastTime: hidden ? "" : peer && peer.lastTime || "",
1532
- unread: hidden ? 0 : Number(peer && peer.unread || 0),
1533
- activityTs: hidden ? 0 : Number(peer && peer.lastTs || 0),
1534
- hidden
1535
- };
1536
- }
1537
- function rosterUnreadTotal(peers, isProto) {
1538
- return (peers || []).reduce((n, p) => n + rosterRow(p, isProto).unread, 0);
1539
- }
1540
- function collapseProtocol(thread, isProto, lang) {
1541
- const out = [];
1542
- let run = null;
1543
- const flush = () => {
1544
- if (!run)
1545
- return;
1546
- out.push({
1547
- protocol: true,
1548
- id: `proto-${run.first.id || run.startIndex}`,
1549
- count: run.items.length,
1550
- label: run.label,
1551
- dir: run.first.from === "me" ? "out" : "in",
1552
- text: run.items.map((m) => m.text).join("\n"),
1553
- line: protocolLine(run, lang)
1554
- });
1555
- run = null;
1556
- };
1557
- (thread || []).forEach((m, i) => {
1558
- const hit = m && !m.day ? isProto({ text: m.text, dir: m && m.from === "me" ? "out" : "in", ts: m && m.ts }) : false;
1559
- if (!hit || m && m.file) {
1560
- flush();
1561
- out.push(m);
1562
- return;
1563
- }
1564
- if (!run)
1565
- run = { first: m, startIndex: i, items: [], label: typeof hit === "string" ? hit : "" };
1566
- if (!run.label && typeof hit === "string")
1567
- run.label = hit;
1568
- run.items.push(m);
1569
- });
1570
- flush();
1571
- return out;
1572
- }
1573
- function protocolLine(run, lang) {
1574
- const arrow = run.first.from === "me" ? "\u2192" : "\u2190";
1575
- const n = run.items.length;
1576
- if (run.label) {
1577
- return `\u2699 ${arrow} ${run.label}${n > 1 ? ` \xB7 ${n}` : ""}`;
1578
- }
1579
- if (n > 1) {
1580
- return lang === "zh" ? `\u2699 ${arrow} ${n} \u6761\u534F\u8BAE\u6D88\u606F` : `\u2699 ${arrow} ${n} protocol messages`;
1581
- }
1582
- return `\u2699 ${arrow} ${protocolSummary(run.first.text)}`;
1583
- }
1584
- function protocolSummary(text, max = 72) {
1585
- const s = String(text == null ? "" : text).replace(/\s+/g, " ").trim();
1586
- return s.length > max ? s.slice(0, max - 1) + "\u2026" : s;
1587
- }
1588
1694
  var DK_CMD_HIST_KEY = "beagle.assistant.history";
1589
1695
  var DK_CMD_HIST_MAX = 50;
1590
1696
  function RequestsBlock({ T, requests, onAct }) {
@@ -1645,9 +1751,9 @@ ${lines}`, { assistant: { kind: "adopt", rows: fresh } }));
1645
1751
  paddingLeft: 8
1646
1752
  } }, r.descr), failed[r.id] && /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 11, color: "var(--danger)" } }, failed[r.id]), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", gap: 6 } }, /* @__PURE__ */ React.createElement(Btn, { tone: "ok", icon: "check", size: "sm", onClick: () => act(r, "accept"), style: { flex: 1 } }, T.accept), /* @__PURE__ */ React.createElement(Btn, { tone: "danger", icon: "x", size: "sm", onClick: () => act(r, "reject"), style: { flex: 1 } }, T.reject))))));
1647
1753
  }
1648
- function PeerRow({ peer, T, active, onClick, muted, isProtocol }) {
1754
+ function PeerRow({ peer, T, active, onClick, muted, isProtocol, fix }) {
1649
1755
  const name = peer.alias || peer.userId;
1650
- const row = rosterRow(peer, isProtocol || (() => false));
1756
+ const row = rosterRow(peer, isProtocol || (() => false), fix);
1651
1757
  return /* @__PURE__ */ React.createElement("button", { onClick, style: {
1652
1758
  display: "flex",
1653
1759
  alignItems: "center",
@@ -1678,11 +1784,11 @@ ${lines}`, { assistant: { kind: "adopt", rows: fresh } }));
1678
1784
  minWidth: 0
1679
1785
  } }, dkRowPreview(row.preview)))), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", flexDirection: "column", alignItems: "flex-end", gap: 4, flexShrink: 0 } }, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 10.5, color: "var(--faint)" } }, row.lastTime), row.unread ? /* @__PURE__ */ React.createElement(Unread, { n: row.unread }) : peer.pending ? /* @__PURE__ */ React.createElement(Icon, { name: "clock", size: 11, stroke: 2.2, color: "var(--warn, #d29922)", title: "friend request pending" }) : /* @__PURE__ */ React.createElement(StatusDot, { online: peer.online })));
1680
1786
  }
1681
- function dkActivityTs(p, isProtocol) {
1682
- const row = rosterRow(p, isProtocol || (() => false));
1787
+ function dkActivityTs(p, isProtocol, fix) {
1788
+ const row = rosterRow(p, isProtocol || (() => false), fix);
1683
1789
  return Math.max(row.activityTs, p.acceptedAt || 0, p.requestedAt || 0);
1684
1790
  }
1685
- function PeerSidebar({ T, peers, requests, activeId, onSelect, onAct, onAdd, prefillAddr, onPrefillConsumed, muted, wide, isProtocol }) {
1791
+ function PeerSidebar({ T, peers, requests, activeId, onSelect, onAct, onAdd, prefillAddr, onPrefillConsumed, muted, wide, isProtocol, protocolFix }) {
1686
1792
  const [q, setQ] = React.useState("");
1687
1793
  const [addr, setAddr] = React.useState("");
1688
1794
  const [addState, setAddState] = React.useState(null);
@@ -1727,7 +1833,8 @@ ${lines}`, { assistant: { kind: "adopt", rows: fresh } }));
1727
1833
  }).slice().sort((a, b) => {
1728
1834
  if (!!a.pinned !== !!b.pinned)
1729
1835
  return a.pinned ? -1 : 1;
1730
- const ta = dkActivityTs(a, isProtocol), tb = dkActivityTs(b, isProtocol);
1836
+ const ta = dkActivityTs(a, isProtocol, protocolFix && protocolFix[a.id]);
1837
+ const tb = dkActivityTs(b, isProtocol, protocolFix && protocolFix[b.id]);
1731
1838
  if (ta !== tb)
1732
1839
  return tb - ta;
1733
1840
  return String(a.alias || a.userId || "").localeCompare(String(b.alias || b.userId || ""));
@@ -1766,7 +1873,7 @@ ${lines}`, { assistant: { kind: "adopt", rows: fresh } }));
1766
1873
  fontSize: 11.5,
1767
1874
  lineHeight: 1.35,
1768
1875
  color: addState.kind === "err" ? "var(--bad, #e5484d)" : addState.kind === "ok" ? "var(--good, #46a758)" : "var(--faint)"
1769
- } }, addState.msg), /* @__PURE__ */ React.createElement("div", { style: { position: "relative", display: "flex", alignItems: "center" } }, /* @__PURE__ */ React.createElement(Icon, { name: "search", size: 15, color: "var(--faint)", stroke: 2, style: { position: "absolute", left: 9 } }), /* @__PURE__ */ React.createElement("input", { value: q, onChange: (e) => setQ(e.target.value), placeholder: T.search, style: { ...inputStyle, paddingLeft: 30 } }))), /* @__PURE__ */ React.createElement("div", { style: { flex: 1, overflow: "auto", padding: "10px 8px 16px" } }, /* @__PURE__ */ React.createElement(RequestsBlock, { T, requests, onAct }), /* @__PURE__ */ React.createElement(Section, { label: T.peers, count: `${online}/${peers.length}`, style: { margin: "6px 4px 8px" } }), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 2 } }, filtered.map((p) => /* @__PURE__ */ React.createElement(PeerRow, { key: p.id, peer: p, T, active: p.id === activeId, onClick: () => onSelect(p.id), muted: !!muted && muted.includes(p.id), isProtocol })), !filtered.length && /* @__PURE__ */ React.createElement("div", { style: { padding: 16, textAlign: "center", fontFamily: "var(--mono)", fontSize: 12, color: "var(--faint)" } }, "no matches"))));
1876
+ } }, addState.msg), /* @__PURE__ */ React.createElement("div", { style: { position: "relative", display: "flex", alignItems: "center" } }, /* @__PURE__ */ React.createElement(Icon, { name: "search", size: 15, color: "var(--faint)", stroke: 2, style: { position: "absolute", left: 9 } }), /* @__PURE__ */ React.createElement("input", { value: q, onChange: (e) => setQ(e.target.value), placeholder: T.search, style: { ...inputStyle, paddingLeft: 30 } }))), /* @__PURE__ */ React.createElement("div", { style: { flex: 1, overflow: "auto", padding: "10px 8px 16px" } }, /* @__PURE__ */ React.createElement(RequestsBlock, { T, requests, onAct }), /* @__PURE__ */ React.createElement(Section, { label: T.peers, count: `${online}/${peers.length}`, style: { margin: "6px 4px 8px" } }), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 2 } }, filtered.map((p) => /* @__PURE__ */ React.createElement(PeerRow, { key: p.id, peer: p, T, active: p.id === activeId, onClick: () => onSelect(p.id), muted: !!muted && muted.includes(p.id), isProtocol, fix: protocolFix && protocolFix[p.id] })), !filtered.length && /* @__PURE__ */ React.createElement("div", { style: { padding: 16, textAlign: "center", fontFamily: "var(--mono)", fontSize: 12, color: "var(--faint)" } }, "no matches"))));
1770
1877
  }
1771
1878
  var inputStyle = {
1772
1879
  flex: 1,
@@ -3367,12 +3474,12 @@ ${peer.address}`
3367
3474
  function ChatEmpty({ T }) {
3368
3475
  return /* @__PURE__ */ React.createElement("div", { style: { flex: 1, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", gap: 12, color: "var(--faint)", background: "var(--bg)" } }, /* @__PURE__ */ React.createElement(Icon, { name: "message", size: 40, stroke: 1.4, color: "var(--line)" }), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 13 } }, T.pickPeer));
3369
3476
  }
3370
- function ChatTab({ T, lang, peers, requests, activeId, thread, isProtocol, onSelect, onAct, onAdd, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread, hasEarlier, onLoadEarlier, onAssistantRemove, onAssistantAdopt, prefillAddr, onPrefillConsumed, muted, onToggleMute, phone, onBack }) {
3477
+ function ChatTab({ T, lang, peers, requests, activeId, thread, isProtocol, protocolFix, onSelect, onAct, onAdd, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread, hasEarlier, onLoadEarlier, onAssistantRemove, onAssistantAdopt, prefillAddr, onPrefillConsumed, muted, onToggleMute, phone, onBack }) {
3371
3478
  const peer = peers.find((p) => p.id === activeId);
3372
3479
  if (phone) {
3373
- return peer ? /* @__PURE__ */ React.createElement(Conversation, { T, peer, lang, peers, onOpenChat: onSelect, thread, isProtocol, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread, hasEarlier, onLoadEarlier, onAssistantRemove, onAssistantAdopt, muted: !!muted && muted.includes(peer.id), onToggleMute, phone: true, onBack }) : /* @__PURE__ */ React.createElement(PeerSidebar, { T, peers, requests, activeId, onSelect, onAct, onAdd, prefillAddr, onPrefillConsumed, muted, isProtocol, wide: true });
3480
+ return peer ? /* @__PURE__ */ React.createElement(Conversation, { T, peer, lang, peers, onOpenChat: onSelect, thread, isProtocol, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread, hasEarlier, onLoadEarlier, onAssistantRemove, onAssistantAdopt, muted: !!muted && muted.includes(peer.id), onToggleMute, phone: true, onBack }) : /* @__PURE__ */ React.createElement(PeerSidebar, { T, peers, requests, activeId, onSelect, onAct, onAdd, prefillAddr, onPrefillConsumed, muted, isProtocol, protocolFix, wide: true });
3374
3481
  }
3375
- return /* @__PURE__ */ React.createElement("div", { style: { flex: 1, display: "flex", minWidth: 0, minHeight: 0 } }, /* @__PURE__ */ React.createElement(PeerSidebar, { T, peers, requests, activeId, onSelect, onAct, onAdd, prefillAddr, onPrefillConsumed, muted, isProtocol }), peer ? /* @__PURE__ */ React.createElement(Conversation, { T, peer, lang, peers, onOpenChat: onSelect, thread, isProtocol, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread, hasEarlier, onLoadEarlier, onAssistantRemove, onAssistantAdopt, muted: !!muted && muted.includes(peer.id), onToggleMute }) : /* @__PURE__ */ React.createElement(ChatEmpty, { T }));
3482
+ return /* @__PURE__ */ React.createElement("div", { style: { flex: 1, display: "flex", minWidth: 0, minHeight: 0 } }, /* @__PURE__ */ React.createElement(PeerSidebar, { T, peers, requests, activeId, onSelect, onAct, onAdd, prefillAddr, onPrefillConsumed, muted, isProtocol, protocolFix }), peer ? /* @__PURE__ */ React.createElement(Conversation, { T, peer, lang, peers, onOpenChat: onSelect, thread, isProtocol, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread, hasEarlier, onLoadEarlier, onAssistantRemove, onAssistantAdopt, muted: !!muted && muted.includes(peer.id), onToggleMute }) : /* @__PURE__ */ React.createElement(ChatEmpty, { T }));
3376
3483
  }
3377
3484
  Object.assign(window, { ChatTab });
3378
3485
  function CopyBtn({ value, copiedText = "Copied", copyFailedText = "Copy failed", copyTitle = "Copy" }) {
@@ -6829,7 +6936,11 @@ ${peer.address}`
6829
6936
  sessionStorage.setItem(key, "1");
6830
6937
  window.location.reload();
6831
6938
  }, [me && me.beagleVer]);
6832
- const peers = React.useMemo(() => [ASSISTANT_PEER].concat(data.peers || []), [data.peers]);
6939
+ const [assistantTs, setAssistantTs] = React.useState(loadAssistantTs);
6940
+ const peers = React.useMemo(() => (data.peers || []).concat([Object.assign({}, ASSISTANT_PEER, {
6941
+ lastTs: assistantTs,
6942
+ lastTime: assistantTs ? dkClock(assistantTs) : ""
6943
+ })]), [data.peers, assistantTs]);
6833
6944
  const [pendingAddr, setPendingAddr] = React.useState(() => dkParseDeepLinkAddress());
6834
6945
  React.useEffect(() => {
6835
6946
  const onHash = () => {
@@ -6884,6 +6995,7 @@ ${peer.address}`
6884
6995
  const mutedIds = Array.isArray(t.alertMuted) ? t.alertMuted : [];
6885
6996
  const onToggleMute = (id) => setTweak("alertMuted", mutedIds.includes(id) ? mutedIds.filter((x) => x !== id) : mutedIds.concat(id));
6886
6997
  const isProtocol = React.useMemo(() => protocolTest(hostIsProtocolMessage), []);
6998
+ const protocolFix = useProtocolTails(peers, isProtocol, !!hostIsProtocolMessage);
6887
6999
  useIncomingAlerts({
6888
7000
  T,
6889
7001
  peers,
@@ -6900,6 +7012,7 @@ ${peer.address}`
6900
7012
  T,
6901
7013
  muted: mutedIds,
6902
7014
  onToggleMute,
7015
+ onActivity: setAssistantTs,
6903
7016
  sound: t.alertSound !== false,
6904
7017
  flash: t.alertFlash !== false,
6905
7018
  setAlert: (which, on) => setTweak(which === "sound" ? "alertSound" : "alertFlash", on),
@@ -7145,7 +7258,7 @@ ${peer.address}`
7145
7258
  const lastPeople = React.useRef("here");
7146
7259
  if (peopleTab)
7147
7260
  lastPeople.current = tab;
7148
- const unreadTotal = rosterUnreadTotal(peers, isProtocol) + (requests || []).length;
7261
+ const unreadTotal = rosterUnreadTotal(peers, isProtocol, protocolFix) + (requests || []).length;
7149
7262
  const NAV_FOR = {
7150
7263
  chat: { icon: "message", label: T.chat, active: tab === "chat", badge: unreadTotal, go: () => setTab("chat") },
7151
7264
  people: { icon: "users", label: T.people, active: peopleTab, go: () => setTab(lastPeople.current) },
@@ -7203,7 +7316,7 @@ ${peer.address}`
7203
7316
  fontWeight: 600,
7204
7317
  background: tab === n.id ? "var(--accent)" : "transparent",
7205
7318
  color: tab === n.id ? "#fff" : "var(--dim)"
7206
- } }, /* @__PURE__ */ React.createElement(Icon, { name: n.icon, size: 14, stroke: 2, color: tab === n.id ? "#fff" : "var(--dim)" }), n.label))), tab === "chat" && /* @__PURE__ */ React.createElement(ChatTab, { T, lang: t.lang, phone, isProtocol, onBack: closeConversation, peers, requests, activeId, thread: activeId === ASSISTANT_ID ? assistant.thread : data.threads[activeId], onSelect, onAct, onAdd, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread: () => activeId && data.loadThread(activeId), hasEarlier: !!(activeId && activeId !== ASSISTANT_ID && data.earlier[activeId]), onAssistantRemove: activeId === ASSISTANT_ID ? assistant.removeFriends : null, onAssistantAdopt: activeId === ASSISTANT_ID ? assistant.adoptServicePeers : null, onLoadEarlier: () => activeId && data.loadEarlier(activeId), prefillAddr: pendingAddr, onPrefillConsumed: () => setPendingAddr(""), muted: mutedIds, onToggleMute }), tab === "here" && /* @__PURE__ */ React.createElement(DiscoverTab, { T, kind: "bridge", peers, meId: me.userId, onAdd, onOpenChat }), tab === "recommended" && /* @__PURE__ */ React.createElement(DiscoverTab, { T, kind: "recommended", peers, meId: me.userId, onAdd, onOpenChat }), tab === "registered" && /* @__PURE__ */ React.createElement(DiscoverTab, { T, kind: "registered", peers, meId: me.userId, onAdd, onOpenChat }), tab === "network" && /* @__PURE__ */ React.createElement(NetworkTab, { T, me, peers, exits, activeExit, reqCount: requests.length, onSetExit, onOpenChat, backend, onArmLan: armLan, onCancelLan: cancelLan }), tab === "apps" && /* @__PURE__ */ React.createElement(AppsTab, { T, t, me, peers, phone, setTab }), tabs.map((x) => x.kind === "host" && tab === x.id ? /* @__PURE__ */ React.createElement(HostTabPane, { key: x.id, tab: x, props: { T, t, me, peers, phone, setTab }, lang: t.lang }) : null), tab === "profile" && /* @__PURE__ */ React.createElement(
7319
+ } }, /* @__PURE__ */ React.createElement(Icon, { name: n.icon, size: 14, stroke: 2, color: tab === n.id ? "#fff" : "var(--dim)" }), n.label))), tab === "chat" && /* @__PURE__ */ React.createElement(ChatTab, { T, lang: t.lang, phone, isProtocol, protocolFix, onBack: closeConversation, peers, requests, activeId, thread: activeId === ASSISTANT_ID ? assistant.thread : data.threads[activeId], onSelect, onAct, onAdd, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread: () => activeId && data.loadThread(activeId), hasEarlier: !!(activeId && activeId !== ASSISTANT_ID && data.earlier[activeId]), onAssistantRemove: activeId === ASSISTANT_ID ? assistant.removeFriends : null, onAssistantAdopt: activeId === ASSISTANT_ID ? assistant.adoptServicePeers : null, onLoadEarlier: () => activeId && data.loadEarlier(activeId), prefillAddr: pendingAddr, onPrefillConsumed: () => setPendingAddr(""), muted: mutedIds, onToggleMute }), tab === "here" && /* @__PURE__ */ React.createElement(DiscoverTab, { T, kind: "bridge", peers, meId: me.userId, onAdd, onOpenChat }), tab === "recommended" && /* @__PURE__ */ React.createElement(DiscoverTab, { T, kind: "recommended", peers, meId: me.userId, onAdd, onOpenChat }), tab === "registered" && /* @__PURE__ */ React.createElement(DiscoverTab, { T, kind: "registered", peers, meId: me.userId, onAdd, onOpenChat }), tab === "network" && /* @__PURE__ */ React.createElement(NetworkTab, { T, me, peers, exits, activeExit, reqCount: requests.length, onSetExit, onOpenChat, backend, onArmLan: armLan, onCancelLan: cancelLan }), tab === "apps" && /* @__PURE__ */ React.createElement(AppsTab, { T, t, me, peers, phone, setTab }), tabs.map((x) => x.kind === "host" && tab === x.id ? /* @__PURE__ */ React.createElement(HostTabPane, { key: x.id, tab: x, props: { T, t, me, peers, phone, setTab }, lang: t.lang }) : null), tab === "profile" && /* @__PURE__ */ React.createElement(
7207
7320
  ProfileTab,
7208
7321
  {
7209
7322
  T,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decentnetwork/beagle",
3
- "version": "0.1.114",
3
+ "version": "0.1.116",
4
4
  "description": "Beagle — P2P chat, file transfer and calls for regular users, on the Decent Network. No admin privilege required.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -35,7 +35,7 @@
35
35
  "yargs": "^17.7.2"
36
36
  },
37
37
  "devDependencies": {
38
- "@decentnetwork/beagle-ui": "^0.2.28",
38
+ "@decentnetwork/beagle-ui": "^0.2.30",
39
39
  "@types/js-yaml": "^4.0.9",
40
40
  "@types/node": "^20.11.0",
41
41
  "@types/yargs": "^17.0.32",