@decentnetwork/beagle 0.1.104 → 0.1.105

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 +132 -45
  2. package/package.json +2 -2
@@ -1,4 +1,4 @@
1
- window.__DK_UI_VERSION="0.1.104";
1
+ window.__DK_UI_VERSION="0.1.105";
2
2
  "use strict";
3
3
  (() => {
4
4
  // packages/beagle-ui/dist/beagle-ui.js
@@ -341,6 +341,42 @@ window.__DK_UI_VERSION="0.1.104";
341
341
  channel: "@next",
342
342
  wire: "163"
343
343
  };
344
+ var DK_PAGE = 30;
345
+ function dkMsgKey(m) {
346
+ return m.id || m.ts + ":" + m.dir;
347
+ }
348
+ function dkThreadMsg(m) {
349
+ return {
350
+ id: m.id,
351
+ from: m.dir === "out" ? "me" : "them",
352
+ time: dkClock(m.ts),
353
+ text: m.text,
354
+ file: m.file ? {
355
+ name: m.file.name,
356
+ size: dkFileSize(m.file.size),
357
+ dir: m.dir,
358
+ media: dkFileMediaKind(m.file.name),
359
+ status: m.file.status,
360
+ pct: m.file.status === "sending" && m.file.size ? Math.min(100, (m.file.sent || 0) / m.file.size * 100) : void 0,
361
+ kbps: m.file.kbps
362
+ } : void 0,
363
+ // Pass the real state through. Collapsing everything that was not
364
+ // 'queued' to 'read' meant a message still in flight — or one that
365
+ // never went out at all — rendered as delivered.
366
+ status: m.dir === "out" ? m.status === "queued" || m.status === "sending" || m.status === "failed" || m.status === "sent" ? m.status : "read" : void 0,
367
+ error: m.error,
368
+ // false = went to a live session but nobody acknowledged it (one tick).
369
+ // Dropped here before, so that state could never render.
370
+ confirmed: m.confirmed,
371
+ // Delivery path: "online" = live session, "offline" = express relay.
372
+ // Both directions carry it now; the bubble labels them the same way.
373
+ via: m.via,
374
+ // Brief 30: a validated component form, already whitelisted host-side.
375
+ // Passed through untouched — the renderer treats every string as text,
376
+ // never markup.
377
+ components: m.components
378
+ };
379
+ }
344
380
  function useDaemonData() {
345
381
  const [snap, setSnap] = React.useState({
346
382
  me: DK_ME_FALLBACK,
@@ -386,52 +422,74 @@ window.__DK_UI_VERSION="0.1.104";
386
422
  return () => clearInterval(t);
387
423
  }, [refresh]);
388
424
  const inFlight = React.useRef(/* @__PURE__ */ new Set());
389
- const loadThread = React.useCallback(async (peerId) => {
390
- if (!peerId || inFlight.current.has(peerId))
425
+ const cache = React.useRef({});
426
+ const [earlier, setEarlier] = React.useState({});
427
+ const publish = React.useCallback((peerId) => {
428
+ const c = cache.current[peerId];
429
+ if (!c)
430
+ return;
431
+ const arr = [...c.byId.values()].sort((a, b) => a.ts - b.ts);
432
+ const msgs = arr.map(dkThreadMsg);
433
+ const withDay = msgs.length ? [{ day: dkDayLabel(arr[0].ts) }].concat(msgs) : [];
434
+ setThreads((t) => Object.assign({}, t, { [peerId]: withDay }));
435
+ setEarlier((e) => e[peerId] === c.more ? e : Object.assign({}, e, { [peerId]: c.more }));
436
+ }, []);
437
+ const loadThread = React.useCallback(async (peerId, opts) => {
438
+ if (!peerId)
439
+ return;
440
+ if (opts && opts.reset)
441
+ delete cache.current[peerId];
442
+ if (inFlight.current.has(peerId))
391
443
  return;
392
444
  inFlight.current.add(peerId);
393
445
  try {
394
- const d = await dkGet("/api/chat-history?peer=" + encodeURIComponent(peerId) + "&limit=200");
446
+ const d = await dkGet("/api/chat-history?peer=" + encodeURIComponent(peerId) + "&limit=" + DK_PAGE);
395
447
  const arr = d.chats && d.chats[peerId] || [];
396
448
  await prefetchThreadFiles(arr);
397
- const msgs = arr.map((m) => ({
398
- id: m.id,
399
- from: m.dir === "out" ? "me" : "them",
400
- time: dkClock(m.ts),
401
- text: m.text,
402
- file: m.file ? {
403
- name: m.file.name,
404
- size: dkFileSize(m.file.size),
405
- dir: m.dir,
406
- media: dkFileMediaKind(m.file.name),
407
- status: m.file.status,
408
- pct: m.file.status === "sending" && m.file.size ? Math.min(100, (m.file.sent || 0) / m.file.size * 100) : void 0,
409
- kbps: m.file.kbps
410
- } : void 0,
411
- // Pass the real state through. Collapsing everything that was not
412
- // 'queued' to 'read' meant a message still in flight — or one that
413
- // never went out at all — rendered as delivered.
414
- status: m.dir === "out" ? m.status === "queued" || m.status === "sending" || m.status === "failed" || m.status === "sent" ? m.status : "read" : void 0,
415
- error: m.error,
416
- // false = went to a live session but nobody acknowledged it (one tick).
417
- // Dropped here before, so that state could never render.
418
- confirmed: m.confirmed,
419
- // Delivery path: "online" = live session, "offline" = express relay.
420
- // Both directions carry it now; the bubble labels them the same way.
421
- via: m.via,
422
- // Brief 30: a validated component form, already whitelisted host-side.
423
- // Passed through untouched — the renderer treats every string as text,
424
- // never markup.
425
- components: m.components
426
- }));
427
- const withDay = msgs.length ? [{ day: dkDayLabel(arr[0].ts) }].concat(msgs) : [];
428
- setThreads((t) => Object.assign({}, t, { [peerId]: withDay }));
449
+ let c = cache.current[peerId];
450
+ if (!c)
451
+ c = cache.current[peerId] = { byId: /* @__PURE__ */ new Map(), more: arr.length >= DK_PAGE };
452
+ if (arr.length) {
453
+ const from = arr[0].ts;
454
+ for (const [k, m] of c.byId)
455
+ if (m.ts >= from)
456
+ c.byId.delete(k);
457
+ }
458
+ for (const m of arr)
459
+ c.byId.set(dkMsgKey(m), m);
460
+ publish(peerId);
429
461
  } catch (e) {
430
462
  } finally {
431
463
  inFlight.current.delete(peerId);
432
464
  }
433
- }, []);
434
- return Object.assign({}, snap, { threads, refresh, loadThread });
465
+ }, [publish]);
466
+ const loadEarlier = React.useCallback(async (peerId) => {
467
+ const c = cache.current[peerId];
468
+ if (!c || !c.more || c.loadingEarlier)
469
+ return;
470
+ let oldest = Infinity;
471
+ for (const m of c.byId.values())
472
+ if (m.ts < oldest)
473
+ oldest = m.ts;
474
+ if (!Number.isFinite(oldest))
475
+ return;
476
+ c.loadingEarlier = true;
477
+ try {
478
+ const d = await dkGet("/api/chat-history?peer=" + encodeURIComponent(peerId) + "&limit=" + DK_PAGE + "&before=" + oldest);
479
+ const arr = d.chats && d.chats[peerId] || [];
480
+ await prefetchThreadFiles(arr);
481
+ if (cache.current[peerId] !== c)
482
+ return;
483
+ for (const m of arr)
484
+ c.byId.set(dkMsgKey(m), m);
485
+ c.more = arr.length >= DK_PAGE;
486
+ publish(peerId);
487
+ } catch (e) {
488
+ } finally {
489
+ c.loadingEarlier = false;
490
+ }
491
+ }, [publish]);
492
+ return Object.assign({}, snap, { threads, earlier, refresh, loadThread, loadEarlier });
435
493
  }
436
494
  Object.assign(window, {
437
495
  dkHash,
@@ -1368,6 +1426,8 @@ window.__DK_UI_VERSION="0.1.104";
1368
1426
  {
1369
1427
  src: dkFileUrl(m.file.name),
1370
1428
  alt: m.file.name,
1429
+ loading: "lazy",
1430
+ decoding: "async",
1371
1431
  onError: (e) => {
1372
1432
  const w = e.currentTarget.closest("[data-media]");
1373
1433
  if (w)
@@ -1616,8 +1676,10 @@ window.__DK_UI_VERSION="0.1.104";
1616
1676
  ) : mine && m.via === "offline" && m.status !== "queued" && m.status !== "sending" ? /* @__PURE__ */ React.createElement(Icon, { name: "check", size: 12, stroke: 2.2, color: "#f59e0b", title: T.viaOutOffline || "posted to the offline relay \u2014 they get it when they come online" }) : mine && m.status === "sent" && m.confirmed === false ? /* @__PURE__ */ React.createElement(Icon, { name: "check", size: 12, stroke: 2.2, color: "var(--faint)", title: T.sentUnconfirmed || "sent \u2014 not confirmed by the peer" }) : mine && m.status && /* @__PURE__ */ React.createElement(Icon, { name: "checkCheck", size: 12, stroke: 2.2, color: m.status === "read" ? "var(--accent)" : "var(--faint)" })))
1617
1677
  );
1618
1678
  }
1619
- function Conversation({ T, peer, lang, peers, onOpenChat, thread: threadProp, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread, muted, onToggleMute, phone, onBack }) {
1679
+ function Conversation({ T, peer, lang, peers, onOpenChat, thread: threadProp, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread, hasEarlier, onLoadEarlier, muted, onToggleMute, phone, onBack }) {
1620
1680
  const scrollRef = React.useRef(null);
1681
+ const prependRef = React.useRef(null);
1682
+ const [earlierBusy, setEarlierBusy] = React.useState(false);
1621
1683
  const isGroup = React.useMemo(() => dkThreadIsGroup(threadProp), [threadProp]);
1622
1684
  const followScrollRef = React.useRef(true);
1623
1685
  const fileRef = React.useRef(null);
@@ -1695,9 +1757,30 @@ window.__DK_UI_VERSION="0.1.104";
1695
1757
  followScrollRef.current = el.scrollHeight - el.scrollTop - el.clientHeight < 80;
1696
1758
  };
1697
1759
  React.useLayoutEffect(() => {
1760
+ const el = scrollRef.current;
1761
+ const pre = prependRef.current;
1762
+ if (pre && el && el.scrollHeight !== pre.h) {
1763
+ el.scrollTop = pre.top + (el.scrollHeight - pre.h);
1764
+ prependRef.current = null;
1765
+ return;
1766
+ }
1698
1767
  if (followScrollRef.current)
1699
1768
  scrollToBottom();
1700
1769
  }, [peer.id, thread]);
1770
+ React.useEffect(() => {
1771
+ prependRef.current = null;
1772
+ setEarlierBusy(false);
1773
+ }, [peer.id]);
1774
+ const loadEarlier = () => {
1775
+ const el = scrollRef.current;
1776
+ if (!onLoadEarlier || earlierBusy)
1777
+ return;
1778
+ if (el)
1779
+ prependRef.current = { h: el.scrollHeight, top: el.scrollTop };
1780
+ followScrollRef.current = false;
1781
+ setEarlierBusy(true);
1782
+ Promise.resolve(onLoadEarlier()).finally(() => setEarlierBusy(false));
1783
+ };
1701
1784
  const toggleSel = (id) => setSel((s) => {
1702
1785
  const n = new Set(s);
1703
1786
  n.has(id) ? n.delete(id) : n.add(id);
@@ -1957,7 +2040,7 @@ ${peer.address}`
1957
2040
  fontSize: 12.5,
1958
2041
  color: "var(--text)"
1959
2042
  } }, /* @__PURE__ */ React.createElement(Icon, { name: "clock", size: 13, stroke: 2.2, color: "#d29922" }), /* @__PURE__ */ React.createElement("span", null, T.pendingFriend || "Waiting for them to accept your friend request \u2014 messages will queue and deliver once they do.")),
1960
- /* @__PURE__ */ React.createElement("div", { ref: scrollRef, onScroll: updateFollowScroll, style: { flex: 1, overflow: "auto", padding: phone ? "12px 10px" : "18px 22px", WebkitOverflowScrolling: "touch" } }, /* @__PURE__ */ React.createElement("div", { style: { maxWidth: 1200, margin: "0 auto" } }, thread.filter((m) => m.day || !hidden.has(m.id)).map((m, i) => m.day ? /* @__PURE__ */ React.createElement("div", { key: i, style: { display: "flex", justifyContent: "center", margin: "14px 0" } }, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 10.5, fontWeight: 600, color: "var(--faint)", background: "var(--chip)", padding: "3px 10px", borderRadius: 999 } }, m.day)) : /* @__PURE__ */ React.createElement(
2043
+ /* @__PURE__ */ React.createElement("div", { ref: scrollRef, onScroll: updateFollowScroll, style: { flex: 1, overflow: "auto", padding: phone ? "12px 10px" : "18px 22px", WebkitOverflowScrolling: "touch" } }, /* @__PURE__ */ React.createElement("div", { style: { maxWidth: 1200, margin: "0 auto" } }, hasEarlier && /* @__PURE__ */ React.createElement("div", { style: { display: "flex", justifyContent: "center", margin: "4px 0 10px" } }, /* @__PURE__ */ React.createElement(Btn, { size: "sm", onClick: loadEarlier, disabled: earlierBusy }, earlierBusy ? T.loadingEarlier || "Loading\u2026" : T.loadEarlier || "Load earlier messages")), thread.filter((m) => m.day || !hidden.has(m.id)).map((m, i) => m.day ? /* @__PURE__ */ React.createElement("div", { key: i, style: { display: "flex", justifyContent: "center", margin: "14px 0" } }, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 10.5, fontWeight: 600, color: "var(--faint)", background: "var(--chip)", padding: "3px 10px", borderRadius: 999 } }, m.day)) : /* @__PURE__ */ React.createElement(
1961
2044
  Msg,
1962
2045
  {
1963
2046
  key: m.id || i,
@@ -2117,12 +2200,12 @@ ${peer.address}`
2117
2200
  function ChatEmpty({ T }) {
2118
2201
  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));
2119
2202
  }
2120
- function ChatTab({ T, lang, peers, requests, activeId, thread, onSelect, onAct, onAdd, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread, prefillAddr, onPrefillConsumed, muted, onToggleMute, phone, onBack }) {
2203
+ function ChatTab({ T, lang, peers, requests, activeId, thread, onSelect, onAct, onAdd, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread, hasEarlier, onLoadEarlier, prefillAddr, onPrefillConsumed, muted, onToggleMute, phone, onBack }) {
2121
2204
  const peer = peers.find((p) => p.id === activeId);
2122
2205
  if (phone) {
2123
- return peer ? /* @__PURE__ */ React.createElement(Conversation, { T, peer, lang, peers, onOpenChat: onSelect, thread, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread, muted: !!muted && muted.includes(peer.id), onToggleMute, phone: true, onBack }) : /* @__PURE__ */ React.createElement(PeerSidebar, { T, peers, requests, activeId, onSelect, onAct, onAdd, prefillAddr, onPrefillConsumed, muted, wide: true });
2206
+ return peer ? /* @__PURE__ */ React.createElement(Conversation, { T, peer, lang, peers, onOpenChat: onSelect, thread, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread, hasEarlier, onLoadEarlier, muted: !!muted && muted.includes(peer.id), onToggleMute, phone: true, onBack }) : /* @__PURE__ */ React.createElement(PeerSidebar, { T, peers, requests, activeId, onSelect, onAct, onAdd, prefillAddr, onPrefillConsumed, muted, wide: true });
2124
2207
  }
2125
- 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 }), peer ? /* @__PURE__ */ React.createElement(Conversation, { T, peer, lang, peers, onOpenChat: onSelect, thread, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread, muted: !!muted && muted.includes(peer.id), onToggleMute }) : /* @__PURE__ */ React.createElement(ChatEmpty, { T }));
2208
+ 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 }), peer ? /* @__PURE__ */ React.createElement(Conversation, { T, peer, lang, peers, onOpenChat: onSelect, thread, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread, hasEarlier, onLoadEarlier, muted: !!muted && muted.includes(peer.id), onToggleMute }) : /* @__PURE__ */ React.createElement(ChatEmpty, { T }));
2126
2209
  }
2127
2210
  Object.assign(window, { ChatTab });
2128
2211
  function CopyBtn({ value, copiedText = "Copied", copyFailedText = "Copy failed", copyTitle = "Copy" }) {
@@ -4885,6 +4968,8 @@ ${peer.address}`
4885
4968
  autoAcceptLabel: "Auto-accept friend requests",
4886
4969
  autoAcceptSub: "Off: requests wait in the sidebar for your approval.",
4887
4970
  groupSenderUnknown: "The group relays only this display name. Register Beagle as a group agent (/agent add <beagle address> in the group) to receive full member identities you can add as friends.",
4971
+ loadEarlier: "Load earlier messages",
4972
+ loadingEarlier: "Loading\u2026",
4888
4973
  pendingFriend: "Waiting for them to accept your friend request \u2014 messages will queue and deliver once they do.",
4889
4974
  cancelling: "Cancelling\u2026",
4890
4975
  cancelled: "Transfer cancelled",
@@ -5077,6 +5162,8 @@ ${peer.address}`
5077
5162
  autoAcceptLabel: "\u81EA\u52A8\u63A5\u53D7\u597D\u53CB\u8BF7\u6C42",
5078
5163
  autoAcceptSub: "\u5173\u95ED\u540E\uFF0C\u597D\u53CB\u8BF7\u6C42\u4F1A\u7B49\u5F85\u4F60\u5728\u4FA7\u8FB9\u680F\u624B\u52A8\u786E\u8BA4\u3002",
5079
5164
  groupSenderUnknown: "\u7FA4\u670D\u52A1\u53EA\u8F6C\u53D1\u4E86\u663E\u793A\u540D\uFF0C\u62FF\u4E0D\u5230\u8BE5\u6210\u5458\u7684\u5B8C\u6574\u8EAB\u4EFD\u3002\u5728\u7FA4\u91CC\u6267\u884C /agent add <beagle \u5730\u5740> \u628A Beagle \u6CE8\u518C\u4E3A\u7FA4 agent \u540E\uFF0C\u5373\u53EF\u6536\u5230\u5B8C\u6574\u8EAB\u4EFD\u5E76\u4E00\u952E\u52A0\u597D\u53CB\u3002",
5165
+ loadEarlier: "\u52A0\u8F7D\u66F4\u65E9\u7684\u6D88\u606F",
5166
+ loadingEarlier: "\u52A0\u8F7D\u4E2D\u2026",
5080
5167
  pendingFriend: "\u7B49\u5F85\u5BF9\u65B9\u63A5\u53D7\u597D\u53CB\u8BF7\u6C42 \u2014 \u6D88\u606F\u4F1A\u5148\u6392\u961F\uFF0C\u5BF9\u65B9\u63A5\u53D7\u540E\u81EA\u52A8\u9001\u8FBE\u3002",
5081
5168
  cancelling: "\u6B63\u5728\u53D6\u6D88\u2026",
5082
5169
  cancelled: "\u5DF2\u53D6\u6D88\u4F20\u8F93",
@@ -5547,7 +5634,7 @@ ${peer.address}`
5547
5634
  React.useEffect(() => {
5548
5635
  if (!activeId)
5549
5636
  return;
5550
- data.loadThread(activeId);
5637
+ data.loadThread(activeId, { reset: true });
5551
5638
  dkApi.markRead(activeId).then(data.refresh);
5552
5639
  const iv = setInterval(() => {
5553
5640
  data.loadThread(activeId);
@@ -5697,7 +5784,7 @@ ${peer.address}`
5697
5784
  fontWeight: 600,
5698
5785
  background: tab === n.id ? "var(--accent)" : "transparent",
5699
5786
  color: tab === n.id ? "#fff" : "var(--dim)"
5700
- } }, /* @__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, onBack: closeConversation, peers, requests, activeId, thread: data.threads[activeId], onSelect, onAct, onAdd, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread: () => activeId && data.loadThread(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 }), tab === "profile" && /* @__PURE__ */ React.createElement(ProfileTab, { T, me, onEdit, t, setTweak, onSetAvatar: (p) => dkApi.setProfileFull(p).then((r) => {
5787
+ } }, /* @__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, onBack: closeConversation, peers, requests, activeId, thread: data.threads[activeId], onSelect, onAct, onAdd, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread: () => activeId && data.loadThread(activeId), hasEarlier: !!(activeId && data.earlier[activeId]), 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 }), tab === "profile" && /* @__PURE__ */ React.createElement(ProfileTab, { T, me, onEdit, t, setTweak, onSetAvatar: (p) => dkApi.setProfileFull(p).then((r) => {
5701
5788
  data.refresh();
5702
5789
  return r;
5703
5790
  }) })), phone && !inConversation && /* @__PURE__ */ React.createElement("div", { style: { flexShrink: 0, display: "flex", borderTop: "1px solid var(--line)", background: "var(--rail)", paddingBottom: "env(safe-area-inset-bottom, 0px)" } }, phoneNav.map((n) => /* @__PURE__ */ React.createElement(TabBarBtn, { key: n.id, icon: n.icon, label: n.label, active: n.active, badge: n.badge, onClick: n.go }))), data.locked && /* @__PURE__ */ React.createElement(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decentnetwork/beagle",
3
- "version": "0.1.104",
3
+ "version": "0.1.105",
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.19",
38
+ "@decentnetwork/beagle-ui": "^0.2.20",
39
39
  "@types/js-yaml": "^4.0.9",
40
40
  "@types/node": "^20.11.0",
41
41
  "@types/yargs": "^17.0.32",