@decentnetwork/beagle-ui 0.2.25 → 0.2.26

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 +107 -21
  2. package/package.json +1 -1
package/dist/beagle-ui.js CHANGED
@@ -1,4 +1,4 @@
1
- export const BEAGLE_UI_VERSION = "0.2.25";
1
+ export const BEAGLE_UI_VERSION = "0.2.26";
2
2
  // src/icons.jsx
3
3
  var ICON_PATHS = {
4
4
  // ---- tab bar (the four must feel like one set) ----
@@ -650,6 +650,18 @@ var actions_default = {
650
650
  confirm: "list",
651
651
  scope: "assistant"
652
652
  },
653
+ {
654
+ name: "nodes-refresh",
655
+ usage: "/nodes refresh",
656
+ summary: "Look for friends that are provably service peers (the shipped exit table, this machine's dora roster) and offer to reclassify them. Nothing changes until you tick and confirm.",
657
+ params: {
658
+ type: "object",
659
+ properties: {},
660
+ required: []
661
+ },
662
+ confirm: "list",
663
+ scope: "assistant"
664
+ },
653
665
  {
654
666
  name: "find",
655
667
  usage: "/find <text>",
@@ -824,7 +836,8 @@ var actions_default = {
824
836
  file: "service-peers.json",
825
837
  loader: "loadServicePeers()",
826
838
  rule: "A peer is a service peer if and only if its userid is in this list. The Assistant never infers it from a name or from having no messages. /friends excludes them entirely; /nodes is only them.",
827
- later: "A dora / ENS / bridge / directory service returns the same shape; the bundled file is v1."
839
+ later: "A dora / ENS / bridge / directory service returns the same shape; the bundled file is v1.",
840
+ refresh: "/nodes refresh asks GET /api/assistant/service-candidates for evidence-backed candidates and writes the ticked ones to this machine's overlay (POST /api/assistant/service-peers, stored at <configDir>/service-peers-local.json). The shipped list is never edited at runtime; the overlay wins on the same userid."
828
841
  }
829
842
  };
830
843
 
@@ -983,6 +996,8 @@ function parseCommand(line) {
983
996
  const sub = (parts[1] || "").toLowerCase();
984
997
  if (sub === "list" || sub === "remove")
985
998
  return { ok: true, name: `${head}-${sub}`, args: parts.slice(2) };
999
+ if (head === "nodes" && sub === "refresh")
1000
+ return { ok: true, name: "nodes-refresh", args: parts.slice(2) };
986
1001
  return { ok: false, name: head, args: parts.slice(1), error: `${head}-subcommand` };
987
1002
  }
988
1003
  return { ok: true, name: head, args: parts.slice(1) };
@@ -1020,6 +1035,19 @@ function servicePeerMap(list) {
1020
1035
  }
1021
1036
  return map;
1022
1037
  }
1038
+ function mergeServicePeers(bundled, local) {
1039
+ const byId = /* @__PURE__ */ new Map();
1040
+ for (const p of bundled && bundled.peers || [])
1041
+ if (p && p.userid)
1042
+ byId.set(p.userid, { ...p, source: "app" });
1043
+ for (const p of local && local.peers || [])
1044
+ if (p && p.userid)
1045
+ byId.set(p.userid, { ...p, source: "local" });
1046
+ return { version: 1, source: "merged", peers: [...byId.values()] };
1047
+ }
1048
+ function newCandidates(candidates, svcMap) {
1049
+ return (candidates != null ? candidates : []).filter((c) => c && c.userid && !(svcMap && svcMap.has(c.userid)));
1050
+ }
1023
1051
  function partitionPeers(friends, svcMap) {
1024
1052
  const people = [];
1025
1053
  const nodes = [];
@@ -1104,8 +1132,13 @@ function resolveFriend(friends, token) {
1104
1132
  }
1105
1133
 
1106
1134
  // src/assistant.jsx
1107
- function loadServicePeers() {
1108
- return service_peers_default;
1135
+ async function loadServicePeers() {
1136
+ try {
1137
+ const local = await fetch("/api/assistant/service-peers").then((r) => r.json());
1138
+ return mergeServicePeers(service_peers_default, local);
1139
+ } catch (e) {
1140
+ return service_peers_default;
1141
+ }
1109
1142
  }
1110
1143
  var ASSISTANT_PEER = {
1111
1144
  id: ASSISTANT_ID,
@@ -1161,10 +1194,12 @@ function helpText(lang) {
1161
1194
  const tail = lang === "zh" ? `\u7B5B\u9009\u6761\u4EF6\uFF08\u53EF\u7EC4\u5408\uFF0C\u53D6\u5E76\u96C6\uFF09\uFF1A
1162
1195
  ${filters}
1163
1196
 
1164
- \u6700\u8FD1 ${RECENT_DAYS} \u5929\u5185\u6DFB\u52A0\u7684\u597D\u53CB\u4E0D\u4F1A\u88AB"\u4ECE\u672A/\u6C89\u9ED8"\u7C7B\u7B5B\u9009\u9009\u4E2D\u3002\u5220\u9664\u6C38\u8FDC\u9700\u8981\u5148\u786E\u8BA4\u3002` : `Filters (combine them; a friend matching any one is listed):
1197
+ \`/rename\`\u3001\`/mute\`\u3001\`/find\` \u5BF9\u597D\u53CB\u548C\u670D\u52A1\u8282\u70B9\u90FD\u9002\u7528\uFF1B\u53EA\u6709\u5217\u8868\u548C\u5220\u9664\u5206\u6210\u4E24\u8FB9\u3002
1198
+ \u6700\u8FD1 ${RECENT_DAYS} \u5929\u5185\u6DFB\u52A0\u7684\u597D\u53CB\u4E0D\u4F1A\u88AB"\u4ECE\u672A/\u6C89\u9ED8"\u7C7B\u7B5B\u9009\u9009\u4E2D\uFF0C\u5E76\u4F1A\u5217\u51FA\u88AB\u8DF3\u8FC7\u7684\u540D\u5B57\u3002\u5220\u9664\u6C38\u8FDC\u9700\u8981\u5148\u786E\u8BA4\uFF0C\u53D6\u6D88\u52FE\u9009\u5373\u53EF\u4FDD\u7559\u3002` : `Filters (combine them; a friend matching any one is listed):
1165
1199
  ${filters}
1166
1200
 
1167
- Friends added in the last ${RECENT_DAYS} days are never matched by the "never/silent" filters. Removal always asks first.`;
1201
+ \`/rename\`, \`/mute\` and \`/find\` work on friends and service peers alike \u2014 only listing and removing are split in two.
1202
+ Friends added in the last ${RECENT_DAYS} days are never matched by the "never/silent" filters, and the ones held back are named. Removal always asks first: untick anyone you want to keep.`;
1168
1203
  return `${intro}
1169
1204
  ${cmds}
1170
1205
 
@@ -1178,8 +1213,9 @@ var asMsg = (from, text, extra) => Object.assign({
1178
1213
  time: (/* @__PURE__ */ new Date()).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }),
1179
1214
  text
1180
1215
  }, extra);
1181
- function AssistantPanel({ m, T, lang, onRemove }) {
1216
+ function AssistantPanel({ m, T, lang, onRemove, onAdopt }) {
1182
1217
  const rows = m.assistant.rows || [];
1218
+ const adopt = m.assistant.kind === "adopt";
1183
1219
  const [sel, setSel] = React.useState(() => new Set(rows.map((r) => r.userid)));
1184
1220
  const [busy, setBusy] = React.useState(false);
1185
1221
  const [done, setDone] = React.useState(null);
@@ -1190,7 +1226,7 @@ function AssistantPanel({ m, T, lang, onRemove }) {
1190
1226
  n.has(id) ? n.delete(id) : n.add(id);
1191
1227
  return n;
1192
1228
  });
1193
- const label = lang === "zh" ? `\u5220\u9664 ${sel.size} \u4F4D` : `Remove ${sel.size}`;
1229
+ const label = adopt ? lang === "zh" ? `\u52A0\u5165 ${sel.size} \u4E2A` : `Add ${sel.size} as ${sel.size === 1 ? "a node" : "nodes"}` : lang === "zh" ? `\u5220\u9664 ${sel.size} \u4F4D` : `Remove ${sel.size}`;
1194
1230
  return /* @__PURE__ */ React.createElement("div", { style: { marginTop: 8, border: "1px solid var(--line)", borderRadius: 10, overflow: "hidden" } }, rows.map((r) => /* @__PURE__ */ React.createElement("label", { key: r.userid, style: {
1195
1231
  display: "flex",
1196
1232
  alignItems: "center",
@@ -1207,12 +1243,14 @@ function AssistantPanel({ m, T, lang, onRemove }) {
1207
1243
  disabled: !!done || busy,
1208
1244
  onChange: () => toggle(r.userid)
1209
1245
  }
1210
- ), /* @__PURE__ */ React.createElement("span", { style: { flex: 1 } }, nameOf(r)), /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 11, color: "var(--faint)" } }, r.userid.slice(0, 8), "\u2026"))), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 10, padding: "8px 10px" } }, done ? /* @__PURE__ */ React.createElement("span", { style: { fontSize: 12.5, color: "var(--faint)" } }, done) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Btn, { size: "sm", tone: "danger", disabled: busy || !sel.size, onClick: () => {
1246
+ ), /* @__PURE__ */ React.createElement("span", { style: { flex: 1 } }, nameOf(r), r.why && /* @__PURE__ */ React.createElement("span", { style: { color: "var(--faint)", fontSize: 11.5 } }, ` \u2014 ${r.why}`)), /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 11, color: "var(--faint)" } }, r.userid.slice(0, 8), "\u2026"))), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 10, padding: "8px 10px" } }, done ? /* @__PURE__ */ React.createElement("span", { style: { fontSize: 12.5, color: "var(--faint)" } }, done) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Btn, { size: "sm", tone: adopt ? "solid" : "danger", disabled: busy || !sel.size, onClick: () => {
1211
1247
  setBusy(true);
1212
- onRemove([...sel]).then((r) => {
1213
- setDone(lang === "zh" ? `\u5DF2\u5220\u9664 ${r.removed} \u4F4D${r.failed ? `\uFF0C${r.failed} \u4F4D\u5931\u8D25` : ""}` : `Removed ${r.removed}${r.failed ? `, ${r.failed} failed` : ""}`);
1248
+ const picked = rows.filter((r) => sel.has(r.userid));
1249
+ const run = adopt ? onAdopt(picked) : onRemove([...sel]);
1250
+ run.then((r) => {
1251
+ setDone(adopt ? lang === "zh" ? `\u5DF2\u52A0\u5165 ${r.added} \u4E2A\u670D\u52A1\u8282\u70B9` : `Added ${r.added} as service peers` : lang === "zh" ? `\u5DF2\u5220\u9664 ${r.removed} \u4F4D${r.failed ? `\uFF0C${r.failed} \u4F4D\u5931\u8D25` : ""}` : `Removed ${r.removed}${r.failed ? `, ${r.failed} failed` : ""}`);
1214
1252
  }).finally(() => setBusy(false));
1215
- } }, busy ? T.working || "Working\u2026" : label), /* @__PURE__ */ React.createElement("span", { style: { fontSize: 12, color: "var(--faint)" } }, lang === "zh" ? "\u53D6\u6D88\u52FE\u9009\u53EF\u4FDD\u7559" : "Untick anyone you want to keep"))));
1253
+ } }, 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"))));
1216
1254
  }
1217
1255
  function useAssistant({ lang, T, muted, onToggleMute, sound, flash, setAlert, onReloadFriends }) {
1218
1256
  const [thread, setThread] = React.useState(() => [{ day: (/* @__PURE__ */ new Date()).toLocaleDateString() }, asMsg("them", helpText(lang))]);
@@ -1222,11 +1260,13 @@ function useAssistant({ lang, T, muted, onToggleMute, sound, flash, setAlert, on
1222
1260
  }, [lang]);
1223
1261
  const fetchFriends = async () => {
1224
1262
  const r = await fetch("/api/assistant/friends").then((x) => x.json());
1225
- const { people, nodes } = partitionPeers(r.friends || [], servicePeerMap(loadServicePeers()));
1263
+ const svcMap = servicePeerMap(await loadServicePeers());
1264
+ const { people, nodes } = partitionPeers(r.friends || [], svcMap);
1226
1265
  return {
1227
1266
  friends: people,
1228
1267
  nodes,
1229
1268
  all: r.friends || [],
1269
+ svcMap,
1230
1270
  historySince: r.historySince || null,
1231
1271
  now: r.now || Date.now(),
1232
1272
  ver: r.beagleVer || ""
@@ -1293,6 +1333,33 @@ ${cands.map((c) => `- ${nameOf(c)}`).join("\n")}`;
1293
1333
  - Waiting to be accepted: ${pending}
1294
1334
  - History on this device since: ${fmtDate(d.historySince, lang)}
1295
1335
  - Version: ${d.ver}`));
1336
+ }
1337
+ case "nodes-refresh": {
1338
+ const d = await fetchFriends();
1339
+ const r = await fetch("/api/assistant/service-candidates").then((x) => x.json());
1340
+ const fresh = newCandidates(r.candidates || [], d.svcMap);
1341
+ const src = r.sources || {};
1342
+ const where = lang === "zh" ? `\u67E5\u8BE2\u6765\u6E90\uFF1A\u5185\u7F6E\u51FA\u53E3\u8868\uFF08${src.exits || 0} \u6761\uFF09${src.dora == null ? "\uFF0C\u672C\u673A\u4E0D\u662F dora" : `\uFF0C\u672C\u673A dora \u540D\u518C\uFF08${src.dora} \u6761\uFF09`}` : `Looked at: the shipped exit table (${src.exits || 0})${src.dora == null ? ", and this machine is not a dora" : `, and this machine's dora roster (${src.dora})`}`;
1343
+ if (!fresh.length) {
1344
+ return append(asMsg("them", lang === "zh" ? `**\u6CA1\u6709\u65B0\u7684\u670D\u52A1\u8282\u70B9**
1345
+
1346
+ \u73B0\u6709 ${d.nodes.length} \u4E2A\u5DF2\u8BC6\u522B\u3002${where}\u3002
1347
+
1348
+ *\u53EA\u91C7\u7528\u8BC1\u636E\uFF0C\u4E0D\u9760\u540D\u5B57\u731C\u6D4B\u3002*` : `**Nothing new**
1349
+
1350
+ ${d.nodes.length} service peers are already classified. ${where}.
1351
+
1352
+ *Evidence only \u2014 names are never guessed from.*`));
1353
+ }
1354
+ const title = lang === "zh" ? `**\u53D1\u73B0 ${fresh.length} \u4E2A\u53EF\u4EE5\u6807\u4E3A\u670D\u52A1\u8282\u70B9\u7684\u597D\u53CB** \u2014 \u52FE\u9009\u540E\u624D\u4F1A\u751F\u6548
1355
+
1356
+ ${where}\u3002` : `**${fresh.length === 1 ? "1 friend looks" : `${fresh.length} friends look`} like service peers** \u2014 nothing changes until you tick and confirm
1357
+
1358
+ ${where}.`;
1359
+ const lines = fresh.map((c) => `- **${c.name}** \`${c.kind}${c.role ? " \xB7 " + c.role : ""}\` \u2014 ${c.why}`).join("\n");
1360
+ return append(asMsg("them", `${title}
1361
+
1362
+ ${lines}`, { assistant: { kind: "adopt", rows: fresh } }));
1296
1363
  }
1297
1364
  case "nodes-list":
1298
1365
  case "friends-list": {
@@ -1382,6 +1449,24 @@ ${cands.map((c) => `- ${nameOf(c)}`).join("\n")}`;
1382
1449
  return append(asMsg("them", lang === "zh" ? `\u51FA\u9519\u4E86\uFF1A${e && e.message || e}` : `That failed: ${e && e.message || e}`));
1383
1450
  }
1384
1451
  }, [append, lang, muted, onToggleMute, setAlert, onReloadFriends]);
1452
+ const adoptServicePeers = React.useCallback(async (rows) => {
1453
+ const add = rows.map((r) => ({ userid: r.userid, name: r.name, kind: r.kind, role: r.role }));
1454
+ let added = 0;
1455
+ try {
1456
+ const r = await fetch("/api/assistant/service-peers", {
1457
+ method: "POST",
1458
+ headers: { "content-type": "application/json" },
1459
+ body: JSON.stringify({ add })
1460
+ }).then((x) => x.json());
1461
+ added = r && r.ok ? add.length : 0;
1462
+ } catch (e) {
1463
+ added = 0;
1464
+ }
1465
+ if (onReloadFriends)
1466
+ onReloadFriends();
1467
+ append(asMsg("them", added ? lang === "zh" ? `\u5DF2\u628A ${added} \u4F4D\u6807\u4E3A\u670D\u52A1\u8282\u70B9\uFF0C\u5B83\u4EEC\u4E0D\u518D\u51FA\u73B0\u5728 \`/friends\` \u4E2D\u3002` : `${added} now count as service peers \u2014 they no longer appear in \`/friends\`.` : lang === "zh" ? "\u5199\u5165\u5931\u8D25\uFF0C\u6CA1\u6709\u4EFB\u4F55\u6539\u52A8\u3002" : "That write failed; nothing changed."));
1468
+ return { added };
1469
+ }, [append, lang, onReloadFriends]);
1385
1470
  const removeFriends = React.useCallback(async (ids) => {
1386
1471
  let removed = 0;
1387
1472
  const failed = [];
@@ -1401,7 +1486,7 @@ ${cands.map((c) => `- ${nameOf(c)}`).join("\n")}`;
1401
1486
  append(asMsg("them", failed.length ? lang === "zh" ? `\u5DF2\u5220\u9664 ${removed} \u4F4D\uFF0C${failed.length} \u4F4D\u5931\u8D25\u3002` : `Removed ${removed}, ${failed.length} failed.` : lang === "zh" ? `\u5DF2\u5220\u9664 ${removed} \u4F4D\u597D\u53CB\u3002` : `Removed ${removed} friend${removed === 1 ? "" : "s"}.`));
1402
1487
  return { removed, failed: failed.length };
1403
1488
  }, [append, lang, onReloadFriends]);
1404
- return { thread, handle, removeFriends };
1489
+ return { thread, handle, removeFriends, adoptServicePeers };
1405
1490
  }
1406
1491
 
1407
1492
  // src/chat.jsx
@@ -2210,7 +2295,7 @@ function DkChatForm({ form, submitted, peer, onSubmit }) {
2210
2295
  return null;
2211
2296
  }), err && /* @__PURE__ */ React.createElement("div", { style: { color: "var(--danger, #ff6b6b)", fontSize: 11.5, fontFamily: "var(--mono)" } }, err));
2212
2297
  }
2213
- function Msg({ m, peer, T, lang, onTheater, onDelete, onCancel, onRetry, onRetryMsg, onReveal, onOpenFile, onCall, onFormSubmit, onNameCard, onAssistantRemove, answered, selMode, selected, onToggleSel, busy, isGroup }) {
2298
+ function Msg({ m, peer, T, lang, onTheater, onDelete, onCancel, onRetry, onRetryMsg, onReveal, onOpenFile, onCall, onFormSubmit, onNameCard, onAssistantRemove, onAssistantAdopt, answered, selMode, selected, onToggleSel, busy, isGroup }) {
2214
2299
  const mine = m.from === "me";
2215
2300
  const nativeMedia = !m.file ? dkNativeMediaFromText(m.text) : null;
2216
2301
  const grp = isGroup && m.dir !== "out" && !m.file && !nativeMedia ? dkGroupParse(m.text) : null;
@@ -2539,7 +2624,7 @@ function Msg({ m, peer, T, lang, onTheater, onDelete, onCancel, onRetry, onRetry
2539
2624
  letterSpacing: -0.1,
2540
2625
  wordBreak: "break-word"
2541
2626
  } }, /* @__PURE__ */ React.createElement(MarkdownText, { text: read.text, onNameCard })));
2542
- })(), read.form && /* @__PURE__ */ React.createElement("div", { style: { marginTop: 6 } }, /* @__PURE__ */ React.createElement(DkChatForm, { form: read.form, submitted, peer, onSubmit: onFormSubmit })), m.assistant && onAssistantRemove && /* @__PURE__ */ React.createElement(AssistantPanel, { m, T, lang, onRemove: onAssistantRemove }), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 4, margin: "3px 3px 0" } }, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 10, color: "var(--faint)" } }, m.time), (() => {
2627
+ })(), read.form && /* @__PURE__ */ React.createElement("div", { style: { marginTop: 6 } }, /* @__PURE__ */ React.createElement(DkChatForm, { form: read.form, submitted, peer, onSubmit: onFormSubmit })), m.assistant && onAssistantRemove && /* @__PURE__ */ React.createElement(AssistantPanel, { m, T, lang, onRemove: onAssistantRemove, onAdopt: onAssistantAdopt }), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 4, margin: "3px 3px 0" } }, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 10, color: "var(--faint)" } }, m.time), (() => {
2543
2628
  const sentDone = mine && m.status && m.status !== "queued" && m.status !== "sending" && m.status !== "failed";
2544
2629
  const via = !mine ? m.via : sentDone ? m.via === "offline" ? "offline" : "online" : null;
2545
2630
  if (!via)
@@ -2584,7 +2669,7 @@ function Msg({ m, peer, T, lang, onTheater, onDelete, onCancel, onRetry, onRetry
2584
2669
  ) : 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)" })))
2585
2670
  );
2586
2671
  }
2587
- function Conversation({ T, peer, lang, peers, onOpenChat, thread: threadProp, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread, hasEarlier, onLoadEarlier, onAssistantRemove, muted, onToggleMute, phone, onBack }) {
2672
+ function Conversation({ T, peer, lang, peers, onOpenChat, thread: threadProp, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread, hasEarlier, onLoadEarlier, onAssistantRemove, onAssistantAdopt, muted, onToggleMute, phone, onBack }) {
2588
2673
  const scrollRef = React.useRef(null);
2589
2674
  const prependRef = React.useRef(null);
2590
2675
  const [earlierBusy, setEarlierBusy] = React.useState(false);
@@ -3017,6 +3102,7 @@ ${peer.address}`
3017
3102
  isGroup,
3018
3103
  onTheater: setTheater,
3019
3104
  onAssistantRemove,
3105
+ onAssistantAdopt,
3020
3106
  onDelete: (id) => doDelete([id]),
3021
3107
  onCancel: doCancel,
3022
3108
  onRetry: doRetry,
@@ -3176,12 +3262,12 @@ function MenuItem({ icon, label, onClick, danger }) {
3176
3262
  function ChatEmpty({ T }) {
3177
3263
  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));
3178
3264
  }
3179
- function ChatTab({ T, lang, peers, requests, activeId, thread, onSelect, onAct, onAdd, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread, hasEarlier, onLoadEarlier, onAssistantRemove, prefillAddr, onPrefillConsumed, muted, onToggleMute, phone, onBack }) {
3265
+ function ChatTab({ T, lang, peers, requests, activeId, thread, onSelect, onAct, onAdd, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread, hasEarlier, onLoadEarlier, onAssistantRemove, onAssistantAdopt, prefillAddr, onPrefillConsumed, muted, onToggleMute, phone, onBack }) {
3180
3266
  const peer = peers.find((p) => p.id === activeId);
3181
3267
  if (phone) {
3182
- return peer ? /* @__PURE__ */ React.createElement(Conversation, { T, peer, lang, peers, onOpenChat: onSelect, thread, onSend, onSendFile, onSendRtcFile, onAlias, onRemove, onOpenNet, onCall, onReloadThread, hasEarlier, onLoadEarlier, onAssistantRemove, 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 });
3268
+ return peer ? /* @__PURE__ */ React.createElement(Conversation, { T, peer, lang, peers, onOpenChat: onSelect, thread, 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, wide: true });
3183
3269
  }
3184
- 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, onAssistantRemove, muted: !!muted && muted.includes(peer.id), onToggleMute }) : /* @__PURE__ */ React.createElement(ChatEmpty, { T }));
3270
+ 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, onAssistantRemove, onAssistantAdopt, muted: !!muted && muted.includes(peer.id), onToggleMute }) : /* @__PURE__ */ React.createElement(ChatEmpty, { T }));
3185
3271
  }
3186
3272
  Object.assign(window, { ChatTab });
3187
3273
 
@@ -6800,7 +6886,7 @@ function DkApp() {
6800
6886
  fontWeight: 600,
6801
6887
  background: tab === n.id ? "var(--accent)" : "transparent",
6802
6888
  color: tab === n.id ? "#fff" : "var(--dim)"
6803
- } }, /* @__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: 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, 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) => {
6889
+ } }, /* @__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: 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 }), tab === "profile" && /* @__PURE__ */ React.createElement(ProfileTab, { T, me, onEdit, t, setTweak, onSetAvatar: (p) => dkApi.setProfileFull(p).then((r) => {
6804
6890
  data.refresh();
6805
6891
  return r;
6806
6892
  }) })), 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.25",
3
+ "version": "0.2.26",
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",