@androidsup/dsh-plugin-updates 0.0.0-stage → 0.1.0

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/CHANGELOG.md ADDED
@@ -0,0 +1,12 @@
1
+ # 更新日志
2
+
3
+ 创建于 2026-10-23
4
+
5
+ ## 0.1.0
6
+
7
+ - npm 包使用账号作用域 `@androidsup/dsh-plugin-updates`,避免与其他作者同名包混淆;桌面安装走 DSH 官方界面。
8
+ - 从路由插件抽出,拥有独立目录、Git 仓库和 npm 包。
9
+ - 只管理已安装插件:npm 版本与频道、GitHub 提交检查、逐项和批量确认更新、结果核验及脱敏诊断。
10
+ - 每日自动检查、同版提醒去重,保留启用状态,不提供插件市场或新插件安装。
11
+ - 默认遵守新版本等待期;更新管理自身安排在批次最后。
12
+ - 顶部展示 QQ 交流群与独立仓库链接,支持窄窗口换行及键盘聚焦。
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 DSH Multi Router contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,91 @@
1
- # Temporary Holding Version
1
+ # DSH 插件更新管理
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ 创建于 2026-10-23
4
+
5
+ [加入 QQ 交流群](https://qm.qq.com/q/eQC7OQXQe4) · 群号 **1124103038** · [GitHub 仓库](https://github.com/AndroidSix/dsh-plugin-updates)。觉得好用?点个 **Star** 鼓励一下。插件页面顶部也有入口。
6
+
7
+ **只管已安装插件,让每一次更新有据可查。**
8
+
9
+ `@androidsup/dsh-plugin-updates` 是独立的 DeepSeek Harness 插件,初始版本 **0.1.0**。它从 `dsh-multi-router` 拆出,不依赖路由、账号池、模型供应商、签到或 API 服务。拥有独立目录、Git 仓库、依赖锁文件和 npm 包,不需要 `dsh-multi-router` 源码、依赖或构建脚本。
10
+
11
+ ## 不是插件市场
12
+
13
+ 插件市场解决“装什么”,本插件解决“已经装的插件,该不该更新、更新到了哪里、为什么没成功”。
14
+
15
+ | 核心优势 | 具体实现 |
16
+ | --- | --- |
17
+ | 来源感知,而非只看包版本 | npm 比较 SemVer、沿用 `latest` / `next` 等频道;GitHub 比较原分支或标签的提交,包版本没变也能发现更新。固定 Git 提交不解除。 |
18
+ | 用户决定,而非后台代装 | DSH 运行期间每 24 小时检查;同一目标处理后不重复提醒,更高版本或新提交再次提醒。逐个或批量查看目标、明确确认后才更新。 |
19
+ | 核验结果,而非相信成功提示 | 更新后复查实际 npm 版本或锁文件中的 Git 提交。命令退出码为零但版本不符仍判为失败;区分已安装与重启后生效。 |
20
+ | 不扰动现有配置 | 只操作当前 DSH 配置中用户安装的插件,包含停用项;排除内置与受保护组件,保留原启用状态,不自动换源。 |
21
+ | 失败能继续处理 | 逐项显示失败,单项失败不阻断其它项;区分等待期、脚本授权、文件占用、兼容、网络及加载问题,可查看和复制脱敏诊断。 |
22
+
23
+ **明确不做:**插件搜索发现、远端目录、推荐、排行、评分、新插件安装、卸载、启停管理、任意版本降级。页面中的搜索仅筛选已安装清单。首次安装及依赖脚本授权仍使用 DSH 原生插件页。
24
+
25
+ ## 安装与使用
26
+
27
+ 独立 npm 包名为 `@androidsup/dsh-plugin-updates`。当前提供源码与本地安装包,尚未发布到 npm;完成 npm 登录后可按下文发布。开发环境要求 Node.js `^22.19.0 || >=24.0.0`。
28
+
29
+ ```bash
30
+ # 克隆独立仓库;私有仓库需要访问权限
31
+ git clone https://github.com/AndroidSix/dsh-plugin-updates.git
32
+ cd dsh-plugin-updates
33
+ npm ci
34
+ npm run check
35
+ npm run pack:release
36
+
37
+ # 将生成的安装包添加到所需 DSH 配置;替换成实际绝对路径
38
+ # 桌面版:在 DSH 插件页「添加插件」输入安装包绝对路径。
39
+ # desktop 配置由 Electron 独占管理,CLI 不可修改。
40
+ # 以下命令仅适用于非桌面配置,例如 web:
41
+ dsh plugin --profile web add /absolute/path/androidsup-dsh-plugin-updates-0.1.0.tgz
42
+ ```
43
+
44
+ 在 DSH 插件页打开本插件配置即可进入「插件更新管理」。支持按名称、来源、可更新状态筛选、单项或批量更新及取消。所有操作作用于当前配置,不跨配置、跨设备同步。
45
+
46
+ - npm 查询沿用 DSH 的 registry、认证及回退设置;可为本次更新选择 npm 官方或 npmmirror。npm 镜像不能解决 GitHub 的网络问题。
47
+ - 固定 npm 版本或范围在明确确认后更新到所查到的 `latest` 版本;显式 dist-tag 保留频道。GitHub 更新继续跟随原分支或标签,并核验已检查的提交;期间远端推进则要求重新检查。
48
+ - 检查结果有效期 10 分钟;安装来源、已安装版本或启用状态发生变化时拒绝旧操作,要求重新检查。
49
+ - **默认遵守新包等待期。**用户主动勾选「允许本次指定版本立即安装」后,才在当前配置的 `pnpm-workspace.yaml` 保存所列确切版本的等待期例外;不关闭全局策略,不放行其它版本、间接依赖或构建脚本。
50
+ - 更新管理插件自身放在批次最后,通过 DSH 官方更新执行器更换文件;不在运行中的更新事务内重载自身。按结果提示完全退出并重开 DSH。其它插件是否即时生效由 DSH 的安装结果决定;不承诺第三方插件无中断热更新。
51
+ - 本地文件或开发链接只标记为「原方式更新」,不猜测来源、不自动改源。GitHub 私有仓库元数据、限流与离线错误明确显示,不当作“已有更新”。
52
+
53
+ 注意:npm 上不带作用域的 `dsh-plugin-updates` 属于其他作者,不是本项目。请使用完整的 `@androidsup/dsh-plugin-updates` 包名。
54
+
55
+ ## 独立发布
56
+
57
+ 源码仓库:<https://github.com/AndroidSix/dsh-plugin-updates>。当前 GitHub 仓库保持私有;面板顶部仓库链接也指向此独立仓库,未获访问权限的用户无法查看或 Star。是否公开仓库与是否发布 npm 是两个独立决定。
58
+
59
+ ```bash
60
+ npm run check
61
+ npm pack --dry-run
62
+ npm login --registry=https://registry.npmjs.org/
63
+ npm publish
64
+ ```
65
+
66
+ `prepublishOnly` 会在发布前重新构建并运行测试;npm 包配置为公开发布。首次发布需要账号拥有 `@androidsup/dsh-plugin-updates` 包名权限,并按 npm 要求完成二次验证。未成功发布前,请使用本地 `.tgz`,不要把包名安装说明当作已上线。
67
+
68
+ GitHub 仓库单独维护提交和版本,路由项目不再调用本项目的构建或测试。`node_modules/`、本地安装包、认证配置和运行时数据不纳入 Git。
69
+
70
+ ## 数据与安全边界
71
+
72
+ 检查记录和提醒去重保存在当前 DSH 配置目录的 `.dsh-plugin-updates/state.json`,目录 0700、文件 0600;不同配置相互独立。安装与实际文件变更交给 DSH 的插件管理接口及官方执行器,不自建安装器。
73
+
74
+ 本地 HTTP 接口为 `/.well-known/dsh-plugin-updates`:仅允许回环地址和同源请求,写操作要求会话随机密钥,只接受检查、清单刷新、更新、提醒处理及取消操作。后台没有新插件安装接口。浏览器诊断不返回原始安装输出、账号令牌或用户路径。
75
+
76
+ 没有远端插件目录或自建统计服务;检查只访问当前已安装插件对应的 npm registry / GitHub 元数据,更新由 DSH 下载相应包。不会上传路由配置、账号或对话。
77
+
78
+ ## 从路由插件迁移
79
+
80
+ 升级到拆分后的 `dsh-multi-router` 后,旧更新页签、全局提醒和后台扫描移除;新插件需独立安装。旧路由、账号、用量和设置均保持不变。旧 `multi-router/plugin-updates.json` 保留但不再使用,不导入可能来自其它配置的旧检查结果或提醒状态;独立插件首次运行重新检查。若仍启用拆分前的旧路由插件,两套检查和提醒可能并存,应先升级路由插件。
81
+
82
+ ## 验证与限制
83
+
84
+ ```bash
85
+ npm run check # 独立构建与测试
86
+ npm pack --dry-run # 核对发布内容
87
+ ```
88
+
89
+ 测试覆盖来源解析、内置排除、停用项、版本和提交核验、批次隔离、取消、提醒持久化、等待期授权、HTTP 安全边界、前端模块注册及独立宿主生命周期。真实 registry 凭证、私有仓库、下载失败回滚和不同系统下的文件占用仍需在目标 DSH 中验收;不保证任意第三方插件都支持运行中更新。
90
+
91
+ 许可证为 MIT。SemVer 工具的上游来源及许可证见 [第三方声明](THIRD_PARTY_NOTICES.md)。
@@ -0,0 +1,29 @@
1
+ # Third-Party Notices
2
+
3
+ 创建于 2026-10-23
4
+
5
+ `src/version.mjs` adapts only the SemVer parser and comparator from `dsh-workbuddy-connect` (`upstream/workbuddy/src/update.ts`, commit `caad7a9447e9c5c4f09653c6d5c2c34782c9ad81`). Names and errors are generalized; no WorkBuddy runtime or API integration is included.
6
+
7
+ Source: https://github.com/corrinehu/dsh-workbuddy-connect
8
+
9
+ MIT License
10
+
11
+ Copyright (c) 2026 Corrine Hu
12
+
13
+ Permission is hereby granted, free of charge, to any person obtaining a copy
14
+ of this software and associated documentation files (the "Software"), to deal
15
+ in the Software without restriction, including without limitation the rights
16
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
+ copies of the Software, and to permit persons to whom the Software is
18
+ furnished to do so, subject to the following conditions:
19
+
20
+ The above copyright notice and this permission notice shall be included in all
21
+ copies or substantial portions of the Software.
22
+
23
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
+ SOFTWARE.
@@ -0,0 +1,3 @@
1
+ - insert:
2
+ - id: dsh-plugin-updates-0.1.0
3
+ name: ./lib/host-v0.1.0.js
package/lib/client.js ADDED
@@ -0,0 +1,444 @@
1
+ window.__ModuleLoader__.load({ id: "@androidsup/dsh-plugin-updates", factory: (require) => { var module = { exports: {} };
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name3 in all)
8
+ __defProp(target, name3, { get: all[name3], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/client.jsx
21
+ var client_exports = {};
22
+ __export(client_exports, {
23
+ UpdateReminder: () => UpdateReminder,
24
+ UpdatesIntro: () => UpdatesIntro,
25
+ UpdatesPage: () => UpdatesPage,
26
+ apply: () => apply,
27
+ inject: () => inject,
28
+ name: () => name2
29
+ });
30
+ module.exports = __toCommonJS(client_exports);
31
+ var import_react2 = require("react");
32
+
33
+ // package.json
34
+ var name = "@androidsup/dsh-plugin-updates";
35
+ var version = "0.1.0";
36
+ var repository = {
37
+ type: "git",
38
+ url: "https://github.com/AndroidSix/dsh-plugin-updates.git"
39
+ };
40
+
41
+ // src/panel.jsx
42
+ var import_react = require("react");
43
+ var import_jsx_runtime = require("react/jsx-runtime");
44
+ var when = (n) => n ? new Date(n).toLocaleString("zh-CN", { hour12: false }) : "\u5C1A\u672A\u83B7\u53D6";
45
+ function Modal({ title, close, children, busy }) {
46
+ const ref = (0, import_react.useRef)(null);
47
+ (0, import_react.useEffect)(() => {
48
+ const dialog = ref.current;
49
+ dialog.showModal();
50
+ return () => dialog.close();
51
+ }, []);
52
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("dialog", { ref, "aria-label": title, onCancel: (e) => {
53
+ e.preventDefault();
54
+ if (!busy) close();
55
+ }, children: [
56
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "modal-head bar", children: [
57
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { children: title }),
58
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", disabled: busy, onClick: close, children: "\u5173\u95ED" })
59
+ ] }),
60
+ children
61
+ ] });
62
+ }
63
+ var updateNames = { unchecked: "\u5F85\u68C0\u67E5", available: "\u53EF\u66F4\u65B0", current: "\u5DF2\u540C\u6B65", pinned: "\u56FA\u5B9A\u63D0\u4EA4", unsupported: "\u672C\u5730\u6765\u6E90", failed: "\u68C0\u67E5 / \u5B89\u88C5\u5931\u8D25", installed: "\u5DF2\u5B89\u88C5" };
64
+ function PluginUpdatesPanel({ data, busy, act }) {
65
+ const [selected, setSelected] = (0, import_react.useState)([]), [plan, setPlan] = (0, import_react.useState)(null), [registry, setRegistry] = (0, import_react.useState)(""), [filter, setFilter] = (0, import_react.useState)("all"), [pluginSearch, setPluginSearch] = (0, import_react.useState)("");
66
+ const [diagnostic, setDiagnostic] = (0, import_react.useState)(null), [copied, setCopied] = (0, import_react.useState)(false);
67
+ const running = data?.job?.state === "running", blocked = busy || running;
68
+ const rows = (data?.rows ?? []).filter((r) => r.source.kind !== "builtin"), available = rows.filter((r) => r.status === "available" || r.status === "failed" && r.target);
69
+ (0, import_react.useEffect)(() => {
70
+ if (data?.available && !running) void act({ action: !data.checkedAt || Date.now() - data.checkedAt > 864e5 ? "plugins.check" : "plugins.list" });
71
+ }, []);
72
+ const chosen = available.filter((r) => selected.includes(r.name));
73
+ const freshVersions = plan ? [...new Set(plan.rows.filter((r) => r.source.kind === "npm" && r.latestVersion).map((r) => `${r.source.package}@${r.latestVersion}`))] : [];
74
+ const visibleRows = rows.filter((r) => r.name.toLowerCase().includes(pluginSearch.toLowerCase()) && (filter === "all" || (filter === "updates" ? available.includes(r) : r.source.kind === filter)));
75
+ async function submitPlan() {
76
+ if (await act({ action: "plugins.update", names: plan.rows.map((r) => r.name), registry: registry || void 0, allowFreshVersions: plan.allowFresh === true ? freshVersions : [] })) {
77
+ setPlan(null);
78
+ setSelected([]);
79
+ }
80
+ }
81
+ if (!data?.available) return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "box stack", children: [
82
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: "\u5F53\u524D DSH \u672A\u63D0\u4F9B\u63D2\u4EF6\u7BA1\u7406\u63A5\u53E3" }),
83
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: "\u8BF7\u5347\u7EA7 DeepSeek Harness \u540E\u91CD\u8BD5\u3002\u672C\u9875\u7BA1\u7406\u5F53\u524D DSH \u914D\u7F6E\u4E2D\u7531\u7528\u6237\u5B89\u88C5\u7684\u63D2\u4EF6\u3002" })
84
+ ] });
85
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "stack", children: [
86
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "box stack", children: [
87
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bar", children: [
88
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
89
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: "\u5DF2\u5B89\u88C5\u63D2\u4EF6" }),
90
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "notice", children: [
91
+ rows.length,
92
+ " \u4E2A\u7528\u6237\u5B89\u88C5\u63D2\u4EF6 \xB7 ",
93
+ available.length,
94
+ " \u4E2A\u53EF\u66F4\u65B0"
95
+ ] })
96
+ ] }),
97
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "primary", disabled: blocked, onClick: () => {
98
+ void act({ action: "plugins.check" });
99
+ }, children: "\u68C0\u67E5\u5168\u90E8\u66F4\u65B0" })
100
+ ] }),
101
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "notice", children: [
102
+ "\u4E0A\u6B21\u68C0\u67E5 ",
103
+ when(data.checkedAt),
104
+ " \xB7 \u6BCF\u5929\u81EA\u52A8\u68C0\u67E5\u5E76\u63D0\u9192"
105
+ ] }),
106
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("details", { className: "help", children: [
107
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("summary", { children: "\u5B89\u88C5\u6E90\u4E0E\u68C0\u67E5\u89C4\u5219" }),
108
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "stack", children: [
109
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "filter", children: [
110
+ "npm \u5B89\u88C5\u6E90",
111
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", { value: registry, disabled: blocked, onChange: (e) => setRegistry(e.target.value), children: [
112
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "", children: "DSH \u8BBE\u7F6E\uFF08\u542B\u56DE\u9000\u6765\u6E90\uFF09" }),
113
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "https://registry.npmjs.org/", children: "npm \u5B98\u65B9" }),
114
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "https://registry.npmmirror.com/", children: "npmmirror" })
115
+ ] })
116
+ ] }),
117
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "notice", children: "npm \u6309\u7248\u672C / \u53D1\u5E03\u9891\u9053\u68C0\u67E5\uFF0CGitHub \u6309\u5206\u652F\u63D0\u4EA4\u68C0\u67E5\uFF1B\u4FDD\u7559\u542F\u7528\u72B6\u6001\uFF0C\u56FA\u5B9A Git \u63D0\u4EA4\u4FDD\u6301\u4E0D\u52A8\u3002" })
118
+ ] })
119
+ ] })
120
+ ] }),
121
+ data.error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "error", role: "alert", children: data.error }),
122
+ data.job && !(data.job.kind === "check" && data.job.state === "done") && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "box stack", "aria-live": "polite", children: [
123
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bar", children: [
124
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: running ? data.job.kind === "check" ? "\u6B63\u5728\u68C0\u67E5\u66F4\u65B0\u2026" : "\u6B63\u5728\u5B89\u88C5\u63D2\u4EF6\u2026" : data.job.state === "cancelled" ? "\u64CD\u4F5C\u5DF2\u53D6\u6D88" : data.job.state === "failed" || data.job.results.some((r) => r.status === "failed") ? "\u6709\u63D2\u4EF6\u672A\u5B8C\u6210\u5B89\u88C5" : "\u64CD\u4F5C\u5DF2\u5B8C\u6210" }),
125
+ running && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { disabled: busy, onClick: () => {
126
+ void act({ action: "plugins.cancel" });
127
+ }, children: "\u53D6\u6D88" })
128
+ ] }),
129
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("progress", { max: Math.max(1, data.job.total), value: data.job.results.length, "aria-label": "\u63D2\u4EF6\u64CD\u4F5C\u8FDB\u5EA6" }),
130
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "notice", children: [
131
+ data.job.results.length,
132
+ " / ",
133
+ data.job.total,
134
+ data.job.current ? ` \xB7 ${data.job.current}` : ""
135
+ ] }),
136
+ data.job.message && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "error", children: data.job.message }),
137
+ data.job.results.map((r, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "item", children: [
138
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: r.name }),
139
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: r.status === "failed" ? "error" : "notice", children: r.message || updateNames[r.status] || r.status }),
140
+ r.diagnostic && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", onClick: () => {
141
+ setCopied(false);
142
+ setDiagnostic(JSON.stringify({ plugin: r.name, targetVersion: r.expectedVersion, ...r.diagnostic }, null, 2));
143
+ }, children: "\u67E5\u770B\u8131\u654F\u8BCA\u65AD" }),
144
+ r.pendingBuilds?.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "notice", children: [
145
+ "\u5F85\u6388\u6743\u811A\u672C\uFF1A",
146
+ r.pendingBuilds.join("\u3001"),
147
+ "\u3002\u8BF7\u5728 DSH \u539F\u63D2\u4EF6\u5B89\u88C5\u9875\u6388\u6743\u540E\u91CD\u8BD5\u3002"
148
+ ] })
149
+ ] }, r.name + i)),
150
+ data.job.results.some((r) => r.restartRequired) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "steps", children: "\u66F4\u65B0\u6587\u4EF6\u5DF2\u5B89\u88C5\u3002macOS \u4F7F\u7528 \u2318Q \u5B8C\u5168\u9000\u51FA DSH \u540E\u91CD\u5F00\uFF1BWindows \u9000\u51FA\u6258\u76D8\u5E94\u7528\u540E\u91CD\u5F00\uFF1B\u7F51\u9875\u7248\u91CD\u542F\u670D\u52A1\u3002\u5173\u95ED\u7A97\u53E3\u548C\u300C\u5237\u65B0\u9762\u677F\u300D\u4E0D\u4F1A\u91CD\u8F7D\u540E\u53F0\u3002" })
151
+ ] }),
152
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "box table-shell", children: [
153
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "bar section-pad", children: [
154
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "row", children: [
155
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { disabled: blocked || !available.length, onClick: () => setSelected(available.map((r) => r.name)), children: "\u9009\u62E9\u5168\u90E8\u53EF\u66F4\u65B0" }),
156
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { disabled: blocked || !selected.length, onClick: () => setSelected([]), children: "\u6E05\u7A7A\u9009\u62E9" })
157
+ ] }),
158
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { className: "primary", disabled: blocked || !chosen.length, onClick: () => setPlan({ kind: "update", rows: chosen }), children: [
159
+ "\u66F4\u65B0\u6240\u9009\uFF08",
160
+ chosen.length,
161
+ "\uFF09"
162
+ ] })
163
+ ] }),
164
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "row section-pad", children: [
165
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { style: { flex: 1 }, children: [
166
+ "\u641C\u7D22\u5DF2\u5B89\u88C5\u63D2\u4EF6",
167
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { value: pluginSearch, onChange: (e) => setPluginSearch(e.target.value), placeholder: "\u6309\u63D2\u4EF6\u540D\u79F0\u7B5B\u9009" })
168
+ ] }),
169
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "filter", children: [
170
+ "\u663E\u793A\u8303\u56F4",
171
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", { value: filter, onChange: (e) => setFilter(e.target.value), children: [
172
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "all", children: "\u5168\u90E8\u7528\u6237\u5B89\u88C5\u63D2\u4EF6" }),
173
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "updates", children: "\u53EF\u66F4\u65B0 / \u91CD\u8BD5" }),
174
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "npm", children: "npm \u6765\u6E90" }),
175
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "github", children: "GitHub \u6765\u6E90" })
176
+ ] })
177
+ ] })
178
+ ] }),
179
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "scroll", role: "region", "aria-label": "\u7528\u6237\u5B89\u88C5\u63D2\u4EF6\u5217\u8868", tabIndex: "0", children: [
180
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("table", { className: "table", children: [
181
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
182
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { scope: "col", children: "\u63D2\u4EF6 / \u9009\u62E9" }),
183
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { scope: "col", children: "\u6765\u6E90\u4E0E\u7248\u672C" }),
184
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { scope: "col", children: "\u72B6\u6001" }),
185
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { scope: "col", className: "cell-actions", children: "\u64CD\u4F5C" })
186
+ ] }) }),
187
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tbody", { children: visibleRows.map((r) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { className: selected.includes(r.name) ? "selected" : "", children: [
188
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("td", { children: [
189
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "check", children: [
190
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { "aria-label": `\u9009\u62E9\u66F4\u65B0 ${r.name}`, type: "checkbox", disabled: blocked || !available.includes(r), checked: selected.includes(r.name), onChange: (e) => setSelected(e.target.checked ? [...selected, r.name] : selected.filter((n) => n !== r.name)) }),
191
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { className: "mono", children: r.name })
192
+ ] }),
193
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { className: "muted", children: r.enabled ? "\u542F\u7528" : "\u505C\u7528" })
194
+ ] }),
195
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("td", { children: [
196
+ r.source.kind === "npm" ? `npm \xB7 ${r.version || "\u672A\u77E5"}${r.latestVersion ? " \u2192 " + r.latestVersion : ""}` : r.source.kind === "github" ? `GitHub \xB7 ${r.version || "\u672A\u77E5\u7248\u672C"}` : "\u672C\u5730\u6587\u4EF6 / \u5F00\u53D1\u94FE\u63A5",
197
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { className: "muted mono", children: r.source.kind === "npm" ? `\u5B89\u88C5\u58F0\u660E ${r.source.source} \xB7 \u68C0\u67E5 ${r.source.tag}` : r.source.kind === "github" ? `${r.commit?.slice(0, 8) || "\u672A\u77E5\u63D0\u4EA4"}${r.latestCommit ? " \u2192 " + r.latestCommit.slice(0, 8) : ""}` : "" })
198
+ ] }),
199
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("td", { children: [
200
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: r.status === "failed" ? "error" : r.status === "available" ? "good" : "muted", children: updateNames[r.status] }),
201
+ r.message && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { className: r.status === "failed" ? "error" : "muted", children: r.message })
202
+ ] }),
203
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: "cell-actions", children: available.includes(r) ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { disabled: blocked, onClick: () => setPlan({ kind: "update", rows: [r] }), children: "\u67E5\u770B\u5E76\u66F4\u65B0" }) : "\u2014" })
204
+ ] }, r.name)) })
205
+ ] }),
206
+ !visibleRows.length && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "empty", children: running ? "\u6B63\u5728\u8BFB\u53D6\u63D2\u4EF6\u6E05\u5355\u2026" : rows.length ? "\u6CA1\u6709\u5339\u914D\u7684\u5DF2\u5B89\u88C5\u63D2\u4EF6\uFF0C\u8BF7\u8C03\u6574\u7B5B\u9009\u3002" : "\u5F53\u524D\u914D\u7F6E\u5C1A\u672A\u8BFB\u53D6\u63D2\u4EF6\uFF0C\u8BF7\u68C0\u67E5\u66F4\u65B0\u3002" })
207
+ ] })
208
+ ] }),
209
+ diagnostic && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Modal, { title: "\u63D2\u4EF6\u5B89\u88C5\u8BCA\u65AD", close: () => setDiagnostic(null), children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "modal-body stack", children: [
210
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "notice", children: "\u4EE5\u4E0B\u4FE1\u606F\u53EA\u542B\u63D2\u4EF6\u540D\u3001\u7248\u672C\u3001\u7CFB\u7EDF\u548C\u9519\u8BEF\u5206\u7C7B\uFF0C\u53EF\u590D\u5236\u7528\u4E8E\u6392\u67E5\uFF1B\u672A\u5305\u542B\u539F\u59CB\u65E5\u5FD7\u3001\u7528\u6237\u540D\u8DEF\u5F84\u6216\u51ED\u8BC1\u3002\u82E5\u539F\u56E0\u672A\u77E5\uFF0C\u8BF7\u5728 DSH \u539F\u751F\u63D2\u4EF6\u9875\u83B7\u53D6\u5B89\u88C5\u65E5\u5FD7\u3002" }),
211
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("textarea", { "aria-label": "\u8131\u654F\u5B89\u88C5\u8BCA\u65AD", readOnly: true, rows: "9", value: diagnostic, onFocus: (e) => e.currentTarget.select() }),
212
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { onClick: async () => {
213
+ try {
214
+ await navigator.clipboard.writeText(diagnostic);
215
+ setCopied(true);
216
+ } catch {
217
+ setCopied(false);
218
+ }
219
+ }, children: copied ? "\u5DF2\u590D\u5236" : "\u590D\u5236\u8BCA\u65AD" })
220
+ ] }) }),
221
+ plan && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Modal, { title: "\u786E\u8BA4\u66F4\u65B0\u63D2\u4EF6", close: () => setPlan(null), busy: blocked, children: [
222
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "modal-body stack", children: [
223
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: "\u8FD9\u4E9B\u64CD\u4F5C\u4F5C\u7528\u4E8E\u5F53\u524D DSH \u914D\u7F6E\u7684\u6240\u6709\u4F1A\u8BDD\u3002\u5B89\u88C5\u540E\u662F\u5426\u9700\u8981\u91CD\u542F\uFF0C\u4F1A\u5728\u7ED3\u679C\u4E2D\u8BF4\u660E\u3002" }),
224
+ plan.rows.map((r) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "steps", children: [
225
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: r.name }),
226
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "mono", children: r.target }),
227
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: r.source.kind === "npm" ? `${r.version || "\u672A\u77E5"} \u2192 ${r.latestVersion}` : `${r.commit?.slice(0, 8) || "\u672A\u77E5\u63D0\u4EA4"} \u2192 ${r.latestCommit?.slice(0, 8) || "\u672A\u77E5\u63D0\u4EA4"}` }),
228
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "notice", children: [
229
+ "\u4FDD\u7559\u4E3A",
230
+ r.enabled ? "\u542F\u7528" : "\u505C\u7528",
231
+ "\uFF1B",
232
+ r.source.kind === "github" ? "\u7EE7\u7EED\u8DDF\u968F\u539F\u5206\u652F / \u6807\u7B7E\uFF0C\u5E76\u6838\u9A8C\u63D0\u4EA4" : "\u6309\u539F\u9891\u9053\u66F4\u65B0\uFF0C\u5E76\u6838\u9A8C\u6240\u793A npm \u7248\u672C",
233
+ "\u3002"
234
+ ] })
235
+ ] }, r.name)),
236
+ freshVersions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "steps stack", children: [
237
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "check", children: [
238
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "checkbox", disabled: blocked, checked: plan.allowFresh === true, onChange: (e) => setPlan((p) => ({ ...p, allowFresh: e.target.checked })) }),
239
+ "\u5141\u8BB8\u672C\u6B21\u6307\u5B9A\u7248\u672C\u7ACB\u5373\u5B89\u88C5\uFF08\u4E0D\u7B49\u5F85 24 \u5C0F\u65F6\uFF09"
240
+ ] }),
241
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "notice", children: [
242
+ "\u786E\u8BA4\u66F4\u65B0\u540E\uFF0C\u4EC5\u4E3A\u4EE5\u4E0B\u7248\u672C\u4FDD\u5B58\u7B49\u5F85\u671F\u4F8B\u5916\uFF1A",
243
+ freshVersions.join("\u3001"),
244
+ "\u3002\u65B0\u5305\u7B49\u5F85\u671F\u7528\u4E8E\u964D\u4F4E\u4F9B\u5E94\u94FE\u98CE\u9669\uFF1B\u5176\u4ED6\u7248\u672C\u3001\u4F9D\u8D56\u548C\u811A\u672C\u6388\u6743\u4ECD\u9075\u5B88 DSH \u7B56\u7565\u3002\u9ED8\u8BA4\u9075\u5B88\u7B49\u5F85\u671F\uFF0C\u4EC5\u5728\u660E\u786E\u77E5\u6089\u98CE\u9669\u540E\u52FE\u9009\u3002"
245
+ ] })
246
+ ] }),
247
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "notice", children: [
248
+ "npm \u5B89\u88C5\u6E90\uFF1A",
249
+ registry || "\u6CBF\u7528 DSH \u8BBE\u7F6E"
250
+ ] })
251
+ ] }),
252
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "modal-foot row", children: [
253
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "primary", disabled: blocked, onClick: () => {
254
+ void submitPlan();
255
+ }, children: "\u786E\u8BA4\u66F4\u65B0" }),
256
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { disabled: blocked, onClick: () => setPlan(null), children: "\u53D6\u6D88" })
257
+ ] })
258
+ ] })
259
+ ] });
260
+ }
261
+
262
+ // src/styles.mjs
263
+ var styles = `
264
+ .pu{--pu-accent:var(--dsw-alias-state-active-primary,#3a71db);--pu-border:var(--dsw-alias-border-l2,#555);--pu-bg:var(--dsw-alias-bg-layer-1,#202020);--pu-card:var(--dsw-alias-bg-module-platform,#282828);--pu-text:var(--dsw-alias-label-primary,#f4f4f4);--pu-muted:var(--dsw-alias-label-secondary,#b8b8b8);--pu-soft:color-mix(in srgb,var(--pu-text) 4%,var(--pu-card));color:var(--pu-text);font:inherit;font-size:14px;line-height:1.6;max-width:1280px;min-width:0}
265
+ .pu *{box-sizing:border-box}.pu h2,.pu h3,.pu p{margin:0}.pu h2{font-size:24px;font-weight:650;letter-spacing:-.4px}.pu h3{font-size:16px}.pu small,.pu .muted,.pu .notice{color:var(--pu-muted)}.pu .notice{font-size:13px}.pu .mono{font:12px ui-monospace,monospace;overflow-wrap:anywhere}
266
+ .pu .bar,.pu .row{display:flex;gap:12px;align-items:center;flex-wrap:wrap}.pu .bar{justify-content:space-between}.pu.stack,.pu .stack{display:flex;flex-direction:column;gap:20px}.pu .box{border:1px solid var(--pu-border);border-radius:14px;padding:20px;background:var(--pu-card)}.pu header.box{background:linear-gradient(130deg,var(--pu-soft),var(--pu-card))}.pu .principles{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:20px}.pu .principles h3{margin-bottom:4px}
267
+ .pu .community{padding:10px 14px;border:1px solid var(--pu-border);border-radius:8px;background:var(--pu-card);font-size:13px;column-gap:16px;row-gap:8px}.pu .community a{display:inline-flex;align-items:center;min-height:44px;color:var(--pu-accent);font-weight:500;text-decoration:underline;text-underline-offset:3px}.pu .community a:hover{opacity:.8}.pu .community a:focus-visible{outline:2px solid var(--pu-accent);outline-offset:4px;border-radius:2px}.pu .community span{min-width:0;overflow-wrap:anywhere}
268
+ .pu button,.pu input,.pu select,.pu textarea{font:inherit;color:inherit;border:1px solid var(--pu-border);border-radius:8px;background:var(--pu-bg);min-height:44px;padding:9px 12px}.pu button{cursor:pointer;transition:background .15s}.pu button:hover{background:var(--pu-soft)}.pu button:disabled{opacity:.45;cursor:default}.pu button.primary{background:var(--pu-text);color:var(--pu-card);border-color:var(--pu-text)}.pu :is(button,input,select,textarea,summary):focus-visible{outline:2px solid var(--pu-accent);outline-offset:3px}.pu input:not([type=checkbox]),.pu select,.pu textarea{width:100%;min-width:0}.pu input[type=checkbox]{width:18px;height:18px;min-height:0;accent-color:var(--pu-accent)}.pu label{display:flex;flex-direction:column;gap:6px;font-size:13px}.pu label.check{flex-direction:row;align-items:center;gap:8px;min-height:44px}.pu .filter{min-width:170px;max-width:240px}
269
+ .pu .error{color:var(--dsw-alias-state-error-primary,#ef9393)}.pu .good{color:var(--dsw-alias-state-success-primary,#77ca9d)}.pu summary{cursor:pointer;color:var(--pu-muted);min-height:44px;display:flex;align-items:center}.pu details>div{padding-top:12px}.pu progress{width:100%;height:8px;accent-color:var(--pu-accent)}.pu .item{padding:16px 0;border-top:1px solid var(--pu-border)}.pu .empty{padding:24px;text-align:center;color:var(--pu-muted)}.pu .steps{padding:14px 16px;border:1px solid var(--pu-border);background:var(--pu-soft);border-radius:10px}
270
+ .pu .table-shell{padding:0;overflow:hidden}.pu .section-pad{padding:16px 20px}.pu .scroll{overflow:auto;max-width:100%}.pu .scroll:focus-visible{outline:2px solid var(--pu-accent);outline-offset:-3px}.pu .table{width:100%;min-width:760px;border-collapse:collapse;font-size:14px}.pu th,.pu td{text-align:left;border-bottom:1px solid var(--pu-border);padding:16px 20px;vertical-align:middle}.pu th{background:var(--pu-soft);font-size:12px;color:var(--pu-muted)}.pu .table td small{display:block;font-size:12px;margin-top:3px}.pu .table tr:last-child td{border-bottom:0}.pu .table tr.selected,.pu .table tbody tr:hover{background:var(--pu-soft)}.pu .cell-actions{text-align:right;white-space:nowrap}
271
+ .pu dialog{color:var(--pu-text);background:var(--pu-bg);border:1px solid var(--pu-border);border-radius:16px;width:min(900px,calc(100vw - 32px));max-height:calc(100dvh - 40px);padding:0;box-shadow:0 24px 80px #0008}.pu dialog::backdrop{background:#000a}.pu .modal-head,.pu .modal-foot{padding:18px 24px;background:var(--pu-bg);position:sticky;z-index:1}.pu .modal-head{top:0;border-bottom:1px solid var(--pu-border)}.pu .modal-foot{bottom:0;border-top:1px solid var(--pu-border)}.pu .modal-body{padding:24px}
272
+ @media(max-width:600px){.pu .principles{grid-template-columns:1fr;gap:12px}.pu .box,.pu .section-pad,.pu .modal-body,.pu .modal-head,.pu .modal-foot{padding:16px}.pu .box.table-shell{padding:0}.pu .filter{max-width:none;flex:1}.pu h2{font-size:22px}}
273
+ @media(prefers-reduced-motion:reduce){.pu *{transition:none!important}}
274
+ `;
275
+
276
+ // src/client.jsx
277
+ var import_jsx_runtime2 = require("react/jsx-runtime");
278
+ var name2 = "dsh-plugin-updates-client";
279
+ var inject = ["slots"];
280
+ var ENDPOINT = "/.well-known/dsh-plugin-updates";
281
+ var openUpdates;
282
+ function apply(ctx) {
283
+ openUpdates = () => ctx.get("pluginNavigation")?.openBundle(name);
284
+ ctx.slots.inject("plugins.bundle.config", () => ctx.slots.register({ name: "plugins.bundle.config", key: name }, UpdatesPage));
285
+ ctx.slots.inject("shell.overlay", () => ctx.slots.register({ name: "shell.overlay", id: "dsh-plugin-updates-reminder" }, UpdateReminder));
286
+ }
287
+ async function readResponse(response) {
288
+ let value;
289
+ try {
290
+ value = await response.json();
291
+ } catch {
292
+ throw Error("\u66F4\u65B0\u7BA1\u7406\u540E\u53F0\u5C1A\u672A\u8FDE\u63A5\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5");
293
+ }
294
+ if (!response.ok) throw Error(value.error || "\u64CD\u4F5C\u672A\u5B8C\u6210");
295
+ if (!value?.pluginUpdates || value.pluginVersion !== version) throw Error("\u66F4\u65B0\u7BA1\u7406\u524D\u540E\u53F0\u7248\u672C\u672A\u540C\u6B65\uFF0C\u8BF7\u5B8C\u5168\u9000\u51FA\u5E76\u91CD\u65B0\u6253\u5F00 DSH");
296
+ return value;
297
+ }
298
+ function useUpdates() {
299
+ const [data, setData] = (0, import_react2.useState)(null), [error, setError] = (0, import_react2.useState)(""), [busy, setBusy] = (0, import_react2.useState)(false);
300
+ const running = data?.pluginUpdates.job?.state === "running";
301
+ (0, import_react2.useEffect)(() => {
302
+ let live = true, pending = false;
303
+ const abort = new AbortController();
304
+ async function poll() {
305
+ if (document.hidden || pending) return;
306
+ pending = true;
307
+ try {
308
+ const value = await readResponse(await fetch(ENDPOINT, { cache: "no-store", signal: AbortSignal.any([abort.signal, AbortSignal.timeout(8e3)]) }));
309
+ if (live) {
310
+ setData(value);
311
+ setError("");
312
+ }
313
+ } catch (e) {
314
+ if (live) setError(e.message);
315
+ } finally {
316
+ pending = false;
317
+ }
318
+ }
319
+ void poll();
320
+ const timer = setInterval(poll, running ? 2e3 : 6e4);
321
+ document.addEventListener("visibilitychange", poll);
322
+ return () => {
323
+ live = false;
324
+ abort.abort();
325
+ clearInterval(timer);
326
+ document.removeEventListener("visibilitychange", poll);
327
+ };
328
+ }, [running]);
329
+ async function act(action) {
330
+ if (busy || !data) return false;
331
+ setBusy(true);
332
+ setError("");
333
+ try {
334
+ const value = await readResponse(await fetch(ENDPOINT, {
335
+ method: "POST",
336
+ headers: { "Content-Type": "application/json", "X-Plugin-Updates-Key": data.key },
337
+ body: JSON.stringify(action),
338
+ signal: AbortSignal.timeout(15e3)
339
+ }));
340
+ setData(value);
341
+ return value;
342
+ } catch (e) {
343
+ setError(e.message);
344
+ return false;
345
+ } finally {
346
+ setBusy(false);
347
+ }
348
+ }
349
+ return { data, error, busy, act };
350
+ }
351
+ function UpdatesIntro() {
352
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("header", { className: "box stack", children: [
353
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "bar", children: [
354
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h2", { children: "\u63D2\u4EF6\u66F4\u65B0\u7BA1\u7406" }),
355
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "notice", children: [
356
+ "v",
357
+ version
358
+ ] })
359
+ ] }),
360
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "row community", "aria-label": "\u9879\u76EE\u4EA4\u6D41\u4E0E\u652F\u6301", children: [
361
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("a", { href: "https://qm.qq.com/q/eQC7OQXQe4", target: "_blank", rel: "noopener noreferrer", children: "\u52A0\u5165 QQ \u4EA4\u6D41\u7FA4 \u2197" }),
362
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "muted", children: [
363
+ "AndroidSix \u7684\u5F00\u6E90\u9879\u76EE\u4EA4\u6D41\u7FA4 \xB7 \u7FA4\u53F7 ",
364
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("strong", { children: "1124103038" })
365
+ ] }),
366
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("a", { href: repository.url.replace(/\.git$/, ""), target: "_blank", rel: "noopener noreferrer", children: "GitHub \u4ED3\u5E93 \u2197" }),
367
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "muted", children: "\u89C9\u5F97\u597D\u7528\uFF1F\u70B9\u4E2A Star \u9F13\u52B1\u4E00\u4E0B" })
368
+ ] }),
369
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { children: "\u53EA\u7BA1\u5DF2\u5B89\u88C5\u63D2\u4EF6\uFF0C\u8BA9\u6BCF\u4E00\u6B21\u66F4\u65B0\u6709\u636E\u53EF\u67E5\u3002" }),
370
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "principles", children: [
371
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("section", { children: [
372
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { children: "\u6765\u6E90\u4E0D\u4E71\u6362" }),
373
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "notice", children: "npm \u9891\u9053\u4E0E GitHub \u63D0\u4EA4\u5206\u522B\u68C0\u67E5\uFF0C\u4FDD\u7559\u542F\u7528\u72B6\u6001\u3002" })
374
+ ] }),
375
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("section", { children: [
376
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { children: "\u66F4\u65B0\u5148\u786E\u8BA4" }),
377
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "notice", children: "\u6BCF\u5929\u68C0\u67E5\u3001\u540C\u7248\u63D0\u9192\u4E00\u6B21\uFF1B\u4E0D\u4F1A\u81EA\u52A8\u5B89\u88C5\u3002" })
378
+ ] }),
379
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("section", { children: [
380
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { children: "\u7ED3\u679C\u8981\u6838\u9A8C" }),
381
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "notice", children: "\u6838\u5BF9\u5B9E\u9645\u7248\u672C\u6216\u9501\u6587\u4EF6\u63D0\u4EA4\uFF0C\u5B89\u88C5\u4E0E\u751F\u6548\u5206\u5F00\u8BF4\u660E\u3002" })
382
+ ] })
383
+ ] }),
384
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "notice", children: "\u4E0D\u662F\u63D2\u4EF6\u5E02\u573A\uFF1A\u4E0D\u63D0\u4F9B\u53D1\u73B0\u3001\u63A8\u8350\u3001\u6392\u884C\u6216\u65B0\u63D2\u4EF6\u5B89\u88C5\u3002\u5B89\u88C5\u3001\u5378\u8F7D\u548C\u811A\u672C\u6388\u6743\u4ECD\u5728 DSH \u539F\u751F\u63D2\u4EF6\u9875\u5904\u7406\u3002" })
385
+ ] });
386
+ }
387
+ function UpdatesPage() {
388
+ const { data, error, busy, act } = useUpdates();
389
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("main", { className: "pu stack", children: [
390
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("style", { children: styles }),
391
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(UpdatesIntro, {}),
392
+ error && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "error", role: "alert", children: error }),
393
+ data ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(PluginUpdatesPanel, { data: data.pluginUpdates, busy, act }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { role: "status", children: "\u6B63\u5728\u8FDE\u63A5\u66F4\u65B0\u7BA1\u7406\u540E\u53F0\u2026" })
394
+ ] });
395
+ }
396
+ function UpdateReminder() {
397
+ const { data, busy, act } = useUpdates();
398
+ const [opened, setOpened] = (0, import_react2.useState)(null), [error, setError] = (0, import_react2.useState)("");
399
+ const reminders = data?.pluginUpdates.reminders;
400
+ (0, import_react2.useEffect)(() => {
401
+ if (reminders?.length && !document.querySelector("dialog[open]")) setOpened((current) => current ?? reminders);
402
+ if (!reminders?.length) setOpened(null);
403
+ }, [reminders]);
404
+ async function dismiss(open = false) {
405
+ const result = await act({ action: "plugins.dismiss", keys: opened.map((row) => row.key) });
406
+ if (!result) {
407
+ setError("\u63D0\u9192\u5904\u7406\u672A\u4FDD\u5B58\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5");
408
+ return;
409
+ }
410
+ setOpened(null);
411
+ if (open) openUpdates?.();
412
+ }
413
+ if (!opened || !data) return null;
414
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "pu", children: [
415
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("style", { children: styles }),
416
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Modal, { title: "\u5DF2\u5B89\u88C5\u63D2\u4EF6\u6709\u66F4\u65B0", busy, close: () => {
417
+ void dismiss();
418
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "modal-body stack", children: [
419
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { children: [
420
+ opened.length,
421
+ " \u4E2A\u63D2\u4EF6\u53EF\u66F4\u65B0\u3002\u4E0D\u4F1A\u81EA\u52A8\u5B89\u88C5\uFF0C\u8BF7\u5148\u67E5\u770B\u76EE\u6807\u7248\u672C\u3002"
422
+ ] }),
423
+ error && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { role: "alert", className: "error", children: error }),
424
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ul", { children: opened.map((row) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("li", { children: [
425
+ row.name,
426
+ " \xB7 ",
427
+ row.version || "\u672A\u77E5\u7248\u672C",
428
+ " \u2192 ",
429
+ row.latestVersion || row.latestCommit?.slice(0, 8)
430
+ ] }, row.key)) }),
431
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "notice", children: "\u540C\u4E00\u66F4\u65B0\u5904\u7406\u540E\u4E0D\u91CD\u590D\u63D0\u9192\uFF1B\u66F4\u9AD8\u7248\u672C\u6216\u65B0\u63D0\u4EA4\u4F1A\u518D\u6B21\u63D0\u9192\u3002" }),
432
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "row", children: [
433
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: "primary", disabled: busy, onClick: () => {
434
+ void dismiss(true);
435
+ }, children: "\u67E5\u770B\u66F4\u65B0\u7BA1\u7406" }),
436
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { disabled: busy, onClick: () => {
437
+ void dismiss();
438
+ }, children: "\u7A0D\u540E\u5904\u7406" })
439
+ ] })
440
+ ] }) })
441
+ ] });
442
+ }
443
+ return module.exports; } });
444
+ //# sourceMappingURL=client.js.map