@decentnetwork/beagle-ui 0.2.19 → 0.2.20

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/beagle-ui.js +132 -45
  2. package/package.json +1 -1
package/dist/beagle-ui.js CHANGED
@@ -1,4 +1,4 @@
1
- export const BEAGLE_UI_VERSION = "0.2.19";
1
+ export const BEAGLE_UI_VERSION = "0.2.20";
2
2
  // src/icons.jsx
3
3
  var ICON_PATHS = {
4
4
  // ---- tab bar (the four must feel like one set) ----
@@ -345,6 +345,42 @@ var DK_ME_FALLBACK = {
345
345
  channel: "@next",
346
346
  wire: "163"
347
347
  };
348
+ var DK_PAGE = 30;
349
+ function dkMsgKey(m) {
350
+ return m.id || m.ts + ":" + m.dir;
351
+ }
352
+ function dkThreadMsg(m) {
353
+ return {
354
+ id: m.id,
355
+ from: m.dir === "out" ? "me" : "them",
356
+ time: dkClock(m.ts),
357
+ text: m.text,
358
+ file: m.file ? {
359
+ name: m.file.name,
360
+ size: dkFileSize(m.file.size),
361
+ dir: m.dir,
362
+ media: dkFileMediaKind(m.file.name),
363
+ status: m.file.status,
364
+ pct: m.file.status === "sending" && m.file.size ? Math.min(100, (m.file.sent || 0) / m.file.size * 100) : void 0,
365
+ kbps: m.file.kbps
366
+ } : void 0,
367
+ // Pass the real state through. Collapsing everything that was not
368
+ // 'queued' to 'read' meant a message still in flight — or one that
369
+ // never went out at all — rendered as delivered.
370
+ status: m.dir === "out" ? m.status === "queued" || m.status === "sending" || m.status === "failed" || m.status === "sent" ? m.status : "read" : void 0,
371
+ error: m.error,
372
+ // false = went to a live session but nobody acknowledged it (one tick).
373
+ // Dropped here before, so that state could never render.
374
+ confirmed: m.confirmed,
375
+ // Delivery path: "online" = live session, "offline" = express relay.
376
+ // Both directions carry it now; the bubble labels them the same way.
377
+ via: m.via,
378
+ // Brief 30: a validated component form, already whitelisted host-side.
379
+ // Passed through untouched — the renderer treats every string as text,
380
+ // never markup.
381
+ components: m.components
382
+ };
383
+ }
348
384
  function useDaemonData() {
349
385
  const [snap, setSnap] = React.useState({
350
386
  me: DK_ME_FALLBACK,
@@ -390,52 +426,74 @@ function useDaemonData() {
390
426
  return () => clearInterval(t);
391
427
  }, [refresh]);
392
428
  const inFlight = React.useRef(/* @__PURE__ */ new Set());
393
- const loadThread = React.useCallback(async (peerId) => {
394
- if (!peerId || inFlight.current.has(peerId))
429
+ const cache = React.useRef({});
430
+ const [earlier, setEarlier] = React.useState({});
431
+ const publish = React.useCallback((peerId) => {
432
+ const c = cache.current[peerId];
433
+ if (!c)
434
+ return;
435
+ const arr = [...c.byId.values()].sort((a, b) => a.ts - b.ts);
436
+ const msgs = arr.map(dkThreadMsg);
437
+ const withDay = msgs.length ? [{ day: dkDayLabel(arr[0].ts) }].concat(msgs) : [];
438
+ setThreads((t) => Object.assign({}, t, { [peerId]: withDay }));
439
+ setEarlier((e) => e[peerId] === c.more ? e : Object.assign({}, e, { [peerId]: c.more }));
440
+ }, []);
441
+ const loadThread = React.useCallback(async (peerId, opts) => {
442
+ if (!peerId)
443
+ return;
444
+ if (opts && opts.reset)
445
+ delete cache.current[peerId];
446
+ if (inFlight.current.has(peerId))
395
447
  return;
396
448
  inFlight.current.add(peerId);
397
449
  try {
398
- const d = await dkGet("/api/chat-history?peer=" + encodeURIComponent(peerId) + "&limit=200");
450
+ const d = await dkGet("/api/chat-history?peer=" + encodeURIComponent(peerId) + "&limit=" + DK_PAGE);
399
451
  const arr = d.chats && d.chats[peerId] || [];
400
452
  await prefetchThreadFiles(arr);
401
- const msgs = arr.map((m) => ({
402
- id: m.id,
403
- from: m.dir === "out" ? "me" : "them",
404
- time: dkClock(m.ts),
405
- text: m.text,
406
- file: m.file ? {
407
- name: m.file.name,
408
- size: dkFileSize(m.file.size),
409
- dir: m.dir,
410
- media: dkFileMediaKind(m.file.name),
411
- status: m.file.status,
412
- pct: m.file.status === "sending" && m.file.size ? Math.min(100, (m.file.sent || 0) / m.file.size * 100) : void 0,
413
- kbps: m.file.kbps
414
- } : void 0,
415
- // Pass the real state through. Collapsing everything that was not
416
- // 'queued' to 'read' meant a message still in flight — or one that
417
- // never went out at all — rendered as delivered.
418
- status: m.dir === "out" ? m.status === "queued" || m.status === "sending" || m.status === "failed" || m.status === "sent" ? m.status : "read" : void 0,
419
- error: m.error,
420
- // false = went to a live session but nobody acknowledged it (one tick).
421
- // Dropped here before, so that state could never render.
422
- confirmed: m.confirmed,
423
- // Delivery path: "online" = live session, "offline" = express relay.
424
- // Both directions carry it now; the bubble labels them the same way.
425
- via: m.via,
426
- // Brief 30: a validated component form, already whitelisted host-side.
427
- // Passed through untouched — the renderer treats every string as text,
428
- // never markup.
429
- components: m.components
430
- }));
431
- const withDay = msgs.length ? [{ day: dkDayLabel(arr[0].ts) }].concat(msgs) : [];
432
- setThreads((t) => Object.assign({}, t, { [peerId]: withDay }));
453
+ let c = cache.current[peerId];
454
+ if (!c)
455
+ c = cache.current[peerId] = { byId: /* @__PURE__ */ new Map(), more: arr.length >= DK_PAGE };
456
+ if (arr.length) {
457
+ const from = arr[0].ts;
458
+ for (const [k, m] of c.byId)
459
+ if (m.ts >= from)
460
+ c.byId.delete(k);
461
+ }
462
+ for (const m of arr)
463
+ c.byId.set(dkMsgKey(m), m);
464
+ publish(peerId);
433
465
  } catch (e) {
434
466
  } finally {
435
467
  inFlight.current.delete(peerId);
436
468
  }
437
- }, []);
438
- return Object.assign({}, snap, { threads, refresh, loadThread });
469
+ }, [publish]);
470
+ const loadEarlier = React.useCallback(async (peerId) => {
471
+ const c = cache.current[peerId];
472
+ if (!c || !c.more || c.loadingEarlier)
473
+ return;
474
+ let oldest = Infinity;
475
+ for (const m of c.byId.values())
476
+ if (m.ts < oldest)
477
+ oldest = m.ts;
478
+ if (!Number.isFinite(oldest))
479
+ return;
480
+ c.loadingEarlier = true;
481
+ try {
482
+ const d = await dkGet("/api/chat-history?peer=" + encodeURIComponent(peerId) + "&limit=" + DK_PAGE + "&before=" + oldest);
483
+ const arr = d.chats && d.chats[peerId] || [];
484
+ await prefetchThreadFiles(arr);
485
+ if (cache.current[peerId] !== c)
486
+ return;
487
+ for (const m of arr)
488
+ c.byId.set(dkMsgKey(m), m);
489
+ c.more = arr.length >= DK_PAGE;
490
+ publish(peerId);
491
+ } catch (e) {
492
+ } finally {
493
+ c.loadingEarlier = false;
494
+ }
495
+ }, [publish]);
496
+ return Object.assign({}, snap, { threads, earlier, refresh, loadThread, loadEarlier });
439
497
  }
440
498
  Object.assign(window, {
441
499
  dkHash,
@@ -1374,6 +1432,8 @@ function Msg({ m, peer, T, onTheater, onDelete, onCancel, onRetry, onRetryMsg, o
1374
1432
  {
1375
1433
  src: dkFileUrl(m.file.name),
1376
1434
  alt: m.file.name,
1435
+ loading: "lazy",
1436
+ decoding: "async",
1377
1437
  onError: (e) => {
1378
1438
  const w = e.currentTarget.closest("[data-media]");
1379
1439
  if (w)
@@ -1622,8 +1682,10 @@ function Msg({ m, peer, T, onTheater, onDelete, onCancel, onRetry, onRetryMsg, o
1622
1682
  ) : 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)" })))
1623
1683
  );
1624
1684
  }
1625
- function Conversation({ T, peer, lang, peers, onOpenChat, thread: threadProp, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread, muted, onToggleMute, phone, onBack }) {
1685
+ function Conversation({ T, peer, lang, peers, onOpenChat, thread: threadProp, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread, hasEarlier, onLoadEarlier, muted, onToggleMute, phone, onBack }) {
1626
1686
  const scrollRef = React.useRef(null);
1687
+ const prependRef = React.useRef(null);
1688
+ const [earlierBusy, setEarlierBusy] = React.useState(false);
1627
1689
  const isGroup = React.useMemo(() => dkThreadIsGroup(threadProp), [threadProp]);
1628
1690
  const followScrollRef = React.useRef(true);
1629
1691
  const fileRef = React.useRef(null);
@@ -1701,9 +1763,30 @@ function Conversation({ T, peer, lang, peers, onOpenChat, thread: threadProp, on
1701
1763
  followScrollRef.current = el.scrollHeight - el.scrollTop - el.clientHeight < 80;
1702
1764
  };
1703
1765
  React.useLayoutEffect(() => {
1766
+ const el = scrollRef.current;
1767
+ const pre = prependRef.current;
1768
+ if (pre && el && el.scrollHeight !== pre.h) {
1769
+ el.scrollTop = pre.top + (el.scrollHeight - pre.h);
1770
+ prependRef.current = null;
1771
+ return;
1772
+ }
1704
1773
  if (followScrollRef.current)
1705
1774
  scrollToBottom();
1706
1775
  }, [peer.id, thread]);
1776
+ React.useEffect(() => {
1777
+ prependRef.current = null;
1778
+ setEarlierBusy(false);
1779
+ }, [peer.id]);
1780
+ const loadEarlier = () => {
1781
+ const el = scrollRef.current;
1782
+ if (!onLoadEarlier || earlierBusy)
1783
+ return;
1784
+ if (el)
1785
+ prependRef.current = { h: el.scrollHeight, top: el.scrollTop };
1786
+ followScrollRef.current = false;
1787
+ setEarlierBusy(true);
1788
+ Promise.resolve(onLoadEarlier()).finally(() => setEarlierBusy(false));
1789
+ };
1707
1790
  const toggleSel = (id) => setSel((s) => {
1708
1791
  const n = new Set(s);
1709
1792
  n.has(id) ? n.delete(id) : n.add(id);
@@ -1963,7 +2046,7 @@ ${peer.address}`
1963
2046
  fontSize: 12.5,
1964
2047
  color: "var(--text)"
1965
2048
  } }, /* @__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.")),
1966
- /* @__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(
2049
+ /* @__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(
1967
2050
  Msg,
1968
2051
  {
1969
2052
  key: m.id || i,
@@ -2123,12 +2206,12 @@ function MenuItem({ icon, label, onClick, danger }) {
2123
2206
  function ChatEmpty({ T }) {
2124
2207
  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));
2125
2208
  }
2126
- 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 }) {
2209
+ 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 }) {
2127
2210
  const peer = peers.find((p) => p.id === activeId);
2128
2211
  if (phone) {
2129
- 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 });
2212
+ 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 });
2130
2213
  }
2131
- 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 }));
2214
+ 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 }));
2132
2215
  }
2133
2216
  Object.assign(window, { ChatTab });
2134
2217
 
@@ -4913,6 +4996,8 @@ var STR = {
4913
4996
  autoAcceptLabel: "Auto-accept friend requests",
4914
4997
  autoAcceptSub: "Off: requests wait in the sidebar for your approval.",
4915
4998
  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.",
4999
+ loadEarlier: "Load earlier messages",
5000
+ loadingEarlier: "Loading\u2026",
4916
5001
  pendingFriend: "Waiting for them to accept your friend request \u2014 messages will queue and deliver once they do.",
4917
5002
  cancelling: "Cancelling\u2026",
4918
5003
  cancelled: "Transfer cancelled",
@@ -5105,6 +5190,8 @@ var STR = {
5105
5190
  autoAcceptLabel: "\u81EA\u52A8\u63A5\u53D7\u597D\u53CB\u8BF7\u6C42",
5106
5191
  autoAcceptSub: "\u5173\u95ED\u540E\uFF0C\u597D\u53CB\u8BF7\u6C42\u4F1A\u7B49\u5F85\u4F60\u5728\u4FA7\u8FB9\u680F\u624B\u52A8\u786E\u8BA4\u3002",
5107
5192
  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",
5193
+ loadEarlier: "\u52A0\u8F7D\u66F4\u65E9\u7684\u6D88\u606F",
5194
+ loadingEarlier: "\u52A0\u8F7D\u4E2D\u2026",
5108
5195
  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",
5109
5196
  cancelling: "\u6B63\u5728\u53D6\u6D88\u2026",
5110
5197
  cancelled: "\u5DF2\u53D6\u6D88\u4F20\u8F93",
@@ -5575,7 +5662,7 @@ function DkApp() {
5575
5662
  React.useEffect(() => {
5576
5663
  if (!activeId)
5577
5664
  return;
5578
- data.loadThread(activeId);
5665
+ data.loadThread(activeId, { reset: true });
5579
5666
  dkApi.markRead(activeId).then(data.refresh);
5580
5667
  const iv = setInterval(() => {
5581
5668
  data.loadThread(activeId);
@@ -5725,7 +5812,7 @@ function DkApp() {
5725
5812
  fontWeight: 600,
5726
5813
  background: tab === n.id ? "var(--accent)" : "transparent",
5727
5814
  color: tab === n.id ? "#fff" : "var(--dim)"
5728
- } }, /* @__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) => {
5815
+ } }, /* @__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) => {
5729
5816
  data.refresh();
5730
5817
  return r;
5731
5818
  }) })), 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-ui",
3
- "version": "0.2.19",
3
+ "version": "0.2.20",
4
4
  "description": "The Beagle chat UI, minus the machine it runs on — shared by the desktop install and the browser build.",
5
5
  "license": "MIT",
6
6
  "type": "module",