@linxin666/dsh-remote-web-ui 0.1.13 → 0.1.15

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 (38) hide show
  1. package/README.i18n.yaml +2 -2
  2. package/README.md +5 -2
  3. package/README.zh.md +1 -1
  4. package/lib/client.js +30 -17
  5. package/lib/client.js.map +1 -1
  6. package/lib/index.js +177 -34
  7. package/lib/mobile.js +44 -25
  8. package/lib/mobile.js.map +1 -1
  9. package/lib/types/client/PluginSettingsCard.d.ts +26 -11
  10. package/lib/types/client/PluginSettingsCard.d.ts.map +1 -1
  11. package/lib/types/client/PluginSettingsCard.js +27 -7
  12. package/lib/types/client/locales.js +2 -2
  13. package/lib/types/client/settings-form.d.ts +14 -5
  14. package/lib/types/client/settings-form.d.ts.map +1 -1
  15. package/lib/types/client/settings-form.js +38 -10
  16. package/lib/types/mobile/mux.d.ts +13 -4
  17. package/lib/types/mobile/mux.d.ts.map +1 -1
  18. package/lib/types/mobile/mux.js +50 -30
  19. package/lib/types/mobile/mux.test.js +75 -6
  20. package/lib/types/mobile-api.d.ts +2 -0
  21. package/lib/types/mobile-api.d.ts.map +1 -1
  22. package/lib/types/mobile-api.js +4 -1
  23. package/lib/types/routes.d.ts +17 -0
  24. package/lib/types/routes.d.ts.map +1 -1
  25. package/lib/types/routes.js +49 -10
  26. package/lib/types/update.d.ts +5 -1
  27. package/lib/types/update.d.ts.map +1 -1
  28. package/lib/types/update.js +122 -26
  29. package/package.json +1 -1
  30. package/src/client/PluginSettingsCard.tsx +83 -17
  31. package/src/client/locales.ts +2 -2
  32. package/src/client/settings-card.module.css +6 -0
  33. package/src/client/settings-form.ts +41 -9
  34. package/src/mobile/mux.test.ts +84 -6
  35. package/src/mobile/mux.ts +46 -25
  36. package/src/mobile-api.ts +6 -1
  37. package/src/routes.ts +51 -10
  38. package/src/update.ts +116 -26
package/README.i18n.yaml CHANGED
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record with:
4
4
  # node scripts/verify-docs.mjs --write <dir>
5
- README.md: de8361a7e0e284e664f8e4d87d53eb508f82d5a4
6
- README.zh.md: 6dfbf4cc8664c3eac6f45087abe9293d185ce946
5
+ README.md: 97f5f6b000e12fd89ad8723fd25306075a10df06
6
+ README.zh.md: 30c7d55a4f31e914091318c3205d23f3b9979f60
package/README.md CHANGED
@@ -39,8 +39,11 @@ actions, and the update panel that probes and runs the update.
39
39
  phone icon) opens the update panel, which probes the npm registry for the
40
40
  installed `@linxin666/dsh-*` family releases. When a newer release exists
41
41
  the panel runs the update automatically (`pnpm update` inside the owning
42
- dsh profile; the loopback-only `/api/update/status` + `/api/update/run`
43
- endpoints drive it) and asks for a dsh web restart to pick it up. Local
42
+ dsh profile; when pnpm is missing it falls back to `corepack pnpm` and
43
+ then `npx --yes pnpm`, and on Windows the command runs through `cmd.exe`
44
+ so npm-installed `.cmd` shims resolve; the loopback-only
45
+ `/api/update/status` + `/api/update/run` endpoints drive it) and asks for
46
+ a dsh web restart to pick it up. Local
44
47
  link installs (development mode) are detected and report the npm state
45
48
  without updating.
46
49
 
package/README.zh.md CHANGED
@@ -13,7 +13,7 @@
13
13
  - **手机侧**:扫码将手机与一次性、限时令牌绑定,并落地到 **`/m` 独立移动端界面**——一款专为小屏设计的轻客户端(见[截图](#截图)),而不是把桌面 UI 塞进手机。链接携带 `workspace` 参数,手机落地到桌面正在查看的同一工作区。
14
14
  - **安全**:一个有效的一次性令牌(刷新会使旧链接失效;已接受的令牌不可复用;令牌会过期)。停止会撤销每一台已配对设备与当前令牌——已配对设备在下一次请求时被切断。当插件 `requirePairingForLan` 门开启(默认)时,每个非 loopback 的 `/api` 请求必须携带有效的已配对设备 cookie,因此二维码是进入暴露在局域网上的 dsh web 的唯一途径。
15
15
  - **实时状态**:桌面面板经 SSE 流实时镜像配对状态(等待 → 已连接 → 已断开)。
16
- - **远程更新**:侧边栏底部的下载触发按钮(手机图标左侧)打开更新面板,它探测 npm registry 上已安装的 `@linxin666/dsh-*` 全家桶版本。当存在较新版本时,面板自动执行更新(在所属 dsh profile 内 `pnpm update`;由仅 loopback 的 `/api/update/status` + `/api/update/run` 端点驱动)并请求重启 dsh web 以生效。本地 link 安装(开发模式)会被探测到,只报告 npm 状态而不更新。
16
+ - **远程更新**:侧边栏底部的下载触发按钮(手机图标左侧)打开更新面板,它探测 npm registry 上已安装的 `@linxin666/dsh-*` 全家桶版本。当存在较新版本时,面板自动执行更新(在所属 dsh profile 内 `pnpm update`;pnpm 缺失时依次回退 `corepack pnpm`、`npx --yes pnpm`,Windows 上经 `cmd.exe` 执行以解析 npm 安装的 `.cmd` shim;由仅 loopback 的 `/api/update/status` + `/api/update/run` 端点驱动)并请求重启 dsh web 以生效。本地 link 安装(开发模式)会被探测到,只报告 npm 状态而不更新。
17
17
 
18
18
  ## 截图
19
19
 
package/lib/client.js CHANGED
@@ -2217,7 +2217,7 @@ window.__ModuleLoader__.load({
2217
2217
  }
2218
2218
  //#endregion
2219
2219
  //#region \0dsh-css:packages/dsh-remote-web-ui/src/client/settings-card.module.css.mjs
2220
- const css = ".Kwoi6G_card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}.Kwoi6G_card:hover{border-color:var(--dsw-alias-label-dimmed)}.Kwoi6G_cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}.Kwoi6G_header{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}.Kwoi6G_header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.Kwoi6G_headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.Kwoi6G_name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.Kwoi6G_description{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.Kwoi6G_pending{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.Kwoi6G_chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.Kwoi6G_chevronOpen{transform:rotate(180deg)}.Kwoi6G_body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:8px}.Kwoi6G_readOnly{color:var(--dsw-alias-label-tertiary);margin:12px 0 0;font-size:12px;line-height:1.5}.Kwoi6G_notExposed{color:var(--dsw-alias-state-warn-primary);margin:12px 0 0;font-size:12px;line-height:1.5}.Kwoi6G_footer{border-top:1px solid var(--dsw-alias-border-l2);justify-content:flex-end;align-items:center;gap:8px;padding:12px 0 4px;display:flex}.Kwoi6G_failed{min-width:0;color:var(--dsw-alias-label-error);flex:1;margin:0;font-size:12px;line-height:1.5}.Kwoi6G_discard,.Kwoi6G_save{appearance:none;font:inherit;cursor:pointer;border:1px solid #0000;border-radius:8px;padding:5px 14px;font-size:13px;line-height:1.5}.Kwoi6G_discard{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:0 0}.Kwoi6G_discard:hover:not(:disabled){color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}.Kwoi6G_save{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3)}.Kwoi6G_discard:disabled,.Kwoi6G_save:disabled{opacity:.4;cursor:default}.Kwoi6G_discard:focus-visible,.Kwoi6G_save:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.Kwoi6G_field{flex-direction:column;gap:6px;padding:12px 0;display:flex}.Kwoi6G_field+.Kwoi6G_field{border-top:1px solid var(--dsw-alias-border-l2)}.Kwoi6G_head{align-items:center;gap:8px;display:flex}.Kwoi6G_label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}.Kwoi6G_badges{align-items:center;gap:8px;display:inline-flex}.Kwoi6G_badge{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.Kwoi6G_reset{font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;padding:0;font-size:12px;line-height:1.5}.Kwoi6G_reset:hover:not(:disabled){color:var(--dsw-alias-label-primary)}.Kwoi6G_reset:disabled{cursor:default}.Kwoi6G_reset:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}.Kwoi6G_input,.Kwoi6G_select{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}.Kwoi6G_input:focus-visible,.Kwoi6G_select:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}.Kwoi6G_input:disabled,.Kwoi6G_select:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.Kwoi6G_inputInvalid{border:1px solid var(--dsw-alias-label-error);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}.Kwoi6G_inputInvalid:focus-visible{outline:2px solid var(--dsw-alias-label-error);outline-offset:1px;border-color:var(--dsw-alias-label-error)}.Kwoi6G_invalid{color:var(--dsw-alias-label-error);margin:0;font-size:12px;line-height:1.5}.Kwoi6G_hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}@media (prefers-reduced-motion:reduce){.Kwoi6G_card,.Kwoi6G_header,.Kwoi6G_chevron,.Kwoi6G_chevronOpen,.Kwoi6G_discard,.Kwoi6G_save{transition:none}}";
2220
+ const css = ".Kwoi6G_card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}.Kwoi6G_card:hover{border-color:var(--dsw-alias-label-dimmed)}.Kwoi6G_cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}.Kwoi6G_header{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}.Kwoi6G_header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.Kwoi6G_headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.Kwoi6G_name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.Kwoi6G_description{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.Kwoi6G_pending{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.Kwoi6G_chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.Kwoi6G_chevronOpen{transform:rotate(180deg)}.Kwoi6G_body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:8px}.Kwoi6G_readOnly{color:var(--dsw-alias-label-tertiary);margin:12px 0 0;font-size:12px;line-height:1.5}.Kwoi6G_notExposed{color:var(--dsw-alias-state-warn-primary);margin:12px 0 0;font-size:12px;line-height:1.5}.Kwoi6G_footer{border-top:1px solid var(--dsw-alias-border-l2);justify-content:flex-end;align-items:center;gap:8px;padding:12px 0 4px;display:flex}.Kwoi6G_failed{min-width:0;color:var(--dsw-alias-label-error);flex:1;margin:0;font-size:12px;line-height:1.5}.Kwoi6G_discard,.Kwoi6G_save{appearance:none;font:inherit;cursor:pointer;border:1px solid #0000;border-radius:8px;padding:5px 14px;font-size:13px;line-height:1.5}.Kwoi6G_discard{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:0 0}.Kwoi6G_discard:hover:not(:disabled){color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}.Kwoi6G_save{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3)}.Kwoi6G_discard:disabled,.Kwoi6G_save:disabled{opacity:.4;cursor:default}.Kwoi6G_discard:focus-visible,.Kwoi6G_save:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.Kwoi6G_field{flex-direction:column;gap:6px;padding:12px 0;display:flex}.Kwoi6G_field+.Kwoi6G_field{border-top:1px solid var(--dsw-alias-border-l2)}.Kwoi6G_head{align-items:center;gap:8px;display:flex}.Kwoi6G_label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}.Kwoi6G_badges{align-items:center;gap:8px;display:inline-flex}.Kwoi6G_badge{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.Kwoi6G_reset{font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;padding:0;font-size:12px;line-height:1.5}.Kwoi6G_reset:hover:not(:disabled){color:var(--dsw-alias-label-primary)}.Kwoi6G_reset:disabled{cursor:default}.Kwoi6G_reset:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px;outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}.Kwoi6G_input,.Kwoi6G_select{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}.Kwoi6G_input:focus-visible,.Kwoi6G_select:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}.Kwoi6G_input:disabled,.Kwoi6G_select:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.Kwoi6G_inputInvalid{border:1px solid var(--dsw-alias-label-error);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}.Kwoi6G_inputInvalid:focus-visible{outline:2px solid var(--dsw-alias-label-error);outline-offset:1px;border-color:var(--dsw-alias-label-error)}.Kwoi6G_invalid{color:var(--dsw-alias-label-error);margin:0;font-size:12px;line-height:1.5}.Kwoi6G_hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}@media (prefers-reduced-motion:reduce){.Kwoi6G_card,.Kwoi6G_header,.Kwoi6G_chevron,.Kwoi6G_chevronOpen,.Kwoi6G_discard,.Kwoi6G_save{transition:none}}";
2221
2221
  const tagId = "@linxin666/dsh-remote-web-ui/settings-card.module.css";
2222
2222
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
2223
2223
  const tag = document.createElement("style");
@@ -2258,12 +2258,12 @@ window.__ModuleLoader__.load({
2258
2258
  //#endregion
2259
2259
  //#region src/client/PluginSettingsCard.tsx
2260
2260
  /**
2261
- * Shared chrome for the plugin settings card: a disclosure header naming the
2262
- * plugin and what its settings govern, the controls inside, and the save that
2263
- * writes them. Renders nothing while the namespace is unavailable — a
2261
+ * Family-shared chrome for plugin settings cards: a disclosure header naming
2262
+ * the plugin and what its settings govern, the controls inside, and the save
2263
+ * that writes them. Renders nothing while the namespace is unavailable — a
2264
2264
  * deployment that does not compose the owning plugin should show no trace of
2265
- * it. Mirrors the official ui-plugin-config PluginCard in a self-contained
2266
- * slice (this package must not depend on a sibling UI package).
2265
+ * it. Inlined into each consumer's client bundle; mirrors the official
2266
+ * ui-plugin-config PluginCard in a self-contained slice.
2267
2267
  */
2268
2268
  /**
2269
2269
  * Render one plugin settings card.
@@ -2275,6 +2275,7 @@ window.__ModuleLoader__.load({
2275
2275
  const { state } = props;
2276
2276
  if (!state.available) return null;
2277
2277
  const title = props.t(props.titleKey);
2278
+ const description = props.t(props.descriptionKey);
2278
2279
  const blocked = !state.dirty || state.invalid || state.saving;
2279
2280
  const cardClass = open ? `${settings_card_module_css_default.cardOpen} ${settings_card_module_css_default.card}` : settings_card_module_css_default.card;
2280
2281
  if (!state.exposed) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
@@ -2291,10 +2292,12 @@ window.__ModuleLoader__.load({
2291
2292
  className: settings_card_module_css_default.headText,
2292
2293
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2293
2294
  className: settings_card_module_css_default.name,
2295
+ title,
2294
2296
  children: title
2295
2297
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2296
2298
  className: settings_card_module_css_default.description,
2297
- children: props.t(props.descriptionKey)
2299
+ title: description,
2300
+ children: description
2298
2301
  })]
2299
2302
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
2300
2303
  width: "14",
@@ -2332,14 +2335,17 @@ window.__ModuleLoader__.load({
2332
2335
  className: settings_card_module_css_default.headText,
2333
2336
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2334
2337
  className: settings_card_module_css_default.name,
2338
+ title,
2335
2339
  children: title
2336
2340
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2337
2341
  className: settings_card_module_css_default.description,
2338
- children: props.t(props.descriptionKey)
2342
+ title: description,
2343
+ children: description
2339
2344
  })]
2340
2345
  }),
2341
2346
  state.dirty ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2342
2347
  className: settings_card_module_css_default.pending,
2348
+ title: props.t("settings.unsaved"),
2343
2349
  children: props.t("settings.unsaved")
2344
2350
  }) : null,
2345
2351
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
@@ -2494,8 +2500,9 @@ window.__ModuleLoader__.load({
2494
2500
  }
2495
2501
  //#endregion
2496
2502
  //#region src/client/settings-form.ts
2497
- /** A whole-number field. An empty draft clears the field; any other draft that is not a finite number blocks the save. */
2498
- function numberField(field) {
2503
+ /** A whole- or decimal-number field. An empty draft clears the field; any other draft that is not a finite number within the constraints blocks the save. */
2504
+ function numberField(field, constraints = {}) {
2505
+ const { integer = false, min } = constraints;
2499
2506
  return {
2500
2507
  field,
2501
2508
  format: (value) => typeof value === "number" ? String(value) : "",
@@ -2503,10 +2510,13 @@ window.__ModuleLoader__.load({
2503
2510
  const trimmed = text.trim();
2504
2511
  if (trimmed === "") return { kind: "clear" };
2505
2512
  const parsed = Number(trimmed);
2506
- return Number.isFinite(parsed) ? {
2513
+ if (!Number.isFinite(parsed)) return void 0;
2514
+ if (integer && !Number.isInteger(parsed)) return void 0;
2515
+ if (min !== void 0 && parsed < min) return void 0;
2516
+ return {
2507
2517
  kind: "set",
2508
2518
  value: parsed
2509
- } : void 0;
2519
+ };
2510
2520
  }
2511
2521
  };
2512
2522
  }
@@ -2530,11 +2540,13 @@ window.__ModuleLoader__.load({
2530
2540
  field,
2531
2541
  format: (value) => typeof value === "boolean" ? String(value) : "",
2532
2542
  parse: (text) => {
2533
- if (text === "true") return {
2543
+ const trimmed = text.trim();
2544
+ if (trimmed === "") return { kind: "clear" };
2545
+ if (trimmed === "true") return {
2534
2546
  kind: "set",
2535
2547
  value: true
2536
2548
  };
2537
- if (text === "false") return {
2549
+ if (trimmed === "false") return {
2538
2550
  kind: "set",
2539
2551
  value: false
2540
2552
  };
@@ -2636,12 +2648,13 @@ window.__ModuleLoader__.load({
2636
2648
  const plan = this.plan();
2637
2649
  const writes = plan.flatMap((item) => item.run === void 0 ? [] : [item.run]);
2638
2650
  if (plan.length === 0 || this.saving || writes.length !== plan.length) return;
2651
+ const fields = new Set(plan.map((item) => item.field));
2639
2652
  this.saving = true;
2640
2653
  this.failed = false;
2641
2654
  this.publish();
2642
2655
  let landed = true;
2643
2656
  for (const write of writes) landed = await write() && landed;
2644
- if (landed) this.staged.clear();
2657
+ if (landed) for (const field of fields) this.staged.delete(field);
2645
2658
  this.saving = false;
2646
2659
  this.failed = !landed;
2647
2660
  this.publish();
@@ -3022,7 +3035,7 @@ window.__ModuleLoader__.load({
3022
3035
  "update.packages": "组件版本",
3023
3036
  "update.retry": "重新检查",
3024
3037
  "update.close": "关闭更新面板",
3025
- "update.error.pnpmMissing": "未找到 pnpm,请先安装 pnpm(brew install pnpm 或 corepack enable)。",
3038
+ "update.error.pnpmMissing": "未找到 pnpm,已尝试 pnpm / corepack / npx 仍不可用。请安装 pnpm 后重启应用(Windows: npm install -g pnpm 或 corepack enable;macOS: brew install pnpm 或 corepack enable)。",
3026
3039
  "update.error.timeout": "更新超时,已终止安装进程,请重试。",
3027
3040
  "update.error.notFound": "未找到 dsh-web-ui 聚合包安装,无法更新。",
3028
3041
  "update.error.link": "当前为本地开发模式(链接安装),无法自动更新。",
@@ -3124,7 +3137,7 @@ window.__ModuleLoader__.load({
3124
3137
  "update.packages": "Component versions",
3125
3138
  "update.retry": "Check again",
3126
3139
  "update.close": "Close update panel",
3127
- "update.error.pnpmMissing": "pnpm was not found. Install it first (brew install pnpm or corepack enable).",
3140
+ "update.error.pnpmMissing": "pnpm was not found (tried pnpm / corepack / npx). Install pnpm and restart the app (Windows: npm install -g pnpm or corepack enable; macOS: brew install pnpm or corepack enable).",
3128
3141
  "update.error.timeout": "The update timed out and the install process was killed; try again.",
3129
3142
  "update.error.notFound": "The dsh-web-ui aggregate package is not installed; nothing to update.",
3130
3143
  "update.error.link": "Local development mode (link install) — automatic update is unavailable.",