@decentnetwork/beagle 0.1.112 → 0.1.113

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 +242 -38
  2. package/package.json +2 -2
@@ -1,4 +1,4 @@
1
- window.__DK_UI_VERSION="0.1.112";
1
+ window.__DK_UI_VERSION="0.1.113";
2
2
  "use strict";
3
3
  (() => {
4
4
  // packages/beagle-ui/dist/beagle-ui.js
@@ -68,8 +68,8 @@ window.__DK_UI_VERSION="0.1.112";
68
68
  speaker: '<path d="M11 5 6 9H3v6h3l5 4V5Z"/><path d="M16 9a3 3 0 0 1 0 6"/><path d="M19 7a6 6 0 0 1 0 10"/>',
69
69
  micFill: '<rect x="9" y="2.5" width="6" height="11.5" rx="3"/><path d="M5.5 11a6.5 6.5 0 0 0 13 0"/><path d="M12 17.5v3.5"/>'
70
70
  };
71
- function Icon({ name, size = 24, stroke = 1.75, color = "currentColor", fill = "none", style }) {
72
- const d = ICON_PATHS[name];
71
+ function Icon({ name, size = 24, stroke = 1.75, color = "currentColor", fill = "none", style, svgPath }) {
72
+ const d = svgPath || ICON_PATHS[name];
73
73
  if (!d)
74
74
  return null;
75
75
  return /* @__PURE__ */ React.createElement(
@@ -111,7 +111,10 @@ window.__DK_UI_VERSION="0.1.112";
111
111
  zap: '<path d="M13 2 4 13h7l-1 9 9-11h-7l1-9Z"/>',
112
112
  clock: '<circle cx="12" cy="12" r="9"/><path d="M12 7v5l3.5 2"/>',
113
113
  bellOff: '<path d="M8.7 3.4A6 6 0 0 1 18 8.5c0 3 .5 5 1.1 6.3M6.1 6.1A6 6 0 0 0 6 8.5c0 6.5-2.5 8-2.5 8H16"/><path d="M13.7 20.5a2 2 0 0 1-3.4 0"/><path d="m3 3 18 18"/>',
114
- plug: '<path d="M9 2v6M15 2v6"/><path d="M7 8h10v3a5 5 0 0 1-10 0V8Z"/><path d="M12 16v6"/>'
114
+ plug: '<path d="M9 2v6M15 2v6"/><path d="M7 8h10v3a5 5 0 0 1-10 0V8Z"/><path d="M12 16v6"/>',
115
+ // A form with a filled-in field: the glyph for "Fill with Beagle" and any
116
+ // other host app that puts your identity into someone else's page.
117
+ form: '<rect x="4" y="3" width="16" height="18" rx="2"/><path d="M8 8h8M8 12h5"/><path d="M8 16h3"/><path d="m14.5 17.5 1.6 1.6 3.4-3.6"/>'
115
118
  });
116
119
  var H = {};
117
120
  var hostNeedsWelcome = false;
@@ -119,8 +122,16 @@ window.__DK_UI_VERSION="0.1.112";
119
122
  var hostHasKeyBackup = false;
120
123
  var hostEnsWrite = true;
121
124
  var hostBrandHref = "";
125
+ var hostTabs = null;
126
+ var hostHideApps = null;
127
+ var hostApps = null;
128
+ var hostProfileSections = null;
122
129
  function setUiHost(host2) {
123
130
  H = host2 || {};
131
+ hostTabs = Array.isArray(H.hostTabs) ? H.hostTabs : null;
132
+ hostHideApps = Array.isArray(H.hideApps) ? H.hideApps : null;
133
+ hostApps = Array.isArray(H.hostApps) ? H.hostApps : null;
134
+ hostProfileSections = Array.isArray(H.hostProfileSections) ? H.hostProfileSections : null;
124
135
  hostNeedsWelcome = !!H.hostNeedsWelcome;
125
136
  hostPrefersRtcFile = !!H.hostPrefersRtcFile;
126
137
  hostHasKeyBackup = !!H.hostHasKeyBackup;
@@ -3018,7 +3029,7 @@ ${lines}`, { assistant: { kind: "adopt", rows: fresh } }));
3018
3029
  // The internal id is not an address anyone can use and not a name
3019
3030
  // anyone chose; saying what this row IS is more use than showing it.
3020
3031
  /* @__PURE__ */ React.createElement("div", { style: { marginTop: 2, fontFamily: "var(--mono)", fontSize: 11.5, color: "var(--faint)" } }, T && T.assistantSub || "Local \xB7 APP")
3021
- ) : phone ? /* @__PURE__ */ React.createElement("div", { style: { marginTop: 2, display: "flex" } }, /* @__PURE__ */ React.createElement(RouteTag, { peer })) : /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 8, marginTop: 2 } }, /* @__PURE__ */ React.createElement(Mono, { size: 11.5, dim: true, copy: peer.userId, title: peer.userId }, shortKey(peer.userId, 10, 6)), /* @__PURE__ */ React.createElement("span", { style: { color: "var(--line)" } }, "\xB7"), /* @__PURE__ */ React.createElement("button", { onClick: () => onOpenNet(peer), title: T && T.openNet || "Network view", style: { background: "none", border: "none", cursor: "pointer", padding: 0, color: "var(--accent)", display: "inline-flex", alignItems: "center" } }, /* @__PURE__ */ React.createElement(Icon, { name: "network", size: 12, stroke: 2 })), /* @__PURE__ */ React.createElement(Mono, { size: 11.5, dim: true, copy: peer.ip, title: peer.ip }, peer.ip), peer.address && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", { style: { color: "var(--line)" } }, "\xB7"), /* @__PURE__ */ React.createElement(
3032
+ ) : phone ? /* @__PURE__ */ React.createElement("div", { style: { marginTop: 2, display: "flex" } }, /* @__PURE__ */ React.createElement(RouteTag, { peer })) : /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 8, marginTop: 2 } }, /* @__PURE__ */ React.createElement(Mono, { size: 11.5, dim: true, copy: peer.userId, title: peer.userId }, shortKey(peer.userId, 10, 6)), onOpenNet && /* @__PURE__ */ React.createElement("span", { style: { color: "var(--line)" } }, "\xB7"), onOpenNet && /* @__PURE__ */ React.createElement("button", { onClick: () => onOpenNet(peer), title: T && T.openNet || "Network view", style: { background: "none", border: "none", cursor: "pointer", padding: 0, color: "var(--accent)", display: "inline-flex", alignItems: "center" } }, /* @__PURE__ */ React.createElement(Icon, { name: "network", size: 12, stroke: 2 })), /* @__PURE__ */ React.createElement(Mono, { size: 11.5, dim: true, copy: peer.ip, title: peer.ip }, peer.ip), peer.address && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", { style: { color: "var(--line)" } }, "\xB7"), /* @__PURE__ */ React.createElement(
3022
3033
  Mono,
3023
3034
  {
3024
3035
  size: 11.5,
@@ -3053,7 +3064,7 @@ ${peer.address}`
3053
3064
  } }), phone && onSendRtcFile && !peer.app && /* @__PURE__ */ React.createElement(MenuItem, { icon: "file", label: T && T.sendWebrtcFile || "Send file via WebRTC", onClick: () => {
3054
3065
  setMenu(false);
3055
3066
  rtcFileRef.current && rtcFileRef.current.click();
3056
- } }), phone && !peer.app && /* @__PURE__ */ React.createElement(MenuItem, { icon: "network", label: T && T.openNet || "Network view", onClick: () => {
3067
+ } }), phone && !peer.app && onOpenNet && /* @__PURE__ */ React.createElement(MenuItem, { icon: "network", label: T && T.openNet || "Network view", onClick: () => {
3057
3068
  setMenu(false);
3058
3069
  onOpenNet(peer);
3059
3070
  } }), !peer.app && /* @__PURE__ */ React.createElement(MenuItem, { icon: "hash", label: T.alias, onClick: () => {
@@ -4026,7 +4037,7 @@ ${peer.address}`
4026
4037
  return /* @__PURE__ */ React.createElement(DkPunkPicker, { T, onPick: pickPunk, onClose: () => setPicking(false) });
4027
4038
  return /* @__PURE__ */ React.createElement("div", { onClick: onClose, style: { position: "fixed", inset: 0, zIndex: 90, background: "color-mix(in oklab, #000, transparent 38%)", display: "flex", alignItems: "center", justifyContent: "center", padding: 16 } }, /* @__PURE__ */ React.createElement("div", { onClick: (e) => e.stopPropagation(), style: { width: 360, maxWidth: "100%", background: "var(--panel)", border: "1px solid var(--line)", borderRadius: 16, padding: 16, display: "flex", flexDirection: "column", gap: 8 } }, /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 10, marginBottom: 4 } }, /* @__PURE__ */ React.createElement(DkAvatar, { peer: { ...me, id: me.userId, agent: false }, size: 40, radius: 10, dot: false }), /* @__PURE__ */ React.createElement("span", { style: { flex: 1, fontFamily: "var(--mono)", fontSize: 13.5, fontWeight: 700, color: "var(--text)" } }, zh ? "\u66F4\u6362\u5934\u50CF" : "Change avatar"), /* @__PURE__ */ React.createElement(Btn, { icon: "x", size: "sm", onClick: onClose })), item("sparkles", zh ? "\u9009\u4E00\u4E2A CryptoPunk" : "Choose a CryptoPunk", () => setPicking(true)), canUpload && item("image", zh ? "\u4E0A\u4F20\u7167\u7247" : "Upload a photo", () => fileRef.current && fileRef.current.click()), has && item("trash", zh ? "\u79FB\u9664\u5934\u50CF" : "Remove avatar", () => save(canUpload ? { punkId: null, avatarDataUrl: null } : { punkId: null }), true), /* @__PURE__ */ React.createElement("input", { ref: fileRef, type: "file", accept: "image/*", onChange: onFile, style: { display: "none" } }), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 11.5, color: "var(--faint)", lineHeight: 1.5, marginTop: 2 } }, zh ? "\u4E0D\u9700\u8981\u6CE8\u518C beagles.eth \u540D\u5B57\u3002\u597D\u53CB\u4F1A\u5728\u4E0B\u6B21\u6536\u5230\u4F60\u7684\u8D44\u6599\u65F6\u770B\u5230\u65B0\u5934\u50CF\u3002" : "No beagles.eth name needed. Friends see it the next time your profile reaches them."), err && /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 11.5, color: "var(--danger)" } }, err)));
4028
4039
  }
4029
- function ProfileTab({ T, me, onEdit, t, setTweak, onSetAvatar }) {
4040
+ function ProfileTab({ T, me, onEdit, t, setTweak, onSetAvatar, sections, sectionProps }) {
4030
4041
  var _a;
4031
4042
  const [avatarSheet, setAvatarSheet] = React.useState(false);
4032
4043
  const [qr, setQr] = React.useState(null);
@@ -4058,12 +4069,33 @@ ${peer.address}`
4058
4069
  },
4059
4070
  style: { width: 16, height: 16, accentColor: "var(--accent)" }
4060
4071
  }
4061
- ), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }, /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 13.5, fontWeight: 600, color: "var(--text)" } }, T && T.autoAcceptLabel || "Auto-accept friend requests"), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 11.5, color: "var(--dim)", marginTop: 2 } }, T && T.autoAcceptSub || "Off: requests wait in the sidebar for your approval.")))), hostHasKeyBackup && /* @__PURE__ */ React.createElement(DkKeyBackup, { t: t || {} }), t && setTweak && /* @__PURE__ */ React.createElement(DkAlertSettings, { t, setTweak }), t && setTweak && /* @__PURE__ */ React.createElement(DkDisplaySettings, { t, setTweak }), /* @__PURE__ */ React.createElement(Card, { label: T.network }, /* @__PURE__ */ React.createElement(FieldRow, { T, label: T.virtualIp, value: me.ip, copy: true }), /* @__PURE__ */ React.createElement(FieldRow, { T, label: T.wireLabel, value: `${me.wire} \xB7 lossless`, mono: false }), /* @__PURE__ */ React.createElement(DkVersionRow, { T, me }), /* @__PURE__ */ React.createElement(DkUpdateCheck, { T })), /* @__PURE__ */ React.createElement(Card, { label: T.dangerZone }, /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 13, padding: "14px 16px" } }, /* @__PURE__ */ React.createElement("div", { style: { width: 34, height: 34, borderRadius: 8, flexShrink: 0, background: "color-mix(in oklab, var(--danger), transparent 86%)", display: "flex", alignItems: "center", justifyContent: "center", color: "var(--danger)" } }, /* @__PURE__ */ React.createElement(Icon, { name: "trash", size: 17, stroke: 2 })), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }, /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 13.5, fontWeight: 600, color: "var(--danger)" } }, T.deleteNode), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12, color: "var(--faint)", marginTop: 1 } }, T.deleteSub)), /* @__PURE__ */ React.createElement(Btn, { tone: "danger", size: "sm" }, T.delete)))), qr && /* @__PURE__ */ React.createElement(DkQrModal, { T, value: qr.value, label: qr.label, onClose: () => setQr(null) }), avatarSheet && /* @__PURE__ */ React.createElement(DkAvatarSheet, { T, me, zh: t && t.lang === "zh", onSet: onSetAvatar, onClose: () => setAvatarSheet(false) }), editing && /* @__PURE__ */ React.createElement(DkEditModal, { T, me, onClose: () => setEditing(false), onSave: (name, description) => {
4072
+ ), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }, /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 13.5, fontWeight: 600, color: "var(--text)" } }, T && T.autoAcceptLabel || "Auto-accept friend requests"), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 11.5, color: "var(--dim)", marginTop: 2 } }, T && T.autoAcceptSub || "Off: requests wait in the sidebar for your approval.")))), hostHasKeyBackup && /* @__PURE__ */ React.createElement(DkKeyBackup, { t: t || {} }), t && setTweak && /* @__PURE__ */ React.createElement(DkAlertSettings, { t, setTweak }), t && setTweak && /* @__PURE__ */ React.createElement(DkDisplaySettings, { t, setTweak }), /* @__PURE__ */ React.createElement(Card, { label: T.network }, /* @__PURE__ */ React.createElement(FieldRow, { T, label: T.virtualIp, value: me.ip, copy: true }), /* @__PURE__ */ React.createElement(FieldRow, { T, label: T.wireLabel, value: `${me.wire} \xB7 lossless`, mono: false }), /* @__PURE__ */ React.createElement(DkVersionRow, { T, me }), /* @__PURE__ */ React.createElement(DkUpdateCheck, { T })), /* @__PURE__ */ React.createElement(Card, { label: T.dangerZone }, /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 13, padding: "14px 16px" } }, /* @__PURE__ */ React.createElement("div", { style: { width: 34, height: 34, borderRadius: 8, flexShrink: 0, background: "color-mix(in oklab, var(--danger), transparent 86%)", display: "flex", alignItems: "center", justifyContent: "center", color: "var(--danger)" } }, /* @__PURE__ */ React.createElement(Icon, { name: "trash", size: 17, stroke: 2 })), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }, /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 13.5, fontWeight: 600, color: "var(--danger)" } }, T.deleteNode), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12, color: "var(--faint)", marginTop: 1 } }, T.deleteSub)), /* @__PURE__ */ React.createElement(Btn, { tone: "danger", size: "sm" }, T.delete)))), Array.isArray(sections) && sections.length > 0 && /* @__PURE__ */ React.createElement("div", { style: { maxWidth: 760, margin: "0 auto", padding: "0 20px 40px", display: "flex", flexDirection: "column", gap: 16 } }, sections.map((sec) => sec && typeof sec.render === "function" ? /* @__PURE__ */ React.createElement(DkHostSection, { key: sec.id, sec, props: sectionProps, lang: t && t.lang }) : null)), qr && /* @__PURE__ */ React.createElement(DkQrModal, { T, value: qr.value, label: qr.label, onClose: () => setQr(null) }), avatarSheet && /* @__PURE__ */ React.createElement(DkAvatarSheet, { T, me, zh: t && t.lang === "zh", onSet: onSetAvatar, onClose: () => setAvatarSheet(false) }), editing && /* @__PURE__ */ React.createElement(DkEditModal, { T, me, onClose: () => setEditing(false), onSave: (name, description) => {
4062
4073
  if (onEdit)
4063
4074
  onEdit(name, description);
4064
4075
  setEditing(false);
4065
4076
  } }));
4066
4077
  }
4078
+ var DkHostSection = class extends React.Component {
4079
+ constructor(props) {
4080
+ super(props);
4081
+ this.state = { err: null };
4082
+ }
4083
+ static getDerivedStateFromError(err) {
4084
+ return { err };
4085
+ }
4086
+ render() {
4087
+ var _a;
4088
+ const { sec, props, lang } = this.props;
4089
+ if (this.state.err) {
4090
+ return /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 12, color: "var(--faint)" } }, lang === "zh" ? "\u8FD9\u4E2A\u9644\u52A0\u677F\u5757\u51FA\u9519\u4E86\u3002" : "This host section failed to render.");
4091
+ }
4092
+ try {
4093
+ return (_a = sec.render(props)) != null ? _a : null;
4094
+ } catch (e) {
4095
+ return null;
4096
+ }
4097
+ }
4098
+ };
4067
4099
  Object.assign(window, { DkShareSheet, ProfileTab, DkPunkAvatar, DkEnsAvatar, DkPublicProfile });
4068
4100
  var DK_PHONE_MQ = "(max-width: 700px)";
4069
4101
  function useIsPhone() {
@@ -4277,6 +4309,82 @@ ${peer.address}`
4277
4309
  return /* @__PURE__ */ React.createElement("div", { style: { display: "flex", alignItems: "center", gap: 8, padding: "0 2px", ...style } }, /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 11, fontWeight: 700, letterSpacing: 1, textTransform: "uppercase", color: "var(--faint)" } }, label), count != null && /* @__PURE__ */ React.createElement("span", { style: { fontFamily: "var(--mono)", fontSize: 11, fontWeight: 700, color: "var(--faint)" } }, count), /* @__PURE__ */ React.createElement("div", { style: { flex: 1, height: 1, background: "var(--line)" } }), trailing);
4278
4310
  }
4279
4311
  Object.assign(window, { useIsPhone, DkPhoneStyles, DkIdenticon, DkBot, DkAvatar, DkImgAvatar, StatusDot, Mono, shortKey, RouteTag, Tag, Unread, Btn, Section });
4312
+ var BUILTIN_TABS = ["chat", "people", "network", "apps", "profile"];
4313
+ var warned = /* @__PURE__ */ new Set();
4314
+ function warnOnce(what) {
4315
+ if (warned.has(what))
4316
+ return;
4317
+ warned.add(what);
4318
+ try {
4319
+ console.warn(`[beagle-ui] ignoring unknown host tab or app: ${what}`);
4320
+ } catch (e) {
4321
+ }
4322
+ }
4323
+ function pickText(value, lang, fallback = "") {
4324
+ if (typeof value === "string")
4325
+ return value;
4326
+ if (value && typeof value === "object")
4327
+ return value[lang] || value.en || value.zh || fallback;
4328
+ return fallback;
4329
+ }
4330
+ function resolveTabs(hostTabs2) {
4331
+ if (!Array.isArray(hostTabs2) || !hostTabs2.length) {
4332
+ return BUILTIN_TABS.map((id) => ({ id, kind: "builtin" }));
4333
+ }
4334
+ const out = [];
4335
+ const seen = /* @__PURE__ */ new Set();
4336
+ for (const entry of hostTabs2) {
4337
+ if (typeof entry === "string") {
4338
+ if (!BUILTIN_TABS.includes(entry)) {
4339
+ warnOnce(entry);
4340
+ continue;
4341
+ }
4342
+ if (seen.has(entry))
4343
+ continue;
4344
+ seen.add(entry);
4345
+ out.push({ id: entry, kind: "builtin" });
4346
+ continue;
4347
+ }
4348
+ if (entry && typeof entry === "object" && typeof entry.id === "string" && entry.id) {
4349
+ if (typeof entry.render !== "function") {
4350
+ warnOnce(`${entry.id} (no render function)`);
4351
+ continue;
4352
+ }
4353
+ if (BUILTIN_TABS.includes(entry.id)) {
4354
+ warnOnce(`${entry.id} (a built-in id cannot be redefined)`);
4355
+ continue;
4356
+ }
4357
+ if (seen.has(entry.id))
4358
+ continue;
4359
+ seen.add(entry.id);
4360
+ out.push({
4361
+ id: entry.id,
4362
+ kind: "host",
4363
+ label: entry.label,
4364
+ icon: entry.icon,
4365
+ render: entry.render,
4366
+ badge: typeof entry.badge === "function" ? entry.badge : void 0
4367
+ });
4368
+ continue;
4369
+ }
4370
+ warnOnce(String(entry && entry.id ? entry.id : entry));
4371
+ }
4372
+ return out.length ? out : BUILTIN_TABS.map((id) => ({ id, kind: "builtin" }));
4373
+ }
4374
+ function hasTab(tabs, id) {
4375
+ return (tabs != null ? tabs : []).some((t) => t.id === id);
4376
+ }
4377
+ function resolveApps(builtins, hideApps, hostApps2) {
4378
+ const hidden = new Set(Array.isArray(hideApps) ? hideApps : []);
4379
+ const host2 = (Array.isArray(hostApps2) ? hostApps2 : []).filter((a) => {
4380
+ if (a && typeof a.id === "string" && a.id && typeof a.open === "function")
4381
+ return true;
4382
+ warnOnce(`app ${a && a.id ? a.id : String(a)}`);
4383
+ return false;
4384
+ }).map((a) => ({ ...a, host: true }));
4385
+ const kept = (builtins != null ? builtins : []).filter((a) => !hidden.has(a.id));
4386
+ return host2.concat(kept);
4387
+ }
4280
4388
  var APPS = [
4281
4389
  {
4282
4390
  id: "meet",
@@ -4316,11 +4424,24 @@ ${peer.address}`
4316
4424
  const b64 = btoa(unescape(encodeURIComponent(json)));
4317
4425
  return b64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
4318
4426
  }
4319
- function AppsTab({ T, t }) {
4427
+ function AppsTab({ T, t, me, peers, phone, setTab }) {
4320
4428
  const zh = t && t.lang === "zh";
4429
+ const apps = React.useMemo(
4430
+ () => resolveApps(APPS, hostHideApps, hostApps),
4431
+ []
4432
+ );
4321
4433
  const [busy, setBusy] = React.useState(null);
4322
4434
  const [err, setErr] = React.useState(null);
4323
4435
  const open = async (app) => {
4436
+ if (app.host) {
4437
+ setErr(null);
4438
+ try {
4439
+ app.open({ T, t, me, peers, phone, setTab });
4440
+ } catch (e) {
4441
+ setErr(String(e && e.message || e));
4442
+ }
4443
+ return;
4444
+ }
4324
4445
  setBusy(app.id);
4325
4446
  setErr(null);
4326
4447
  const win = window.open("", "_blank");
@@ -4342,7 +4463,7 @@ ${peer.address}`
4342
4463
  setBusy(null);
4343
4464
  }
4344
4465
  };
4345
- return /* @__PURE__ */ React.createElement("div", { style: { flex: 1, overflow: "auto", background: "var(--bg)" } }, /* @__PURE__ */ React.createElement("div", { style: { maxWidth: 760, margin: "0 auto", padding: "24px 28px 60px", display: "flex", flexDirection: "column", gap: 16 } }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 11, fontWeight: 700, letterSpacing: 0.6, textTransform: "uppercase", color: "var(--faint)" } }, zh ? "\u5E94\u7528" : "Apps"), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12.5, color: "var(--faint)", marginTop: 6, lineHeight: 1.6 } }, zh ? "\u8FD9\u4E9B\u5E94\u7528\u63A5\u53D7 Beagle \u8EAB\u4EFD\u3002\u4ECE\u8FD9\u91CC\u6253\u5F00\u4E0D\u9700\u8981\u518D\u767B\u5F55\u4E00\u6B21 \u2014\u2014 \u4F1A\u5E26\u4E0A\u4E00\u4E2A\u4E24\u5206\u949F\u5185\u6709\u6548\u7684\u7B7E\u540D\u3002" : "These accept a Beagle identity. Opening one from here does not ask you to sign in again \u2014 it carries a signature that is good for two minutes.")), err && /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 11.5, color: "#f85149" } }, err), APPS.map((app) => /* @__PURE__ */ React.createElement("div", { key: app.id, style: {
4466
+ return /* @__PURE__ */ React.createElement("div", { style: { flex: 1, overflow: "auto", background: "var(--bg)" } }, /* @__PURE__ */ React.createElement("div", { style: { maxWidth: 760, margin: "0 auto", padding: "24px 28px 60px", display: "flex", flexDirection: "column", gap: 16 } }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 11, fontWeight: 700, letterSpacing: 0.6, textTransform: "uppercase", color: "var(--faint)" } }, zh ? "\u5E94\u7528" : "Apps"), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12.5, color: "var(--faint)", marginTop: 6, lineHeight: 1.6 } }, zh ? "\u8FD9\u4E9B\u5E94\u7528\u63A5\u53D7 Beagle \u8EAB\u4EFD\u3002\u4ECE\u8FD9\u91CC\u6253\u5F00\u4E0D\u9700\u8981\u518D\u767B\u5F55\u4E00\u6B21 \u2014\u2014 \u4F1A\u5E26\u4E0A\u4E00\u4E2A\u4E24\u5206\u949F\u5185\u6709\u6548\u7684\u7B7E\u540D\u3002" : "These accept a Beagle identity. Opening one from here does not ask you to sign in again \u2014 it carries a signature that is good for two minutes.")), err && /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 11.5, color: "#f85149" } }, err), apps.map((app) => /* @__PURE__ */ React.createElement("div", { key: app.id, style: {
4346
4467
  display: "flex",
4347
4468
  alignItems: "center",
4348
4469
  gap: 14,
@@ -4359,7 +4480,7 @@ ${peer.address}`
4359
4480
  display: "flex",
4360
4481
  alignItems: "center",
4361
4482
  justifyContent: "center"
4362
- } }, /* @__PURE__ */ React.createElement(Icon, { name: app.icon, size: 19, color: "var(--accent)", stroke: 1.8 })), /* @__PURE__ */ React.createElement("div", { style: { flex: 1, minWidth: 0 } }, /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 14, fontWeight: 600, color: "var(--text)" } }, app.name), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12.5, color: "var(--faint)", marginTop: 2 } }, zh ? app.blurb.zh : app.blurb.en), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 11, color: "var(--dim)", marginTop: 3 } }, app.origin.replace(/^https?:\/\//, ""))), /* @__PURE__ */ React.createElement(Btn, { tone: "accent", size: "sm", onClick: busy ? void 0 : () => open(app) }, busy === app.id ? zh ? "\u6253\u5F00\u4E2D\u2026" : "opening\u2026" : zh ? "\u6253\u5F00" : "Open")))));
4483
+ } }, /* @__PURE__ */ React.createElement(Icon, { name: app.icon, size: 19, color: "var(--accent)", stroke: 1.8 })), /* @__PURE__ */ React.createElement("div", { style: { flex: 1, minWidth: 0 } }, /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 14, fontWeight: 600, color: "var(--text)" } }, pickText(app.name, t && t.lang, app.id)), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--ui)", fontSize: 12.5, color: "var(--faint)", marginTop: 2 } }, pickText(app.blurb, t && t.lang, "")), /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 11, color: "var(--dim)", marginTop: 3 } }, app.host ? zh ? "\u672C\u673A\u5E94\u7528" : "runs in this app" : String(app.origin || "").replace(/^https?:\/\//, ""))), /* @__PURE__ */ React.createElement(Btn, { tone: "accent", size: "sm", onClick: busy ? void 0 : () => open(app) }, busy === app.id ? zh ? "\u6253\u5F00\u4E2D\u2026" : "opening\u2026" : zh ? "\u6253\u5F00" : "Open")))));
4363
4484
  }
4364
4485
  var DK_FALLBACK_ICE_SERVERS = [
4365
4486
  { urls: "stun:stun.l.google.com:19302" },
@@ -6348,7 +6469,7 @@ ${peer.address}`
6348
6469
  meetingDesc: "\u8DE8\u670D\u52A1\u5668\u521B\u5EFA\u97F3\u89C6\u9891\u623F\u95F4\uFF0C\u901A\u8FC7\u865A\u62DF IP \u9080\u8BF7\u597D\u53CB\u3002\u7FA4\u7EC4\u901A\u8BDD\u3001\u5C4F\u5E55\u5171\u4EAB\u4E0E\u5171\u4EAB\u51FA\u53E3\u8DEF\u7531\u5C06\u5728\u4E0B\u4E2A\u7248\u672C\u4E0A\u7EBF\u3002"
6349
6470
  }
6350
6471
  };
6351
- function RailBtn({ icon, label, active, soon, onClick }) {
6472
+ function RailBtn({ icon, label, active, soon, onClick, svgPath }) {
6352
6473
  return /* @__PURE__ */ React.createElement("button", { onClick, title: label, style: {
6353
6474
  width: 52,
6354
6475
  height: 52,
@@ -6363,9 +6484,9 @@ ${peer.address}`
6363
6484
  alignItems: "center",
6364
6485
  justifyContent: "center",
6365
6486
  gap: 3
6366
- } }, /* @__PURE__ */ React.createElement(Icon, { name: icon, size: 20, stroke: active ? 2.1 : 1.8, color: active ? "var(--accent)" : "var(--dim)" }), /* @__PURE__ */ React.createElement("span", { style: { fontSize: 9, fontWeight: 600, letterSpacing: 0.2, color: active ? "var(--accent)" : "var(--faint)" } }, label), soon && /* @__PURE__ */ React.createElement("span", { style: { position: "absolute", top: 5, right: 6, width: 6, height: 6, borderRadius: 999, background: "var(--warn)" } }));
6487
+ } }, /* @__PURE__ */ React.createElement(Icon, { name: icon, svgPath, size: 20, stroke: active ? 2.1 : 1.8, color: active ? "var(--accent)" : "var(--dim)" }), /* @__PURE__ */ React.createElement("span", { style: { fontSize: 9, fontWeight: 600, letterSpacing: 0.2, color: active ? "var(--accent)" : "var(--faint)" } }, label), soon && /* @__PURE__ */ React.createElement("span", { style: { position: "absolute", top: 5, right: 6, width: 6, height: 6, borderRadius: 999, background: "var(--warn)" } }));
6367
6488
  }
6368
- function TabBarBtn({ icon, label, active, badge, onClick }) {
6489
+ function TabBarBtn({ icon, label, active, badge, onClick, svgPath }) {
6369
6490
  return /* @__PURE__ */ React.createElement("button", { onClick, style: {
6370
6491
  flex: 1,
6371
6492
  minWidth: 0,
@@ -6380,9 +6501,36 @@ ${peer.address}`
6380
6501
  justifyContent: "center",
6381
6502
  gap: 3,
6382
6503
  fontFamily: "var(--mono)"
6383
- } }, /* @__PURE__ */ React.createElement(Icon, { name: icon, size: 22, stroke: active ? 2.1 : 1.8, color: active ? "var(--accent)" : "var(--dim)" }), /* @__PURE__ */ React.createElement("span", { style: { fontSize: 10, fontWeight: 600, color: active ? "var(--accent)" : "var(--faint)", whiteSpace: "nowrap" } }, label), badge ? /* @__PURE__ */ React.createElement("span", { style: { position: "absolute", top: 4, left: "50%", marginLeft: 6 } }, /* @__PURE__ */ React.createElement(Unread, { n: badge })) : null);
6504
+ } }, /* @__PURE__ */ React.createElement(Icon, { name: icon, svgPath, size: 22, stroke: active ? 2.1 : 1.8, color: active ? "var(--accent)" : "var(--dim)" }), /* @__PURE__ */ React.createElement("span", { style: { fontSize: 10, fontWeight: 600, color: active ? "var(--accent)" : "var(--faint)", whiteSpace: "nowrap" } }, label), badge ? /* @__PURE__ */ React.createElement("span", { style: { position: "absolute", top: 4, left: "50%", marginLeft: 6 } }, /* @__PURE__ */ React.createElement(Unread, { n: badge })) : null);
6384
6505
  }
6385
6506
  var DK_PEOPLE_TABS = ["here", "recommended", "registered"];
6507
+ var HostTabPane = class extends React.Component {
6508
+ constructor(props) {
6509
+ super(props);
6510
+ this.state = { err: null };
6511
+ }
6512
+ static getDerivedStateFromError(err) {
6513
+ return { err };
6514
+ }
6515
+ componentDidCatch(err) {
6516
+ try {
6517
+ console.error("[beagle-ui] host tab failed", err);
6518
+ } catch (e) {
6519
+ }
6520
+ }
6521
+ render() {
6522
+ var _a;
6523
+ const { tab, props, lang } = this.props;
6524
+ if (this.state.err) {
6525
+ return /* @__PURE__ */ React.createElement("div", { style: { flex: 1, display: "flex", alignItems: "center", justifyContent: "center", padding: 24, textAlign: "center" } }, /* @__PURE__ */ React.createElement("div", { style: { fontFamily: "var(--mono)", fontSize: 12.5, color: "var(--faint)", maxWidth: 420 } }, lang === "zh" ? `"${pickText(tab.label, lang, tab.id)}" \u8FD9\u4E2A\u6807\u7B7E\u9875\u51FA\u9519\u4E86\u3002\u5176\u5B83\u6807\u7B7E\u9875\u4E0D\u53D7\u5F71\u54CD\u3002` : `The "${pickText(tab.label, lang, tab.id)}" tab failed to render. The rest of the app is unaffected.`));
6526
+ }
6527
+ try {
6528
+ return (_a = tab.render(props)) != null ? _a : null;
6529
+ } catch (e) {
6530
+ return null;
6531
+ }
6532
+ }
6533
+ };
6386
6534
  function Brand({ T, children, style }) {
6387
6535
  if (!hostBrandHref)
6388
6536
  return /* @__PURE__ */ React.createElement("span", { style }, children);
@@ -6499,7 +6647,17 @@ ${peer.address}`
6499
6647
  }
6500
6648
  function DkApp() {
6501
6649
  const [t, setTweak] = useTweaks({ ...DK_DEFAULTS, lang: dkDetectLang() });
6502
- const [tab, setTab] = React.useState(t.startTab || "chat");
6650
+ const tabs = React.useMemo(() => resolveTabs(hostTabs), []);
6651
+ const showPeople = hasTab(tabs, "people");
6652
+ const showNetwork = hasTab(tabs, "network");
6653
+ const showProfile = hasTab(tabs, "profile");
6654
+ const firstTab = tabs[0] ? tabs[0].id === "people" ? "here" : tabs[0].id : "chat";
6655
+ const [tab, setTab] = React.useState(() => {
6656
+ const want = t.startTab || "chat";
6657
+ if (want === "chat" && hasTab(tabs, "chat"))
6658
+ return "chat";
6659
+ return hasTab(tabs, want) || DK_PEOPLE_TABS.includes(want) && showPeople ? want : firstTab;
6660
+ });
6503
6661
  const [activeId, setActiveId] = React.useState(null);
6504
6662
  const data = useDaemonData();
6505
6663
  const me = data.me;
@@ -6534,11 +6692,13 @@ ${peer.address}`
6534
6692
  });
6535
6693
  }, []);
6536
6694
  React.useEffect(() => {
6695
+ if (!showNetwork)
6696
+ return void 0;
6537
6697
  refreshBackend();
6538
6698
  const ms = backend && backend.state === "releasing" ? 1500 : 8e3;
6539
6699
  const t2 = setInterval(refreshBackend, ms);
6540
6700
  return () => clearInterval(t2);
6541
- }, [refreshBackend, backend && backend.state]);
6701
+ }, [refreshBackend, backend && backend.state, showNetwork]);
6542
6702
  const armLan = React.useCallback(async () => {
6543
6703
  await dkPost("/api/lan-arm", {});
6544
6704
  refreshBackend();
@@ -6807,29 +6967,61 @@ ${peer.address}`
6807
6967
  setTab("chat");
6808
6968
  openConversation(id);
6809
6969
  };
6810
- const onOpenNet = () => setTab("network");
6811
- const nav = [
6812
- { id: "chat", icon: "message", label: T.chat },
6813
- { id: "here", icon: "users", label: T.here },
6814
- { id: "recommended", icon: "sparkles", label: T.recommended },
6815
- { id: "registered", icon: "at", label: T.registered },
6816
- { id: "network", icon: "network", label: T.network },
6817
- { id: "apps", icon: "grid", label: T.apps }
6818
- ];
6970
+ const onOpenNet = showNetwork ? () => setTab("network") : null;
6971
+ const RAIL_FOR = {
6972
+ chat: [{ id: "chat", icon: "message", label: T.chat }],
6973
+ people: [
6974
+ { id: "here", icon: "users", label: T.here },
6975
+ { id: "recommended", icon: "sparkles", label: T.recommended },
6976
+ { id: "registered", icon: "at", label: T.registered }
6977
+ ],
6978
+ network: [{ id: "network", icon: "network", label: T.network }],
6979
+ apps: [{ id: "apps", icon: "grid", label: T.apps }],
6980
+ profile: []
6981
+ };
6982
+ const nav = tabs.flatMap((x) => x.kind === "builtin" ? RAIL_FOR[x.id] || [] : [{ id: x.id, icon: x.icon, label: pickText(x.label, t.lang, x.id) }]);
6819
6983
  const peopleTab = DK_PEOPLE_TABS.includes(tab);
6820
6984
  const lastPeople = React.useRef("here");
6821
6985
  if (peopleTab)
6822
6986
  lastPeople.current = tab;
6823
6987
  const unreadTotal = (peers || []).reduce((n, p) => n + (Number(p.unread) || 0), 0) + (requests || []).length;
6824
- const phoneNav = [
6825
- { id: "chat", icon: "message", label: T.chat, active: tab === "chat", badge: unreadTotal, go: () => setTab("chat") },
6826
- { id: "people", icon: "users", label: T.people, active: peopleTab, go: () => setTab(lastPeople.current) },
6827
- { id: "network", icon: "network", label: T.network, active: tab === "network", go: () => setTab("network") },
6828
- { id: "apps", icon: "grid", label: T.apps, active: tab === "apps", go: () => setTab("apps") },
6829
- { id: "profile", icon: "userRound", label: T.profile, active: tab === "profile", go: () => setTab("profile") }
6830
- ];
6988
+ const NAV_FOR = {
6989
+ chat: { icon: "message", label: T.chat, active: tab === "chat", badge: unreadTotal, go: () => setTab("chat") },
6990
+ people: { icon: "users", label: T.people, active: peopleTab, go: () => setTab(lastPeople.current) },
6991
+ network: { icon: "network", label: T.network, active: tab === "network", go: () => setTab("network") },
6992
+ apps: { icon: "grid", label: T.apps, active: tab === "apps", go: () => setTab("apps") },
6993
+ profile: { icon: "userRound", label: T.profile, active: tab === "profile", go: () => setTab("profile") }
6994
+ };
6995
+ const phoneNav = tabs.map((x) => x.kind === "builtin" ? { id: x.id, ...NAV_FOR[x.id] } : {
6996
+ id: x.id,
6997
+ icon: typeof x.icon === "string" ? x.icon : void 0,
6998
+ svgPath: x.icon && typeof x.icon === "object" ? x.icon.svgPath : void 0,
6999
+ label: pickText(x.label, t.lang, x.id),
7000
+ active: tab === x.id,
7001
+ // A host badge is called on every render, like the unread count beside
7002
+ // it; a throwing one must not take the tab bar down with it.
7003
+ badge: (() => {
7004
+ try {
7005
+ return x.badge ? x.badge() : 0;
7006
+ } catch (e) {
7007
+ return 0;
7008
+ }
7009
+ })(),
7010
+ go: () => setTab(x.id)
7011
+ });
6831
7012
  const inConversation = phone && tab === "chat" && !!activeId && (peers || []).some((p) => p.id === activeId);
6832
- return /* @__PURE__ */ React.createElement("div", { style: { ...vars, "--row-pad": rowPad, position: "fixed", inset: 0, display: "flex", flexDirection: phone ? "column" : "row", background: "var(--bg)", color: "var(--text)", fontFamily: "var(--ui)" } }, /* @__PURE__ */ React.createElement(DkPhoneStyles, null), !phone && /* @__PURE__ */ React.createElement("div", { style: { width: 68, flexShrink: 0, borderRight: "1px solid var(--line)", background: "var(--rail)", display: "flex", flexDirection: "column", alignItems: "center", padding: "14px 0", gap: 8 } }, /* @__PURE__ */ React.createElement(Brand, { T, style: { width: 38, height: 38, borderRadius: 10, background: "var(--accent)", display: "flex", alignItems: "center", justifyContent: "center", marginBottom: 8, flexShrink: 0 } }, /* @__PURE__ */ React.createElement(Icon, { name: "terminal", size: 20, color: "#fff", stroke: 2.2 })), nav.map((n) => /* @__PURE__ */ React.createElement(RailBtn, { key: n.id, icon: n.icon, label: n.label, active: tab === n.id, soon: n.soon, onClick: () => setTab(n.id) })), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }), /* @__PURE__ */ React.createElement("button", { onClick: () => setTab("profile"), title: T.profile, style: {
7013
+ return /* @__PURE__ */ React.createElement("div", { style: { ...vars, "--row-pad": rowPad, position: "fixed", inset: 0, display: "flex", flexDirection: phone ? "column" : "row", background: "var(--bg)", color: "var(--text)", fontFamily: "var(--ui)" } }, /* @__PURE__ */ React.createElement(DkPhoneStyles, null), !phone && /* @__PURE__ */ React.createElement("div", { style: { width: 68, flexShrink: 0, borderRight: "1px solid var(--line)", background: "var(--rail)", display: "flex", flexDirection: "column", alignItems: "center", padding: "14px 0", gap: 8 } }, /* @__PURE__ */ React.createElement(Brand, { T, style: { width: 38, height: 38, borderRadius: 10, background: "var(--accent)", display: "flex", alignItems: "center", justifyContent: "center", marginBottom: 8, flexShrink: 0 } }, /* @__PURE__ */ React.createElement(Icon, { name: "terminal", size: 20, color: "#fff", stroke: 2.2 })), nav.map((n) => /* @__PURE__ */ React.createElement(
7014
+ RailBtn,
7015
+ {
7016
+ key: n.id,
7017
+ icon: typeof n.icon === "string" ? n.icon : void 0,
7018
+ svgPath: n.icon && typeof n.icon === "object" ? n.icon.svgPath : void 0,
7019
+ label: n.label,
7020
+ active: tab === n.id,
7021
+ soon: n.soon,
7022
+ onClick: () => setTab(n.id)
7023
+ }
7024
+ )), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }), showProfile && /* @__PURE__ */ React.createElement("button", { onClick: () => setTab("profile"), title: T.profile, style: {
6833
7025
  padding: 4,
6834
7026
  borderRadius: 12,
6835
7027
  cursor: "pointer",
@@ -6850,10 +7042,22 @@ ${peer.address}`
6850
7042
  fontWeight: 600,
6851
7043
  background: tab === n.id ? "var(--accent)" : "transparent",
6852
7044
  color: tab === n.id ? "#fff" : "var(--dim)"
6853
- } }, /* @__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) => {
6854
- data.refresh();
6855
- return r;
6856
- }) })), 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(
7045
+ } }, /* @__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, me, peers, phone, setTab }), tabs.map((x) => x.kind === "host" && tab === x.id ? /* @__PURE__ */ React.createElement(HostTabPane, { key: x.id, tab: x, props: { T, t, me, peers, phone, setTab }, lang: t.lang }) : null), tab === "profile" && /* @__PURE__ */ React.createElement(
7046
+ ProfileTab,
7047
+ {
7048
+ T,
7049
+ me,
7050
+ onEdit,
7051
+ t,
7052
+ setTweak,
7053
+ onSetAvatar: (p) => dkApi.setProfileFull(p).then((r) => {
7054
+ data.refresh();
7055
+ return r;
7056
+ }),
7057
+ sections: hostProfileSections,
7058
+ sectionProps: { T, t, me, peers, phone, setTab }
7059
+ }
7060
+ )), 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, svgPath: n.svgPath, label: n.label, active: n.active, badge: n.badge, onClick: n.go }))), data.locked && /* @__PURE__ */ React.createElement(
6857
7061
  DkLockedOut,
6858
7062
  {
6859
7063
  compact: true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decentnetwork/beagle",
3
- "version": "0.1.112",
3
+ "version": "0.1.113",
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.26",
38
+ "@decentnetwork/beagle-ui": "^0.2.27",
39
39
  "@types/js-yaml": "^4.0.9",
40
40
  "@types/node": "^20.11.0",
41
41
  "@types/yargs": "^17.0.32",