@linxin666/dsh-remote-web-ui 0.1.16 → 0.1.18

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 (48) hide show
  1. package/README.i18n.yaml +2 -2
  2. package/README.md +5 -2
  3. package/README.zh.md +2 -2
  4. package/assets/apple-touch-icon.png +0 -0
  5. package/lib/client.js +48 -58
  6. package/lib/client.js.map +1 -1
  7. package/lib/index.js +76 -14
  8. package/lib/mobile.js +649 -45
  9. package/lib/mobile.js.map +1 -1
  10. package/lib/types/client/PluginSettingsCard.d.ts +11 -0
  11. package/lib/types/client/PluginSettingsCard.d.ts.map +1 -1
  12. package/lib/types/client/PluginSettingsCard.js +11 -5
  13. package/lib/types/mobile/markdown.d.ts +26 -0
  14. package/lib/types/mobile/markdown.d.ts.map +1 -0
  15. package/lib/types/mobile/markdown.js +272 -0
  16. package/lib/types/mobile/markdown.test.d.ts +2 -0
  17. package/lib/types/mobile/markdown.test.d.ts.map +1 -0
  18. package/lib/types/mobile/markdown.test.js +67 -0
  19. package/lib/types/mobile/messages.d.ts +14 -0
  20. package/lib/types/mobile/messages.d.ts.map +1 -1
  21. package/lib/types/mobile/messages.js +54 -2
  22. package/lib/types/mobile/messages.test.js +99 -0
  23. package/lib/types/mobile/mobile-styles.d.ts +1 -1
  24. package/lib/types/mobile/mobile-styles.d.ts.map +1 -1
  25. package/lib/types/mobile/mobile-styles.js +167 -0
  26. package/lib/types/mobile/views/ChatView.d.ts.map +1 -1
  27. package/lib/types/mobile/views/ChatView.js +81 -5
  28. package/lib/types/mobile/views/ChatView.test.js +105 -0
  29. package/lib/types/mobile-api.d.ts.map +1 -1
  30. package/lib/types/mobile-api.js +19 -2
  31. package/lib/types/mobile-routes.d.ts.map +1 -1
  32. package/lib/types/mobile-routes.js +32 -0
  33. package/lib/types/update.d.ts +5 -3
  34. package/lib/types/update.d.ts.map +1 -1
  35. package/lib/types/update.js +22 -6
  36. package/package.json +2 -1
  37. package/src/client/PluginSettingsCard.tsx +50 -47
  38. package/src/client/settings-card.module.css +9 -0
  39. package/src/mobile/markdown.test.ts +77 -0
  40. package/src/mobile/markdown.ts +278 -0
  41. package/src/mobile/messages.test.ts +106 -0
  42. package/src/mobile/messages.ts +66 -2
  43. package/src/mobile/mobile-styles.ts +167 -0
  44. package/src/mobile/views/ChatView.test.tsx +121 -0
  45. package/src/mobile/views/ChatView.tsx +164 -6
  46. package/src/mobile-api.ts +19 -2
  47. package/src/mobile-routes.ts +33 -0
  48. package/src/update.ts +23 -6
package/lib/index.js CHANGED
@@ -981,6 +981,10 @@ function makeRoutes(deps) {
981
981
  function mobileBundlePath() {
982
982
  return fileURLToPath(new URL("../lib/mobile.js", import.meta.url));
983
983
  }
984
+ /** The apple-touch-icon shipped with the package (iOS home-screen icon, 180x180 PNG). */
985
+ function appleTouchIconPath() {
986
+ return fileURLToPath(new URL("../assets/apple-touch-icon.png", import.meta.url));
987
+ }
984
988
  /** The mobile page shell: minimal, offline-safe, no external assets. */
985
989
  function pageHtml(bundleUrl) {
986
990
  return [
@@ -991,6 +995,7 @@ function pageHtml(bundleUrl) {
991
995
  "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover\">",
992
996
  "<meta name=\"theme-color\" content=\"#f3f5f9\">",
993
997
  "<meta name=\"referrer\" content=\"no-referrer\">",
998
+ "<link rel=\"apple-touch-icon\" href=\"/m/apple-touch-icon.png\">",
994
999
  "<title>移动端远程控制</title>",
995
1000
  "</head>",
996
1001
  "<body>",
@@ -1009,6 +1014,15 @@ function writeStatic(res, status, type, body) {
1009
1014
  });
1010
1015
  res.end(body);
1011
1016
  }
1017
+ /** Send the PNG icon as a binary body (unlike text assets, the PNG must not be decoded to utf-8). */
1018
+ function writePng(res, status, body) {
1019
+ res.writeHead(status, {
1020
+ "content-type": "image/png",
1021
+ "cache-control": "public, max-age=31536000, immutable",
1022
+ "referrer-policy": "no-referrer"
1023
+ });
1024
+ res.end(body);
1025
+ }
1012
1026
  /**
1013
1027
  * Build the mobile page routes.
1014
1028
  * @returns the two exact routes to register on webServer.
@@ -1029,15 +1043,35 @@ function makeMobileRoutes() {
1029
1043
  writeStatic(res, 500, "text/plain", "failed to read the mobile bundle");
1030
1044
  }
1031
1045
  };
1032
- return [{
1033
- kind: "exact",
1034
- path: "/m",
1035
- handler: handlePage
1036
- }, {
1037
- kind: "exact",
1038
- path: "/m/mobile.js",
1039
- handler: handleBundle
1040
- }];
1046
+ const handleIcon = async (_req, res) => {
1047
+ const path = appleTouchIconPath();
1048
+ if (!existsSync(path)) {
1049
+ writeStatic(res, 404, "text/plain", "apple-touch-icon not found");
1050
+ return;
1051
+ }
1052
+ try {
1053
+ writePng(res, 200, await readFile(path));
1054
+ } catch {
1055
+ writeStatic(res, 500, "text/plain", "failed to read the apple-touch-icon");
1056
+ }
1057
+ };
1058
+ return [
1059
+ {
1060
+ kind: "exact",
1061
+ path: "/m",
1062
+ handler: handlePage
1063
+ },
1064
+ {
1065
+ kind: "exact",
1066
+ path: "/m/mobile.js",
1067
+ handler: handleBundle
1068
+ },
1069
+ {
1070
+ kind: "exact",
1071
+ path: "/m/apple-touch-icon.png",
1072
+ handler: handleIcon
1073
+ }
1074
+ ];
1041
1075
  }
1042
1076
  //#endregion
1043
1077
  //#region src/mobile-api.ts
@@ -1096,10 +1130,22 @@ const MOBILE_API_METHOD_PREFIX = `${MOBILE_API_PREFIX}/`;
1096
1130
  function makeMobileApiRoutes(deps) {
1097
1131
  const { service, apiProxy, mobileEnterToSend } = deps;
1098
1132
  const eventsHeartbeatMs = deps.eventsHeartbeatMs ?? DEFAULT_EVENTS_HEARTBEAT_MS;
1133
+ /**
1134
+ * Refresh the paired device's presence and report whether it is live.
1135
+ * The mobile surface (unlike the desktop Web UI) has no `/api/pair/heartbeat`
1136
+ * sender, so any activity on the mobile channel — a gated RPC, or the live
1137
+ * SSE stream staying open — must count as presence. Without this, an
1138
+ * idle-but-connected phone ages past `offlineAfterMs` and the desktop panel
1139
+ * wrongly reports it as disconnected.
1140
+ */
1141
+ const touchDeviceFor = (req) => {
1142
+ const deviceId = readCookie(req.headers.cookie, service.config.cookieName);
1143
+ if (deviceId === void 0) return false;
1144
+ return service.touchDevice(deviceId);
1145
+ };
1099
1146
  /** The phone gate: a live paired-device cookie, or nothing else proceeds. */
1100
1147
  const gateOk = (req) => {
1101
- const deviceId = readCookie(req.headers.cookie, service.config.cookieName);
1102
- return deviceId !== void 0 && service.hasDevice(deviceId);
1148
+ return touchDeviceFor(req);
1103
1149
  };
1104
1150
  const writeJson = (res, status, body) => {
1105
1151
  res.writeHead(status, { "content-type": "application/json; charset=utf-8" });
@@ -1219,6 +1265,7 @@ function makeMobileApiRoutes(deps) {
1219
1265
  let closed = false;
1220
1266
  const heartbeat = setInterval(() => {
1221
1267
  if (closed) return;
1268
+ touchDeviceFor(req);
1222
1269
  try {
1223
1270
  res.write(": ping\n\n");
1224
1271
  } catch {}
@@ -1787,6 +1834,16 @@ const OUTPUT_CAP = 16 * 1024;
1787
1834
  */
1788
1835
  const WIN_CMD_MISSING_RE = /not recognized as an internal or external command/i;
1789
1836
  /**
1837
+ * Bypass for pnpm 11's supply-chain gate: `minimumReleaseAge` (default 24 h)
1838
+ * silently skips same-day releases — `pnpm update --latest` then exits 0
1839
+ * without moving anything and the post-run verification would misreport a
1840
+ * stale no-op. The update here is explicitly user-initiated and the panel
1841
+ * shows exactly which versions are being installed, so the gate only adds a
1842
+ * confusing silent no-op; override it per-invocation instead of asking every
1843
+ * user to edit the profile's pnpm-workspace.yaml.
1844
+ */
1845
+ const MIN_RELEASE_AGE_OVERRIDE = "--config.minimumReleaseAge=0";
1846
+ /**
1790
1847
  * Run the update inside the profile directory. Tries pnpm first, falls back
1791
1848
  * to corepack and then npx when the previous command is missing (ENOENT);
1792
1849
  * all candidates share one hard timeout and keep accumulating output.
@@ -1813,6 +1870,7 @@ function runUpdate(deps) {
1813
1870
  args: [
1814
1871
  "update",
1815
1872
  "--latest",
1873
+ MIN_RELEASE_AGE_OVERRIDE,
1816
1874
  ...packages
1817
1875
  ]
1818
1876
  },
@@ -1822,6 +1880,7 @@ function runUpdate(deps) {
1822
1880
  "pnpm",
1823
1881
  "update",
1824
1882
  "--latest",
1883
+ MIN_RELEASE_AGE_OVERRIDE,
1825
1884
  ...packages
1826
1885
  ]
1827
1886
  },
@@ -1832,6 +1891,7 @@ function runUpdate(deps) {
1832
1891
  "pnpm",
1833
1892
  "update",
1834
1893
  "--latest",
1894
+ MIN_RELEASE_AGE_OVERRIDE,
1835
1895
  ...packages
1836
1896
  ]
1837
1897
  }
@@ -1930,9 +1990,11 @@ function runUpdate(deps) {
1930
1990
  }
1931
1991
  /**
1932
1992
  * Run the update, then verify the installed versions actually moved. pnpm
1933
- * exits 0 even when it silently kept the installed versions — the pnpm 11
1934
- * `minimumReleaseAge` gate refuses same-day releases by default — so a green
1935
- * exit alone would report a misleading "update complete". Re-read the
1993
+ * exits 0 even when it silently kept the installed versions — runUpdate
1994
+ * overrides pnpm 11's `minimumReleaseAge` gate (default 24 h) with
1995
+ * `--config.minimumReleaseAge=0`, but an older pnpm without the override or
1996
+ * another silent no-op can still keep versions in place — so a green exit
1997
+ * alone must not report a misleading "update complete". Re-read the
1936
1998
  * installed versions afterwards and surface a `stale` failure (with the
1937
1999
  * captured pnpm output) when nothing moved, so the panel can tell the user
1938
2000
  * how to unblock the gate instead of claiming success.