@decentnetwork/beagle-ui 0.2.26 → 0.2.27

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 +244 -38
  2. package/package.json +1 -1
package/dist/beagle-ui.js CHANGED
@@ -1,4 +1,4 @@
1
- export const BEAGLE_UI_VERSION = "0.2.26";
1
+ export const BEAGLE_UI_VERSION = "0.2.27";
2
2
  // src/icons.jsx
3
3
  var ICON_PATHS = {
4
4
  // ---- tab bar (the four must feel like one set) ----
@@ -66,8 +66,8 @@ var ICON_PATHS = {
66
66
  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"/>',
67
67
  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"/>'
68
68
  };
69
- function Icon({ name, size = 24, stroke = 1.75, color = "currentColor", fill = "none", style }) {
70
- const d = ICON_PATHS[name];
69
+ function Icon({ name, size = 24, stroke = 1.75, color = "currentColor", fill = "none", style, svgPath }) {
70
+ const d = svgPath || ICON_PATHS[name];
71
71
  if (!d)
72
72
  return null;
73
73
  return /* @__PURE__ */ React.createElement(
@@ -111,7 +111,10 @@ Object.assign(ICON_PATHS, {
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
 
117
120
  // src/ui-host.jsx
@@ -121,8 +124,16 @@ var hostPrefersRtcFile = false;
121
124
  var hostHasKeyBackup = false;
122
125
  var hostEnsWrite = true;
123
126
  var hostBrandHref = "";
127
+ var hostTabs = null;
128
+ var hostHideApps = null;
129
+ var hostApps = null;
130
+ var hostProfileSections = null;
124
131
  function setUiHost(host) {
125
132
  H = host || {};
133
+ hostTabs = Array.isArray(H.hostTabs) ? H.hostTabs : null;
134
+ hostHideApps = Array.isArray(H.hideApps) ? H.hideApps : null;
135
+ hostApps = Array.isArray(H.hostApps) ? H.hostApps : null;
136
+ hostProfileSections = Array.isArray(H.hostProfileSections) ? H.hostProfileSections : null;
126
137
  hostNeedsWelcome = !!H.hostNeedsWelcome;
127
138
  hostPrefersRtcFile = !!H.hostPrefersRtcFile;
128
139
  hostHasKeyBackup = !!H.hostHasKeyBackup;
@@ -3032,7 +3043,7 @@ function Conversation({ T, peer, lang, peers, onOpenChat, thread: threadProp, on
3032
3043
  // The internal id is not an address anyone can use and not a name
3033
3044
  // anyone chose; saying what this row IS is more use than showing it.
3034
3045
  /* @__PURE__ */ React.createElement("div", { style: { marginTop: 2, fontFamily: "var(--mono)", fontSize: 11.5, color: "var(--faint)" } }, T && T.assistantSub || "Local \xB7 APP")
3035
- ) : 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(
3046
+ ) : 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(
3036
3047
  Mono,
3037
3048
  {
3038
3049
  size: 11.5,
@@ -3067,7 +3078,7 @@ ${peer.address}`
3067
3078
  } }), phone && onSendRtcFile && !peer.app && /* @__PURE__ */ React.createElement(MenuItem, { icon: "file", label: T && T.sendWebrtcFile || "Send file via WebRTC", onClick: () => {
3068
3079
  setMenu(false);
3069
3080
  rtcFileRef.current && rtcFileRef.current.click();
3070
- } }), phone && !peer.app && /* @__PURE__ */ React.createElement(MenuItem, { icon: "network", label: T && T.openNet || "Network view", onClick: () => {
3081
+ } }), phone && !peer.app && onOpenNet && /* @__PURE__ */ React.createElement(MenuItem, { icon: "network", label: T && T.openNet || "Network view", onClick: () => {
3071
3082
  setMenu(false);
3072
3083
  onOpenNet(peer);
3073
3084
  } }), !peer.app && /* @__PURE__ */ React.createElement(MenuItem, { icon: "hash", label: T.alias, onClick: () => {
@@ -4042,7 +4053,7 @@ function DkAvatarSheet({ T, me, zh, onSet, onClose }) {
4042
4053
  return /* @__PURE__ */ React.createElement(DkPunkPicker, { T, onPick: pickPunk, onClose: () => setPicking(false) });
4043
4054
  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)));
4044
4055
  }
4045
- function ProfileTab({ T, me, onEdit, t, setTweak, onSetAvatar }) {
4056
+ function ProfileTab({ T, me, onEdit, t, setTweak, onSetAvatar, sections, sectionProps }) {
4046
4057
  var _a;
4047
4058
  const [avatarSheet, setAvatarSheet] = React.useState(false);
4048
4059
  const [qr, setQr] = React.useState(null);
@@ -4074,12 +4085,33 @@ function ProfileTab({ T, me, onEdit, t, setTweak, onSetAvatar }) {
4074
4085
  },
4075
4086
  style: { width: 16, height: 16, accentColor: "var(--accent)" }
4076
4087
  }
4077
- ), /* @__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) => {
4088
+ ), /* @__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) => {
4078
4089
  if (onEdit)
4079
4090
  onEdit(name, description);
4080
4091
  setEditing(false);
4081
4092
  } }));
4082
4093
  }
4094
+ var DkHostSection = class extends React.Component {
4095
+ constructor(props) {
4096
+ super(props);
4097
+ this.state = { err: null };
4098
+ }
4099
+ static getDerivedStateFromError(err) {
4100
+ return { err };
4101
+ }
4102
+ render() {
4103
+ var _a;
4104
+ const { sec, props, lang } = this.props;
4105
+ if (this.state.err) {
4106
+ 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.");
4107
+ }
4108
+ try {
4109
+ return (_a = sec.render(props)) != null ? _a : null;
4110
+ } catch (e) {
4111
+ return null;
4112
+ }
4113
+ }
4114
+ };
4083
4115
  Object.assign(window, { DkShareSheet, ProfileTab, DkPunkAvatar, DkEnsAvatar, DkPublicProfile });
4084
4116
 
4085
4117
  // src/ui.jsx
@@ -4296,6 +4328,84 @@ function Section({ label, count, trailing, style }) {
4296
4328
  }
4297
4329
  Object.assign(window, { useIsPhone, DkPhoneStyles, DkIdenticon, DkBot, DkAvatar, DkImgAvatar, StatusDot, Mono, shortKey, RouteTag, Tag, Unread, Btn, Section });
4298
4330
 
4331
+ // src/host-tabs.js
4332
+ var BUILTIN_TABS = ["chat", "people", "network", "apps", "profile"];
4333
+ var warned = /* @__PURE__ */ new Set();
4334
+ function warnOnce(what) {
4335
+ if (warned.has(what))
4336
+ return;
4337
+ warned.add(what);
4338
+ try {
4339
+ console.warn(`[beagle-ui] ignoring unknown host tab or app: ${what}`);
4340
+ } catch (e) {
4341
+ }
4342
+ }
4343
+ function pickText(value, lang, fallback = "") {
4344
+ if (typeof value === "string")
4345
+ return value;
4346
+ if (value && typeof value === "object")
4347
+ return value[lang] || value.en || value.zh || fallback;
4348
+ return fallback;
4349
+ }
4350
+ function resolveTabs(hostTabs2) {
4351
+ if (!Array.isArray(hostTabs2) || !hostTabs2.length) {
4352
+ return BUILTIN_TABS.map((id) => ({ id, kind: "builtin" }));
4353
+ }
4354
+ const out = [];
4355
+ const seen = /* @__PURE__ */ new Set();
4356
+ for (const entry of hostTabs2) {
4357
+ if (typeof entry === "string") {
4358
+ if (!BUILTIN_TABS.includes(entry)) {
4359
+ warnOnce(entry);
4360
+ continue;
4361
+ }
4362
+ if (seen.has(entry))
4363
+ continue;
4364
+ seen.add(entry);
4365
+ out.push({ id: entry, kind: "builtin" });
4366
+ continue;
4367
+ }
4368
+ if (entry && typeof entry === "object" && typeof entry.id === "string" && entry.id) {
4369
+ if (typeof entry.render !== "function") {
4370
+ warnOnce(`${entry.id} (no render function)`);
4371
+ continue;
4372
+ }
4373
+ if (BUILTIN_TABS.includes(entry.id)) {
4374
+ warnOnce(`${entry.id} (a built-in id cannot be redefined)`);
4375
+ continue;
4376
+ }
4377
+ if (seen.has(entry.id))
4378
+ continue;
4379
+ seen.add(entry.id);
4380
+ out.push({
4381
+ id: entry.id,
4382
+ kind: "host",
4383
+ label: entry.label,
4384
+ icon: entry.icon,
4385
+ render: entry.render,
4386
+ badge: typeof entry.badge === "function" ? entry.badge : void 0
4387
+ });
4388
+ continue;
4389
+ }
4390
+ warnOnce(String(entry && entry.id ? entry.id : entry));
4391
+ }
4392
+ return out.length ? out : BUILTIN_TABS.map((id) => ({ id, kind: "builtin" }));
4393
+ }
4394
+ function hasTab(tabs, id) {
4395
+ return (tabs != null ? tabs : []).some((t) => t.id === id);
4396
+ }
4397
+ function resolveApps(builtins, hideApps, hostApps2) {
4398
+ const hidden = new Set(Array.isArray(hideApps) ? hideApps : []);
4399
+ const host = (Array.isArray(hostApps2) ? hostApps2 : []).filter((a) => {
4400
+ if (a && typeof a.id === "string" && a.id && typeof a.open === "function")
4401
+ return true;
4402
+ warnOnce(`app ${a && a.id ? a.id : String(a)}`);
4403
+ return false;
4404
+ }).map((a) => ({ ...a, host: true }));
4405
+ const kept = (builtins != null ? builtins : []).filter((a) => !hidden.has(a.id));
4406
+ return host.concat(kept);
4407
+ }
4408
+
4299
4409
  // src/apps.jsx
4300
4410
  var APPS = [
4301
4411
  {
@@ -4336,11 +4446,24 @@ function dkB64Url(obj) {
4336
4446
  const b64 = btoa(unescape(encodeURIComponent(json)));
4337
4447
  return b64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
4338
4448
  }
4339
- function AppsTab({ T, t }) {
4449
+ function AppsTab({ T, t, me, peers, phone, setTab }) {
4340
4450
  const zh = t && t.lang === "zh";
4451
+ const apps = React.useMemo(
4452
+ () => resolveApps(APPS, hostHideApps, hostApps),
4453
+ []
4454
+ );
4341
4455
  const [busy, setBusy] = React.useState(null);
4342
4456
  const [err, setErr] = React.useState(null);
4343
4457
  const open = async (app) => {
4458
+ if (app.host) {
4459
+ setErr(null);
4460
+ try {
4461
+ app.open({ T, t, me, peers, phone, setTab });
4462
+ } catch (e) {
4463
+ setErr(String(e && e.message || e));
4464
+ }
4465
+ return;
4466
+ }
4344
4467
  setBusy(app.id);
4345
4468
  setErr(null);
4346
4469
  const win = window.open("", "_blank");
@@ -4362,7 +4485,7 @@ function AppsTab({ T, t }) {
4362
4485
  setBusy(null);
4363
4486
  }
4364
4487
  };
4365
- 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: {
4488
+ 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: {
4366
4489
  display: "flex",
4367
4490
  alignItems: "center",
4368
4491
  gap: 14,
@@ -4379,7 +4502,7 @@ function AppsTab({ T, t }) {
4379
4502
  display: "flex",
4380
4503
  alignItems: "center",
4381
4504
  justifyContent: "center"
4382
- } }, /* @__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")))));
4505
+ } }, /* @__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")))));
4383
4506
  }
4384
4507
 
4385
4508
  // src/rtc-signaling.jsx
@@ -6384,7 +6507,7 @@ var STR = {
6384
6507
  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"
6385
6508
  }
6386
6509
  };
6387
- function RailBtn({ icon, label, active, soon, onClick }) {
6510
+ function RailBtn({ icon, label, active, soon, onClick, svgPath }) {
6388
6511
  return /* @__PURE__ */ React.createElement("button", { onClick, title: label, style: {
6389
6512
  width: 52,
6390
6513
  height: 52,
@@ -6399,9 +6522,9 @@ function RailBtn({ icon, label, active, soon, onClick }) {
6399
6522
  alignItems: "center",
6400
6523
  justifyContent: "center",
6401
6524
  gap: 3
6402
- } }, /* @__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)" } }));
6525
+ } }, /* @__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)" } }));
6403
6526
  }
6404
- function TabBarBtn({ icon, label, active, badge, onClick }) {
6527
+ function TabBarBtn({ icon, label, active, badge, onClick, svgPath }) {
6405
6528
  return /* @__PURE__ */ React.createElement("button", { onClick, style: {
6406
6529
  flex: 1,
6407
6530
  minWidth: 0,
@@ -6416,9 +6539,36 @@ function TabBarBtn({ icon, label, active, badge, onClick }) {
6416
6539
  justifyContent: "center",
6417
6540
  gap: 3,
6418
6541
  fontFamily: "var(--mono)"
6419
- } }, /* @__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);
6542
+ } }, /* @__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);
6420
6543
  }
6421
6544
  var DK_PEOPLE_TABS = ["here", "recommended", "registered"];
6545
+ var HostTabPane = class extends React.Component {
6546
+ constructor(props) {
6547
+ super(props);
6548
+ this.state = { err: null };
6549
+ }
6550
+ static getDerivedStateFromError(err) {
6551
+ return { err };
6552
+ }
6553
+ componentDidCatch(err) {
6554
+ try {
6555
+ console.error("[beagle-ui] host tab failed", err);
6556
+ } catch (e) {
6557
+ }
6558
+ }
6559
+ render() {
6560
+ var _a;
6561
+ const { tab, props, lang } = this.props;
6562
+ if (this.state.err) {
6563
+ 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.`));
6564
+ }
6565
+ try {
6566
+ return (_a = tab.render(props)) != null ? _a : null;
6567
+ } catch (e) {
6568
+ return null;
6569
+ }
6570
+ }
6571
+ };
6422
6572
  function Brand({ T, children, style }) {
6423
6573
  if (!hostBrandHref)
6424
6574
  return /* @__PURE__ */ React.createElement("span", { style }, children);
@@ -6535,7 +6685,17 @@ function DkUpdateModal({ T, info, onClose }) {
6535
6685
  }
6536
6686
  function DkApp() {
6537
6687
  const [t, setTweak] = useTweaks({ ...DK_DEFAULTS, lang: dkDetectLang() });
6538
- const [tab, setTab] = React.useState(t.startTab || "chat");
6688
+ const tabs = React.useMemo(() => resolveTabs(hostTabs), []);
6689
+ const showPeople = hasTab(tabs, "people");
6690
+ const showNetwork = hasTab(tabs, "network");
6691
+ const showProfile = hasTab(tabs, "profile");
6692
+ const firstTab = tabs[0] ? tabs[0].id === "people" ? "here" : tabs[0].id : "chat";
6693
+ const [tab, setTab] = React.useState(() => {
6694
+ const want = t.startTab || "chat";
6695
+ if (want === "chat" && hasTab(tabs, "chat"))
6696
+ return "chat";
6697
+ return hasTab(tabs, want) || DK_PEOPLE_TABS.includes(want) && showPeople ? want : firstTab;
6698
+ });
6539
6699
  const [activeId, setActiveId] = React.useState(null);
6540
6700
  const data = useDaemonData();
6541
6701
  const me = data.me;
@@ -6570,11 +6730,13 @@ function DkApp() {
6570
6730
  });
6571
6731
  }, []);
6572
6732
  React.useEffect(() => {
6733
+ if (!showNetwork)
6734
+ return void 0;
6573
6735
  refreshBackend();
6574
6736
  const ms = backend && backend.state === "releasing" ? 1500 : 8e3;
6575
6737
  const t2 = setInterval(refreshBackend, ms);
6576
6738
  return () => clearInterval(t2);
6577
- }, [refreshBackend, backend && backend.state]);
6739
+ }, [refreshBackend, backend && backend.state, showNetwork]);
6578
6740
  const armLan = React.useCallback(async () => {
6579
6741
  await dkPost("/api/lan-arm", {});
6580
6742
  refreshBackend();
@@ -6843,29 +7005,61 @@ function DkApp() {
6843
7005
  setTab("chat");
6844
7006
  openConversation(id);
6845
7007
  };
6846
- const onOpenNet = () => setTab("network");
6847
- const nav = [
6848
- { id: "chat", icon: "message", label: T.chat },
6849
- { id: "here", icon: "users", label: T.here },
6850
- { id: "recommended", icon: "sparkles", label: T.recommended },
6851
- { id: "registered", icon: "at", label: T.registered },
6852
- { id: "network", icon: "network", label: T.network },
6853
- { id: "apps", icon: "grid", label: T.apps }
6854
- ];
7008
+ const onOpenNet = showNetwork ? () => setTab("network") : null;
7009
+ const RAIL_FOR = {
7010
+ chat: [{ id: "chat", icon: "message", label: T.chat }],
7011
+ people: [
7012
+ { id: "here", icon: "users", label: T.here },
7013
+ { id: "recommended", icon: "sparkles", label: T.recommended },
7014
+ { id: "registered", icon: "at", label: T.registered }
7015
+ ],
7016
+ network: [{ id: "network", icon: "network", label: T.network }],
7017
+ apps: [{ id: "apps", icon: "grid", label: T.apps }],
7018
+ profile: []
7019
+ };
7020
+ 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) }]);
6855
7021
  const peopleTab = DK_PEOPLE_TABS.includes(tab);
6856
7022
  const lastPeople = React.useRef("here");
6857
7023
  if (peopleTab)
6858
7024
  lastPeople.current = tab;
6859
7025
  const unreadTotal = (peers || []).reduce((n, p) => n + (Number(p.unread) || 0), 0) + (requests || []).length;
6860
- const phoneNav = [
6861
- { id: "chat", icon: "message", label: T.chat, active: tab === "chat", badge: unreadTotal, go: () => setTab("chat") },
6862
- { id: "people", icon: "users", label: T.people, active: peopleTab, go: () => setTab(lastPeople.current) },
6863
- { id: "network", icon: "network", label: T.network, active: tab === "network", go: () => setTab("network") },
6864
- { id: "apps", icon: "grid", label: T.apps, active: tab === "apps", go: () => setTab("apps") },
6865
- { id: "profile", icon: "userRound", label: T.profile, active: tab === "profile", go: () => setTab("profile") }
6866
- ];
7026
+ const NAV_FOR = {
7027
+ chat: { icon: "message", label: T.chat, active: tab === "chat", badge: unreadTotal, go: () => setTab("chat") },
7028
+ people: { icon: "users", label: T.people, active: peopleTab, go: () => setTab(lastPeople.current) },
7029
+ network: { icon: "network", label: T.network, active: tab === "network", go: () => setTab("network") },
7030
+ apps: { icon: "grid", label: T.apps, active: tab === "apps", go: () => setTab("apps") },
7031
+ profile: { icon: "userRound", label: T.profile, active: tab === "profile", go: () => setTab("profile") }
7032
+ };
7033
+ const phoneNav = tabs.map((x) => x.kind === "builtin" ? { id: x.id, ...NAV_FOR[x.id] } : {
7034
+ id: x.id,
7035
+ icon: typeof x.icon === "string" ? x.icon : void 0,
7036
+ svgPath: x.icon && typeof x.icon === "object" ? x.icon.svgPath : void 0,
7037
+ label: pickText(x.label, t.lang, x.id),
7038
+ active: tab === x.id,
7039
+ // A host badge is called on every render, like the unread count beside
7040
+ // it; a throwing one must not take the tab bar down with it.
7041
+ badge: (() => {
7042
+ try {
7043
+ return x.badge ? x.badge() : 0;
7044
+ } catch (e) {
7045
+ return 0;
7046
+ }
7047
+ })(),
7048
+ go: () => setTab(x.id)
7049
+ });
6867
7050
  const inConversation = phone && tab === "chat" && !!activeId && (peers || []).some((p) => p.id === activeId);
6868
- 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: {
7051
+ 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(
7052
+ RailBtn,
7053
+ {
7054
+ key: n.id,
7055
+ icon: typeof n.icon === "string" ? n.icon : void 0,
7056
+ svgPath: n.icon && typeof n.icon === "object" ? n.icon.svgPath : void 0,
7057
+ label: n.label,
7058
+ active: tab === n.id,
7059
+ soon: n.soon,
7060
+ onClick: () => setTab(n.id)
7061
+ }
7062
+ )), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }), showProfile && /* @__PURE__ */ React.createElement("button", { onClick: () => setTab("profile"), title: T.profile, style: {
6869
7063
  padding: 4,
6870
7064
  borderRadius: 12,
6871
7065
  cursor: "pointer",
@@ -6886,10 +7080,22 @@ function DkApp() {
6886
7080
  fontWeight: 600,
6887
7081
  background: tab === n.id ? "var(--accent)" : "transparent",
6888
7082
  color: tab === n.id ? "#fff" : "var(--dim)"
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) => {
6890
- data.refresh();
6891
- return r;
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(
7083
+ } }, /* @__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(
7084
+ ProfileTab,
7085
+ {
7086
+ T,
7087
+ me,
7088
+ onEdit,
7089
+ t,
7090
+ setTweak,
7091
+ onSetAvatar: (p) => dkApi.setProfileFull(p).then((r) => {
7092
+ data.refresh();
7093
+ return r;
7094
+ }),
7095
+ sections: hostProfileSections,
7096
+ sectionProps: { T, t, me, peers, phone, setTab }
7097
+ }
7098
+ )), 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(
6893
7099
  DkLockedOut,
6894
7100
  {
6895
7101
  compact: true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decentnetwork/beagle-ui",
3
- "version": "0.2.26",
3
+ "version": "0.2.27",
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",