@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.
- package/dist/beagle-ui.js +107 -21
- package/package.json +1 -1
package/dist/beagle-ui.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const BEAGLE_UI_VERSION = "0.2.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1213
|
-
|
|
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
|
|
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