@mrrisega/dsh-remote 0.7.2-beta.1 → 0.7.2-beta.2

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.
@@ -246,7 +246,12 @@ window.__ModuleLoader__.load({
246
246
  ".dru-captcha .dru-input{flex:1;min-width:0}",
247
247
  ".dru-captcha-box{width:118px;height:44px;flex:none;border-radius:8px;border:1px solid var(--dru-border-ctl);cursor:pointer;background:var(--dru-surface);display:flex;align-items:center;justify-content:center;font-size:12px;color:var(--dru-fg-muted);overflow:hidden}",
248
248
  ".dru-captcha-box svg{display:block;width:100%;height:100%}",
249
- ".dru-user{display:flex;align-items:center;gap:10px;margin-bottom:12px}",
249
+ ".dru-user{display:flex;align-items:center;gap:10px;margin-bottom:12px;flex-wrap:wrap}",
250
+ // 退出登录:跟着账号行走(业主 2026-10-17),右侧对齐、次要视觉(不抢主行动):
251
+ // 触控目标 ≥40px、与账号信息 8px 间距;危险色但不用实底(它不是主行动)。
252
+ ".dru-user-logout{margin-left:auto;flex:none;min-height:40px;padding:8px 12px;border:1px solid var(--dru-border-ctl);border-radius:8px;background:transparent;color:var(--dru-danger);font:500 12.5px/1.2 inherit;font-family:inherit;cursor:pointer;white-space:nowrap}",
253
+ ".dru-user-logout:hover:not(:disabled){background:var(--dru-danger-soft);border-color:var(--dru-danger-border)}",
254
+ ".dru-user-logout:disabled{opacity:.55;cursor:default}",
250
255
  ".dru-avatar{width:40px;height:40px;border-radius:50%;background:var(--dru-accent-fill);color:var(--dru-on-accent);display:flex;align-items:center;justify-content:center;font-size:17px;font-weight:700;flex-shrink:0}",
251
256
  ".dru-user .mail{font-size:14px;font-weight:600;color:var(--dru-fg);word-break:break-all}",
252
257
  ".dru-user .plan{font-size:12px;color:var(--dru-fg-muted);margin-top:2px}",
@@ -1368,16 +1373,6 @@ window.__ModuleLoader__.load({
1368
1373
  function annReportClick(id) {
1369
1374
  try { post("/dsh-remote/announcements/click", { id: id }).catch(function () { /* 静默:服务端才是权威 */ }); } catch (e) { /* 忽略 */ }
1370
1375
  }
1371
- /** 本地把某条标记为已读(只为了让徽标/标签立刻更新;真正的已读在服务端)。 */
1372
- function annMarkReadLocal(id) {
1373
- var hit = false;
1374
- annState.items = annState.items.map(function (x) {
1375
- if (x.id !== id || !x.unread) return x;
1376
- hit = true;
1377
- return Object.assign({}, x, { unread: false });
1378
- });
1379
- if (hit) { annState.unread = Math.max(0, annState.unread - 1); annEmit(); }
1380
- }
1381
1376
 
1382
1377
  // ── 🤖 微信机器人「命令帮助」镜像 ─────────────────────────────────────────
1383
1378
  // ⚠️ 真身是 clients/dsh-remote/wechat-channel.mjs 的 HELP_TEXT(用户在微信里看到的唯一说明书)。
@@ -2894,7 +2889,7 @@ window.__ModuleLoader__.load({
2894
2889
  var restartPending = !!restartInfo.pending;
2895
2890
  // 宿主形态(node 半下发):桌面端(Electron)Host 由桌面应用托管,重启这个动作
2896
2891
  // 在那边**做不到也不该做**(强行退出会弹原生致命错误)。面板据此:
2897
- // ① 不自动倒计时重启;② 保留提醒但改文案,引导"退出并重开桌面应用"。
2892
+ // ① 不自动倒计时重启;② 桌面端只提示「刷新页面即可」,不引导重启/重开应用(2026-10-17)。
2898
2893
  // 旧版 host 的响应里没有该字段 → false,行为与改动前完全一致。
2899
2894
  // 🔴 2026-10-11 修:node 半把该字段下在 **connect** 里(`connect.desktopHost`),
2900
2895
  // 这里以前读顶层 `st.desktopHost` → **永远 false** → 桌面端被当成 Web 版:
@@ -3988,6 +3983,55 @@ window.__ModuleLoader__.load({
3988
3983
  // 「更多」里只留下面那两个**折叠的营销入口**(升级 PRO / 带新用户换会员),用户仍要它们收在这里。
3989
3984
  // 🔒 顺带更强的一条不变量:整个面板从此不渲染任何 Mbps / GB / 价格数字
3990
3985
  // (「已限速 / 不限速」仍由服务端 quota.limit_enabled / quota.max_mbps 驱动,但只给定性措辞)。
3986
+ /**
3987
+ * 🔧 安装/配对自检提示(2026-10-17):node 半在 /dsh-remote/status 的 `setup` 里下发
3988
+ * `{ kind: ok|runtime_missing|pairing_missing|all_missing, text, detail, backup? }`。
3989
+ *
3990
+ * 由来:真机事故 —— 用户本机 `~/.dsh-remote` 被误删后,面板只显示模糊的"未运行",
3991
+ * 用户不知道是"运行环境没了"还是"没登录",最后手机端干等 47 秒才报错。
3992
+ * 所以这里**必须说清结论 + 给出下一步动作**,但绝不阻塞面板其它功能。
3993
+ */
3994
+ function renderSetupAlert() {
3995
+ var setup = (st && (st.setup || (st.connect && st.connect.setup))) || null;
3996
+ if (!setup || !setup.kind || setup.kind === "ok") return null;
3997
+ var backup = setup.backup && setup.backup.available ? setup.backup : null;
3998
+ return h("div", { className: "dru-restart-alert", role: "status", style: { marginBottom: 12 } },
3999
+ h("span", { className: "dru-restart-alert-icon" }, "🔧"),
4000
+ h("div", { style: { flex: "1" } },
4001
+ h("div", { className: "dru-restart-alert-title" }, setup.text || "运行环境需要处理"),
4002
+ setup.detail ? h("div", { className: "dru-restart-alert-sub" }, setup.detail) : null,
4003
+ backup ? h("div", { className: "dru-restart-alert-sub" }, backup.text || "检测到历史配对备份,可一键恢复") : null,
4004
+ h("div", { className: "dru-actions", style: { marginTop: 10 } },
4005
+ backup
4006
+ ? h("button", {
4007
+ type: "button",
4008
+ className: "dru-btn dru-btn-primary",
4009
+ disabled: busy !== "",
4010
+ onClick: function () {
4011
+ var action = backup.action || {};
4012
+ setBusy("setup");
4013
+ post(action.path || "/dsh-remote/config/restore", action.body || {})
4014
+ .then(function (body) {
4015
+ if (body && body.ok) setMessage({ kind: "ok", text: "已恢复历史配对,正在重新启动 bridge…" });
4016
+ else setMessage({ kind: "err", text: (body && body.error && body.error.message) || "恢复失败,请重试" });
4017
+ if (body && body.connect) setSt(body); else refresh();
4018
+ })
4019
+ .catch(function (e) { setMessage({ kind: "err", text: "恢复失败: " + e.message }); })
4020
+ .finally(function () { setBusy(""); });
4021
+ }
4022
+ }, busy === "setup" ? "恢复中…" : "恢复历史配对")
4023
+ : null,
4024
+ h("button", {
4025
+ type: "button",
4026
+ className: "dru-btn dru-btn-ghost",
4027
+ disabled: busy !== "",
4028
+ onClick: function () { refresh(); }
4029
+ }, "刷新状态")
4030
+ )
4031
+ )
4032
+ );
4033
+ }
4034
+
3991
4035
  function renderAccount() {
3992
4036
  var a = account;
3993
4037
  var plan = a ? a.plan : "free";
@@ -4038,17 +4082,11 @@ window.__ModuleLoader__.load({
4038
4082
  var invTipOn = !invOff && !inviteTipDismissed;
4039
4083
  var invTipRule = invTipOn ? inviteRuleOf() : null;
4040
4084
  // 【0.6.10】营销入口「在哪一层」的四个开关(约定:新用户第一次看得见,用户关掉后收进「更多」):
4041
- // · 没关过(mktFolded=false)→ 「升级 PRO」与「带新用户换会员」摆在账号卡正面(与今天一致)
4042
- // · 关过(mktFolded=true) → 正面只留一个「更多」,两条入口搬进「更多」里
4043
- // 两个边界:
4044
- // · invOff(运营关掉拉新)→ 拉新入口**整体不渲染**,收纳前后都不出现
4045
- // (老注释:否则会点进一个没有内容的邀请页)
4046
- // · 付费用户的按钮是「续费 / 转正式 PRO」—— 那是账号维护、不是营销露出,收纳后仍留在正面:
4047
- // 把付费用户的续费入口藏进「更多」是真实风险(续费靠它,:3096-3098 的到期日也靠它提醒)
4048
- var showUpsellOnFace = !mktFolded || isMember;
4049
- var showInviteOnFace = !invOff && !mktFolded;
4050
- var showUpsellInMore = mktFolded && !isMember;
4051
- var showInviteInMore = mktFolded && !invOff;
4085
+ // 2026-10-17 版式简化(业主:按钮太多、换行逻辑太多):
4086
+ // 正面**只留一行三个按钮** —— 主行动(升级/续费)+ 用户反馈 + 「更多」;
4087
+ // 「带新用户换会员 / 修改密码 / 加入交流群」全部收进「更多」。
4088
+ // 位置稳定 > 首次曝光:入口永远在同一个地方,用户不用记"上次在哪",也不再需要
4089
+ // mktFolded 那套"先摆正面、关过才收纳"的两层逻辑(气泡仍会提示一次,见 renderInviteTip)。
4052
4090
  return h("div", null,
4053
4091
  h("div", { className: "dru-user" },
4054
4092
  h("div", { className: "dru-avatar" }, (st.config.phone || "D").charAt(0).toUpperCase()),
@@ -4060,7 +4098,16 @@ window.__ModuleLoader__.load({
4060
4098
  // (「PRO 会员 · 到期 …」)讲的是**功能性**的到期时间,不是营销。
4061
4099
  // 免费用户这里什么都不显示 —— 把「免费档」当标签挂在脸上,正是要减少的营销露出。
4062
4100
  isMember ? h("div", { className: "plan" }, plan === "pro_max" ? "Pro Max" : "PRO") : null
4063
- )
4101
+ ),
4102
+ // 2026-10-17(业主):退出登录从按钮行挪到**账号行右侧** —— 它属于"账号维护",
4103
+ // 与账号信息同一行动线;主按钮行也因此少一个按钮。
4104
+ h("button", {
4105
+ type: "button",
4106
+ className: "dru-user-logout",
4107
+ disabled: busy !== "",
4108
+ title: "退出登录(电脑端后台服务与手机端连接不受影响)",
4109
+ onClick: function () { doLogout(false); }
4110
+ }, "退出登录")
4064
4111
  ),
4065
4112
  // 额度状态:不限量说明 / 剩余额度百分比 / 会员到期日。
4066
4113
  // 首屏只说「还剩多少」,不提档位名、不写带宽数值(数值在下面「更多」里)。
@@ -4069,90 +4116,57 @@ window.__ModuleLoader__.load({
4069
4116
  h("span", null, planText)
4070
4117
  ),
4071
4118
  h("div", { className: "dru-actions", style: { marginTop: 10 } },
4072
- // 升级/续费:免费用户这条是**营销入口**,用户关掉引导后搬进「更多」;
4073
- // 付费用户的续费/转正入口始终留在正面(账号维护,不是营销露出)。
4074
- showUpsellOnFace
4075
- ? h("button", { type: "button", className: "dru-btn dru-btn-primary", style: { display: "inline-flex", alignItems: "center" }, disabled: busy !== "", title: "升级/续费(带登录态打开)", onClick: openUpgradeAuth },
4076
- busy === "upgrade" ? "生成链接中…" : (!isMember ? "🚀 升级 PRO" : source === "trial" ? "🚀 转正式 PRO" : "🔄 续费会员"))
4077
- : null,
4078
- // 拉新入口:invOff(运营关掉拉新)整体隐藏,收纳前后都不出现(否则会点进一个没有内容的邀请页);
4079
- // 用户收纳过后搬进「更多」。
4080
- // 按钮文字自带可访问名称;气泡在屏时用 aria-describedby 把「能得多少」也读出来。
4081
- showInviteOnFace ? h("button", {
4082
- type: "button",
4083
- className: "dru-btn dru-btn-ghost",
4084
- disabled: busy !== "",
4085
- title: invTipRule ? "带 " + invTipRule.n + " 位新用户可得 " + invTipRule.days + " 天 PRO 会员" : "带新用户换会员",
4086
- "aria-describedby": invTipOn ? INVITE_TIP_ID : undefined,
4087
- onClick: function () { setView("invite"); loadInvite(); }
4088
- }, "🎁 带新用户换会员") : null,
4119
+ // 主行动:免费=升级入口,会员=续费/转正(账号维护,永远在正面第一位)
4120
+ h("button", {
4121
+ type: "button", className: "dru-btn dru-btn-primary",
4122
+ style: { display: "inline-flex", alignItems: "center" }, disabled: busy !== "",
4123
+ onClick: openUpgradeAuth
4124
+ },
4125
+ busy === "upgrade" ? "生成链接中…" : (!isMember ? "🚀 升级 PRO" : source === "trial" ? "🚀 转正式 PRO" : "🔄 续费会员")),
4126
+ // 次要:用户反馈("联系我们"的高频入口,留在正面)
4089
4127
  h("button", { type: "button", className: "dru-btn dru-btn-ghost", disabled: busy !== "", onClick: function () { setView("feedback"); } }, "💬 用户反馈"),
4090
- // 💬 加入交流群(0.6.18 从「关于」卡搬到这里):它和「用户反馈」是同一类东西 ——
4091
- // 「跟我们联系」的两个入口。原来它躺在最底下的「关于 dsh-remote」里,用户要读完
4092
- // 六段技术说明才找得到,而那时的位置离"有事想找人问"的场景已经很远了。
4093
- // 未配置二维码(后台上传才有)→ 整体不渲染,不留空壳按钮。
4094
- community && community.qrcode
4095
- ? h("button", {
4096
- type: "button",
4097
- className: "dru-btn dru-btn-ghost",
4098
- disabled: busy !== "",
4099
- onClick: function () { setCommOpen(true); }
4100
- }, "💬 加入交流群")
4101
- : null,
4102
- h("button", { type: "button", className: "dru-btn dru-btn-ghost", disabled: busy !== "", onClick: togglePwdForm }, pwdOpen ? "收起修改密码" : "🔒 修改密码"),
4103
- h("button", { type: "button", className: "dru-btn dru-btn-danger", disabled: busy !== "", onClick: function () { doLogout(false); } }, "退出登录")
4128
+ // 「更多」变成同一行里的普通按钮(原来是一条整宽披露条,视觉重量和按钮完全不匹配)。
4129
+ // 无障碍不变:id / aria-expanded / aria-controls 保持原样(用例按它们定位)。
4130
+ h("button", {
4131
+ type: "button",
4132
+ id: MKT_MORE_TOGGLE_ID,
4133
+ className: "dru-btn dru-btn-ghost dru-disclose-inline",
4134
+ "aria-expanded": moreOpen ? "true" : "false",
4135
+ "aria-controls": MKT_MORE_BODY_ID,
4136
+ onClick: function () { setMoreOpen(!moreOpen); }
4137
+ },
4138
+ h("span", null, "更多"),
4139
+ h("span", { className: "dru-disclose-caret", "aria-hidden": "true" }, moreOpen ? "▲" : "▼")
4140
+ )
4104
4141
  ),
4105
- // 🎁 邀请收益引导气泡:紧贴上面那排按钮(不遮挡按钮、窄屏独占一行)。
4106
- // 它的 ✕ 同时是「营销入口收纳」的触发信号(0.6.10):点过之后两个入口搬进下面的「更多」。
4142
+ // 🎁 邀请引导气泡:紧跟按钮行(不遮挡按钮)。点 ✕ 只做"不再提示",不再改变入口位置。
4107
4143
  renderInviteTip(),
4108
- h("div", { className: "dru-hint", style: { marginTop: 8 } },
4109
- "升级/续费以带登录态方式打开:点击后生成一次性访问链接并直接跳转,无需重新登录。" +
4110
- (endsAt && isMember ? "到期后如需继续使用会员权益,请在到期前续费。" : "")),
4111
- // 🧰 「更多」:主界面上唯一常驻的那一个营销入口(0.6.10)—— 收纳态下正面不再摆按钮行的一部分。
4112
- // 折叠时它就是一个 .dru-disclose 按钮:命中区 ≥44px、键盘可聚焦(焦点环见样式表末尾的
4113
- // :focus-visible 基线),开合状态用「展开 ▼ / 收起 ▲」**文字**加箭头表达,不靠颜色区分。
4114
- // 无障碍:aria-expanded 表达开合,aria-controls 指向下面的内容体(内容体始终在 DOM 里、
4115
- // 收起时 hidden,所以 aria-controls 永远指向一个真实存在的元素,不指向空气)。
4116
- // 【0.6.18】加 .dru-disclose-inline:原来它继承 .dru-disclose 的 width:100%,
4117
- // 在账号卡里被撑成一条横贯整卡的宽条,和上面那排按钮完全不是一个视觉重量(用户:"宽而丑")。
4118
- // ⚠️ 变体只加在**这一处**:邀请页的「活动规则」披露仍是整行,那是对的(它是那一屏的主折叠)。
4119
- // id / aria-controls / aria-expanded 一个都不能动 —— remote-access-ui.test.mjs 按它们定位。
4120
- h("button", {
4121
- type: "button",
4122
- id: MKT_MORE_TOGGLE_ID,
4123
- className: "dru-disclose dru-disclose-inline",
4124
- style: { marginTop: 10 },
4125
- "aria-expanded": moreOpen ? "true" : "false",
4126
- "aria-controls": MKT_MORE_BODY_ID,
4127
- onClick: function () { setMoreOpen(!moreOpen); }
4128
- },
4129
- h("span", null, "更多"),
4130
- h("span", { className: "dru-disclose-caret", "aria-hidden": "true" }, moreOpen ? "收起 ▲" : "展开 ▼")
4131
- ),
4144
+ // 🧰 「更多」折叠体:带新用户 / 修改密码 / 加入交流群 + 修改密码表单
4132
4145
  h("div", {
4133
4146
  id: MKT_MORE_BODY_ID,
4134
4147
  className: "dru-more-body",
4135
4148
  hidden: !moreOpen
4136
4149
  },
4137
- // 【0.6.11】这里**不再**有「套餐与额度」标题与档位明细(renderPlanDetail 已删):
4138
- // 用户约定 —— 本地面板不展示 PRO 的带宽/流量/价格数字。「更多」只剩下面两个折叠入口。
4139
- // 用户收纳过之后,两条营销入口就住在这里(正面不再摆它们)
4140
- (showUpsellInMore || showInviteInMore)
4141
- ? h("div", { className: "dru-more-actions" },
4142
- showUpsellInMore
4143
- ? h("button", { type: "button", className: "dru-btn dru-btn-primary", disabled: busy !== "", title: "升级/续费(带登录态打开)", onClick: openUpgradeAuth },
4144
- busy === "upgrade" ? "生成链接中…" : "🚀 升级 PRO")
4145
- : null,
4146
- showInviteInMore
4147
- ? h("button", { type: "button", className: "dru-btn dru-btn-ghost", disabled: busy !== "", title: "带新用户换会员", onClick: function () { setView("invite"); loadInvite(); } }, "🎁 带新用户换会员")
4148
- : null)
4149
- : null
4150
+ h("div", { className: "dru-more-actions" },
4151
+ invOff ? null : h("button", {
4152
+ type: "button",
4153
+ className: "dru-btn dru-btn-ghost",
4154
+ disabled: busy !== "",
4155
+ title: invTipRule ? "带 " + invTipRule.n + " 位新用户可得 " + invTipRule.days + " 天 PRO 会员" : "带新用户换会员",
4156
+ onClick: function () { setView("invite"); loadInvite(); }
4157
+ }, "🎁 带新用户换会员"),
4158
+ h("button", { type: "button", className: "dru-btn dru-btn-ghost", disabled: busy !== "", onClick: togglePwdForm }, pwdOpen ? "收起修改密码" : "🔒 修改密码"),
4159
+ community && community.qrcode
4160
+ ? h("button", {
4161
+ type: "button",
4162
+ className: "dru-btn dru-btn-ghost",
4163
+ disabled: busy !== "",
4164
+ onClick: function () { setCommOpen(true); }
4165
+ }, "💬 加入交流群")
4166
+ : null
4167
+ ),
4168
+ pwdOpen ? renderResetPwdForm(false) : null
4150
4169
  ),
4151
- // 额度将尽 / 已被限速 → 🔀 双路块:升级套餐(转化)or 带新用户换会员时长(拉新)。
4152
- // 用户收纳过(mktFolded)时 renderDualPath 自己返回 null —— 那两条路这时都在「更多」里,
4153
- // 这里不再重复摆一遍(也不留一个没有按钮的空壳块)。
4154
- quotaTight ? renderDualPath("quota") : null,
4155
- pwdOpen ? renderResetPwdForm(false) : null
4156
4170
  );
4157
4171
  }
4158
4172
 
@@ -5021,7 +5035,7 @@ window.__ModuleLoader__.load({
5021
5035
  // 在线更新成功后出现,若照旧 15 秒自动重启,等于"更新完必定打断用户正在做的事"。
5022
5036
  useEffect(function () {
5023
5037
  // ⚠️ desktopHost 时**不自动重启**:桌面端重启 Host 由 Electron 托管,插件无权也不该做
5024
- // (见 node 半 restartHarness 的说明)。此时只保留文案提醒,动作交给用户「退出并重开应用」。
5038
+ // (见 node 半 restartHarness 的说明)。此时只提示刷新页面,不再引导重开应用。
5025
5039
  if (!restartPending || isRefreshOnly || isRestartHard || desktopHost || busy !== "" || restartCancelled) { setRestartCountdown(null); return undefined; }
5026
5040
  var at = restartInfo.at || 0;
5027
5041
  if (autoRestartCancelled(at)) { setRestartCountdown(null); return undefined; }
@@ -5094,10 +5108,10 @@ window.__ModuleLoader__.load({
5094
5108
  function openAnnItem(it) {
5095
5109
  if (!it) return;
5096
5110
  setAnnOpenId(it.id);
5097
- if (it.unread) {
5098
- annReportClick(it.id);
5099
- annMarkReadLocal(it.id);
5100
- }
5111
+ // 2026-10-17 业主约定:**点开不等于已读** —— 点开只上报「点击」,不动「已读」。
5112
+ // 「已读」由服务端在列表/弹窗下发时记(shown),后台把已读与点击分开统计;
5113
+ // 客户端也不在本地改 unread(下一次拉取以服务端为准,避免客户端自造已读口径)。
5114
+ if (it.unread) annReportClick(it.id);
5101
5115
  }
5102
5116
  /**
5103
5117
  * 📣 公告卡:插在「🔑 账号」卡**之前**(运营要用户看到的东西,与"远程访问/账号"同属首屏)。
@@ -5130,14 +5144,6 @@ window.__ModuleLoader__.load({
5130
5144
  h("span", { className: "dru-ann-item-time" }, it.created_at ? fmtDate(it.created_at) : "")
5131
5145
  );
5132
5146
  })),
5133
- h("div", { className: "dru-hint", style: { marginTop: 8 } },
5134
- "点开即视为已读(后台按已读与点击分别统计);公告顺序由后台设定。"),
5135
- h("div", { className: "dru-actions", style: { marginTop: 8 } },
5136
- h("button", {
5137
- type: "button",
5138
- className: "dru-btn dru-btn-ghost",
5139
- onClick: function () { annFetch({ openPopup: false }); } // 主动刷新:只更新列表,不弹窗
5140
- }, "刷新公告"))
5141
5147
  ]);
5142
5148
  }
5143
5149
  /** 公告详情(复用 .dru-popup 视觉;点遮罩/关闭即收起)。正文按**纯文本**渲染(一期不做富文本)。 */
@@ -5155,7 +5161,6 @@ window.__ModuleLoader__.load({
5155
5161
  it.image ? h("img", { className: "dru-ann-img", src: it.image, alt: "公告图片" }) : null
5156
5162
  ),
5157
5163
  h("div", { className: "dru-popup-foot" },
5158
- h("span", null, "已读状态已同步到后台"),
5159
5164
  h("button", { type: "button", onClick: close }, "关闭")
5160
5165
  )
5161
5166
  )
@@ -5181,17 +5186,15 @@ window.__ModuleLoader__.load({
5181
5186
  if (restartPending) {
5182
5187
  // 桌面端:刷新类照旧(刷新页面即可);需要重启 Host 的那一类**不给「立即重启」按钮** ——
5183
5188
  // 桌面端 Host 由 Electron 托管,插件点了也做不到,给个点不动的按钮只会让人以为是 bug。
5184
- // 改成如实引导:退出并重开 DeepSeek Harness。
5189
+ // 改成只提示刷新页面(2026-10-17:不再引导重开应用)。
5185
5190
  // ⚠️ 非桌面端必须保持**原有判据 isRestartHard**:改成 isRefreshOnly 会让"历史遗留 kind"
5186
5191
  // 的文案从"刷新页面即可生效"变成"重启 dsh web 后生效" —— 属于发布版用户可见的文案变更,
5187
5192
  // 不该混在桌面端适配里顺手做掉。桌面端才走新文案。
5188
- var restartRowText = desktopHost
5189
- ? (isRefreshOnly
5190
- ? "🔄 插件已更新:刷新页面即可生效"
5191
- : "🔄 插件已更新:退出并重新打开 DeepSeek Harness 后生效")
5192
- : (isRestartHard
5193
- ? "🔄 插件已更新:重启 dsh web 后生效"
5194
- : "🔄 插件已更新:刷新页面即可生效");
5193
+ // 2026-10-17(业主):桌面端运行环境由应用托管、更新自动热加载 ——
5194
+ // **不再引导用户重启 DeepSeek Harness**,统一只说「刷新页面即可生效」。
5195
+ var restartRowText = (desktopHost || !isRestartHard)
5196
+ ? "🔄 插件已更新:刷新页面即可生效"
5197
+ : "🔄 插件已更新:重启 dsh web 后生效";
5195
5198
  rows.push(h("div", { className: "dru-access-tip-row", key: "restart" },
5196
5199
  h("span", null, restartRowText),
5197
5200
  (isRefreshOnly || !desktopHost) ? h("button", {
@@ -5228,6 +5231,7 @@ window.__ModuleLoader__.load({
5228
5231
  )
5229
5232
  ),
5230
5233
  h("div", { className: "dru-settings-body" },
5234
+ renderSetupAlert(),
5231
5235
  restartPending
5232
5236
  // 【0.6.18】三种"待用户动一下"的情形,文案与主按钮**按 kind 一一对应**:
5233
5237
  // · refresh(插件文件在运行中被改写)→ 软提示 + 主按钮「刷新页面」(零风险动作)
@@ -5238,28 +5242,25 @@ window.__ModuleLoader__.load({
5238
5242
  className: "dru-restart-alert" + (isRestartHard ? " dru-restart-alert-strong" : ""),
5239
5243
  role: isRestartHard ? "alert" : "status"
5240
5244
  },
5241
- h("span", { className: "dru-restart-alert-icon" }, isRestartHard ? "⚠️" : "🔔"),
5245
+ h("span", { className: "dru-restart-alert-icon" }, (!desktopHost && isRestartHard) ? "⚠️" : "🔔"),
5242
5246
  h("div", { style: { flex: "1" } },
5243
5247
  h("div", { className: "dru-restart-alert-title" },
5244
- // 插件已改为 patch 热加载:装插件/在线更新都不再需要重启 harness,
5245
- // 唯一要用户动一下的情形是"插件文件在运行中被改写"→ 刷新页面即可。
5246
- // 桌面端:需要换 Host 进程的那一类改为引导「退出并重开应用」(插件无权重启 Host)。
5247
- isRestartHard
5248
- ? (desktopHost ? "插件已更新,退出并重新打开 DeepSeek Harness 后生效"
5249
- : "插件已更新,重启 dsh web 后生效")
5250
- : isRefreshOnly ? "插件已更新,刷新页面即可生效"
5251
- : (desktopHost ? "需要重新打开 DeepSeek Harness 才能载入新插件"
5252
- : "需要重启 dsh web 才能载入新插件")),
5248
+ // 2026-10-17(业主):桌面端运行环境由应用托管、插件安装/更新自动热加载 ——
5249
+ // **任何情况都不再引导"重启/退出重开应用"**,只说「刷新页面即可生效」。
5250
+ // 非桌面端(自建 dsh web)保留原有 isRestartHard 判据,文案不变。
5251
+ desktopHost
5252
+ ? "插件已更新:刷新页面即可生效"
5253
+ : (isRestartHard
5254
+ ? "插件已更新,重启 dsh web 后生效"
5255
+ : (isRefreshOnly ? "插件已更新,刷新页面即可生效" : "需要重启 dsh web 才能载入新插件"))),
5253
5256
  h("div", { className: "dru-restart-alert-sub" },
5254
- isRestartHard
5255
- ? (desktopHost
5256
- ? "新版本已经装到这台电脑上,但当前运行的 DeepSeek Harness 还是旧代码(新的后台路由与面板要等它重新载入)。桌面端的运行环境由应用本身托管,插件不能单独重启它 —— 请退出并重新打开 DeepSeek Harness 应用。手机端连接与已登录状态都不受影响。"
5257
- : "新版本已经装到这台电脑上,但当前运行的 dsh web 还是旧代码(新的后台路由与面板要等它重新载入)。手机端连接与已登录状态都不受影响;重启期间页面会短暂断开,然后自动恢复。")
5258
- : isRefreshOnly
5259
- ? "刚装好/刚更新的插件已在磁盘上,浏览器里的面板还是旧版本。刷新一下这个页面即可(手机端连接与已登录状态都不受影响)。"
5260
- : (desktopHost
5261
- ? "当前运行中的 DeepSeek Harness 里还没有载入这个插件。先刷新页面;如果刷新后仍然看不到面板,请退出并重新打开 DeepSeek Harness 应用。"
5262
- : "当前运行中的 dsh web 里还没有载入这个插件。先刷新页面;如果刷新后仍然看不到面板,再重启一次 dsh web。")),
5257
+ desktopHost
5258
+ ? "新插件已经在磁盘上,刷新一下这个页面即可载入(手机端连接与已登录状态都不受影响)。如果刷新后仍看不到面板,在应用内「插件」页重新安装一次即可。"
5259
+ : (isRestartHard
5260
+ ? "新版本已经装到这台电脑上,但当前运行的 dsh web 还是旧代码(新的后台路由与面板要等它重新载入)。手机端连接与已登录状态都不受影响;重启期间页面会短暂断开,然后自动恢复。"
5261
+ : (isRefreshOnly
5262
+ ? "刚装好/刚更新的插件已在磁盘上,浏览器里的面板还是旧版本。刷新一下这个页面即可(手机端连接与已登录状态都不受影响)。"
5263
+ : "当前运行中的 dsh web 里还没有载入这个插件。先刷新页面;如果刷新后仍然看不到面板,再重启一次 dsh web。"))),
5263
5264
  // 【自动化】待重启时自动倒计时重启(用户零操作);倒计时期间可一键取消
5264
5265
  restartCountdown !== null && restartCountdown > 0
5265
5266
  ? h("div", { className: "dru-restart-auto", role: "status" },
@@ -5311,21 +5312,17 @@ window.__ModuleLoader__.load({
5311
5312
  : view === "invite" ? renderInvite()
5312
5313
  : renderHome()
5313
5314
  ),
5314
- // 常驻入口最底部:「重启 DeepSeek harness」按钮始终可达(首次安装/更新/排查都用它)
5315
- // 桌面端:Host 由 Electron 托管,重启这个动作不存在 → 不摆一个点了必然失败的按钮,
5316
- // 改为如实说明「退出并重开桌面应用」。后台服务与手机端连接在两种形态下都不受影响。
5317
- h("div", { className: "dru-restart-foot" },
5318
- h("div", { className: "dru-hint" }, desktopHost
5319
- ? "重启 DeepSeek Harness:桌面端由应用本身托管运行环境,请直接退出并重新打开应用(无需其他操作)。日常安装/更新插件已自动热加载,装完刷新页面即可;重启不影响后台服务与手机端连接。"
5320
- : "重启 dsh web:仅在「刷新页面后仍看不到面板」时才需要。日常安装/更新插件已自动热加载,装完刷新页面即可;重启只影响 dsh web 本身,不影响后台服务与手机端连接。"),
5321
- desktopHost
5322
- ? null
5323
- : h("button", {
5324
- type: "button",
5325
- className: "dru-btn " + (restartPending ? "dru-btn-primary" : "dru-btn-ghost"),
5326
- disabled: busy !== "",
5327
- onClick: function () { restartHarness(); }
5328
- }, busy === "restart" ? "重启中…" : "🔄 重启 dsh web")
5315
+ // 2026-10-17(业主):删除「重启 DeepSeek Harness」整段说明 ——
5316
+ // 桌面端运行环境由应用本身托管、插件安装/更新自动热加载,刷新页面即可;
5317
+ // 只有非桌面端(自建 dsh web)才保留重启入口,且文案只讲这一种场景。
5318
+ desktopHost ? null : h("div", { className: "dru-restart-foot" },
5319
+ h("div", { className: "dru-hint" }, "刷新页面后仍看不到面板时,才需要重启 dsh web;日常安装/更新插件会自动热加载。"),
5320
+ h("button", {
5321
+ type: "button",
5322
+ className: "dru-btn " + (restartPending ? "dru-btn-primary" : "dru-btn-ghost"),
5323
+ disabled: busy !== "",
5324
+ onClick: function () { restartHarness(); }
5325
+ }, busy === "restart" ? "重启中…" : "🔄 重启 dsh web")
5329
5326
  ),
5330
5327
  renderCommunityModal(),
5331
5328
  // 📣 公告详情弹窗(与交流群弹窗同一层:都挂在面板根上,点遮罩即收起)