soulnet-dsh 0.1.3 → 0.1.4

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/lib/client.js CHANGED
@@ -600,11 +600,11 @@ window.__ModuleLoader__.load({
600
600
  /** Browser-side settle guarantee: no API call may hang past this (a request lost to a
601
601
  * server restart would otherwise leave UI loading states frozen forever). */
602
602
  const CALL_TIMEOUT_MS = 3e4;
603
- async function call(route, body) {
603
+ async function call(route, body, timeoutMs = CALL_TIMEOUT_MS) {
604
604
  const ctl = new AbortController();
605
605
  const timer = setTimeout(() => {
606
606
  ctl.abort();
607
- }, CALL_TIMEOUT_MS);
607
+ }, timeoutMs);
608
608
  let response;
609
609
  try {
610
610
  response = await fetch(`${API_BASE}${route}`, body === void 0 && route === "state" ? {
@@ -621,7 +621,7 @@ window.__ModuleLoader__.load({
621
621
  signal: ctl.signal
622
622
  });
623
623
  } catch (e) {
624
- if (e instanceof DOMException && e.name === "AbortError") throw new ApiError(`${route}: no answer within ${CALL_TIMEOUT_MS / 1e3}s (server busy or restarting)`, -32603, 0);
624
+ if (e instanceof DOMException && e.name === "AbortError") throw new ApiError(`${route}: no answer within ${timeoutMs / 1e3}s (server busy or restarting)`, -32603, 0);
625
625
  throw e;
626
626
  } finally {
627
627
  clearTimeout(timer);
@@ -639,6 +639,13 @@ window.__ModuleLoader__.load({
639
639
  }
640
640
  const api = {
641
641
  state: () => call("state"),
642
+ /** Self-upgrade: is a newer soulnet-dsh published? */
643
+ upgradeCheck: () => call("upgrade.check", {}),
644
+ /** Self-upgrade: install `soulnet-dsh@version` (pnpm may take minutes — long timeout). */
645
+ upgradeRun: (version, registry) => call("upgrade.run", {
646
+ version,
647
+ ...registry === void 0 ? {} : { registry }
648
+ }, 6e5),
642
649
  createIdentity: (name) => call("identity.create", { name }),
643
650
  parseCard: (uri) => call("card.parse", { uri }),
644
651
  addFriend: (cardUri, note) => call("friends.add", {
@@ -2366,6 +2373,19 @@ window.__ModuleLoader__.load({
2366
2373
  "settings.config.home": "Data directory",
2367
2374
  "settings.config.hint": "Changes apply the next time dsh starts (the peer process is spawned with them). Empty path = PATH lookup, then the plugin's bin/; empty directory = $SOULNET_HOME, then ~/.soulnet.",
2368
2375
  "settings.config.unavailable": "Settings are editable from a local (loopback) browser only.",
2376
+ "settings.upgrade": "Version & updates",
2377
+ "settings.upgrade.badge": "update",
2378
+ "settings.upgrade.current": "Current version",
2379
+ "settings.upgrade.check": "Check for updates",
2380
+ "settings.upgrade.checking": "Checking…",
2381
+ "settings.upgrade.uptodate": "v{version} is the latest published version.",
2382
+ "settings.upgrade.available": "New version v{latest} is available (you run v{current}).",
2383
+ "settings.upgrade.do": "Upgrade to v{version}",
2384
+ "settings.upgrade.installing": "Installing v{version}… (pnpm in the dsh profile; this can take a minute)",
2385
+ "settings.upgrade.restarting": "Installed. Restarting dsh — this page reloads by itself once it is back.",
2386
+ "settings.upgrade.manual": "Installed v{version}. Refresh this page to load the new interface; the host half takes effect only after dsh is restarted.",
2387
+ "settings.upgrade.failed": "Upgrade failed ({message}).",
2388
+ "settings.upgrade.hint": "Upgrades install soulnet-dsh from the npm registry (npmjs, falling back to npmmirror) into the dsh profile.",
2369
2389
  "settings.friends": "Friends",
2370
2390
  "settings.friends.hint": "Friends are read-only threads on the SoulMirror page: your alter writes to them, you tell your alter what to say.",
2371
2391
  "settings.friends.none": "No friends yet. Paste a card URI below or use /add.",
@@ -2639,7 +2659,8 @@ window.__ModuleLoader__.load({
2639
2659
  "settings.agents.intro": "Named seat agents behind your identity: each runs as its own dsh session in its own working directory, wakes when a group message @mentions its name (whitelist-gated), and posts as \"you · <name>\".",
2640
2660
  "settings.agents.none": "No agents yet — add one below.",
2641
2661
  "settings.agents.name": "Name (used as @mention)",
2642
- "settings.agents.cwd": "Working directory (the repository it works in)",
2662
+ "settings.agents.cwd": "Working directory - the folder it reads, writes and runs commands in (optional)",
2663
+ "settings.agents.cwdHint": "Leave empty for its own default folder",
2643
2664
  "settings.agents.preset": "Capability preset",
2644
2665
  "settings.agents.preset.default": "standard coding agent (default)",
2645
2666
  "settings.agents.prompt": "Persona / instructions (its system brief; edits apply from its next turn)",
@@ -2780,6 +2801,19 @@ window.__ModuleLoader__.load({
2780
2801
  "settings.config.home": "数据目录",
2781
2802
  "settings.config.hint": "改动下次启动 dsh 时生效(轻端进程按这些参数启动)。路径留空 = 查 PATH,再查插件 bin/;目录留空 = $SOULNET_HOME,再 ~/.soulnet。",
2782
2803
  "settings.config.unavailable": "只有本机(loopback)浏览器可以编辑设置。",
2804
+ "settings.upgrade": "版本与更新",
2805
+ "settings.upgrade.badge": "有新版",
2806
+ "settings.upgrade.current": "当前版本",
2807
+ "settings.upgrade.check": "检查更新",
2808
+ "settings.upgrade.checking": "检查中…",
2809
+ "settings.upgrade.uptodate": "v{version} 已是最新发布版本。",
2810
+ "settings.upgrade.available": "发现新版本 v{latest}(当前 v{current})。",
2811
+ "settings.upgrade.do": "升级到 v{version}",
2812
+ "settings.upgrade.installing": "正在安装 v{version}…(在 dsh profile 里跑 pnpm,可能要一会儿)",
2813
+ "settings.upgrade.restarting": "安装完成。正在重启 dsh——恢复后本页会自动刷新。",
2814
+ "settings.upgrade.manual": "已安装 v{version}。界面部分刷新本页即生效;宿主部分要重启 dsh 才完整生效。",
2815
+ "settings.upgrade.failed": "升级失败({message})。",
2816
+ "settings.upgrade.hint": "升级会从 npm 源(npmjs,不通时退 npmmirror)把 soulnet-dsh 装进 dsh profile。",
2783
2817
  "settings.friends": "好友",
2784
2818
  "settings.friends.hint": "好友在灵镜页面里是只读的往来记录:分身替你写信,你只吩咐分身。",
2785
2819
  "settings.friends.none": "还没有好友。在下面贴名片链接,或用 /add。",
@@ -3053,7 +3087,8 @@ window.__ModuleLoader__.load({
3053
3087
  "settings.agents.intro": "挂在你身份下的具名代理:各自一条 dsh 会话、各有工作目录,群里被 @名字 才唤醒(白名单把关),发言署名「你 · 名字」。",
3054
3088
  "settings.agents.none": "还没有代理——在下面添加一个。",
3055
3089
  "settings.agents.name": "名字(群里 @ 它用)",
3056
- "settings.agents.cwd": "工作目录(它干活的仓库)",
3090
+ "settings.agents.cwd": "工作目录——它读写文件、跑命令的文件夹(选填)",
3091
+ "settings.agents.cwdHint": "留空 = 使用它自己的默认目录",
3057
3092
  "settings.agents.preset": "能力预设",
3058
3093
  "settings.agents.preset.default": "标准编码 Agent(默认)",
3059
3094
  "settings.agents.prompt": "人设 / 指令(写进它的系统提示;保存后下一回合生效)",
@@ -3181,7 +3216,7 @@ window.__ModuleLoader__.load({
3181
3216
  overflowY: "auto",
3182
3217
  borderRadius: 12,
3183
3218
  border: "1px solid rgba(127,127,127,.3)",
3184
- background: "var(--dsw-alias-bg-elevated, var(--dsw-alias-bg-primary, #fff))",
3219
+ background: "var(--dsw-specific-menu, var(--dsw-alias-bg-layer-2, #fff))",
3185
3220
  boxShadow: "0 12px 40px rgba(0,0,0,.28)",
3186
3221
  padding: 16,
3187
3222
  display: "grid",
@@ -3599,6 +3634,7 @@ window.__ModuleLoader__.load({
3599
3634
  const [agentNames, setAgentNames] = (0, react.useState)([]);
3600
3635
  const [agentsSheetOpen, setAgentsSheetOpen] = (0, react.useState)(false);
3601
3636
  const [mention, setMention] = (0, react.useState)(void 0);
3637
+ const [mentionIdx, setMentionIdx] = (0, react.useState)(0);
3602
3638
  const [busyVoices, setBusyVoices] = (0, react.useState)({});
3603
3639
  const [workFeeds, setWorkFeeds] = (0, react.useState)({});
3604
3640
  const [openWork, setOpenWork] = (0, react.useState)(/* @__PURE__ */ new Set());
@@ -3797,6 +3833,7 @@ window.__ModuleLoader__.load({
3797
3833
  start: caret - m[2].length - 1,
3798
3834
  query: m[2]
3799
3835
  });
3836
+ setMentionIdx(0);
3800
3837
  };
3801
3838
  const pickMention = (name) => {
3802
3839
  if (mention === void 0) return;
@@ -4109,19 +4146,23 @@ window.__ModuleLoader__.load({
4109
4146
  overflowY: "auto",
4110
4147
  borderRadius: 8,
4111
4148
  border: "1px solid rgba(127,127,127,.35)",
4112
- background: "var(--dsw-alias-bg-elevated, var(--dsw-alias-bg-primary, #fff))",
4149
+ background: "var(--dsw-specific-menu, var(--dsw-alias-bg-layer-2, #fff))",
4113
4150
  boxShadow: "0 6px 24px rgba(0,0,0,.18)",
4114
4151
  padding: 4,
4115
4152
  display: "grid"
4116
4153
  },
4117
4154
  "data-soulmirror-mention-pop": true,
4118
- children: mentionMatches.map((p) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
4155
+ children: mentionMatches.map((p, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
4119
4156
  type: "button",
4120
4157
  className: "sm-linkbtn",
4121
4158
  style: {
4122
4159
  textAlign: "left",
4123
4160
  padding: "6px 8px",
4124
- borderRadius: 6
4161
+ borderRadius: 6,
4162
+ ...i === Math.min(mentionIdx, mentionMatches.length - 1) ? { background: "var(--dsw-alias-interactive-bg-hover)" } : {}
4163
+ },
4164
+ onMouseEnter: () => {
4165
+ setMentionIdx(i);
4125
4166
  },
4126
4167
  onMouseDown: (e) => {
4127
4168
  e.preventDefault();
@@ -4159,12 +4200,20 @@ window.__ModuleLoader__.load({
4159
4200
  },
4160
4201
  onKeyDown: (e) => {
4161
4202
  if (e.key === "Escape" && mention !== void 0) {
4203
+ e.preventDefault();
4204
+ e.stopPropagation();
4162
4205
  setMention(void 0);
4163
4206
  return;
4164
4207
  }
4208
+ if (mention !== void 0 && mentionMatches.length > 0 && (e.key === "ArrowDown" || e.key === "ArrowUp")) {
4209
+ e.preventDefault();
4210
+ const len = mentionMatches.length;
4211
+ setMentionIdx((i) => (Math.min(i, len - 1) + (e.key === "ArrowDown" ? 1 : len - 1)) % len);
4212
+ return;
4213
+ }
4165
4214
  if (e.key === "Enter" && !e.shiftKey && !e.nativeEvent.isComposing) {
4166
4215
  e.preventDefault();
4167
- if (mention !== void 0 && mentionMatches.length > 0) pickMention(mentionMatches[0].name);
4216
+ if (mention !== void 0 && mentionMatches.length > 0) pickMention(mentionMatches[Math.min(mentionIdx, mentionMatches.length - 1)].name);
4168
4217
  else send();
4169
4218
  }
4170
4219
  },
@@ -4208,6 +4257,156 @@ window.__ModuleLoader__.load({
4208
4257
  ] });
4209
4258
  }
4210
4259
  //#endregion
4260
+ //#region src/client/upgrade-store.ts
4261
+ /**
4262
+ * Client store of the plugin self-upgrade (Settings → SoulMirror network →
4263
+ * "Version & updates"; host routes `upgrade.check` / `upgrade.run`).
4264
+ *
4265
+ * The client half does a SILENT check once per page load (client/index.ts);
4266
+ * `hasUpdate` then puts the badge on the Settings entry. `run()` drives the
4267
+ * whole one-click path: install → the host answers `restarting: true` and
4268
+ * exits → this store polls `/state` until the RESTARTED server answers, then
4269
+ * reloads the page (phase `reloading`). A host that could not arm the
4270
+ * relaunch helper answers `restarting: false` and the UI shows the
4271
+ * manual-restart hint instead (phase `done-manual`).
4272
+ */
4273
+ const EMPTY = {
4274
+ phase: "idle",
4275
+ current: void 0,
4276
+ latest: void 0,
4277
+ hasUpdate: false,
4278
+ registry: void 0,
4279
+ error: void 0,
4280
+ output: void 0,
4281
+ checked: false
4282
+ };
4283
+ /** How long the restart may take before the store gives up and shows the manual hint. */
4284
+ const RESTART_LIMIT_MS = 18e4;
4285
+ const RESTART_POLL_MS = 2e3;
4286
+ var UpgradeStore = class {
4287
+ snapshot = EMPTY;
4288
+ listeners = /* @__PURE__ */ new Set();
4289
+ checkedOnce = false;
4290
+ getSnapshot = () => this.snapshot;
4291
+ subscribe = (listener) => {
4292
+ this.listeners.add(listener);
4293
+ return () => {
4294
+ this.listeners.delete(listener);
4295
+ };
4296
+ };
4297
+ set(patch) {
4298
+ this.snapshot = {
4299
+ ...this.snapshot,
4300
+ ...patch
4301
+ };
4302
+ for (const listener of this.listeners) listener();
4303
+ }
4304
+ /**
4305
+ * Query the host for the latest published version. Silent mode (page load)
4306
+ * swallows errors — an offline machine must not grow error banners on its
4307
+ * own; the manual button reports them.
4308
+ */
4309
+ check = async (options = {}) => {
4310
+ if (this.snapshot.phase === "checking" || this.snapshot.phase === "installing" || this.snapshot.phase === "restarting") return;
4311
+ if (options.silent === true && this.checkedOnce) return;
4312
+ this.checkedOnce = true;
4313
+ this.set({
4314
+ phase: "checking",
4315
+ ...options.silent === true ? {} : { error: void 0 }
4316
+ });
4317
+ try {
4318
+ const result = await api.upgradeCheck();
4319
+ this.set({
4320
+ phase: "idle",
4321
+ current: result.current,
4322
+ latest: result.latest,
4323
+ hasUpdate: result.hasUpdate,
4324
+ registry: result.registry,
4325
+ checked: true,
4326
+ error: void 0
4327
+ });
4328
+ } catch (error) {
4329
+ this.set({
4330
+ phase: "idle",
4331
+ checked: true,
4332
+ ...options.silent === true ? {} : { error: error instanceof Error ? error.message : String(error) }
4333
+ });
4334
+ }
4335
+ };
4336
+ /** One-click upgrade to the checked `latest`. */
4337
+ run = async () => {
4338
+ const { latest, registry } = this.snapshot;
4339
+ if (latest === void 0 || this.snapshot.phase === "installing" || this.snapshot.phase === "restarting") return;
4340
+ this.set({
4341
+ phase: "installing",
4342
+ error: void 0,
4343
+ output: void 0
4344
+ });
4345
+ let result;
4346
+ try {
4347
+ result = await api.upgradeRun(latest, registry);
4348
+ } catch (error) {
4349
+ this.set({
4350
+ phase: "failed",
4351
+ error: error instanceof Error ? error.message : String(error)
4352
+ });
4353
+ return;
4354
+ }
4355
+ if (!result.ok) {
4356
+ this.set({
4357
+ phase: "failed",
4358
+ error: `pnpm exit ${result.exitCode}`,
4359
+ output: result.output
4360
+ });
4361
+ return;
4362
+ }
4363
+ if (!result.restarting) {
4364
+ this.set({
4365
+ phase: "done-manual",
4366
+ hasUpdate: false,
4367
+ current: result.version
4368
+ });
4369
+ return;
4370
+ }
4371
+ this.set({
4372
+ phase: "restarting",
4373
+ hasUpdate: false,
4374
+ current: result.version
4375
+ });
4376
+ await this.waitForRestart();
4377
+ };
4378
+ /**
4379
+ * The old server exits ~500 ms after answering; poll `/state` until it
4380
+ * FIRST disappears and THEN answers again, so an answer from the old
4381
+ * process does not reload us into the old bundle.
4382
+ */
4383
+ async waitForRestart() {
4384
+ const deadline = Date.now() + RESTART_LIMIT_MS;
4385
+ let sawDown = false;
4386
+ while (Date.now() < deadline) {
4387
+ await new Promise((resolve) => setTimeout(resolve, RESTART_POLL_MS));
4388
+ try {
4389
+ if (!(await fetch(`/soulmirror/api/state`, {
4390
+ method: "GET",
4391
+ signal: AbortSignal.timeout(1500)
4392
+ })).ok) {
4393
+ sawDown = true;
4394
+ continue;
4395
+ }
4396
+ if (sawDown) {
4397
+ this.set({ phase: "reloading" });
4398
+ location.reload();
4399
+ return;
4400
+ }
4401
+ } catch {
4402
+ sawDown = true;
4403
+ }
4404
+ }
4405
+ this.set({ phase: "done-manual" });
4406
+ }
4407
+ };
4408
+ const upgradeStore = new UpgradeStore();
4409
+ //#endregion
4211
4410
  //#region src/client/SettingsSection.tsx
4212
4411
  /**
4213
4412
  * `settings.section` "SoulMirror network": backend status, identity (create on
@@ -4367,6 +4566,121 @@ window.__ModuleLoader__.load({
4367
4566
  })]
4368
4567
  });
4369
4568
  }
4569
+ /**
4570
+ * "Version & updates": the plugin self-upgrade (host routes `upgrade.check` /
4571
+ * `upgrade.run`, client state in upgrade-store.ts). One click installs the
4572
+ * new version and, when the peer supports `host.relaunch`, restarts dsh and
4573
+ * reloads this page by itself; otherwise it says what to restart manually.
4574
+ */
4575
+ function UpgradeCard({ t }) {
4576
+ const up = (0, react.useSyncExternalStore)(upgradeStore.subscribe, upgradeStore.getSnapshot);
4577
+ const busy = up.phase !== "idle" && up.phase !== "done-manual" && up.phase !== "failed";
4578
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4579
+ style: card,
4580
+ "data-soulmirror-upgrade": true,
4581
+ children: [
4582
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4583
+ style: rowStyle,
4584
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
4585
+ style: h4,
4586
+ children: t("settings.upgrade")
4587
+ }), up.hasUpdate ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4588
+ style: {
4589
+ background: "rgb(220,80,60)",
4590
+ color: "#fff",
4591
+ borderRadius: 999,
4592
+ padding: "1px 8px",
4593
+ fontSize: "0.72em"
4594
+ },
4595
+ "data-soulmirror-upgrade-badge": true,
4596
+ children: t("settings.upgrade.badge")
4597
+ }) : null]
4598
+ }),
4599
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4600
+ style: rowStyle,
4601
+ children: [
4602
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4603
+ style: small$1,
4604
+ children: [
4605
+ t("settings.upgrade.current"),
4606
+ ": ",
4607
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: up.current !== void 0 ? `v${up.current}` : "—" })
4608
+ ]
4609
+ }),
4610
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4611
+ type: "button",
4612
+ disabled: busy,
4613
+ onClick: () => {
4614
+ upgradeStore.check();
4615
+ },
4616
+ children: up.phase === "checking" ? t("settings.upgrade.checking") : t("settings.upgrade.check")
4617
+ }),
4618
+ up.hasUpdate && up.latest !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4619
+ type: "button",
4620
+ disabled: busy,
4621
+ onClick: () => {
4622
+ upgradeStore.run();
4623
+ },
4624
+ "data-soulmirror-upgrade-run": true,
4625
+ children: t("settings.upgrade.do", { version: up.latest })
4626
+ }) : null
4627
+ ]
4628
+ }),
4629
+ up.phase === "idle" && up.hasUpdate && up.latest !== void 0 && up.current !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4630
+ style: small$1,
4631
+ children: t("settings.upgrade.available", {
4632
+ latest: up.latest,
4633
+ current: up.current
4634
+ })
4635
+ }) : null,
4636
+ up.phase === "idle" && !up.hasUpdate && up.checked && up.error === void 0 && up.current !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4637
+ style: small$1,
4638
+ children: t("settings.upgrade.uptodate", { version: up.current })
4639
+ }) : null,
4640
+ up.phase === "installing" && up.latest !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4641
+ style: small$1,
4642
+ children: t("settings.upgrade.installing", { version: up.latest })
4643
+ }) : null,
4644
+ up.phase === "restarting" || up.phase === "reloading" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4645
+ style: small$1,
4646
+ role: "status",
4647
+ children: t("settings.upgrade.restarting")
4648
+ }) : null,
4649
+ up.phase === "done-manual" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4650
+ style: {
4651
+ ...small$1,
4652
+ opacity: .95
4653
+ },
4654
+ role: "status",
4655
+ children: t("settings.upgrade.manual", { version: up.current ?? "" })
4656
+ }) : null,
4657
+ (up.phase === "failed" || up.error !== void 0) && up.error !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4658
+ style: {
4659
+ ...small$1,
4660
+ color: "rgb(220,80,60)"
4661
+ },
4662
+ role: "alert",
4663
+ children: t("settings.upgrade.failed", { message: up.error })
4664
+ }) : null,
4665
+ up.phase === "failed" && up.output !== void 0 && up.output !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
4666
+ style: {
4667
+ ...mono,
4668
+ whiteSpace: "pre-wrap",
4669
+ maxHeight: 160,
4670
+ overflow: "auto",
4671
+ margin: 0,
4672
+ opacity: .8,
4673
+ userSelect: "text"
4674
+ },
4675
+ children: up.output
4676
+ }) : null,
4677
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4678
+ style: small$1,
4679
+ children: t("settings.upgrade.hint")
4680
+ })
4681
+ ]
4682
+ });
4683
+ }
4370
4684
  const BINARY_SOURCE_KEYS = {
4371
4685
  "setting": "settings.binary.setting",
4372
4686
  "platform-package": "settings.binary.platform-package",
@@ -4603,6 +4917,7 @@ window.__ModuleLoader__.load({
4603
4917
  })
4604
4918
  ]
4605
4919
  }),
4920
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UpgradeCard, { t }),
4606
4921
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4607
4922
  style: card,
4608
4923
  "data-soulmirror-settings-alter": true,
@@ -5175,7 +5490,7 @@ window.__ModuleLoader__.load({
5175
5490
  overflowY: "auto",
5176
5491
  borderRadius: 12,
5177
5492
  border: "1px solid rgba(127,127,127,.3)",
5178
- background: "var(--dsw-alias-bg-elevated, var(--dsw-alias-bg-primary, #fff))",
5493
+ background: "var(--dsw-specific-menu, var(--dsw-alias-bg-layer-2, #fff))",
5179
5494
  boxShadow: "0 12px 40px rgba(0,0,0,.28)",
5180
5495
  padding: 16,
5181
5496
  display: "grid",
@@ -5232,6 +5547,7 @@ window.__ModuleLoader__.load({
5232
5547
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
5233
5548
  style: field,
5234
5549
  value: cwd,
5550
+ placeholder: t("settings.agents.cwdHint"),
5235
5551
  onChange: (e) => {
5236
5552
  setCwd(e.target.value);
5237
5553
  },
@@ -10663,6 +10979,7 @@ window.__ModuleLoader__.load({
10663
10979
  if (rootEl === null) return;
10664
10980
  const target = e.target instanceof Node ? e.target : e.target;
10665
10981
  if (target === null) return;
10982
+ if (!target.isConnected) return;
10666
10983
  if (rootEl.contains(target)) return;
10667
10984
  if (target?.closest?.("[data-soulmirror-footer]") != null) return;
10668
10985
  pageStore.close();
@@ -10765,6 +11082,12 @@ window.__ModuleLoader__.load({
10765
11082
  */
10766
11083
  const STYLE_ID = "soulmirror-styles";
10767
11084
  const CSS = `
11085
+ /* Native form controls follow the ACTIVE theme: the option list of a <select>
11086
+ (and scrollbars, checkboxes...) is OS-rendered and ignores CSS tokens - it
11087
+ only obeys color-scheme. dsh marks its dark theme on <body>. */
11088
+ body[data-ds-dark-theme] { color-scheme: dark; }
11089
+ .sm-page-root select option, .sm-page-root select optgroup { background: var(--dsw-specific-menu); color: var(--dsw-alias-label-primary); }
11090
+
10768
11091
  .sm-footer {
10769
11092
  flex: none; display: flex; align-items: center; gap: 8px;
10770
11093
  width: calc(100% + 4px); height: 42px; margin: 4px -2px 0; padding: 0 10px 0 8px;
@@ -11237,6 +11560,7 @@ body[data-ds-dark-theme] .sm-page-root {
11237
11560
  locale: NS,
11238
11561
  inject: overlayInjected
11239
11562
  }, InboxOverlay));
11563
+ upgradeStore.check({ silent: true });
11240
11564
  const settingsInjected = () => ({
11241
11565
  openSession,
11242
11566
  scope
@@ -11246,7 +11570,7 @@ body[data-ds-dark-theme] .sm-page-root {
11246
11570
  id: "soulmirror",
11247
11571
  order: 80,
11248
11572
  locale: NS,
11249
- label: () => t("settings.nav"),
11573
+ label: () => upgradeStore.getSnapshot().hasUpdate ? `${t("settings.nav")} ●` : t("settings.nav"),
11250
11574
  inject: settingsInjected
11251
11575
  }, SoulmirrorSettingsSection));
11252
11576
  ctx.slots.inject("settings.onboarding", () => ctx.slots.register({