dsh-plugin 1.0.0 → 1.1.1

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 (39) hide show
  1. package/README.en.md +12 -7
  2. package/README.md +12 -7
  3. package/client/client.js +725 -223
  4. package/client/client.js.map +1 -1
  5. package/lib/http/routes.js +43 -7
  6. package/lib/services/installed-versions.js +16 -1
  7. package/lib/services/loader.js +12 -5
  8. package/lib/services/npm-resolve.js +67 -0
  9. package/lib/services/task-queue.js +55 -10
  10. package/lib/types/services/install-types.d.ts +8 -0
  11. package/lib/types/services/installed-versions.d.ts +10 -0
  12. package/lib/types/services/loader.d.ts +6 -4
  13. package/lib/types/services/npm-resolve.d.ts +6 -0
  14. package/lib/types/services/task-queue.d.ts +11 -5
  15. package/package.json +1 -1
  16. package/src/client/components/AboutModal.tsx +72 -0
  17. package/src/client/components/CatalogHeader.tsx +30 -5
  18. package/src/client/components/HubUpdateModal.tsx +84 -0
  19. package/src/client/components/NotificationsModal.tsx +84 -58
  20. package/src/client/components/PluginCard.tsx +3 -0
  21. package/src/client/components/PluginHubSection.tsx +73 -14
  22. package/src/client/components/modals.tsx +42 -18
  23. package/src/client/hooks/useCatalog.ts +96 -18
  24. package/src/client/hooks/useTaskQueue.ts +164 -54
  25. package/src/client/lib/catalog.ts +81 -13
  26. package/src/client/lib/failures.ts +14 -2
  27. package/src/client/lib/renderMarkdown.ts +116 -0
  28. package/src/client/locales.ts +47 -4
  29. package/src/client/styles/Header.module.css +83 -3
  30. package/src/client/styles/List.module.css +4 -4
  31. package/src/client/styles/Modal.module.css +320 -5
  32. package/src/client/styles/tokens.module.css +8 -3
  33. package/src/client/types.ts +3 -1
  34. package/src/server/http/routes.ts +42 -7
  35. package/src/server/services/install-types.ts +8 -0
  36. package/src/server/services/installed-versions.ts +20 -1
  37. package/src/server/services/loader.ts +12 -5
  38. package/src/server/services/npm-resolve.ts +67 -0
  39. package/src/server/services/task-queue.ts +57 -10
package/client/client.js CHANGED
@@ -27,6 +27,8 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
27
27
  failedDesc: "请稍后重试,或点击右上角按钮在浏览器中打开插件中心。",
28
28
  retry: "重试",
29
29
  install: "安装",
30
+ cliOnlyHint: "此插件可能需命令行辅助,失败时复制上方命令到 dsh 终端",
31
+ failedCopyHint: "安装失败。请复制上方安装命令到 dsh 终端手动执行(原生依赖构建脚本可能需要人工放行,如 pnpm approve-builds)",
30
32
  toastCopied: "安装命令已复制,去 dsh 终端粘贴即可安装",
31
33
  installed: "已安装",
32
34
  notInstalled: "未安装",
@@ -54,8 +56,8 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
54
56
  installNow: "直接安装",
55
57
  installing: "安装中…",
56
58
  installDone: "安装成功",
57
- installResultTitle: "安装完成",
58
- installResultDesc: "插件已安装。",
59
+ installResultTitle: "插件安装成功",
60
+ installResultDesc: "",
59
61
  confirmUpdateTitle: "确认更新",
60
62
  updateNow: "直接更新",
61
63
  updating: "更新中…",
@@ -69,6 +71,7 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
69
71
  restartLater: "稍后重启",
70
72
  restarting: "正在重启…",
71
73
  restartHint: "部分插件(如插件市场、顶层 bundle)需重启后才会生效。",
74
+ done: "完成",
72
75
  restartPendingHint: "安装成功,重启后生效",
73
76
  restartPendingHintUninstall: "卸载成功,重启后移除",
74
77
  sectionPendingRestart: "待重启",
@@ -91,6 +94,7 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
91
94
  notificationsDesc: "安装、卸载任务的成败都会记录在这里,即使错过提示也能随时回来查看。",
92
95
  notificationsEmpty: "暂无通知记录",
93
96
  notificationsClear: "清空通知",
97
+ removeNotification: "删除这条通知",
94
98
  failCopy: "复制完整错误信息",
95
99
  failIssueHint: "带着错误日志去作者仓库一键提交 Issue(正文含官网收录链接)",
96
100
  failIssueBig: "一键提交 BUG 到 GitHub Issue 为开源作贡献",
@@ -114,6 +118,21 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
114
118
  update: "可更新",
115
119
  updateAvailable: "有更新",
116
120
  updateAvailableHint: "检测到更新:有版本号的仓库比对版本号,无版本号的仓库比对仓库最近更新时间",
121
+ hubUpdateHint: "检测到 DSH-Plugin Hub 新版本,点击查看更新内容并更新",
122
+ hubUpdateTitle: "DSH-Plugin Hub 有新版本",
123
+ hubUpdateDesc: "v{version} 已发布,点击下方按钮即可更新,完成后需重启宿主生效。",
124
+ hubUpdatePublished: "发布于",
125
+ hubUpdateNotes: "更新记录",
126
+ hubUpdateLater: "稍后再说",
127
+ versionHint: "查看版本信息与更新记录",
128
+ hubCurrentTitle: "当前版本",
129
+ hubCurrentDesc: "你正在使用 v{version},已是最新版本。",
130
+ hubUpToDate: "已是最新版本",
131
+ followUs: "关注我们",
132
+ aboutTitle: "关注我们",
133
+ aboutDesc: "了解 DSH-Plugin Hub 是做什么的,加入用户反馈群随时交流。",
134
+ aboutEmpty: "内容暂未发布,敬请期待。",
135
+ aboutUpdated: "更新于",
117
136
  empty: "该分类暂无插件",
118
137
  dataFrom: "数据源 dsh-plugin.org · 每日人工更新"
119
138
  };
@@ -138,6 +157,8 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
138
157
  failedDesc: "Please retry, or open the hub in your browser with the button above.",
139
158
  retry: "Retry",
140
159
  install: "Install",
160
+ cliOnlyHint: "This plugin may need command-line help; if it fails, copy the command above and run it in your dsh terminal",
161
+ failedCopyHint: "Install failed. Copy the command above and run it manually in your dsh terminal (native build scripts may need manual approval, e.g. pnpm approve-builds)",
141
162
  toastCopied: "Install command copied — paste it in your dsh terminal",
142
163
  installed: "Installed",
143
164
  notInstalled: "Not installed",
@@ -165,8 +186,8 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
165
186
  installNow: "Install",
166
187
  installing: "Installing…",
167
188
  installDone: "Installed",
168
- installResultTitle: "Installed",
169
- installResultDesc: "The plugin has been installed.",
189
+ installResultTitle: "Plugin installed successfully",
190
+ installResultDesc: "",
170
191
  confirmUpdateTitle: "Confirm update",
171
192
  updateNow: "Update",
172
193
  updating: "Updating…",
@@ -180,6 +201,7 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
180
201
  restartLater: "Later",
181
202
  restarting: "Restarting…",
182
203
  restartHint: "Some plugins (such as plugin markets / top-level bundles) only take effect after a restart.",
204
+ done: "Done",
183
205
  restartPendingHint: "Installed — takes effect after restart",
184
206
  restartPendingHintUninstall: "Removed — cleaned up after restart",
185
207
  sectionPendingRestart: "Pending restart",
@@ -202,6 +224,7 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
202
224
  notificationsDesc: "Every install and remove result is logged here — even if you missed the prompt, you can always check back.",
203
225
  notificationsEmpty: "No notifications yet",
204
226
  notificationsClear: "Clear notifications",
227
+ removeNotification: "Remove this notification",
205
228
  failCopy: "Copy full log",
206
229
  failIssueHint: "Open a pre-filled issue on the author repo with this error log (includes catalog links)",
207
230
  failIssueBig: "Report this bug to GitHub — contribute to open source",
@@ -225,12 +248,27 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
225
248
  update: "Update",
226
249
  updateAvailable: "Update available",
227
250
  updateAvailableHint: "An update was detected: repos with a release are compared by tag, repos without one by last push time",
251
+ hubUpdateHint: "A new DSH-Plugin Hub version is available — click to view the changelog and update",
252
+ hubUpdateTitle: "A new DSH-Plugin Hub version is available",
253
+ hubUpdateDesc: "v{version} has been released — click below to update, then restart the host to apply it.",
254
+ hubUpdatePublished: "Published",
255
+ hubUpdateNotes: "What's new",
256
+ hubUpdateLater: "Later",
257
+ versionHint: "View version info and changelog",
258
+ hubCurrentTitle: "Current version",
259
+ hubCurrentDesc: "You are on v{version} — this is the latest version.",
260
+ hubUpToDate: "Up to date",
261
+ followUs: "Follow us",
262
+ aboutTitle: "Follow us",
263
+ aboutDesc: "Learn what DSH-Plugin Hub does and join our user feedback group.",
264
+ aboutEmpty: "Content not published yet — stay tuned.",
265
+ aboutUpdated: "Updated",
228
266
  empty: "No plugins in this category yet",
229
267
  dataFrom: "Data from dsh-plugin.org · curated daily"
230
268
  };
231
269
  //#endregion
232
270
  //#region \0dsh-css:src/client/styles/Header.module.css.mjs
233
- const css$4 = ".qikqja_root{min-width:0;height:100%;color:var(--hub-text-primary);flex-direction:column;gap:8px;display:flex}.qikqja_header{flex-direction:column;gap:2px;padding:2px 2px 0;display:flex}.qikqja_headerRight{flex-shrink:0;align-items:center;gap:2px;display:inline-flex}.qikqja_langBtn{height:24px;color:var(--hub-text-tertiary);cursor:pointer;-webkit-user-select:none;user-select:none;background:0 0;border:none;border-radius:6px;flex-shrink:0;justify-content:center;align-items:center;padding:0 8px;font-size:12px;font-weight:600;transition:color .15s,background-color .15s;display:inline-flex}.qikqja_langBtn:hover{color:var(--hub-purple-1);background-color:var(--hub-purple-tint)}.qikqja_githubLink{color:var(--hub-text-tertiary);cursor:pointer;border-radius:6px;flex-shrink:0;align-items:center;padding:3px;text-decoration:none;transition:color .15s,background-color .15s;display:inline-flex}.qikqja_githubLink:hover{color:var(--hub-purple-1);background-color:var(--hub-purple-tint)}.qikqja_githubIcon{flex-shrink:0;display:block}.qikqja_headerTitleRow{justify-content:space-between;align-items:center;gap:8px;min-width:0;display:flex}.qikqja_brandTitle{min-width:0;color:inherit;cursor:pointer;align-items:center;gap:10px;text-decoration:none;display:flex}.qikqja_taglineLink{min-width:0;max-width:100%;color:inherit;cursor:pointer;align-self:flex-start;text-decoration:none}.qikqja_title{margin:0;font-size:14px;font-weight:600;line-height:20px}.qikqja_version{color:var(--hub-text-secondary)}.qikqja_logoIcon{flex-shrink:0;display:block}.qikqja_copyIcon{flex-shrink:0}.qikqja_tagline{color:var(--hub-text-tertiary);white-space:nowrap;text-overflow:ellipsis;margin:0;font-size:12px;line-height:18px;overflow:hidden}.qikqja_controls{flex-shrink:0;align-items:center;gap:4px;display:flex}.qikqja_filterResults{color:var(--hub-text-tertiary);white-space:nowrap;flex-shrink:0;align-items:baseline;font-size:12px;line-height:18px;display:inline-flex}.qikqja_resultCount{font-variant-numeric:tabular-nums;text-align:center;min-width:5ch}.qikqja_adBanner{border:1px solid var(--hub-purple-border);background:linear-gradient(90deg, var(--hub-purple-1) 0%, var(--hub-purple-2) 100%);color:#fff;cursor:pointer;-webkit-user-select:none;user-select:none;box-shadow:0 2px 10px var(--hub-purple-shadow);border-radius:8px;align-items:center;gap:8px;padding:7px 12px;text-decoration:none;transition:filter .15s,box-shadow .15s;display:flex}.qikqja_adBanner:hover{filter:brightness(1.1);box-shadow:0 4px 16px var(--hub-purple-shadow-strong)}.qikqja_adBadge{letter-spacing:.04em;color:#fff;white-space:nowrap;background:#ffffff29;border:1px solid #ffffff8c;border-radius:4px;flex-shrink:0;padding:3px 6px;font-size:10px;font-weight:700;line-height:1}.qikqja_adText{text-overflow:ellipsis;white-space:nowrap;color:#fff;min-width:0;font-size:12px;line-height:18px;overflow:hidden}.qikqja_adArrow{color:#fff;flex-shrink:0;margin-left:auto;font-size:13px}.qikqja_searchRow{padding:0 2px;display:flex}.qikqja_search{width:50%;color:inherit;border:1px solid var(--hub-border-2);background:0 0;border-radius:6px;outline:none;padding:4px 9px;font-size:12px;line-height:18px;transition:border-color .12s}.qikqja_search::placeholder{color:var(--hub-text-tertiary)}.qikqja_search:focus{border-color:var(--hub-brand)}.qikqja_installedBtn,.qikqja_installedBtnActive,.qikqja_installedBtnDisabled{cursor:pointer;-webkit-user-select:none;user-select:none;border:none;border-radius:6px;flex-shrink:0;align-items:center;gap:5px;height:24px;padding:0 8px;font-size:12px;line-height:22px;transition:color .12s,background .12s;display:inline-flex}.qikqja_installedBtn{color:var(--hub-text-primary);background:0 0}.qikqja_installedBtn:hover{color:var(--hub-text-primary);background:var(--hub-bg-hover)}.qikqja_installedBtnDisabled,.qikqja_installedBtnDisabled:hover{color:var(--hub-text-disabled);cursor:not-allowed;background:0 0}.qikqja_installedBtnDisabled .qikqja_segCount{color:var(--hub-text-disabled);background:#8080800f}.qikqja_installedBtnActive{color:var(--hub-text-on-fill);background:var(--hub-btn-fill)}.qikqja_segCount,.qikqja_segCountActive{text-align:center;border-radius:999px;min-width:14px;padding:0 4px;font-size:10px;line-height:14px}.qikqja_segCount{color:var(--hub-text-tertiary);background:var(--hub-bg-btn)}.qikqja_installedBtnActive .qikqja_segCountActive{color:var(--hub-text-on-fill);background:var(--hub-bg-on-fill)}.qikqja_tabs{border-bottom:1px solid var(--hub-border-1);flex-wrap:wrap;align-items:center;gap:6px;padding:2px 2px 8px;display:flex}.qikqja_tab,.qikqja_tabActive{cursor:pointer;-webkit-user-select:none;user-select:none;border:none;border-radius:999px;flex-shrink:0;align-items:center;gap:6px;padding:3px 10px;font-size:12px;line-height:18px;transition:color .12s,background .12s;display:inline-flex}.qikqja_tab{color:var(--hub-text-secondary);background:0 0}.qikqja_tab:hover{color:var(--hub-text-primary);background:var(--hub-bg-hover)}.qikqja_tabActive{color:var(--hub-text-on-fill);background:var(--hub-btn-fill)}.qikqja_tabCount{text-align:center;min-width:16px;color:var(--hub-text-tertiary);background:var(--hub-bg-btn);border-radius:999px;padding:0 5px;font-size:10px;line-height:14px}.qikqja_tabActive .qikqja_tabCount{color:var(--hub-text-on-fill);background:var(--hub-bg-on-fill)}.qikqja_failBtn{box-sizing:border-box;border:1px solid var(--hub-border-2);height:24px;color:var(--hub-text-primary);background:var(--hub-bg-hover);cursor:pointer;-webkit-user-select:none;user-select:none;border-radius:6px;flex-shrink:0;align-items:center;margin-left:auto;padding:0 10px;font-size:12px;line-height:22px;transition:color .12s,background .12s;display:inline-flex;position:relative}.qikqja_failBtn:hover{color:var(--hub-text-primary);background:#80808047}.qikqja_failBadge{z-index:1;text-align:center;color:#fff;background:var(--hub-danger);border-radius:999px;min-width:16px;height:16px;padding:0 5px;font-size:10px;font-weight:600;line-height:16px;position:absolute;top:-16px;right:-7px;box-shadow:0 1px 4px #d1242f66}";
271
+ const css$4 = ".qikqja_root{min-width:0;height:100%;color:var(--hub-text-primary);flex-direction:column;gap:8px;display:flex}.qikqja_header{flex-direction:column;gap:2px;padding:2px 2px 0;display:flex}.qikqja_headerRight{flex-shrink:0;align-items:center;gap:2px;margin-left:auto;display:inline-flex}.qikqja_langBtn{height:24px;color:var(--hub-text-tertiary);cursor:pointer;-webkit-user-select:none;user-select:none;background:0 0;border:none;border-radius:6px;flex-shrink:0;justify-content:center;align-items:center;padding:0 8px;font-size:12px;font-weight:600;transition:color .15s,background-color .15s;display:inline-flex}.qikqja_langBtn:hover{color:var(--hub-purple-1);background-color:var(--hub-purple-tint)}.qikqja_githubLink{color:var(--hub-text-tertiary);cursor:pointer;border-radius:6px;flex-shrink:0;align-items:center;padding:3px;text-decoration:none;transition:color .15s,background-color .15s;display:inline-flex}.qikqja_githubLink:hover{color:var(--hub-purple-1);background-color:var(--hub-purple-tint)}.qikqja_githubIcon{flex-shrink:0;display:block}.qikqja_aboutBtn{height:24px;color:var(--hub-text-tertiary);cursor:pointer;-webkit-user-select:none;user-select:none;background:0 0;border:none;border-radius:6px;flex-shrink:0;justify-content:center;align-items:center;padding:0 8px;font-size:12px;font-weight:600;line-height:1;transition:color .15s,background-color .15s;display:inline-flex}.qikqja_aboutBtn:hover{color:var(--hub-purple-1);background-color:var(--hub-purple-tint)}.qikqja_headerTitleRow{align-items:center;gap:8px;min-width:0;display:flex}.qikqja_brandTitle{min-width:0;color:inherit;cursor:pointer;align-items:center;gap:10px;text-decoration:none;display:flex}.qikqja_taglineLink{min-width:0;max-width:100%;color:inherit;cursor:pointer;align-self:flex-start;text-decoration:none}.qikqja_title{margin:0;font-size:14px;font-weight:600;line-height:20px}.qikqja_version{color:var(--hub-text-secondary)}.qikqja_versionBtn{height:20px;color:var(--hub-text-secondary);cursor:pointer;-webkit-user-select:none;user-select:none;background:0 0;border:none;border-radius:5px;flex-shrink:0;align-items:center;gap:5px;padding:0 6px;font-size:12px;font-weight:600;line-height:1;transition:color .12s;display:inline-flex}.qikqja_versionBtn:hover{color:var(--hub-text-secondary)}.qikqja_hubUpdateBadge{letter-spacing:.02em;color:#fff;background:var(--hub-danger);-webkit-user-select:none;user-select:none;border-radius:999px;flex-shrink:0;align-items:center;height:18px;padding:0 7px;font-size:11px;font-weight:700;line-height:1;transition:background .12s,box-shadow .12s,transform .12s;display:inline-flex;box-shadow:0 1px 4px #d1242f66}.qikqja_hubUpdateBadge:hover{background:var(--hub-danger-hover);transform:translateY(-1px);box-shadow:0 2px 8px #d1242f80}.qikqja_logoIcon{flex-shrink:0;display:block}.qikqja_copyIcon{flex-shrink:0}.qikqja_tagline{color:var(--hub-text-tertiary);white-space:nowrap;text-overflow:ellipsis;margin:0;font-size:12px;line-height:18px;overflow:hidden}.qikqja_controls{flex-shrink:0;align-items:center;gap:4px;display:flex}.qikqja_filterResults{color:var(--hub-text-tertiary);white-space:nowrap;flex-shrink:0;align-items:baseline;font-size:12px;line-height:18px;display:inline-flex}.qikqja_resultCount{font-variant-numeric:tabular-nums;text-align:center;min-width:5ch}.qikqja_adBanner{border:1px solid var(--hub-purple-border);background:linear-gradient(90deg, var(--hub-purple-1) 0%, var(--hub-purple-2) 100%);color:#fff;cursor:pointer;-webkit-user-select:none;user-select:none;box-shadow:0 2px 10px var(--hub-purple-shadow);border-radius:8px;align-items:center;gap:8px;padding:7px 12px;text-decoration:none;transition:filter .15s,box-shadow .15s;display:flex}.qikqja_adBanner:hover{filter:brightness(1.1);box-shadow:0 4px 16px var(--hub-purple-shadow-strong)}.qikqja_adBadge{letter-spacing:.04em;color:#fff;white-space:nowrap;background:#ffffff29;border:1px solid #ffffff8c;border-radius:4px;flex-shrink:0;padding:3px 6px;font-size:10px;font-weight:700;line-height:1}.qikqja_adText{text-overflow:ellipsis;white-space:nowrap;color:#fff;min-width:0;font-size:12px;line-height:18px;overflow:hidden}.qikqja_adArrow{color:#fff;flex-shrink:0;margin-left:auto;font-size:13px}.qikqja_searchRow{padding:0 2px;display:flex}.qikqja_search{width:50%;color:inherit;border:1px solid var(--hub-border-2);background:0 0;border-radius:6px;outline:none;padding:4px 9px;font-size:12px;line-height:18px;transition:border-color .12s}.qikqja_search::placeholder{color:var(--hub-text-tertiary)}.qikqja_search:focus{border-color:var(--hub-brand)}.qikqja_installedBtn,.qikqja_installedBtnActive,.qikqja_installedBtnDisabled{cursor:pointer;-webkit-user-select:none;user-select:none;border:none;border-radius:6px;flex-shrink:0;align-items:center;gap:5px;height:24px;padding:0 8px;font-size:12px;line-height:22px;transition:color .12s,background .12s;display:inline-flex}.qikqja_installedBtn{color:var(--hub-text-primary);background:0 0}.qikqja_installedBtn:hover{color:var(--hub-text-primary);background:var(--hub-bg-hover)}.qikqja_installedBtnDisabled,.qikqja_installedBtnDisabled:hover{color:var(--hub-text-disabled);cursor:not-allowed;background:0 0}.qikqja_installedBtnDisabled .qikqja_segCount{color:var(--hub-text-disabled);background:#8080800f}.qikqja_installedBtnActive{color:var(--hub-text-on-fill);background:var(--hub-btn-fill)}.qikqja_segCount,.qikqja_segCountActive{text-align:center;border-radius:999px;min-width:14px;padding:0 4px;font-size:10px;line-height:14px}.qikqja_segCount{color:var(--hub-text-tertiary);background:var(--hub-bg-btn)}.qikqja_installedBtnActive .qikqja_segCountActive{color:var(--hub-text-on-fill);background:var(--hub-bg-on-fill)}.qikqja_tabs{border-bottom:1px solid var(--hub-border-1);flex-wrap:wrap;align-items:center;gap:6px;padding:2px 2px 8px;display:flex}.qikqja_tab,.qikqja_tabActive{cursor:pointer;-webkit-user-select:none;user-select:none;border:none;border-radius:999px;flex-shrink:0;align-items:center;gap:6px;padding:3px 10px;font-size:12px;line-height:18px;transition:color .12s,background .12s;display:inline-flex}.qikqja_tab{color:var(--hub-text-secondary);background:0 0}.qikqja_tab:hover{color:var(--hub-text-primary);background:var(--hub-bg-hover)}.qikqja_tabActive{color:var(--hub-text-on-fill);background:var(--hub-btn-fill)}.qikqja_tabCount{text-align:center;min-width:16px;color:var(--hub-text-tertiary);background:var(--hub-bg-btn);border-radius:999px;padding:0 5px;font-size:10px;line-height:14px}.qikqja_tabActive .qikqja_tabCount{color:var(--hub-text-on-fill);background:var(--hub-bg-on-fill)}.qikqja_failBtn{box-sizing:border-box;border:1px solid var(--hub-border-2);height:24px;color:var(--hub-text-primary);background:var(--hub-bg-hover);cursor:pointer;-webkit-user-select:none;user-select:none;border-radius:6px;flex-shrink:0;align-items:center;margin-left:auto;padding:0 10px;font-size:12px;line-height:22px;transition:color .12s,background .12s;display:inline-flex;position:relative}.qikqja_failBtn:hover{color:var(--hub-text-primary);background:#80808047}.qikqja_failBadge{z-index:1;text-align:center;color:#fff;background:var(--hub-danger);border-radius:999px;min-width:16px;height:16px;padding:0 5px;font-size:10px;font-weight:600;line-height:16px;position:absolute;top:-16px;right:-7px;box-shadow:0 1px 4px #d1242f66}";
234
272
  const tagId$4 = "dsh-plugin/Header.module.css";
235
273
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$4) + "]") === null) {
236
274
  const tag = document.createElement("style");
@@ -240,40 +278,43 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
240
278
  document.head.appendChild(tag);
241
279
  }
242
280
  var Header_module_css_default = {
243
- "tab": "qikqja_tab",
244
- "adText": "qikqja_adText",
245
- "failBtn": "qikqja_failBtn",
246
- "root": "qikqja_root",
247
- "header": "qikqja_header",
248
281
  "githubIcon": "qikqja_githubIcon",
249
- "search": "qikqja_search",
250
- "title": "qikqja_title",
251
- "segCount": "qikqja_segCount",
252
- "logoIcon": "qikqja_logoIcon",
282
+ "installedBtnActive": "qikqja_installedBtnActive",
283
+ "taglineLink": "qikqja_taglineLink",
284
+ "tagline": "qikqja_tagline",
285
+ "installedBtn": "qikqja_installedBtn",
253
286
  "installedBtnDisabled": "qikqja_installedBtnDisabled",
287
+ "searchRow": "qikqja_searchRow",
288
+ "segCount": "qikqja_segCount",
289
+ "resultCount": "qikqja_resultCount",
290
+ "title": "qikqja_title",
291
+ "segCountActive": "qikqja_segCountActive",
292
+ "githubLink": "qikqja_githubLink",
293
+ "tabActive": "qikqja_tabActive",
254
294
  "failBadge": "qikqja_failBadge",
255
- "tagline": "qikqja_tagline",
256
- "headerTitleRow": "qikqja_headerTitleRow",
257
- "version": "qikqja_version",
295
+ "header": "qikqja_header",
296
+ "tab": "qikqja_tab",
297
+ "search": "qikqja_search",
298
+ "tabCount": "qikqja_tabCount",
258
299
  "headerRight": "qikqja_headerRight",
300
+ "brandTitle": "qikqja_brandTitle",
259
301
  "controls": "qikqja_controls",
260
- "taglineLink": "qikqja_taglineLink",
261
- "copyIcon": "qikqja_copyIcon",
302
+ "filterResults": "qikqja_filterResults",
262
303
  "adArrow": "qikqja_adArrow",
263
- "installedBtnActive": "qikqja_installedBtnActive",
304
+ "root": "qikqja_root",
305
+ "adBanner": "qikqja_adBanner",
264
306
  "adBadge": "qikqja_adBadge",
307
+ "logoIcon": "qikqja_logoIcon",
308
+ "version": "qikqja_version",
309
+ "versionBtn": "qikqja_versionBtn",
310
+ "failBtn": "qikqja_failBtn",
311
+ "adText": "qikqja_adText",
312
+ "aboutBtn": "qikqja_aboutBtn",
313
+ "headerTitleRow": "qikqja_headerTitleRow",
314
+ "hubUpdateBadge": "qikqja_hubUpdateBadge",
315
+ "copyIcon": "qikqja_copyIcon",
265
316
  "tabs": "qikqja_tabs",
266
- "githubLink": "qikqja_githubLink",
267
- "brandTitle": "qikqja_brandTitle",
268
- "adBanner": "qikqja_adBanner",
269
- "filterResults": "qikqja_filterResults",
270
- "resultCount": "qikqja_resultCount",
271
- "searchRow": "qikqja_searchRow",
272
- "segCountActive": "qikqja_segCountActive",
273
- "installedBtn": "qikqja_installedBtn",
274
- "langBtn": "qikqja_langBtn",
275
- "tabActive": "qikqja_tabActive",
276
- "tabCount": "qikqja_tabCount"
317
+ "langBtn": "qikqja_langBtn"
277
318
  };
278
319
  //#endregion
279
320
  //#region src/client/lib/failures.ts
@@ -281,7 +322,8 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
281
322
  const MAX = 50;
282
323
  /**
283
324
  * 失败归类,三态。无论底层机制如何(pnpm 白名单拦截 / 构建脚本被忽略 / prepare 失败),
284
- * 对用户而言结果都一样 —— 用官方默认安装方式装不上,就是插件仓库的问题,一律引导提 Issue:
325
+ * 对用户而言结果都一样 —— 当前安装通道(npm 或 git)装不上,就是插件分发/依赖的问题,
326
+ * 一律引导提 Issue:
285
327
  * - pnpmIgnoredBuild:插件依赖里的原生模块构建脚本被 pnpm 默认拦截(如 node-pty,
286
328
  * `ERR_PNPM_IGNORED_BUILDS`)。只影响带原生模块的插件,其他插件不受影响 —— 差异在
287
329
  * 插件的依赖选择,属插件依赖/打包问题 → 引导去仓库提 Issue(建议改用预编译版本)
@@ -393,6 +435,12 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
393
435
  save([]);
394
436
  return [];
395
437
  }
438
+ /** 按 id 删除单条通知记录,返回更新后的列表(仅移除该条,不影响其余记录)。 */
439
+ function removeNotification(id) {
440
+ const next = loadNotifications().filter((r) => r.id !== id);
441
+ save(next);
442
+ return next;
443
+ }
396
444
  //#endregion
397
445
  //#region src/client/lib/catalog.ts
398
446
  /** 插件中心官网地址。 */
@@ -400,11 +448,21 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
400
448
  /** 插件中心源码仓库:头部右上角 GitHub 图标的跳转地址 */
401
449
  const GITHUB_URL = "https://github.com/dshplugin/dsh-plugin-hub";
402
450
  /**
451
+ * Hub 自我更新版本控制中心(Cloudflare Worker:dsh-update)。
452
+ * 发新版后向 Worker 管理接口写入最新版本 + Markdown 变更记录(见
453
+ * dsh-update-worker 的 POST /admin/hub),所有已装用户的「可更新」徽标即时出现,
454
+ * 不再依赖主站目录数据管道(每日刷新 + 重建站点)。
455
+ * 响应:{ version: string | null, publishedAt: string | null, notes: string | {zh,en} | null }
456
+ */
457
+ const HUB_UPDATE_URL = "https://hub.dsh-plugin.org/hub";
458
+ /** Worker「关注我们」内容接口(同款 Worker,key = hub:about,Markdown 推送非写死)。 */
459
+ const HUB_ABOUT_URL = "https://hub.dsh-plugin.org/about";
460
+ /**
403
461
  * 插件当前版本号,供头部标题展示「DSH-Plugin Hub v0.1.1」。
404
462
  * tsdown 构建时用 define 把 __PLUGIN_VERSION__ 替换成 package.json 的版本号;
405
463
  * node --test 直接 import 本模块时该标识符不存在,typeof 守卫兜底为空串。
406
464
  */
407
- const PLUGIN_VERSION = "1.0.0";
465
+ const PLUGIN_VERSION = "1.1.1";
408
466
  /** dsh-plugin.org 中文页挂在 /zh/ 前缀下,英文在根路径。 */
409
467
  function langPathOf(lang) {
410
468
  return lang === "zh" ? "zh/" : "";
@@ -426,15 +484,17 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
426
484
  }
427
485
  /**
428
486
  * 一键反馈 GitHub Issue 的预填链接:标题带「来自 dsh-plugin.org」标识,正文只带
429
- * 核心信息 —— 原因判定 + 关键错误代码 + 宿主机器环境快照 + 错误核心摘要(完整日志
430
- * 太长,塞进 URL 会被 GitHub 以「request URL too long」拒绝,故只收集重点)。
431
- * 错误弹窗、失败记录共用此逻辑。
487
+ * 核心信息 —— 原因判定 + 关键错误代码 + 尝试过的安装方式 + 宿主机器环境快照 +
488
+ * 错误核心摘要(完整日志太长,塞进 URL 会被 GitHub 以「request URL too long」拒绝,
489
+ * 故只收集重点)。错误弹窗、失败记录共用此逻辑。
432
490
  * env 取不到时为 null,链接照常生成、只是少环境段。
491
+ * attempts(尝试过的安装方式,npm 反查 + 实际执行命令):作者看到我们查过/试过的命令,
492
+ * 组织 scope 与 GitHub 用户名不一致时也能直接指认正确的 npm 包名。
433
493
  */
434
- function pluginIssueUrl(repo, message, env) {
494
+ function pluginIssueUrl(repo, message, env, command, attempts) {
435
495
  const title = `[dsh-plugin.org | dsh-plugin-hub] Install/Remove failed: ${repo}`;
436
496
  const kind = classifyFailure(message);
437
- const reason = /\[packaging\]/i.test(message) ? "plugin does NOT support the official default install method (dsh plugin add github:owner/repo — requires build output committed to the repo or a prepare script); its git distribution lacks the entry file declared in package.json" : kind === "pluginPrepare" ? "plugin prepare/build script failed during install (packaging/distribution issue)" : kind === "pnpmIgnoredBuild" ? "plugin depends on a native module whose build script pnpm blocks by default (use a prebuilt variant)" : "plugin-side install failure";
497
+ const reason = /\[packaging\]/i.test(message) ? "plugin distribution is incomplete — the entry file declared in package.json is missing from the published package (github tarball or npm package); please commit build output or publish a complete package" : kind === "pluginPrepare" ? "plugin prepare/build script failed during install (packaging/distribution issue)" : kind === "pnpmIgnoredBuild" ? "plugin depends on a native module whose build script pnpm blocks by default (use a prebuilt variant)" : "plugin-side install failure";
438
498
  const code = coreErrorCode(message);
439
499
  const build = (coreChars) => {
440
500
  const body = [
@@ -444,12 +504,17 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
444
504
  "",
445
505
  `## [DSH-Plugin 插件中心](${SITE_URL}) · 安装 Plugin 失败错误信息`,
446
506
  `本错误信息由 [dsh-plugin-hub](${GITHUB_URL}) 插件中心的安装程序自动生成,随本次安装失败一并提交。`,
447
- `- 使用的安装命令(官方默认安装方式):\`dsh plugin${env?.profile ? ` --profile ${env.profile}` : ""} add github:${repo}\``,
507
+ `- 实际执行的安装命令:\`${command ?? `dsh plugin${env?.profile ? ` --profile ${env.profile}` : ""} add github:${repo}`}\``,
448
508
  `- 执行结果:安装失败,未能安装该插件。`,
509
+ ...attempts && attempts.length > 0 ? [
510
+ "",
511
+ "## Attempted install channels(已尝试的安装方式)",
512
+ ...attempts.map((a) => `- ${a}`)
513
+ ] : [],
449
514
  .../\[packaging\]/i.test(message) ? [
450
515
  "",
451
- "## 官方默认安装方式",
452
- "DSH 生态的官方默认安装方式是 `dsh plugin add github:owner/repo`(git 直装):插件仓库需提交构建产物,或在 package.json 提供 `prepare` 脚本让 pnpm 安装时自动构建。当前插件两者皆不具备,因此无法按官方方式安装。"
516
+ "## 安装方式说明",
517
+ "DSH 插件支持两种官方安装通道:`dsh plugin add <npm-package>`(npm 分发,需发布完整构建产物)与 `dsh plugin add github:owner/repo`(git 直装,仓库需提交构建产物或在 package.json 提供 `prepare` 脚本)。当前插件的分发物缺少 package.json 声明的入口文件,请按所用通道补齐后重新发布。"
453
518
  ] : [],
454
519
  "",
455
520
  "## Environment",
@@ -520,7 +585,11 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
520
585
  topics: Array.isArray(raw.t) ? raw.t : void 0,
521
586
  features: Array.isArray(raw.f) ? raw.f : void 0,
522
587
  description: typeof raw.d === "string" ? raw.d : void 0,
523
- source: typeof raw.r === "string" ? { repo: raw.r } : void 0,
588
+ source: typeof raw.r === "string" ? { repo: raw.r } : raw.r !== null && typeof raw.r === "object" ? {
589
+ repo: typeof raw.r.repo === "string" ? raw.r.repo : void 0,
590
+ npmPackage: typeof raw.r.npmPackage === "string" ? raw.r.npmPackage : void 0
591
+ } : void 0,
592
+ install: raw.wi === false ? { webInstallable: false } : void 0,
524
593
  compatibility: typeof raw.v === "string" ? { status: raw.v } : void 0,
525
594
  dates: {
526
595
  repoUpdatedAt: typeof raw.u === "string" ? raw.u : void 0,
@@ -533,6 +602,28 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
533
602
  };
534
603
  return raw;
535
604
  }
605
+ /**
606
+ * 安装通道决策(用户无感知):目录探测到 npm 包名 → 用 npm 包名安装
607
+ * (走 npm registry tarball,更快、与 GitHub 网络无关);无 npm 包名 → git 直装。
608
+ * 返回值 target 即传给后端 /install 的安装目标(npm 包名 或 owner/repo)。
609
+ */
610
+ function installTargetOf(p) {
611
+ const pkg = (p.source?.npmPackage ?? "").trim();
612
+ const repo = (p.source?.repo ?? "").trim();
613
+ if (pkg && repo) return {
614
+ target: pkg,
615
+ via: "npm"
616
+ };
617
+ return {
618
+ target: repo,
619
+ via: "github"
620
+ };
621
+ }
622
+ /** 展示用安装命令(复制/弹窗):npm 通道显示包名,git 通道显示 github: 源。 */
623
+ function installCommandOf(p, withProfile = false) {
624
+ const { target, via } = installTargetOf(p);
625
+ return via === "npm" ? `dsh plugin${withProfile ? " --profile web" : ""} add ${target}` : `dsh plugin${withProfile ? " --profile web" : ""} add github:${target}`;
626
+ }
536
627
  const CATEGORY_LABELS = {
537
628
  interface: {
538
629
  zh: "界面与体验",
@@ -650,6 +741,8 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
650
741
  */
651
742
  const PLUGINS_URL = (lang) => `https://dsh-plugin.org/api/plugins.${lang}.json`;
652
743
  const STATS_URL = "https://dsh-plugin.org/api/stats.json";
744
+ /** 插件市场自身仓库:DSH-Plugin Hub 不显示在目录里(自己不进自己的插件列表) */
745
+ const SELF_REPO = "dshplugin/dsh-plugin-hub";
653
746
  function useCatalog(lang) {
654
747
  /** 目录插件(仅保留人工验证通过的条目) */
655
748
  const [plugins, setPlugins] = (0, react.useState)(null);
@@ -664,8 +757,13 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
664
757
  const [installedFilter, setInstalledFilter] = (0, react.useState)("all");
665
758
  /** 当前 profile 已安装插件:npm 包名 -> manifest spec(来自宿主本地路由) */
666
759
  const [installed, setInstalled] = (0, react.useState)({});
667
- /** 安装时记录的目录信号:repo(小写) -> { version, updatedAt }(来自宿主本地路由) */
760
+ /** 安装时记录的目录信号:repo(小写) -> { version, updatedAt }(来自宿主本地路由);
761
+ * npmPackage 为 npm 优先通道反查命中的包名映射(目录数据未下发时客户端靠它把依赖 key 匹配回仓库) */
668
762
  const [versions, setVersions] = (0, react.useState)({});
763
+ /** Hub 自我更新信息:来自 CF Worker 版本控制中心(hub.dsh-plugin.org),与目录数据解耦 */
764
+ const [hubUpdateInfo, setHubUpdateInfo] = (0, react.useState)(null);
765
+ /** 头部「关注我们」弹窗内容(平台介绍 + 反馈群二维码):来自同款 Worker /about,Markdown 推送非写死 */
766
+ const [hubAboutInfo, setHubAboutInfo] = (0, react.useState)(null);
669
767
  (0, react.useEffect)(() => {
670
768
  let cancelled = false;
671
769
  setPlugins(null);
@@ -675,20 +773,23 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
675
773
  if (!res.ok) throw new Error(String(res.status));
676
774
  return res.json();
677
775
  });
678
- fetchData(PLUGINS_URL(lang)).then((data) => {
776
+ const load = () => Promise.all([fetchData(PLUGINS_URL(lang)), fetchData(STATS_URL)]);
777
+ load().then(([data, s]) => {
679
778
  if (cancelled) return;
680
779
  const list = (Array.isArray(data) ? data : []).map((item) => normalize(item));
681
- setPlugins(list.filter((p) => p.compatibility?.status === "verified"));
682
- }).catch(() => {
683
- if (!cancelled) setFailed(true);
684
- });
685
- fetchData(STATS_URL).then((s) => {
780
+ const hadSelf = list.some((p) => p.source?.repo === SELF_REPO);
781
+ setPlugins(list.filter((p) => p.compatibility?.status === "verified" && p.source?.repo !== SELF_REPO));
686
782
  const stats = s;
687
- if (!cancelled && stats && typeof stats.total === "number" && typeof stats.verified === "number") setStats({
783
+ if (stats && typeof stats.total === "number" && typeof stats.verified === "number") setStats(hadSelf ? {
784
+ total: stats.total - 1,
785
+ verified: stats.verified - 1
786
+ } : {
688
787
  total: stats.total,
689
788
  verified: stats.verified
690
789
  });
691
- }).catch(() => {});
790
+ }).catch(() => {
791
+ if (!cancelled) setFailed(true);
792
+ });
692
793
  return () => {
693
794
  cancelled = true;
694
795
  };
@@ -706,12 +807,56 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
706
807
  (0, react.useEffect)(() => {
707
808
  refreshInstalled();
708
809
  }, []);
709
- /** 插件是否已安装:匹配 installed spec 中的 `github:<owner>/<repo>`;命中返回 npm 包名。 */
810
+ (0, react.useEffect)(() => {
811
+ let cancelled = false;
812
+ fetch(HUB_UPDATE_URL, { cache: "no-store" }).then((res) => res.ok ? res.json() : Promise.resolve(null)).then((data) => {
813
+ if (cancelled || !data) {
814
+ if (!cancelled) setHubUpdateInfo(null);
815
+ return;
816
+ }
817
+ const version = typeof data.version === "string" && data.version.length > 0 ? data.version : null;
818
+ setHubUpdateInfo(version === null ? null : {
819
+ version,
820
+ publishedAt: data.publishedAt ?? null,
821
+ notes: data.notes ?? null
822
+ });
823
+ }).catch(() => {
824
+ if (!cancelled) setHubUpdateInfo(null);
825
+ });
826
+ return () => {
827
+ cancelled = true;
828
+ };
829
+ }, [reloadKey]);
830
+ (0, react.useEffect)(() => {
831
+ let cancelled = false;
832
+ fetch(HUB_ABOUT_URL, { cache: "no-store" }).then((res) => res.ok ? res.json() : Promise.resolve(null)).then((data) => {
833
+ if (cancelled) return;
834
+ if (!data || data.content === null || data.content === void 0) {
835
+ setHubAboutInfo(null);
836
+ return;
837
+ }
838
+ const content = typeof data.content === "string" || typeof data.content === "object" ? data.content : null;
839
+ setHubAboutInfo(content === null ? null : {
840
+ content,
841
+ updatedAt: data.updatedAt ?? null
842
+ });
843
+ }).catch(() => {
844
+ if (!cancelled) setHubAboutInfo(null);
845
+ });
846
+ return () => {
847
+ cancelled = true;
848
+ };
849
+ }, [reloadKey]);
850
+ /** 插件是否已安装:匹配 installed spec 中的 `github:<owner>/<repo>`,或 npm 通道安装的依赖包名;命中返回 npm 包名。 */
710
851
  const installedName = (p) => {
711
852
  const repo = p.source?.repo;
712
853
  if (!repo) return null;
713
854
  const needle = `github:${repo.toLowerCase()}`;
714
855
  for (const [name, spec] of Object.entries(installed)) if (spec.toLowerCase().includes(needle)) return name;
856
+ const pkg = ((p.source?.npmPackage || versions[repo.toLowerCase()]?.npmPackage) ?? "").toLowerCase();
857
+ if (pkg) {
858
+ for (const name of Object.keys(installed)) if (name.toLowerCase() === pkg) return name;
859
+ }
715
860
  return null;
716
861
  };
717
862
  /** 该插件安装时记录的目录版本(无记录/未安装 → null)。 */
@@ -735,6 +880,16 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
735
880
  const current = p.dates?.repoUpdatedAt;
736
881
  return Boolean(current && rec.updatedAt && current > rec.updatedAt);
737
882
  };
883
+ /**
884
+ * Hub 自身是否有可用更新:Hub 就是当前运行的应用(始终已安装,无需 installedName 命中)。
885
+ * 以 Worker 版本控制中心返回的「最新版本」为准,与当前运行的版本号比对——
886
+ * 构建时注入的 PLUGIN_VERSION 是运行 bundle 的真实版本;测试/异常场景缺失时
887
+ * 回退到安装时记录的版本。版本号不等即新版(版本号只在发版时变更,绝无降级场景)。
888
+ */
889
+ const hubHasUpdate = (() => {
890
+ if (!hubUpdateInfo) return false;
891
+ return hubUpdateInfo.version !== "1.1.1";
892
+ })();
738
893
  /** 当前分类下的插件(「全部」时为整个目录)。 */
739
894
  const categoryPlugins = (0, react.useMemo)(() => {
740
895
  if (!plugins) return [];
@@ -793,6 +948,9 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
793
948
  installedName,
794
949
  installedVersion,
795
950
  hasUpdate,
951
+ hubHasUpdate,
952
+ hubUpdateInfo,
953
+ hubAboutInfo,
796
954
  refreshInstalled,
797
955
  category,
798
956
  setCategory,
@@ -832,11 +990,15 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
832
990
  resolvePendingRef.current = resolvePending;
833
991
  /** 当前打开弹窗所对应的任务 id:该任务完成时弹窗切换为结果视图 */
834
992
  const modalTaskRef = (0, react.useRef)(null);
993
+ /** 已开始收尾但尚未结束的任务 id(服务端 /active 已消失 → 查 /status 中):并发轮询时跳过,避免重复收尾 */
994
+ const settlingRef = (0, react.useRef)(/* @__PURE__ */ new Set());
835
995
  /** 轮询定时器句柄(队列清空/组件卸载时清理) */
836
996
  const pollRef = (0, react.useRef)(null);
837
997
  /** 请求在途的目标集合(同步防重):双击安装/卸载时第二击直接忽略。
838
998
  * 本地队列任务要等 fetch 返回后才入队,仅靠 queueRef 检查拦不住请求窗口内的重复点击。 */
839
999
  const submittingRef = (0, react.useRef)(/* @__PURE__ */ new Set());
1000
+ /** 乐观入队临时 id 计数器:递减产生唯一负数,与服务端自增正数 id 永不冲突。 */
1001
+ const tempIdRef = (0, react.useRef)(0);
840
1002
  /** 请求在途标记:弹窗据此禁用确认按钮,避免等待响应期间被再次点击。 */
841
1003
  const [submitting, setSubmitting] = (0, react.useState)(false);
842
1004
  /** 停止后台任务轮询(任务结束或组件卸载时清理)。 */
@@ -917,39 +1079,83 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
917
1079
  refreshInstalled();
918
1080
  if (q.kind === "uninstall") {
919
1081
  syncInstalledVersion(q.repo, void 0, void 0);
920
- onUninstallDone(modalTaskRef.current === q.id, q.repo);
1082
+ onUninstallDone(modalTaskRef.current === q.id, q.repo, q.needsRestart);
921
1083
  } else {
922
1084
  syncInstalledVersion(q.repo, q.version, q.updatedAt);
923
1085
  pendingInfoRef.current.set(q.target, {
924
1086
  desc: q.desc,
925
1087
  version: q.version
926
1088
  });
927
- onInstallDone(modalTaskRef.current === q.id, q.repo);
1089
+ onInstallDone(modalTaskRef.current === q.id, q.repo, q.needsRestart);
928
1090
  }
929
1091
  } else {
930
1092
  const detail = lines.length > 0 ? [...lines].reverse().join("\n") : q.kind === "uninstall" ? t("uninstallFail") : t("installFail");
931
- onError(detail, q.repo, q.kind);
1093
+ onError(detail, q.repo, q.kind, q.command, q.attempts);
932
1094
  }
933
1095
  maybeStopPoll();
934
1096
  };
1097
+ /** 卸载成功收尾前的进度过渡:任务已在服务端结束,但保留本地展示,
1098
+ * 让进度条在 ~2.4s 内缓缓跑到 100% 再切结果/Toast,给用户一个交互过程,
1099
+ * 而不是命令一执行进度条就一闪消失。 */
1100
+ const settleDone = (q, lines) => {
1101
+ stopPoll();
1102
+ const from = Math.min(q.progress, 90);
1103
+ const steps = 20;
1104
+ const stepMs = 120;
1105
+ let step = 0;
1106
+ applyQueue((prev) => prev.map((x) => x.id === q.id ? {
1107
+ ...x,
1108
+ status: "running"
1109
+ } : x));
1110
+ const timer = window.setInterval(() => {
1111
+ step += 1;
1112
+ const progress = Math.round(from + (100 - from) * (step / steps));
1113
+ applyQueue((prev) => prev.map((x) => x.id === q.id ? {
1114
+ ...x,
1115
+ progress
1116
+ } : x));
1117
+ if (step >= steps) {
1118
+ window.clearInterval(timer);
1119
+ finishQueueTask(true, q, lines);
1120
+ if (queueRef.current.length > 0) pollQueue();
1121
+ }
1122
+ }, stepMs);
1123
+ };
935
1124
  /** 任务在 /active 中消失 = 已结束:查 /status 拿终态并收尾(cancelled 静默移除)。 */
936
1125
  const settleTask = async (q) => {
937
1126
  let status = "failed";
938
1127
  let lines = [];
1128
+ let attempts;
939
1129
  try {
940
1130
  const res = await fetch(`/dsh-plugin-hub/status?task=${q.id}`, { cache: "no-store" });
941
1131
  if (res.ok) {
942
- const data = await res.json();
943
- status = data.task?.status ?? "failed";
944
- lines = data.task?.lines ?? [];
1132
+ const task = (await res.json()).task;
1133
+ if (task !== void 0) {
1134
+ status = task.status ?? "failed";
1135
+ lines = task.lines ?? [];
1136
+ attempts = Array.isArray(task.attempts) ? task.attempts : void 0;
1137
+ const needsRestart = typeof task.needsRestart === "boolean" ? task.needsRestart : q.needsRestart;
1138
+ if (status === "done") {
1139
+ const settled = {
1140
+ ...q,
1141
+ needsRestart
1142
+ };
1143
+ if (q.kind === "uninstall") settleDone(settled, lines);
1144
+ else finishQueueTask(true, settled, lines);
1145
+ return;
1146
+ }
1147
+ if (status === "failed") {
1148
+ finishQueueTask(false, {
1149
+ ...q,
1150
+ attempts
1151
+ }, lines);
1152
+ return;
1153
+ }
1154
+ }
945
1155
  }
946
1156
  } catch {}
947
- if (status === "done") finishQueueTask(true, q, lines);
948
- else if (status === "failed") finishQueueTask(false, q, lines);
949
- else {
950
- applyQueue((prev) => prev.filter((x) => x.id !== q.id));
951
- maybeStopPoll();
952
- }
1157
+ applyQueue((prev) => prev.filter((x) => x.id !== q.id));
1158
+ maybeStopPoll();
953
1159
  };
954
1160
  /** 轮询整个队列:合并服务端 /active(running 在前、pending 在后),
955
1161
  * 消失的任务查终态收尾;队列清空即停止轮询。 */
@@ -980,13 +1186,27 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
980
1186
  updatedAt: prevTask?.updatedAt,
981
1187
  status: a.status === "running" ? "running" : "pending",
982
1188
  progress: typeof a.progress === "number" ? a.progress : prevTask?.progress ?? 0,
983
- lines: Array.isArray(a.lines) ? a.lines : prevTask?.lines ?? []
1189
+ lines: Array.isArray(a.lines) ? a.lines : prevTask?.lines ?? [],
1190
+ attempts: Array.isArray(a.attempts) ? a.attempts : prevTask?.attempts ?? [],
1191
+ needsRestart: typeof a.needsRestart === "boolean" ? a.needsRestart : prevTask?.needsRestart ?? true
984
1192
  });
985
1193
  }
1194
+ const keepOpt = prev.filter((q) => q.optimistic && !next.some((x) => x.id === q.id));
1195
+ if (keepOpt.length > 0) next.push(...keepOpt);
1196
+ const keepGone = prev.filter((q) => !byId.has(q.id) && !next.some((x) => x.id === q.id) && q.status !== "cancelling" && !q.optimistic);
1197
+ if (keepGone.length > 0) next.push(...keepGone);
986
1198
  return next;
987
1199
  });
988
- const gone = prevQueue.filter((q) => !byId.has(q.id) && q.status !== "cancelling");
989
- for (const q of gone) await settleTask(q);
1200
+ const gone = prevQueue.filter((q) => !byId.has(q.id) && q.status !== "cancelling" && !q.optimistic && !settlingRef.current.has(q.id));
1201
+ for (const q of gone) {
1202
+ if (!queueRef.current.some((x) => x.id === q.id)) continue;
1203
+ settlingRef.current.add(q.id);
1204
+ try {
1205
+ await settleTask(q);
1206
+ } finally {
1207
+ settlingRef.current.delete(q.id);
1208
+ }
1209
+ }
990
1210
  } catch {}
991
1211
  }, 600);
992
1212
  };
@@ -1001,14 +1221,18 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
1001
1221
  applyPending(parsePendingRestarts(data.pendingRestarts));
1002
1222
  const items = (data.tasks ?? []).filter((a) => typeof a.id === "number").map((a) => {
1003
1223
  const isRemove = a.action === "remove";
1224
+ const display = typeof a.displayTarget === "string" ? a.displayTarget.replace(/^github:/, "") : "";
1225
+ const fallback = typeof a.target === "string" ? a.target.replace(/^github:/, "") : "";
1004
1226
  return {
1005
1227
  id: a.id,
1006
1228
  kind: isRemove ? "uninstall" : "install",
1007
- target: typeof a.target === "string" ? a.target.replace(/^github:/, "") : "",
1008
- repo: isRemove ? null : typeof a.target === "string" ? a.target.replace(/^github:/, "") : null,
1229
+ target: isRemove ? fallback : display || fallback,
1230
+ repo: isRemove ? null : display || fallback,
1009
1231
  status: a.status === "running" ? "running" : "pending",
1010
1232
  progress: typeof a.progress === "number" ? a.progress : 0,
1011
- lines: Array.isArray(a.lines) ? a.lines : []
1233
+ lines: Array.isArray(a.lines) ? a.lines : [],
1234
+ attempts: Array.isArray(a.attempts) ? a.attempts : [],
1235
+ needsRestart: typeof a.needsRestart === "boolean" ? a.needsRestart : true
1012
1236
  };
1013
1237
  });
1014
1238
  if (items.length > 0 || pendingRef.current.length > 0) {
@@ -1026,45 +1250,62 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
1026
1250
  const installNow = async (p, opts) => {
1027
1251
  const repo = p.source?.repo ?? "";
1028
1252
  if (!repo) return;
1253
+ const { target } = installTargetOf(p);
1254
+ if (!target) return;
1029
1255
  if (queueRef.current.some((q) => q.kind === "install" && q.target === repo)) return;
1030
1256
  if (submittingRef.current.has(repo)) return;
1031
1257
  submittingRef.current.add(repo);
1258
+ tempIdRef.current -= 1;
1259
+ const tempId = tempIdRef.current;
1260
+ modalTaskRef.current = tempId;
1261
+ applyQueue((prev) => [...prev, {
1262
+ id: tempId,
1263
+ kind: "install",
1264
+ target: repo,
1265
+ repo,
1266
+ desc: p.description,
1267
+ version: p.version,
1268
+ updatedAt: p.dates?.repoUpdatedAt,
1269
+ status: "running",
1270
+ progress: 0,
1271
+ lines: [],
1272
+ optimistic: true,
1273
+ command: installCommandOf(p, true),
1274
+ attempts: [],
1275
+ needsRestart: true
1276
+ }]);
1032
1277
  setSubmitting(true);
1033
1278
  try {
1034
1279
  const res = await fetch("/dsh-plugin-hub/install", {
1035
1280
  method: "POST",
1036
1281
  headers: { "content-type": "application/json" },
1037
1282
  body: JSON.stringify({
1038
- repo,
1283
+ repo: target,
1284
+ display: repo,
1039
1285
  mode: opts?.update ? "update" : void 0
1040
1286
  })
1041
1287
  });
1042
1288
  const data = await res.json();
1043
1289
  if (!data.ok || typeof data.task !== "number") {
1044
- onError(data.error ?? `HTTP ${res.status}`, repo, "install");
1290
+ applyQueue((prev) => prev.filter((x) => x.id !== tempId));
1291
+ onError(data.error ?? `HTTP ${res.status}`, repo, "install", installCommandOf(p, true), data.attempts);
1045
1292
  return;
1046
1293
  }
1047
1294
  const taskId = data.task;
1048
1295
  modalTaskRef.current = taskId;
1049
- applyQueue((prev) => [...prev, {
1296
+ applyQueue((prev) => prev.map((x) => x.id === tempId ? {
1297
+ ...x,
1050
1298
  id: taskId,
1051
- kind: "install",
1052
- target: repo,
1053
- repo,
1054
- desc: p.description,
1055
- version: p.version,
1056
- updatedAt: p.dates?.repoUpdatedAt,
1057
- status: "pending",
1058
- progress: 0,
1059
- lines: []
1060
- }]);
1299
+ optimistic: void 0
1300
+ } : x));
1061
1301
  pendingInfoRef.current.set(repo, {
1062
1302
  desc: p.description,
1063
1303
  version: p.version
1064
1304
  });
1065
1305
  pollQueue();
1066
1306
  } catch {
1067
- onError(t("installFail"), repo, "install");
1307
+ applyQueue((prev) => prev.filter((x) => x.id !== tempId));
1308
+ onError(t("installFail"), repo, "install", installCommandOf(p, true));
1068
1309
  } finally {
1069
1310
  submittingRef.current.delete(repo);
1070
1311
  setSubmitting(false);
@@ -1103,7 +1344,8 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
1103
1344
  repo,
1104
1345
  status: "pending",
1105
1346
  progress: 0,
1106
- lines: []
1347
+ lines: [],
1348
+ needsRestart: true
1107
1349
  }]);
1108
1350
  pollQueue();
1109
1351
  } catch {
@@ -1150,7 +1392,7 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
1150
1392
  }
1151
1393
  //#endregion
1152
1394
  //#region \0dsh-css:src/client/styles/Modal.module.css.mjs
1153
- const css$3 = ".BiQ1zG_overlay{z-index:998;-webkit-user-select:none;user-select:none;background:#00000061;justify-content:center;align-items:center;animation:.16s ease-out BiQ1zG_overlayIn;display:flex;position:fixed;top:0;bottom:0;left:0;right:0}.BiQ1zG_modal,.BiQ1zG_errorModal{background:var(--hub-bg-1);border:1px solid var(--hub-border-2);border-radius:10px;flex-direction:column;gap:10px;width:420px;max-width:calc(100vw - 32px);max-height:calc(100vh - 64px);padding:14px 16px;animation:.18s ease-out BiQ1zG_modalIn;display:flex;box-shadow:0 12px 40px #0000003d}.BiQ1zG_errorModal{width:640px;max-width:calc(100vw - 48px)}.BiQ1zG_modalHead{flex-shrink:0;justify-content:space-between;align-items:center;gap:8px;display:flex}.BiQ1zG_modalTitle{color:var(--hub-text-primary);font-size:14px;font-weight:600;line-height:20px}.BiQ1zG_modalTitleBusy{color:var(--hub-brand)}.BiQ1zG_modalTitleQueued{color:var(--hub-warning)}.BiQ1zG_modalClose{width:24px;height:24px;color:var(--hub-text-tertiary);cursor:pointer;-webkit-user-select:none;user-select:none;background:0 0;border:none;border-radius:6px;flex-shrink:0;justify-content:center;align-items:center;font-size:16px;line-height:1;display:inline-flex}.BiQ1zG_modalClose:hover{color:var(--hub-text-primary);background:var(--hub-bg-hover)}.BiQ1zG_modalCloseIcon{flex-shrink:0;width:14px;height:14px}.BiQ1zG_modalDesc{color:var(--hub-text-secondary);font-size:12px;line-height:18px}.BiQ1zG_trustHint{color:var(--hub-danger-text);background:var(--hub-danger-tint);border:1px solid var(--hub-danger-border-soft);border-radius:6px;padding:6px 10px;font-size:12px;font-weight:500;line-height:18px}.BiQ1zG_modalRow{align-items:baseline;gap:8px;min-width:0;font-size:12px;line-height:18px;display:flex}.BiQ1zG_modalLabel{min-width:64px;color:var(--hub-text-tertiary);flex-shrink:0}.BiQ1zG_modalValue{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--hub-text-primary);font-weight:500;overflow:hidden}.BiQ1zG_modalLink{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--hub-brand);cursor:pointer;-webkit-user-select:none;user-select:none;align-items:center;gap:4px;font-weight:500;text-decoration:none;transition:color .12s;display:inline-flex;overflow:hidden}.BiQ1zG_modalLink:hover{color:var(--hub-brand-hover);text-decoration:underline}.BiQ1zG_linkIcon{flex-shrink:0}.BiQ1zG_modalCmd{color:var(--hub-text-primary);background:var(--hub-bg-2);border:1px solid var(--hub-border-2);cursor:pointer;-webkit-user-select:none;user-select:none;border-radius:6px;justify-content:space-between;align-items:center;gap:8px;padding:6px 6px 6px 10px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11px;line-height:18px;transition:border-color .12s,background .12s;display:flex}.BiQ1zG_modalCmd:hover{border-color:var(--hub-border-ghost);background:var(--hub-bg-3)}.BiQ1zG_modalCmdText{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.BiQ1zG_modalCmdCopy{color:var(--hub-brand);cursor:pointer;-webkit-user-select:none;user-select:none;background:0 0;border:none;border-radius:4px;flex-shrink:0;align-items:center;gap:3px;padding:1px 7px;font-family:inherit;font-size:11px;line-height:16px;transition:color .12s,background .12s;display:inline-flex}.BiQ1zG_modalCmdCopy:hover{color:var(--hub-brand-hover);background:var(--hub-bg-2)}.BiQ1zG_modalActions{justify-content:flex-end;align-items:center;gap:8px;margin-top:2px;display:flex}.BiQ1zG_modalBody{flex-direction:column;flex:auto;gap:10px;min-height:0;display:flex;overflow-y:auto}.BiQ1zG_toast{z-index:1000;background:var(--hub-btn-fill);color:var(--hub-text-on-fill);pointer-events:none;white-space:nowrap;border:none;border-radius:8px;padding:10px 16px;font-size:12px;font-weight:500;line-height:18px;animation:.22s ease-out BiQ1zG_toastIn;position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);box-shadow:0 6px 24px #00000047}@keyframes BiQ1zG_toastIn{0%{opacity:0;transform:translate(-50%,-44%)}to{opacity:1;transform:translate(-50%,-50%)}}.BiQ1zG_toastFail{background:var(--hub-danger);color:#fff;border-color:#ffffff3d}.BiQ1zG_queueSection{flex-direction:column;gap:6px;display:flex}.BiQ1zG_queueSectionTitle{color:var(--hub-text-secondary);padding:4px 2px 0;font-size:12px;font-weight:600;line-height:18px}.BiQ1zG_pendingRowStatus{color:var(--hub-warn);flex-shrink:0;font-weight:500}.BiQ1zG_pendingRowActions{flex-shrink:0;gap:8px;margin-left:auto;display:flex}.BiQ1zG_queueRow{border:1px solid var(--hub-border-2);background:var(--hub-bg-2);cursor:pointer;-webkit-user-select:none;user-select:none;border-radius:8px;flex-direction:column;gap:5px;padding:8px 10px;font-size:12px;line-height:18px;transition:background .12s;display:flex}.BiQ1zG_queueRow:hover{background:var(--hub-brand-tint)}.BiQ1zG_queueRowHead{align-items:center;gap:10px;display:flex}.BiQ1zG_queueRowTarget{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--hub-text-primary);flex:1;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;overflow:hidden}.BiQ1zG_queueRowDesc{color:var(--hub-text-tertiary);-webkit-line-clamp:2;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.BiQ1zG_queueRowBody{align-items:center;gap:8px;display:flex}.BiQ1zG_queueRowStatus{color:var(--hub-brand);flex-shrink:0;font-weight:500}.BiQ1zG_queueRowTrack{flex:1;min-width:0}.BiQ1zG_queueRowPct{color:var(--hub-text-tertiary);font-variant-numeric:tabular-nums;flex-shrink:0}.BiQ1zG_stripCancel{color:var(--hub-text-secondary);cursor:pointer;-webkit-user-select:none;user-select:none;background:0 0;border:none;border-radius:6px;flex-shrink:0;padding:2px 10px;font-size:12px;line-height:18px;transition:background .12s,color .12s}.BiQ1zG_stripCancel:hover{background:var(--hub-danger-tint);color:var(--hub-danger-text)}.BiQ1zG_stripCancel:disabled{opacity:.45;cursor:default}.BiQ1zG_queuedHint{color:var(--hub-brand);background:var(--hub-brand-tint);border:1px solid var(--hub-brand-border-soft);border-radius:6px;padding:5px 10px;font-size:12px;line-height:18px}.BiQ1zG_errorTitle{color:var(--hub-danger-text);font-size:14px;font-weight:600;line-height:20px}.BiQ1zG_errorBox{border:1px solid var(--hub-danger-border);background:var(--hub-danger-tint-weak);max-height:240px;color:var(--hub-text-primary);white-space:pre-wrap;word-break:break-word;-webkit-user-select:none;user-select:none;border-radius:6px;margin:0;padding:10px 12px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11px;line-height:17px;overflow:auto}.BiQ1zG_errorCopySoft{color:var(--hub-text-tertiary);cursor:pointer;-webkit-user-select:none;user-select:none;background:0 0;border:none;border-radius:4px;flex-shrink:0;align-items:center;padding:2px 8px;font-size:11px;line-height:16px;transition:color .12s,background .12s;display:inline-flex}.BiQ1zG_errorCopySoft:hover{color:var(--hub-text-secondary);background:var(--hub-bg-2)}.BiQ1zG_errorHint{color:var(--hub-text-tertiary);font-size:12px;line-height:18px}.BiQ1zG_modalCancel,.BiQ1zG_modalCopy{color:var(--hub-text-secondary);cursor:pointer;-webkit-user-select:none;user-select:none;background:0 0;border:none;border-radius:6px;padding:5px 14px;font-size:12px;line-height:18px;transition:color .12s,background .12s}.BiQ1zG_modalCancel:hover,.BiQ1zG_modalCopy:hover{color:var(--hub-text-primary);background:var(--hub-bg-hover)}.BiQ1zG_modalInstall{color:var(--hub-text-on-fill);background:var(--hub-btn-fill);cursor:pointer;-webkit-user-select:none;user-select:none;border:none;border-radius:6px;padding:5px 14px;font-size:12px;font-weight:600;line-height:18px;transition:background .12s}.BiQ1zG_modalInstall:hover{background:var(--hub-btn-hover)}.BiQ1zG_modalCopy:disabled,.BiQ1zG_modalInstall:disabled,.BiQ1zG_modalCancel:disabled,.BiQ1zG_modalClose:disabled,.BiQ1zG_uninstallConfirm:disabled{opacity:.55;cursor:not-allowed;pointer-events:none}.BiQ1zG_uninstallConfirm{color:#fff;background:var(--hub-danger);cursor:pointer;-webkit-user-select:none;user-select:none;border:none;border-radius:6px;padding:5px 14px;font-size:12px;font-weight:600;line-height:18px;transition:background .12s}.BiQ1zG_uninstallConfirm:hover{background:var(--hub-danger-hover)}.BiQ1zG_result{text-align:center;flex-direction:column;align-items:center;gap:5px;padding:10px 0 2px;display:flex}.BiQ1zG_resultCheck{background:var(--hub-success-tint);width:40px;height:40px;color:var(--hub-success);border-radius:50%;justify-content:center;align-items:center;margin-bottom:3px;display:inline-flex}.BiQ1zG_resultCheckIcon{flex-shrink:0;width:20px;height:20px}.BiQ1zG_resultTitle{color:var(--hub-text-primary);font-size:13px;font-weight:600;line-height:20px}.BiQ1zG_resultDesc{color:var(--hub-text-secondary);font-size:12px;line-height:18px}.BiQ1zG_resultRestarting{color:var(--hub-text-tertiary);padding:12px 0 6px;font-size:12px;line-height:18px}.BiQ1zG_restartLater{color:var(--hub-text-secondary);cursor:pointer;-webkit-user-select:none;user-select:none;background:0 0;border:none;border-radius:6px;padding:5px 14px;font-size:12px;line-height:18px;transition:color .12s,background .12s}.BiQ1zG_restartLater:hover{color:var(--hub-text-primary);background:var(--hub-bg-hover)}.BiQ1zG_restartNow{color:var(--hub-text-on-fill);background:var(--hub-btn-fill);cursor:pointer;-webkit-user-select:none;user-select:none;border:none;border-radius:6px;padding:5px 14px;font-size:12px;font-weight:600;line-height:18px;transition:background .12s}.BiQ1zG_restartNow:hover{background:var(--hub-btn-hover)}.BiQ1zG_restartNow:disabled{opacity:.55;cursor:not-allowed;pointer-events:none}.BiQ1zG_result .BiQ1zG_modalActions{justify-content:center;gap:10px;margin-top:0;padding:6px 0 2px}.BiQ1zG_result .BiQ1zG_modalActions .BiQ1zG_restartLater,.BiQ1zG_result .BiQ1zG_modalActions .BiQ1zG_restartNow{min-width:100px}.BiQ1zG_progress{margin:10px 0 2px}.BiQ1zG_progressHead{justify-content:flex-end;align-items:center;margin-bottom:3px;display:flex}.BiQ1zG_progressText{color:var(--hub-text-secondary);font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11px;line-height:14px}.BiQ1zG_progressTrack{background:var(--hub-border-2);border-radius:2px;height:4px;overflow:hidden}.BiQ1zG_progressFill{background:var(--hub-brand);border-radius:2px;height:100%;transition:width .32s}.BiQ1zG_progressFillFail{background:var(--hub-danger)}@keyframes BiQ1zG_overlayIn{0%{opacity:0}to{opacity:1}}@keyframes BiQ1zG_modalIn{0%{opacity:0;transform:translateY(6px)scale(.98)}to{opacity:1;transform:translateY(0)scale(1)}}.BiQ1zG_noticeList{flex-direction:column;gap:10px;padding-right:2px;display:flex}.BiQ1zG_noticeRow{border:1px solid var(--hub-border-2);background:var(--hub-bg-2);border-radius:8px;align-items:center;gap:10px;padding:10px 12px;display:flex}.BiQ1zG_noticeBadgeOk,.BiQ1zG_noticeBadgeFail{border-radius:50%;flex-shrink:0;justify-content:center;align-items:center;width:28px;height:28px;display:inline-flex;box-shadow:0 2px 6px #0000002e}.BiQ1zG_noticeBadgeOk{background:var(--hub-success)}.BiQ1zG_noticeBadgeFail{background:var(--hub-danger)}.BiQ1zG_noticeBadgeIcon{flex-shrink:0;width:12px;height:12px}.BiQ1zG_noticeMain{flex-direction:column;flex:1;gap:8px;min-width:0;display:flex}.BiQ1zG_noticeHead{align-items:center;gap:8px;min-width:0;display:flex}.BiQ1zG_noticeTextOk{color:var(--hub-success);flex-shrink:0;font-size:12px;font-weight:600;line-height:18px}.BiQ1zG_noticeTextFail{color:var(--hub-danger-text);flex-shrink:0;font-size:12px;font-weight:600;line-height:18px}.BiQ1zG_failList{flex-direction:column;gap:12px;padding-right:2px;display:flex}.BiQ1zG_failRow{border:1px solid var(--hub-border-2);background:var(--hub-bg-2);border-radius:8px;flex-direction:column;gap:8px;padding:10px 12px;display:flex}.BiQ1zG_failHead{align-items:center;gap:8px;min-width:0;display:flex}.BiQ1zG_failKind{border:1px solid #0000;border-radius:4px;flex-shrink:0;padding:0 6px;font-size:10px;font-weight:500;line-height:16px}.BiQ1zG_failKindInstall{color:var(--hub-danger-text);border-color:var(--hub-danger-border);background:var(--hub-danger-tint)}.BiQ1zG_failKindUninstall{color:var(--hub-warn);border-color:var(--hub-warn-border);background:var(--hub-warn-tint)}.BiQ1zG_failRepo{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--hub-brand);cursor:pointer;-webkit-user-select:none;user-select:none;flex:1;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12px;line-height:18px;text-decoration:none;overflow:hidden}.BiQ1zG_failRepo:hover{text-decoration:underline}.BiQ1zG_failTime{color:var(--hub-text-tertiary);font-variant-numeric:tabular-nums;flex-shrink:0;font-size:11px;line-height:16px}.BiQ1zG_failCopy{color:var(--hub-text-tertiary);cursor:pointer;-webkit-user-select:none;user-select:none;background:0 0;border:none;border-radius:4px;flex-shrink:0;align-items:center;padding:2px 8px;font-size:11px;line-height:16px;transition:color .12s,background .12s;display:inline-flex}.BiQ1zG_failCopy:hover{color:var(--hub-text-secondary);background:var(--hub-bg-2)}.BiQ1zG_failEmpty{text-align:center;color:var(--hub-text-tertiary);padding:24px 0;font-size:12px;line-height:18px}.BiQ1zG_failClear{color:var(--hub-danger);cursor:pointer;-webkit-user-select:none;user-select:none;background:0 0;border:none;border-radius:6px;padding:5px 14px;font-size:12px;line-height:18px;transition:color .12s,background .12s}.BiQ1zG_failClear:hover{color:#fff;background:var(--hub-danger)}.BiQ1zG_failBigIssue{box-sizing:border-box;text-align:center;color:#fff;background:var(--hub-danger);border:1px solid var(--hub-danger);cursor:pointer;-webkit-user-select:none;user-select:none;border-radius:8px;width:100%;margin-top:4px;padding:3px 16px;font-size:13px;font-weight:600;line-height:18px;text-decoration:none;transition:background .12s,border-color .12s,box-shadow .12s;display:block}.BiQ1zG_failBigIssue:hover{background:var(--hub-danger-hover);border-color:var(--hub-danger-hover);box-shadow:0 3px 10px #d1242f59}.BiQ1zG_failPrepareHint{color:var(--hub-warning);background:var(--hub-warning-tint);border:1px solid var(--hub-warning-border);border-radius:6px;margin-top:6px;padding:8px 12px;font-size:12px;line-height:18px}";
1395
+ const css$3 = ".BiQ1zG_overlay{z-index:998;-webkit-user-select:none;user-select:none;background:#00000061;justify-content:center;align-items:center;animation:.16s ease-out BiQ1zG_overlayIn;display:flex;position:fixed;top:0;bottom:0;left:0;right:0}.BiQ1zG_modal,.BiQ1zG_errorModal{background:var(--hub-bg-1);border:1px solid var(--hub-border-2);border-radius:10px;flex-direction:column;gap:10px;width:420px;max-width:calc(100vw - 32px);max-height:calc(100vh - 64px);padding:14px 16px;animation:.18s ease-out BiQ1zG_modalIn;display:flex;box-shadow:0 12px 40px #0000003d}.BiQ1zG_errorModal{width:640px;max-width:calc(100vw - 48px)}.BiQ1zG_modalHead{flex-shrink:0;justify-content:space-between;align-items:center;gap:8px;display:flex}.BiQ1zG_modalTitle{color:var(--hub-text-primary);font-size:14px;font-weight:600;line-height:20px}.BiQ1zG_modalTitleBusy{color:var(--hub-brand)}.BiQ1zG_modalTitleQueued{color:var(--hub-warning)}.BiQ1zG_modalClose{width:24px;height:24px;color:var(--hub-text-tertiary);cursor:pointer;-webkit-user-select:none;user-select:none;background:0 0;border:none;border-radius:6px;flex-shrink:0;justify-content:center;align-items:center;font-size:16px;line-height:1;display:inline-flex}.BiQ1zG_modalClose:hover{color:var(--hub-text-primary);background:var(--hub-bg-hover)}.BiQ1zG_modalCloseIcon{flex-shrink:0;width:14px;height:14px}.BiQ1zG_modalDesc{color:var(--hub-text-secondary);font-size:12px;line-height:18px}.BiQ1zG_trustHint{color:var(--hub-danger-text);background:var(--hub-danger-tint);border:1px solid var(--hub-danger-border-soft);border-radius:6px;padding:6px 10px;font-size:12px;font-weight:500;line-height:18px}.BiQ1zG_cliOnlyHint{color:var(--hub-text-secondary);background:var(--hub-bg-hover);border-radius:6px;padding:6px 10px;font-size:12px;line-height:18px}.BiQ1zG_failedCopyHint{color:var(--hub-danger-text);background:var(--hub-danger-tint);border:1px solid var(--hub-danger-border-soft);border-radius:6px;padding:6px 10px;font-size:12px;font-weight:500;line-height:18px}.BiQ1zG_modalRow{align-items:baseline;gap:8px;min-width:0;font-size:12px;line-height:18px;display:flex}.BiQ1zG_modalLabel{min-width:64px;color:var(--hub-text-tertiary);flex-shrink:0}.BiQ1zG_modalValue{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--hub-text-primary);font-weight:500;overflow:hidden}.BiQ1zG_modalLink{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--hub-brand);cursor:pointer;-webkit-user-select:none;user-select:none;align-items:center;gap:4px;font-weight:500;text-decoration:none;transition:color .12s;display:inline-flex;overflow:hidden}.BiQ1zG_modalLink:hover{color:var(--hub-brand-hover);text-decoration:underline}.BiQ1zG_linkIcon{flex-shrink:0}.BiQ1zG_modalCmd{color:var(--hub-text-primary);background:var(--hub-bg-2);border:1px solid var(--hub-border-2);cursor:pointer;-webkit-user-select:none;user-select:none;border-radius:6px;justify-content:space-between;align-items:center;gap:8px;padding:6px 6px 6px 10px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11px;line-height:18px;transition:border-color .12s,background .12s;display:flex}.BiQ1zG_modalCmd:hover{border-color:var(--hub-border-ghost);background:var(--hub-bg-3)}.BiQ1zG_modalCmdText{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.BiQ1zG_modalCmdCopy{color:var(--hub-brand);cursor:pointer;-webkit-user-select:none;user-select:none;background:0 0;border:none;border-radius:4px;flex-shrink:0;align-items:center;gap:3px;padding:1px 7px;font-family:inherit;font-size:11px;line-height:16px;transition:color .12s,background .12s;display:inline-flex}.BiQ1zG_modalCmdCopy:hover{color:var(--hub-brand-hover);background:var(--hub-bg-2)}.BiQ1zG_modalActions{justify-content:flex-end;align-items:center;gap:8px;margin-top:2px;display:flex}.BiQ1zG_modalBody{flex-direction:column;flex:auto;gap:10px;min-height:0;display:flex;overflow-y:auto}.BiQ1zG_toast{z-index:1000;background:var(--hub-btn-fill);color:var(--hub-text-on-fill);pointer-events:none;white-space:nowrap;border:none;border-radius:8px;padding:10px 16px;font-size:12px;font-weight:500;line-height:18px;animation:.22s ease-out BiQ1zG_toastIn;position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);box-shadow:0 6px 24px #00000047}@keyframes BiQ1zG_toastIn{0%{opacity:0;transform:translate(-50%,-44%)}to{opacity:1;transform:translate(-50%,-50%)}}.BiQ1zG_toastFail{background:var(--hub-danger);color:#fff;border-color:#ffffff3d}.BiQ1zG_queueSection{flex-direction:column;gap:6px;display:flex}.BiQ1zG_queueSectionTitle{color:var(--hub-text-secondary);padding:4px 2px 0;font-size:12px;font-weight:600;line-height:18px}.BiQ1zG_pendingRowStatus{color:var(--hub-warn);flex-shrink:0;font-weight:500}.BiQ1zG_pendingRowActions{flex-shrink:0;gap:8px;margin-left:auto;display:flex}.BiQ1zG_queueRow{border:1px solid var(--hub-border-2);background:var(--hub-bg-2);cursor:pointer;-webkit-user-select:none;user-select:none;border-radius:8px;flex-direction:column;gap:5px;padding:8px 10px;font-size:12px;line-height:18px;transition:background .12s;display:flex}.BiQ1zG_queueRow:hover{background:var(--hub-brand-tint)}.BiQ1zG_queueRowHead{align-items:center;gap:10px;display:flex}.BiQ1zG_queueRowTarget{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--hub-text-primary);flex:1;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;overflow:hidden}.BiQ1zG_queueRowDesc{color:var(--hub-text-tertiary);-webkit-line-clamp:2;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.BiQ1zG_queueRowBody{align-items:center;gap:8px;display:flex}.BiQ1zG_queueRowStatus{color:var(--hub-brand);flex-shrink:0;font-weight:500}.BiQ1zG_queueRowTrack{flex:1;min-width:0}.BiQ1zG_queueRowPct{color:var(--hub-text-tertiary);font-variant-numeric:tabular-nums;flex-shrink:0}.BiQ1zG_stripCancel{color:var(--hub-text-secondary);cursor:pointer;-webkit-user-select:none;user-select:none;background:0 0;border:none;border-radius:6px;flex-shrink:0;padding:2px 10px;font-size:12px;line-height:18px;transition:background .12s,color .12s}.BiQ1zG_stripCancel:hover{background:var(--hub-danger-tint);color:var(--hub-danger-text)}.BiQ1zG_stripCancel:disabled{opacity:.45;cursor:default}.BiQ1zG_queuedHint{color:var(--hub-brand);background:var(--hub-brand-tint);border:1px solid var(--hub-brand-border-soft);border-radius:6px;padding:5px 10px;font-size:12px;line-height:18px}.BiQ1zG_errorTitle{color:var(--hub-danger-text);font-size:14px;font-weight:600;line-height:20px}.BiQ1zG_errorBox{border:1px solid var(--hub-danger-border);background:var(--hub-danger-tint-weak);max-height:240px;color:var(--hub-text-primary);white-space:pre-wrap;word-break:break-word;-webkit-user-select:none;user-select:none;border-radius:6px;margin:0;padding:10px 12px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11px;line-height:17px;overflow:auto}.BiQ1zG_errorCopySoft{color:var(--hub-text-tertiary);cursor:pointer;-webkit-user-select:none;user-select:none;background:0 0;border:none;border-radius:4px;flex-shrink:0;align-items:center;padding:2px 8px;font-size:11px;line-height:16px;transition:color .12s,background .12s;display:inline-flex}.BiQ1zG_errorCopySoft:hover{color:var(--hub-text-secondary);background:var(--hub-bg-2)}.BiQ1zG_errorHint{color:var(--hub-text-tertiary);font-size:12px;line-height:18px}.BiQ1zG_modalCancel,.BiQ1zG_modalCopy{color:var(--hub-text-secondary);cursor:pointer;-webkit-user-select:none;user-select:none;background:0 0;border:none;border-radius:6px;padding:5px 14px;font-size:12px;line-height:18px;transition:color .12s,background .12s}.BiQ1zG_modalCancel:hover,.BiQ1zG_modalCopy:hover{color:var(--hub-text-primary);background:var(--hub-bg-hover)}.BiQ1zG_modalInstall{color:var(--hub-text-on-fill);background:var(--hub-btn-fill);cursor:pointer;-webkit-user-select:none;user-select:none;border:none;border-radius:6px;padding:5px 14px;font-size:12px;font-weight:600;line-height:18px;transition:background .12s}.BiQ1zG_modalInstall:hover{background:var(--hub-btn-hover)}.BiQ1zG_modalCopy:disabled,.BiQ1zG_modalInstall:disabled,.BiQ1zG_modalCancel:disabled,.BiQ1zG_modalClose:disabled,.BiQ1zG_uninstallConfirm:disabled{opacity:.55;cursor:not-allowed;pointer-events:none}.BiQ1zG_uninstallConfirm{color:#fff;background:var(--hub-danger);cursor:pointer;-webkit-user-select:none;user-select:none;border:none;border-radius:6px;padding:5px 14px;font-size:12px;font-weight:600;line-height:18px;transition:background .12s}.BiQ1zG_uninstallConfirm:hover{background:var(--hub-danger-hover)}.BiQ1zG_result{text-align:center;flex-direction:column;align-items:center;gap:5px;padding:10px 0 2px;display:flex}.BiQ1zG_resultCheck{background:var(--hub-success-tint);width:40px;height:40px;color:var(--hub-success);border-radius:50%;justify-content:center;align-items:center;margin-bottom:3px;display:inline-flex}.BiQ1zG_resultCheckIcon{flex-shrink:0;width:20px;height:20px}.BiQ1zG_resultTitle{color:var(--hub-text-primary);font-size:13px;font-weight:600;line-height:20px}.BiQ1zG_resultDesc{color:var(--hub-text-secondary);font-size:12px;line-height:18px}.BiQ1zG_resultRestarting{color:var(--hub-text-tertiary);padding:12px 0 6px;font-size:12px;line-height:18px}.BiQ1zG_restartLater{color:var(--hub-text-secondary);cursor:pointer;-webkit-user-select:none;user-select:none;background:0 0;border:none;border-radius:6px;padding:5px 14px;font-size:12px;line-height:18px;transition:color .12s,background .12s}.BiQ1zG_restartLater:hover{color:var(--hub-text-primary);background:var(--hub-bg-hover)}.BiQ1zG_restartNow{color:var(--hub-text-on-fill);background:var(--hub-btn-fill);cursor:pointer;-webkit-user-select:none;user-select:none;border:none;border-radius:6px;padding:5px 14px;font-size:12px;font-weight:600;line-height:18px;transition:background .12s}.BiQ1zG_restartNow:hover{background:var(--hub-btn-hover)}.BiQ1zG_restartNow:disabled{opacity:.55;cursor:not-allowed;pointer-events:none}.BiQ1zG_result .BiQ1zG_modalActions{justify-content:center;gap:10px;margin-top:0;padding:6px 0 2px}.BiQ1zG_result .BiQ1zG_modalActions .BiQ1zG_restartLater,.BiQ1zG_result .BiQ1zG_modalActions .BiQ1zG_restartNow{min-width:100px}.BiQ1zG_progress{margin:10px 0 2px}.BiQ1zG_progressHead{justify-content:flex-end;align-items:center;margin-bottom:3px;display:flex}.BiQ1zG_progressText{color:var(--hub-text-secondary);font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11px;line-height:14px}.BiQ1zG_progressTrack{background:var(--hub-border-2);border-radius:2px;height:4px;overflow:hidden}.BiQ1zG_progressFill{background:var(--hub-brand);border-radius:2px;height:100%;transition:width .32s}.BiQ1zG_progressFillFail{background:var(--hub-danger)}@keyframes BiQ1zG_overlayIn{0%{opacity:0}to{opacity:1}}@keyframes BiQ1zG_modalIn{0%{opacity:0;transform:translateY(6px)scale(.98)}to{opacity:1;transform:translateY(0)scale(1)}}.BiQ1zG_noticeList{flex-direction:column;gap:10px;padding-right:2px;display:flex}.BiQ1zG_noticeRow{border:1px solid var(--hub-border-2);background:var(--hub-bg-2);border-radius:8px;flex-direction:column;align-items:stretch;gap:6px;padding:10px 12px;display:flex}.BiQ1zG_noticeRowMain{align-items:center;gap:10px;width:100%;min-width:0;display:flex}.BiQ1zG_noticeBadgeOk,.BiQ1zG_noticeBadgeFail{border-radius:50%;flex-shrink:0;justify-content:center;align-items:center;width:28px;height:28px;display:inline-flex;box-shadow:0 2px 6px #0000002e}.BiQ1zG_noticeBadgeOk{background:var(--hub-success)}.BiQ1zG_noticeBadgeFail{background:var(--hub-danger)}.BiQ1zG_noticeBadgeIcon{flex-shrink:0;width:12px;height:12px}.BiQ1zG_noticeMain{flex-direction:column;flex:1;gap:8px;min-width:0;display:flex}.BiQ1zG_noticeHead{align-items:center;gap:8px;min-width:0;display:flex}.BiQ1zG_noticeTextOk{color:var(--hub-success);flex-shrink:0;font-size:12px;font-weight:600;line-height:18px}.BiQ1zG_noticeTextFail{color:var(--hub-danger-text);flex-shrink:0;font-size:12px;font-weight:600;line-height:18px}.BiQ1zG_failList{flex-direction:column;gap:12px;padding-right:2px;display:flex}.BiQ1zG_failRow{border:1px solid var(--hub-border-2);background:var(--hub-bg-2);border-radius:8px;flex-direction:column;gap:8px;padding:10px 12px;display:flex}.BiQ1zG_failHead{align-items:center;gap:8px;min-width:0;display:flex}.BiQ1zG_failKind{border:1px solid #0000;border-radius:4px;flex-shrink:0;padding:0 6px;font-size:10px;font-weight:500;line-height:16px}.BiQ1zG_failKindInstall{color:var(--hub-danger-text);border-color:var(--hub-danger-border);background:var(--hub-danger-tint)}.BiQ1zG_failKindUninstall{color:var(--hub-warn);border-color:var(--hub-warn-border);background:var(--hub-warn-tint)}.BiQ1zG_failRepo{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--hub-brand);cursor:pointer;-webkit-user-select:none;user-select:none;flex:1;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12px;line-height:18px;text-decoration:none;overflow:hidden}.BiQ1zG_failRepo:hover{text-decoration:underline}.BiQ1zG_noticeFoot{justify-content:flex-end;align-items:center;display:flex}.BiQ1zG_noticeTime{color:var(--hub-text-tertiary);font-variant-numeric:tabular-nums;flex-shrink:0;font-size:11px;line-height:16px}.BiQ1zG_noticeRemove{width:22px;height:22px;color:var(--hub-text-tertiary);cursor:pointer;-webkit-user-select:none;user-select:none;background:0 0;border:none;border-radius:5px;flex-shrink:0;justify-content:center;align-items:center;margin-left:auto;transition:color .12s,background .12s;display:inline-flex}.BiQ1zG_noticeRemove:hover{color:var(--hub-danger-text);background:var(--hub-danger-tint)}.BiQ1zG_noticeRowOk .BiQ1zG_noticeRemove{margin-left:0}.BiQ1zG_noticeRowOk .BiQ1zG_noticeTime{margin-left:auto}.BiQ1zG_failCopy{color:var(--hub-text-tertiary);cursor:pointer;-webkit-user-select:none;user-select:none;background:0 0;border:none;border-radius:4px;flex-shrink:0;align-items:center;padding:2px 8px;font-size:11px;line-height:16px;transition:color .12s,background .12s;display:inline-flex}.BiQ1zG_failCopy:hover{color:var(--hub-text-secondary);background:var(--hub-bg-2)}.BiQ1zG_failEmpty{text-align:center;color:var(--hub-text-tertiary);padding:24px 0;font-size:12px;line-height:18px}.BiQ1zG_failClear{color:var(--hub-danger);cursor:pointer;-webkit-user-select:none;user-select:none;background:0 0;border:none;border-radius:6px;padding:5px 14px;font-size:12px;line-height:18px;transition:color .12s,background .12s}.BiQ1zG_failClear:hover{color:#fff;background:var(--hub-danger)}.BiQ1zG_failBigIssue{box-sizing:border-box;text-align:center;color:#fff;background:var(--hub-danger);border:1px solid var(--hub-danger);cursor:pointer;-webkit-user-select:none;user-select:none;border-radius:8px;width:100%;margin-top:4px;padding:3px 16px;font-size:13px;font-weight:600;line-height:18px;text-decoration:none;transition:background .12s,border-color .12s,box-shadow .12s;display:block}.BiQ1zG_failBigIssue:hover{background:var(--hub-danger-hover);border-color:var(--hub-danger-hover);box-shadow:0 3px 10px #d1242f59}.BiQ1zG_failPrepareHint{color:var(--hub-warning);background:var(--hub-warning-tint);border:1px solid var(--hub-warning-border);border-radius:6px;margin-top:6px;padding:8px 12px;font-size:12px;line-height:18px}.BiQ1zG_aboutModal{width:460px;max-width:calc(100vw - 48px)}.BiQ1zG_aboutContent{min-height:0;max-height:340px;color:var(--hub-text-secondary);background:var(--hub-bg-2);border:1px solid var(--hub-border-2);border-radius:8px;flex:auto;padding:10px 12px;font-size:12px;line-height:18px;overflow-y:auto}.BiQ1zG_aboutContent>:first-child{margin-top:0}.BiQ1zG_aboutContent>:last-child{margin-bottom:0}.BiQ1zG_aboutContent h2,.BiQ1zG_aboutContent h3,.BiQ1zG_aboutContent h4,.BiQ1zG_aboutContent h5{color:var(--hub-text-primary);margin:10px 0 4px;font-size:13px;font-weight:600;line-height:20px}.BiQ1zG_aboutContent h2{font-size:14px}.BiQ1zG_aboutContent p{margin:4px 0}.BiQ1zG_aboutContent ul,.BiQ1zG_aboutContent ol{margin:4px 0;padding-left:18px}.BiQ1zG_aboutContent li{margin:2px 0}.BiQ1zG_aboutContent code{background:var(--hub-bg-hover);color:var(--hub-text-primary);border-radius:4px;padding:1px 4px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11px}.BiQ1zG_aboutContent pre{background:var(--hub-bg-1);border:1px solid var(--hub-border-2);border-radius:6px;margin:6px 0;padding:8px 10px;overflow-x:auto}.BiQ1zG_aboutContent pre code{background:0 0;border-radius:0;padding:0}.BiQ1zG_aboutContent blockquote{border-left:3px solid var(--hub-brand);background:var(--hub-bg-hover);color:var(--hub-text-tertiary);border-radius:0 6px 6px 0;margin:6px 0;padding:2px 10px}.BiQ1zG_aboutContent a{color:var(--hub-brand);text-decoration:none}.BiQ1zG_aboutContent a:hover{color:var(--hub-brand-hover);text-decoration:underline}.BiQ1zG_aboutContent img{border-radius:8px;max-width:100%;max-height:220px;margin:8px auto;display:block;box-shadow:0 2px 8px #0000001f}.BiQ1zG_aboutMeta{color:var(--hub-text-tertiary);font-size:11px;line-height:16px}.BiQ1zG_hubUpdateModal{width:520px;max-width:calc(100vw - 48px)}.BiQ1zG_hubUpdateMeta{flex-wrap:wrap;align-items:center;gap:4px 14px;font-size:12px;line-height:18px;display:flex}.BiQ1zG_hubUpdateMetaItem{color:var(--hub-text-tertiary)}.BiQ1zG_hubUpdateNotes{min-height:0;max-height:300px;color:var(--hub-text-secondary);background:var(--hub-bg-2);border:1px solid var(--hub-border-2);border-radius:8px;flex:auto;padding:10px 12px;font-size:12px;line-height:18px;overflow-y:auto}.BiQ1zG_hubUpdateNotes>:first-child{margin-top:0}.BiQ1zG_hubUpdateNotes>:last-child{margin-bottom:0}.BiQ1zG_hubUpdateNotes h2,.BiQ1zG_hubUpdateNotes h3,.BiQ1zG_hubUpdateNotes h4,.BiQ1zG_hubUpdateNotes h5{color:var(--hub-text-primary);margin:10px 0 4px;font-size:13px;font-weight:600;line-height:20px}.BiQ1zG_hubUpdateNotes h2{font-size:14px}.BiQ1zG_hubUpdateNotes p{margin:4px 0}.BiQ1zG_hubUpdateNotes ul,.BiQ1zG_hubUpdateNotes ol{margin:4px 0;padding-left:18px}.BiQ1zG_hubUpdateNotes li{margin:2px 0}.BiQ1zG_hubUpdateNotes code{background:var(--hub-bg-hover);color:var(--hub-text-primary);border-radius:4px;padding:1px 4px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11px}.BiQ1zG_hubUpdateNotes pre{background:var(--hub-bg-1);border:1px solid var(--hub-border-2);border-radius:6px;margin:6px 0;padding:8px 10px;overflow-x:auto}.BiQ1zG_hubUpdateNotes pre code{background:0 0;border-radius:0;padding:0}.BiQ1zG_hubUpdateNotes blockquote{border-left:3px solid var(--hub-brand);background:var(--hub-bg-hover);color:var(--hub-text-tertiary);border-radius:0 6px 6px 0;margin:6px 0;padding:2px 10px}.BiQ1zG_hubUpdateNotes a{color:var(--hub-brand);text-decoration:none}.BiQ1zG_hubUpdateNotes a:hover{color:var(--hub-brand-hover);text-decoration:underline}";
1154
1396
  const tagId$3 = "dsh-plugin/Modal.module.css";
1155
1397
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$3) + "]") === null) {
1156
1398
  const tag = document.createElement("style");
@@ -1160,90 +1402,103 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
1160
1402
  document.head.appendChild(tag);
1161
1403
  }
1162
1404
  var Modal_module_css_default = {
1163
- "queueRowTrack": "BiQ1zG_queueRowTrack",
1164
- "pendingRowStatus": "BiQ1zG_pendingRowStatus",
1165
- "resultRestarting": "BiQ1zG_resultRestarting",
1166
- "resultTitle": "BiQ1zG_resultTitle",
1167
- "linkIcon": "BiQ1zG_linkIcon",
1168
- "errorHint": "BiQ1zG_errorHint",
1169
- "modalCloseIcon": "BiQ1zG_modalCloseIcon",
1170
- "modalCmdText": "BiQ1zG_modalCmdText",
1171
- "failList": "BiQ1zG_failList",
1172
- "failCopy": "BiQ1zG_failCopy",
1173
- "pendingRowActions": "BiQ1zG_pendingRowActions",
1174
1405
  "modalTitle": "BiQ1zG_modalTitle",
1175
- "toastIn": "BiQ1zG_toastIn",
1176
- "queueRowTarget": "BiQ1zG_queueRowTarget",
1177
- "restartLater": "BiQ1zG_restartLater",
1178
- "progressFillFail": "BiQ1zG_progressFillFail",
1179
- "toast": "BiQ1zG_toast",
1180
- "modalTitleQueued": "BiQ1zG_modalTitleQueued",
1181
- "queueSection": "BiQ1zG_queueSection",
1406
+ "errorHint": "BiQ1zG_errorHint",
1182
1407
  "resultCheck": "BiQ1zG_resultCheck",
1183
- "progressFill": "BiQ1zG_progressFill",
1184
- "trustHint": "BiQ1zG_trustHint",
1185
- "modalCancel": "BiQ1zG_modalCancel",
1186
- "modalInstall": "BiQ1zG_modalInstall",
1187
- "noticeRow": "BiQ1zG_noticeRow",
1188
- "failClear": "BiQ1zG_failClear",
1189
- "failEmpty": "BiQ1zG_failEmpty",
1190
- "overlayIn": "BiQ1zG_overlayIn",
1408
+ "noticeBadgeIcon": "BiQ1zG_noticeBadgeIcon",
1409
+ "toastIn": "BiQ1zG_toastIn",
1410
+ "modalLabel": "BiQ1zG_modalLabel",
1411
+ "resultDesc": "BiQ1zG_resultDesc",
1412
+ "modalDesc": "BiQ1zG_modalDesc",
1413
+ "pendingRowActions": "BiQ1zG_pendingRowActions",
1191
1414
  "errorCopySoft": "BiQ1zG_errorCopySoft",
1192
- "resultCheckIcon": "BiQ1zG_resultCheckIcon",
1193
- "failBigIssue": "BiQ1zG_failBigIssue",
1194
- "modalActions": "BiQ1zG_modalActions",
1195
- "progressTrack": "BiQ1zG_progressTrack",
1196
- "modalCmd": "BiQ1zG_modalCmd",
1197
- "failTime": "BiQ1zG_failTime",
1415
+ "modalLink": "BiQ1zG_modalLink",
1198
1416
  "overlay": "BiQ1zG_overlay",
1199
- "failHead": "BiQ1zG_failHead",
1200
- "queueRowDesc": "BiQ1zG_queueRowDesc",
1201
- "failRepo": "BiQ1zG_failRepo",
1202
- "modalCopy": "BiQ1zG_modalCopy",
1203
1417
  "uninstallConfirm": "BiQ1zG_uninstallConfirm",
1204
- "modalHead": "BiQ1zG_modalHead",
1418
+ "modalTitleQueued": "BiQ1zG_modalTitleQueued",
1419
+ "failHead": "BiQ1zG_failHead",
1420
+ "cliOnlyHint": "BiQ1zG_cliOnlyHint",
1421
+ "progress": "BiQ1zG_progress",
1422
+ "queuedHint": "BiQ1zG_queuedHint",
1423
+ "modalCancel": "BiQ1zG_modalCancel",
1424
+ "stripCancel": "BiQ1zG_stripCancel",
1205
1425
  "restartNow": "BiQ1zG_restartNow",
1206
- "progressHead": "BiQ1zG_progressHead",
1207
- "queueRowHead": "BiQ1zG_queueRowHead",
1208
- "noticeBadgeOk": "BiQ1zG_noticeBadgeOk",
1209
- "queueRowPct": "BiQ1zG_queueRowPct",
1210
- "queueRowBody": "BiQ1zG_queueRowBody",
1211
- "modalTitleBusy": "BiQ1zG_modalTitleBusy",
1212
- "failKind": "BiQ1zG_failKind",
1213
- "errorBox": "BiQ1zG_errorBox",
1214
- "modalLink": "BiQ1zG_modalLink",
1215
- "failKindUninstall": "BiQ1zG_failKindUninstall",
1426
+ "result": "BiQ1zG_result",
1216
1427
  "modalRow": "BiQ1zG_modalRow",
1428
+ "queueRowBody": "BiQ1zG_queueRowBody",
1429
+ "modalInstall": "BiQ1zG_modalInstall",
1430
+ "noticeMain": "BiQ1zG_noticeMain",
1431
+ "linkIcon": "BiQ1zG_linkIcon",
1432
+ "noticeTime": "BiQ1zG_noticeTime",
1433
+ "queueSection": "BiQ1zG_queueSection",
1434
+ "noticeRowOk": "BiQ1zG_noticeRowOk",
1435
+ "errorModal": "BiQ1zG_errorModal",
1436
+ "aboutMeta": "BiQ1zG_aboutMeta",
1437
+ "hubUpdateNotes": "BiQ1zG_hubUpdateNotes",
1438
+ "noticeRemove": "BiQ1zG_noticeRemove",
1439
+ "modalClose": "BiQ1zG_modalClose",
1440
+ "progressFillFail": "BiQ1zG_progressFillFail",
1441
+ "overlayIn": "BiQ1zG_overlayIn",
1442
+ "aboutContent": "BiQ1zG_aboutContent",
1443
+ "hubUpdateModal": "BiQ1zG_hubUpdateModal",
1444
+ "queueRowPct": "BiQ1zG_queueRowPct",
1445
+ "queueRowDesc": "BiQ1zG_queueRowDesc",
1446
+ "failList": "BiQ1zG_failList",
1217
1447
  "toastFail": "BiQ1zG_toastFail",
1218
- "progress": "BiQ1zG_progress",
1219
- "modalDesc": "BiQ1zG_modalDesc",
1220
- "failKindInstall": "BiQ1zG_failKindInstall",
1221
- "modal": "BiQ1zG_modal",
1222
- "stripCancel": "BiQ1zG_stripCancel",
1448
+ "modalActions": "BiQ1zG_modalActions",
1223
1449
  "progressText": "BiQ1zG_progressText",
1224
- "modalClose": "BiQ1zG_modalClose",
1450
+ "hubUpdateMetaItem": "BiQ1zG_hubUpdateMetaItem",
1451
+ "modalCopy": "BiQ1zG_modalCopy",
1452
+ "resultRestarting": "BiQ1zG_resultRestarting",
1225
1453
  "queueSectionTitle": "BiQ1zG_queueSectionTitle",
1226
1454
  "modalBody": "BiQ1zG_modalBody",
1227
- "modalLabel": "BiQ1zG_modalLabel",
1228
- "noticeMain": "BiQ1zG_noticeMain",
1229
- "noticeTextOk": "BiQ1zG_noticeTextOk",
1230
- "queueRow": "BiQ1zG_queueRow",
1455
+ "failKind": "BiQ1zG_failKind",
1456
+ "queueRowTrack": "BiQ1zG_queueRowTrack",
1457
+ "modalCmd": "BiQ1zG_modalCmd",
1458
+ "noticeRow": "BiQ1zG_noticeRow",
1459
+ "modalCloseIcon": "BiQ1zG_modalCloseIcon",
1460
+ "resultTitle": "BiQ1zG_resultTitle",
1461
+ "failKindUninstall": "BiQ1zG_failKindUninstall",
1462
+ "failCopy": "BiQ1zG_failCopy",
1463
+ "modalTitleBusy": "BiQ1zG_modalTitleBusy",
1464
+ "queueRowHead": "BiQ1zG_queueRowHead",
1465
+ "noticeRowMain": "BiQ1zG_noticeRowMain",
1466
+ "failRepo": "BiQ1zG_failRepo",
1467
+ "progressFill": "BiQ1zG_progressFill",
1468
+ "aboutModal": "BiQ1zG_aboutModal",
1469
+ "failedCopyHint": "BiQ1zG_failedCopyHint",
1231
1470
  "queueRowStatus": "BiQ1zG_queueRowStatus",
1232
- "noticeBadgeFail": "BiQ1zG_noticeBadgeFail",
1233
- "noticeBadgeIcon": "BiQ1zG_noticeBadgeIcon",
1234
- "modalIn": "BiQ1zG_modalIn",
1235
- "noticeHead": "BiQ1zG_noticeHead",
1471
+ "progressTrack": "BiQ1zG_progressTrack",
1472
+ "modalHead": "BiQ1zG_modalHead",
1236
1473
  "modalCmdCopy": "BiQ1zG_modalCmdCopy",
1237
- "failRow": "BiQ1zG_failRow",
1238
- "result": "BiQ1zG_result",
1239
- "errorModal": "BiQ1zG_errorModal",
1240
- "failPrepareHint": "BiQ1zG_failPrepareHint",
1241
- "errorTitle": "BiQ1zG_errorTitle",
1242
- "queuedHint": "BiQ1zG_queuedHint",
1474
+ "modal": "BiQ1zG_modal",
1243
1475
  "modalValue": "BiQ1zG_modalValue",
1244
- "resultDesc": "BiQ1zG_resultDesc",
1476
+ "failKindInstall": "BiQ1zG_failKindInstall",
1477
+ "noticeFoot": "BiQ1zG_noticeFoot",
1478
+ "failClear": "BiQ1zG_failClear",
1479
+ "queueRow": "BiQ1zG_queueRow",
1480
+ "toast": "BiQ1zG_toast",
1481
+ "noticeTextOk": "BiQ1zG_noticeTextOk",
1245
1482
  "noticeTextFail": "BiQ1zG_noticeTextFail",
1246
- "noticeList": "BiQ1zG_noticeList"
1483
+ "noticeBadgeOk": "BiQ1zG_noticeBadgeOk",
1484
+ "noticeHead": "BiQ1zG_noticeHead",
1485
+ "failPrepareHint": "BiQ1zG_failPrepareHint",
1486
+ "trustHint": "BiQ1zG_trustHint",
1487
+ "pendingRowStatus": "BiQ1zG_pendingRowStatus",
1488
+ "progressHead": "BiQ1zG_progressHead",
1489
+ "failBigIssue": "BiQ1zG_failBigIssue",
1490
+ "failRow": "BiQ1zG_failRow",
1491
+ "hubUpdateMeta": "BiQ1zG_hubUpdateMeta",
1492
+ "queueRowTarget": "BiQ1zG_queueRowTarget",
1493
+ "restartLater": "BiQ1zG_restartLater",
1494
+ "modalIn": "BiQ1zG_modalIn",
1495
+ "errorBox": "BiQ1zG_errorBox",
1496
+ "resultCheckIcon": "BiQ1zG_resultCheckIcon",
1497
+ "noticeList": "BiQ1zG_noticeList",
1498
+ "errorTitle": "BiQ1zG_errorTitle",
1499
+ "modalCmdText": "BiQ1zG_modalCmdText",
1500
+ "failEmpty": "BiQ1zG_failEmpty",
1501
+ "noticeBadgeFail": "BiQ1zG_noticeBadgeFail"
1247
1502
  };
1248
1503
  //#endregion
1249
1504
  //#region src/client/components/icons.tsx
@@ -1422,8 +1677,11 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
1422
1677
  * or restart is running, then switch to a result view offering an immediate
1423
1678
  * restart or a "later" deferral.
1424
1679
  */
1425
- /** 完成结果视图:绿色对勾 + 标题/描述 + 「稍后重启 / 立即重启」按钮对(部分插件需重启后才会挂载) */
1426
- function ResultView({ title, desc, t, restarting, onRestart, onClose }) {
1680
+ /** 完成结果视图:绿色对勾 + 标题/描述;
1681
+ * needsRestart=true(插件需重启才生效)→ 「稍后重启 / 立即重启」按钮对,点稍后重启后
1682
+ * 通知中心待重启条目常驻(服务端登记,内存态),直到用户点「立即重启」真正重启后才消失;
1683
+ * needsRestart=false(卸载已即时生效)→ 仅「完成」关闭。 */
1684
+ function ResultView({ title, desc, t, restarting, needsRestart, onRestart, onClose }) {
1427
1685
  return (0, react.createElement)("div", { className: Modal_module_css_default.result }, (0, react.createElement)("div", { className: Modal_module_css_default.resultCheck }, (0, react.createElement)("svg", {
1428
1686
  className: Modal_module_css_default.resultCheckIcon,
1429
1687
  viewBox: "0 0 16 16",
@@ -1437,7 +1695,7 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
1437
1695
  strokeWidth: 2,
1438
1696
  strokeLinecap: "round",
1439
1697
  strokeLinejoin: "round"
1440
- }))), (0, react.createElement)("div", { className: Modal_module_css_default.resultTitle }, title), (0, react.createElement)("div", { className: Modal_module_css_default.resultDesc }, desc), (0, react.createElement)("div", { className: Modal_module_css_default.resultRestarting }, restarting ? t("restarting") : t("restartHint")), (0, react.createElement)("div", { className: Modal_module_css_default.modalActions }, (0, react.createElement)("button", {
1698
+ }))), (0, react.createElement)("div", { className: Modal_module_css_default.resultTitle }, title), desc ? (0, react.createElement)("div", { className: Modal_module_css_default.resultDesc }, desc) : null, needsRestart ? (0, react.createElement)("div", null, [(0, react.createElement)("div", { className: Modal_module_css_default.resultRestarting }, restarting ? t("restarting") : t("restartHint")), (0, react.createElement)("div", { className: Modal_module_css_default.modalActions }, (0, react.createElement)("button", {
1441
1699
  className: Modal_module_css_default.restartLater,
1442
1700
  onClick: onClose,
1443
1701
  disabled: restarting
@@ -1445,14 +1703,17 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
1445
1703
  className: Modal_module_css_default.restartNow,
1446
1704
  onClick: onRestart,
1447
1705
  disabled: restarting
1448
- }, restarting ? t("restarting") : t("restartNow"))));
1706
+ }, restarting ? t("restarting") : t("restartNow")))]) : (0, react.createElement)("div", { className: Modal_module_css_default.modalActions }, (0, react.createElement)("button", {
1707
+ className: Modal_module_css_default.restartNow,
1708
+ onClick: onClose
1709
+ }, t("done"))));
1449
1710
  }
1450
1711
  /**
1451
1712
  * 信任确认弹窗:安装进入后台队列后弹窗仍可关闭(任务继续),
1452
1713
  * 只在本任务执行中展示实时进度;完成后切换为结果视图,与卸载一致。
1453
1714
  */
1454
1715
  function InstallModal(props) {
1455
- const { plugin, done, task, t, langPath, restarting, submitting, update, onClose, onCopy, onInstall, onRestart } = props;
1716
+ const { plugin, done, task, t, langPath, restarting, submitting, update, cliOnly, needsRestart, onClose, onCopy, onInstall, onRestart } = props;
1456
1717
  const busy = submitting || task !== null && (task.status === "pending" || task.status === "running");
1457
1718
  const name = plugin.displayName ?? plugin.slug;
1458
1719
  const busyTitle = (label) => langPath === "zh/" ? `${name} 插件${label}` : `${label} ${name}`;
@@ -1475,6 +1736,7 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
1475
1736
  desc: update ? t("updateResultDesc") : t("installResultDesc"),
1476
1737
  t,
1477
1738
  restarting,
1739
+ needsRestart,
1478
1740
  onRestart,
1479
1741
  onClose
1480
1742
  }) : (0, react.createElement)("div", { className: Modal_module_css_default.modalBody }, (0, react.createElement)("div", { className: Modal_module_css_default.trustHint }, t("confirmDesc")), (0, react.createElement)("div", { className: Modal_module_css_default.modalRow }, (0, react.createElement)("span", { className: Modal_module_css_default.modalLabel }, t("confirmPlugin")), (0, react.createElement)("span", {
@@ -1492,7 +1754,7 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
1492
1754
  title: t("copyInstallCommand"),
1493
1755
  role: "button",
1494
1756
  tabIndex: 0
1495
- }, (0, react.createElement)("span", { className: Modal_module_css_default.modalCmdText }, `dsh plugin add github:${plugin.source?.repo ?? ""}`), (0, react.createElement)("span", { className: Modal_module_css_default.modalCmdCopy }, (0, react.createElement)(CopyIcon), t("copyCmdLabel"))), task && task.status === "pending" ? (0, react.createElement)("div", { className: Modal_module_css_default.queuedHint }, t("queuedHint")) : null, task ? (0, react.createElement)(ProgressView, { task }) : null, (0, react.createElement)("div", { className: Modal_module_css_default.modalActions }, (0, react.createElement)("button", {
1757
+ }, (0, react.createElement)("span", { className: Modal_module_css_default.modalCmdText }, installCommandOf(plugin)), (0, react.createElement)("span", { className: Modal_module_css_default.modalCmdCopy }, (0, react.createElement)(CopyIcon), t("copyCmdLabel"))), cliOnly ? (0, react.createElement)("div", { className: Modal_module_css_default.cliOnlyHint }, t("cliOnlyHint")) : null, task && task.status === "pending" ? (0, react.createElement)("div", { className: Modal_module_css_default.queuedHint }, t("queuedHint")) : null, task ? (0, react.createElement)(ProgressView, { task }) : null, task && task.status === "failed" ? (0, react.createElement)("div", { className: Modal_module_css_default.failedCopyHint }, t("failedCopyHint")) : null, (0, react.createElement)("div", { className: Modal_module_css_default.modalActions }, (0, react.createElement)("button", {
1496
1758
  className: Modal_module_css_default.modalCopy,
1497
1759
  disabled: busy,
1498
1760
  onClick: onCopy
@@ -1504,7 +1766,7 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
1504
1766
  }
1505
1767
  /** 卸载确认弹窗:确认/进行中(后台队列,可关闭);完成后切换为结果视图(成功即生效,仅「完成」关闭)。 */
1506
1768
  function UninstallModal(props) {
1507
- const { plugin, done, task, t, langPath, restarting, submitting, onClose, onCancel, onCopyCommand, onConfirm, onRestart } = props;
1769
+ const { plugin, done, task, t, langPath, restarting, submitting, needsRestart, onClose, onCancel, onCopyCommand, onConfirm, onRestart } = props;
1508
1770
  const busy = submitting || task !== null && (task.status === "pending" || task.status === "running");
1509
1771
  const name = plugin.displayName ?? plugin.slug;
1510
1772
  const busyTitle = (label) => langPath === "zh/" ? `${name} 插件${label}` : `${label} ${name}`;
@@ -1527,6 +1789,7 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
1527
1789
  desc: t("uninstallResultDesc"),
1528
1790
  t,
1529
1791
  restarting,
1792
+ needsRestart,
1530
1793
  onRestart,
1531
1794
  onClose
1532
1795
  }) : (0, react.createElement)("div", { className: Modal_module_css_default.modalBody }, (0, react.createElement)("div", { className: Modal_module_css_default.modalDesc }, t("uninstallDesc")), (0, react.createElement)("div", { className: Modal_module_css_default.modalRow }, (0, react.createElement)("span", { className: Modal_module_css_default.modalLabel }, t("confirmPlugin")), (0, react.createElement)("span", {
@@ -1553,7 +1816,7 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
1553
1816
  }
1554
1817
  /** 预填插件仓库的 GitHub Issue 链接:标题带插件名,正文附完整错误信息,方便用户一键反馈。 */
1555
1818
  /** 安装/卸载失败弹窗:布局与失败记录一致(类型徽标 + 仓库超链接 + 隐蔽复制按钮),报错完整展示,底部一键提交 Issue。 */
1556
- function ErrorModal({ message, repo, kind, t, env, onCopy, onClose }) {
1819
+ function ErrorModal({ message, repo, kind, command, attempts, t, env, onCopy, onClose }) {
1557
1820
  const failureKind = classifyFailure(message);
1558
1821
  return (0, react.createElement)("div", {
1559
1822
  className: Modal_module_css_default.overlay,
@@ -1579,13 +1842,13 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
1579
1842
  onClick: () => onCopy(message)
1580
1843
  }, t("errorCopy"))), (0, react.createElement)("pre", { className: Modal_module_css_default.errorBox }, message), failureKind === "pluginPrepare" || failureKind === "pnpmIgnoredBuild" ? (0, react.createElement)("div", null, [(0, react.createElement)("div", { className: Modal_module_css_default.failPrepareHint }, failureKind === "pnpmIgnoredBuild" ? t("failIgnoredBuild") : /\[packaging\]/i.test(message) ? t("failPackagingHint") : t("failPrepareHint")), repo ? (0, react.createElement)("a", {
1581
1844
  className: Modal_module_css_default.failBigIssue,
1582
- href: pluginIssueUrl(repo, message, env),
1845
+ href: pluginIssueUrl(repo, message, env, command, attempts),
1583
1846
  target: "_blank",
1584
1847
  rel: "noopener noreferrer",
1585
1848
  title: t("failIssueHint")
1586
1849
  }, t("failIssueBig")) : null]) : repo ? (0, react.createElement)("a", {
1587
1850
  className: Modal_module_css_default.failBigIssue,
1588
- href: pluginIssueUrl(repo, message, env),
1851
+ href: pluginIssueUrl(repo, message, env, command, attempts),
1589
1852
  target: "_blank",
1590
1853
  rel: "noopener noreferrer",
1591
1854
  title: t("failIssueHint")
@@ -1604,6 +1867,175 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
1604
1867
  }, text);
1605
1868
  }
1606
1869
  //#endregion
1870
+ //#region src/client/lib/renderMarkdown.ts
1871
+ /**
1872
+ * 轻量 Markdown 渲染器:仅用于 Hub 自我更新的变更记录(Worker 下发的 notes)。
1873
+ *
1874
+ * 内容由插件作者自己编写、属可信内容,但渲染端仍先整体 HTML 转义,再按
1875
+ * Markdown 语法输出结构,杜绝任何未转义的原始 HTML 注入;链接仅放行 http(s)。
1876
+ *
1877
+ * 支持:标题(#~####)、有序/无序列表、代码块(```)、行内代码、加粗、斜体、
1878
+ * 链接、图片(![alt](url),如反馈群二维码)、引用(>)、段落;其余语法一律按
1879
+ * 纯文本处理,不引入外部依赖。图片仅放行 http(s),限定最大宽度不撑破弹窗。
1880
+ */
1881
+ /** HTML 转义:所有文本先进这里,再进入结构变换,保证输出里没有未转义的用户输入。 */
1882
+ function escapeHtml(s) {
1883
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
1884
+ }
1885
+ /** 行内变换:链接 / 加粗 / 斜体 / 行内代码(代码先占位保护,最后还原)。 */
1886
+ function inline(s) {
1887
+ let out = escapeHtml(s);
1888
+ const codeSpans = [];
1889
+ out = out.replace(/`([^`]+)`/g, (_m, code) => {
1890
+ codeSpans.push(String(code));
1891
+ return `\u0000${codeSpans.length - 1}\u0000`;
1892
+ });
1893
+ out = out.replace(/!\[([^\]]*)\]\(([^)\s]+)\)/g, (m, alt, url) => /^https?:\/\//i.test(String(url)) && !/[\u0000]/.test(String(url)) ? `<img src="${String(url)}" alt="${String(alt)}" loading="lazy">` : m);
1894
+ out = out.replace(/\[([^\]]+)\]\(([^)\s]+)\)/g, (m, text, url) => /^https?:\/\//i.test(String(url)) && !/[\u0000]/.test(String(url)) ? `<a href="${String(url)}" target="_blank" rel="noopener noreferrer">${String(text)}</a>` : m);
1895
+ out = out.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>");
1896
+ out = out.replace(/(^|[^*])\*([^*]+)\*/g, "$1<em>$2</em>");
1897
+ return out.replace(/\u0000(\d+)\u0000/g, (_m, idx) => `<code>${codeSpans[Number(idx)]}</code>`);
1898
+ }
1899
+ /** 把变更记录 Markdown 渲染为安全的 HTML 字符串(由弹窗用 dangerouslySetInnerHTML 注入)。 */
1900
+ function renderMarkdown(md) {
1901
+ const lines = md.replace(/\r\n?/g, "\n").split("\n");
1902
+ const blocks = [];
1903
+ let i = 0;
1904
+ while (i < lines.length) {
1905
+ const line = lines[i];
1906
+ if (/^\s*```/.test(line)) {
1907
+ const buf = [];
1908
+ i++;
1909
+ while (i < lines.length && !/^\s*```/.test(lines[i])) {
1910
+ buf.push(lines[i]);
1911
+ i++;
1912
+ }
1913
+ i++;
1914
+ blocks.push(`<pre><code>${escapeHtml(buf.join("\n"))}</code></pre>`);
1915
+ continue;
1916
+ }
1917
+ if (line.trim() === "") {
1918
+ i++;
1919
+ continue;
1920
+ }
1921
+ const heading = line.match(/^(#{1,4})\s+(.*)/);
1922
+ if (heading) {
1923
+ const level = heading[1].length + 1;
1924
+ blocks.push(`<h${level}>${inline(heading[2])}</h${level}>`);
1925
+ i++;
1926
+ continue;
1927
+ }
1928
+ if (/^\s*[-*+]\s+/.test(line) || /^\s*\d+[.)]\s+/.test(line)) {
1929
+ const ordered = /^\s*\d+[.)]\s+/.test(line);
1930
+ const items = [];
1931
+ while (i < lines.length && (/^\s*[-*+]\s+/.test(lines[i]) || /^\s*\d+[.)]\s+/.test(lines[i]))) {
1932
+ items.push(`<li>${inline(lines[i].replace(/^\s*[-*+]|\s*\d+[.)]\s*/, "").trim())}</li>`);
1933
+ i++;
1934
+ }
1935
+ blocks.push(`<${ordered ? "ol" : "ul"}>${items.join("")}</${ordered ? "ol" : "ul"}>`);
1936
+ continue;
1937
+ }
1938
+ if (/^\s*>\s?/.test(line)) {
1939
+ const buf = [];
1940
+ while (i < lines.length && /^\s*>\s?/.test(lines[i])) {
1941
+ buf.push(inline(lines[i].replace(/^\s*>\s?/, "")));
1942
+ i++;
1943
+ }
1944
+ blocks.push(`<blockquote>${buf.join("<br>")}</blockquote>`);
1945
+ continue;
1946
+ }
1947
+ const buf = [];
1948
+ while (i < lines.length && lines[i].trim() !== "" && !/^\s*```/.test(lines[i]) && !/^#{1,4}\s+/.test(lines[i]) && !/^\s*([-*+]|\d+[.)])\s+/.test(lines[i])) {
1949
+ buf.push(inline(lines[i]));
1950
+ i++;
1951
+ }
1952
+ blocks.push(`<p>${buf.join("<br>")}</p>`);
1953
+ }
1954
+ return blocks.join("");
1955
+ }
1956
+ //#endregion
1957
+ //#region src/client/components/AboutModal.tsx
1958
+ /**
1959
+ * 头部「关注我们」弹窗:介绍 DSH-Plugin Hub 是干什么的 + 用户反馈群二维码。
1960
+ * 内容以 Markdown 形式由 dsh-update Worker 的 /about 接口下发(key = hub:about),
1961
+ * 作者想怎么写就怎么写、随写随推,客户端只负责渲染 —— 非写死。
1962
+ * 图片(反馈群二维码)经 renderMarkdown 的 ![](url) 语法嵌入,居中、最大高度受控。
1963
+ */
1964
+ function AboutModal({ info, lang, t, onClose }) {
1965
+ const contentRaw = info ? typeof info.content === "string" ? info.content : info.content && typeof info.content === "object" ? lang === "en" ? info.content.en ?? info.content.zh ?? "" : info.content.zh ?? info.content.en ?? "" : "" : "";
1966
+ const contentHtml = contentRaw.trim() ? renderMarkdown(contentRaw) : null;
1967
+ let updated = null;
1968
+ if (info?.updatedAt) {
1969
+ const d = new Date(info.updatedAt);
1970
+ if (!Number.isNaN(d.getTime())) updated = d.toLocaleString(lang === "en" ? "en-US" : "zh-CN");
1971
+ }
1972
+ return (0, react.createElement)("div", {
1973
+ className: Modal_module_css_default.overlay,
1974
+ onClick: (e) => {
1975
+ if (e.target === e.currentTarget) onClose();
1976
+ }
1977
+ }, (0, react.createElement)("div", {
1978
+ className: `${Modal_module_css_default.modal} ${Modal_module_css_default.aboutModal}`,
1979
+ role: "dialog",
1980
+ "aria-modal": "true"
1981
+ }, (0, react.createElement)("div", { className: Modal_module_css_default.modalHead }, (0, react.createElement)("div", { className: Modal_module_css_default.modalTitle }, t("aboutTitle")), (0, react.createElement)("button", {
1982
+ className: Modal_module_css_default.modalClose,
1983
+ "aria-label": t("confirmCancel"),
1984
+ onClick: () => onClose()
1985
+ }, (0, react.createElement)(CloseIcon))), (0, react.createElement)("div", { className: Modal_module_css_default.modalDesc }, t("aboutDesc")), contentHtml ? (0, react.createElement)("div", {
1986
+ className: Modal_module_css_default.aboutContent,
1987
+ dangerouslySetInnerHTML: { __html: contentHtml }
1988
+ }) : (0, react.createElement)("div", { className: Modal_module_css_default.aboutContent }, t("aboutEmpty")), updated ? (0, react.createElement)("div", { className: Modal_module_css_default.aboutMeta }, `${t("aboutUpdated")} ${updated}`) : null, (0, react.createElement)("div", { className: Modal_module_css_default.modalActions }, (0, react.createElement)("button", {
1989
+ className: Modal_module_css_default.modalInstall,
1990
+ onClick: onClose
1991
+ }, t("doneBtn")))));
1992
+ }
1993
+ //#endregion
1994
+ //#region src/client/components/HubUpdateModal.tsx
1995
+ /**
1996
+ * Hub 版本信息 / 自我更新说明弹窗:
1997
+ * - 有更新(hasUpdate=true):标题「有新版本」,展示新版本号、发布时间与 Worker 下发的
1998
+ * Markdown 变更记录(renderMarkdown 渲染,双语言按界面语言取 {zh,en} 对象),
1999
+ * 按钮「稍后再说 / 直接更新」,确认后进入安装弹窗执行覆盖重装。
2000
+ * - 无更新(hasUpdate=false):标题「当前版本」,展示当前版本信息与更新记录,已是最新。
2001
+ * 点击头部版本号(或「可更新」徽标)打开 —— 无论有无更新都能看到更新内容。
2002
+ */
2003
+ function HubUpdateModal({ info, lang, t, hasUpdate, onProceed, onClose }) {
2004
+ const notesRaw = typeof info.notes === "string" ? info.notes : info.notes && typeof info.notes === "object" ? lang === "en" ? info.notes.en ?? info.notes.zh ?? "" : info.notes.zh ?? info.notes.en ?? "" : "";
2005
+ const notesHtml = notesRaw.trim() ? renderMarkdown(notesRaw) : null;
2006
+ let published = null;
2007
+ if (info.publishedAt) {
2008
+ const d = new Date(info.publishedAt);
2009
+ if (!Number.isNaN(d.getTime())) published = d.toLocaleString(lang === "en" ? "en-US" : "zh-CN");
2010
+ }
2011
+ return (0, react.createElement)("div", {
2012
+ className: Modal_module_css_default.overlay,
2013
+ onClick: (e) => {
2014
+ if (e.target === e.currentTarget) onClose();
2015
+ }
2016
+ }, (0, react.createElement)("div", {
2017
+ className: `${Modal_module_css_default.modal} ${Modal_module_css_default.hubUpdateModal}`,
2018
+ role: "dialog",
2019
+ "aria-modal": "true"
2020
+ }, (0, react.createElement)("div", { className: Modal_module_css_default.modalHead }, (0, react.createElement)("div", { className: Modal_module_css_default.modalTitle }, t(hasUpdate ? "hubUpdateTitle" : "hubCurrentTitle")), (0, react.createElement)("button", {
2021
+ className: Modal_module_css_default.modalClose,
2022
+ "aria-label": t("confirmCancel"),
2023
+ onClick: () => onClose()
2024
+ }, (0, react.createElement)(CloseIcon))), (0, react.createElement)("div", { className: Modal_module_css_default.modalDesc }, t(hasUpdate ? "hubUpdateDesc" : "hubCurrentDesc", { version: info.version })), (0, react.createElement)("div", { className: Modal_module_css_default.hubUpdateMeta }, (0, react.createElement)("span", { className: Modal_module_css_default.hubUpdateMetaItem }, `${t("version")} ${info.version}`), published ? (0, react.createElement)("span", { className: Modal_module_css_default.hubUpdateMetaItem }, `${t("hubUpdatePublished")} ${published}`) : null), notesHtml ? (0, react.createElement)("div", {
2025
+ className: Modal_module_css_default.hubUpdateNotes,
2026
+ dangerouslySetInnerHTML: { __html: notesHtml }
2027
+ }) : null, (0, react.createElement)("div", { className: Modal_module_css_default.modalActions }, hasUpdate ? [(0, react.createElement)("button", {
2028
+ className: Modal_module_css_default.restartLater,
2029
+ onClick: onClose
2030
+ }, t("hubUpdateLater")), (0, react.createElement)("button", {
2031
+ className: Modal_module_css_default.modalInstall,
2032
+ onClick: onProceed
2033
+ }, t("updateNow"))] : (0, react.createElement)("button", {
2034
+ className: Modal_module_css_default.modalInstall,
2035
+ onClick: onClose
2036
+ }, t("hubUpToDate")))));
2037
+ }
2038
+ //#endregion
1607
2039
  //#region src/client/components/NotificationsModal.tsx
1608
2040
  /**
1609
2041
  * Notification-center dialog: a persistent log of every settled install /
@@ -1616,12 +2048,11 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
1616
2048
  * failures keep their copy / fix / file-an-issue actions. Opened from the
1617
2049
  * header entry button.
1618
2050
  */
1619
- /** 记录时间紧凑展示:今年内 MM-DD HH:mm,跨年补年份前缀。 */
2051
+ /** 记录时间完整展示:YYYY-MM-DD HH:mm:ss(每条通知都带精确到秒的时间戳)。 */
1620
2052
  function fmtTime(at) {
1621
2053
  const d = new Date(at);
1622
2054
  const pad = (n) => String(n).padStart(2, "0");
1623
- const mmdd = `${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
1624
- return d.getFullYear() === (/* @__PURE__ */ new Date()).getFullYear() ? mmdd : `${d.getFullYear()}-${mmdd}`;
2055
+ return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
1625
2056
  }
1626
2057
  /** 圆形状态徽标内的白色图形:成功为对勾、失败为叉。 */
1627
2058
  function BadgeGlyph({ ok }) {
@@ -1645,7 +2076,7 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
1645
2076
  strokeLinecap: "round"
1646
2077
  }));
1647
2078
  }
1648
- function NotificationsModal({ records, tasks, pendingRestarts, t, env, onClose, onCopy, onClear, cancelTask, restarting, onRestart }) {
2079
+ function NotificationsModal({ records, tasks, pendingRestarts, t, env, onClose, onCopy, onClear, onRemove, cancelTask, restarting, onRestart }) {
1649
2080
  return (0, react.createElement)("div", {
1650
2081
  className: Modal_module_css_default.overlay,
1651
2082
  onClick: (e) => {
@@ -1698,36 +2129,47 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
1698
2129
  })), (0, react.createElement)("span", { className: Modal_module_css_default.queueRowPct }, `${q.progress}%`))))) : null]) : null, records.length === 0 ? (0, react.createElement)("div", { className: Modal_module_css_default.failEmpty }, t("notificationsEmpty")) : (0, react.createElement)("div", { className: Modal_module_css_default.noticeList }, records.map((r) => {
1699
2130
  return (0, react.createElement)("div", {
1700
2131
  key: r.id,
1701
- className: Modal_module_css_default.noticeRow
1702
- }, (0, react.createElement)("div", { className: r.ok ? Modal_module_css_default.noticeBadgeOk : Modal_module_css_default.noticeBadgeFail }, (0, react.createElement)(BadgeGlyph, { ok: r.ok })), (0, react.createElement)("div", { className: Modal_module_css_default.noticeMain }, (0, react.createElement)("div", { className: Modal_module_css_default.noticeHead }, (0, react.createElement)("span", { className: r.ok ? Modal_module_css_default.noticeTextOk : Modal_module_css_default.noticeTextFail }, r.ok ? r.kind === "install" ? t("installDone") : t("uninstallDone") : r.kind === "install" ? t("errorTitleInstall") : t("errorTitleUninstall")), r.repo ? (0, react.createElement)("a", {
2132
+ className: r.ok ? `${Modal_module_css_default.noticeRow} ${Modal_module_css_default.noticeRowOk}` : Modal_module_css_default.noticeRow
2133
+ }, (0, react.createElement)("div", { className: Modal_module_css_default.noticeRowMain }, (0, react.createElement)("div", { className: r.ok ? Modal_module_css_default.noticeBadgeOk : Modal_module_css_default.noticeBadgeFail }, (0, react.createElement)(BadgeGlyph, { ok: r.ok })), (0, react.createElement)("div", { className: Modal_module_css_default.noticeMain }, (0, react.createElement)("div", { className: Modal_module_css_default.noticeHead }, (0, react.createElement)("span", { className: r.ok ? Modal_module_css_default.noticeTextOk : Modal_module_css_default.noticeTextFail }, r.ok ? r.kind === "install" ? t("installDone") : t("uninstallDone") : r.kind === "install" ? t("errorTitleInstall") : t("errorTitleUninstall")), r.repo ? (0, react.createElement)("a", {
1703
2134
  className: Modal_module_css_default.failRepo,
1704
2135
  href: pluginSiteUrl(r.repo),
1705
2136
  target: "_blank",
1706
2137
  rel: "noopener noreferrer",
1707
2138
  title: r.repo
1708
- }, r.repo) : null, (0, react.createElement)("span", {
1709
- className: Modal_module_css_default.failTime,
1710
- title: new Date(r.at).toLocaleString()
1711
- }, fmtTime(r.at)), !r.ok && (0, react.createElement)("button", {
2139
+ }, r.repo) : null, !r.ok && (0, react.createElement)("button", {
1712
2140
  className: Modal_module_css_default.failCopy,
1713
2141
  onClick: () => onCopy(r.message)
1714
- }, t("failCopy"))), !r.ok && (() => {
2142
+ }, t("failCopy")), r.ok ? (0, react.createElement)("span", {
2143
+ className: Modal_module_css_default.noticeTime,
2144
+ title: new Date(r.at).toLocaleString()
2145
+ }, fmtTime(r.at)) : null, (0, react.createElement)("button", {
2146
+ className: Modal_module_css_default.noticeRemove,
2147
+ "aria-label": t("removeNotification"),
2148
+ title: t("removeNotification"),
2149
+ onClick: (e) => {
2150
+ e.stopPropagation();
2151
+ onRemove(r.id);
2152
+ }
2153
+ }, (0, react.createElement)(CloseIcon))), !r.ok && (() => {
1715
2154
  const kind = classifyFailure(r.message);
1716
2155
  if (kind === "pluginPrepare" || kind === "pnpmIgnoredBuild") return (0, react.createElement)("div", null, [(0, react.createElement)("div", { className: Modal_module_css_default.failPrepareHint }, kind === "pnpmIgnoredBuild" ? t("failIgnoredBuild") : /\[packaging\]/i.test(r.message) ? t("failPackagingHint") : t("failPrepareHint")), r.repo ? (0, react.createElement)("a", {
1717
2156
  className: Modal_module_css_default.failBigIssue,
1718
- href: pluginIssueUrl(r.repo, r.message, env),
2157
+ href: pluginIssueUrl(r.repo, r.message, env, r.command, r.attempts),
1719
2158
  target: "_blank",
1720
2159
  rel: "noopener noreferrer",
1721
2160
  title: t("failIssueHint")
1722
2161
  }, t("failIssueBig")) : null]);
1723
2162
  return r.repo ? (0, react.createElement)("a", {
1724
2163
  className: Modal_module_css_default.failBigIssue,
1725
- href: pluginIssueUrl(r.repo, r.message, env),
2164
+ href: pluginIssueUrl(r.repo, r.message, env, r.command, r.attempts),
1726
2165
  target: "_blank",
1727
2166
  rel: "noopener noreferrer",
1728
2167
  title: t("failIssueHint")
1729
2168
  }, t("failIssueBig")) : null;
1730
- })()));
2169
+ })())), !r.ok ? (0, react.createElement)("div", { className: Modal_module_css_default.noticeFoot }, (0, react.createElement)("span", {
2170
+ className: Modal_module_css_default.noticeTime,
2171
+ title: new Date(r.at).toLocaleString()
2172
+ }, fmtTime(r.at))) : null);
1731
2173
  })), (0, react.createElement)("div", { className: Modal_module_css_default.modalActions }, records.length > 0 ? (0, react.createElement)("button", {
1732
2174
  className: Modal_module_css_default.failClear,
1733
2175
  onClick: onClear
@@ -1742,7 +2184,7 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
1742
2184
  * Section header: brand title row (H1 + open-site button) + tagline, followed
1743
2185
  * by the purple ad banner that promotes the catalog stats.
1744
2186
  */
1745
- function CatalogHeader({ t, langPath, statsTotal, statsVerified, onToggleLang }) {
2187
+ function CatalogHeader({ t, langPath, statsTotal, statsVerified, onToggleLang, hubUpdate, onVersionClick, onAboutClick }) {
1746
2188
  return (0, react.createElement)(react.Fragment, null, (0, react.createElement)("div", { className: Header_module_css_default.header }, (0, react.createElement)("div", { className: Header_module_css_default.headerTitleRow }, (0, react.createElement)("a", {
1747
2189
  className: Header_module_css_default.brandTitle,
1748
2190
  href: `${SITE_URL}${langPath}`,
@@ -1750,13 +2192,25 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
1750
2192
  rel: "noopener noreferrer",
1751
2193
  title: t("openHint"),
1752
2194
  "aria-label": t("openHint")
1753
- }, (0, react.createElement)(LogoIcon), (0, react.createElement)("h1", { className: Header_module_css_default.title }, t("title"), (0, react.createElement)("span", { className: Header_module_css_default.version }, ` v${PLUGIN_VERSION}`))), (0, react.createElement)("div", { className: Header_module_css_default.headerRight }, (0, react.createElement)("button", {
2195
+ }, (0, react.createElement)(LogoIcon), (0, react.createElement)("h1", { className: Header_module_css_default.title }, t("title"))), (0, react.createElement)("button", {
2196
+ className: Header_module_css_default.versionBtn,
2197
+ type: "button",
2198
+ onClick: onVersionClick,
2199
+ title: t(hubUpdate ? "hubUpdateHint" : "versionHint"),
2200
+ "aria-label": t(hubUpdate ? "hubUpdateHint" : "versionHint")
2201
+ }, `v${PLUGIN_VERSION}`, hubUpdate && (0, react.createElement)("span", { className: Header_module_css_default.hubUpdateBadge }, t("update"))), (0, react.createElement)("div", { className: Header_module_css_default.headerRight }, (0, react.createElement)("button", {
1754
2202
  className: Header_module_css_default.langBtn,
1755
2203
  type: "button",
1756
2204
  onClick: onToggleLang,
1757
2205
  title: t("toggleLangHint"),
1758
2206
  "aria-label": t("toggleLangHint")
1759
- }, langPath === "zh/" ? "EN" : "中文"), (0, react.createElement)("a", {
2207
+ }, langPath === "zh/" ? "EN" : "中文"), (0, react.createElement)("button", {
2208
+ className: Header_module_css_default.aboutBtn,
2209
+ type: "button",
2210
+ onClick: onAboutClick,
2211
+ title: t("aboutDesc"),
2212
+ "aria-label": t("aboutTitle")
2213
+ }, t("followUs")), (0, react.createElement)("a", {
1760
2214
  className: Header_module_css_default.githubLink,
1761
2215
  href: GITHUB_URL,
1762
2216
  target: "_blank",
@@ -1812,17 +2266,17 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
1812
2266
  document.head.appendChild(tag);
1813
2267
  }
1814
2268
  var Dropdown_module_css_default = {
2269
+ "dropdownItemLabel": "B_Gxsq_dropdownItemLabel",
2270
+ "dropdownCount": "B_Gxsq_dropdownCount",
2271
+ "dropdownBtn": "B_Gxsq_dropdownBtn",
2272
+ "dropdownArrow": "B_Gxsq_dropdownArrow",
1815
2273
  "dropdownPanel": "B_Gxsq_dropdownPanel",
2274
+ "dropdownItem": "B_Gxsq_dropdownItem",
1816
2275
  "dropdownCountActive": "B_Gxsq_dropdownCountActive",
1817
- "dropdownCount": "B_Gxsq_dropdownCount",
1818
- "dropdown": "B_Gxsq_dropdown",
1819
2276
  "dropdownArrowOpen": "B_Gxsq_dropdownArrowOpen",
1820
- "dropdownLabel": "B_Gxsq_dropdownLabel",
1821
2277
  "dropdownItemActive": "B_Gxsq_dropdownItemActive",
1822
- "dropdownItem": "B_Gxsq_dropdownItem",
1823
- "dropdownBtn": "B_Gxsq_dropdownBtn",
1824
- "dropdownArrow": "B_Gxsq_dropdownArrow",
1825
- "dropdownItemLabel": "B_Gxsq_dropdownItemLabel"
2278
+ "dropdownLabel": "B_Gxsq_dropdownLabel",
2279
+ "dropdown": "B_Gxsq_dropdown"
1826
2280
  };
1827
2281
  //#endregion
1828
2282
  //#region src/client/components/Dropdown.tsx
@@ -1927,7 +2381,7 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
1927
2381
  }
1928
2382
  //#endregion
1929
2383
  //#region \0dsh-css:src/client/styles/List.module.css.mjs
1930
- const css$1 = "._3XaZHa_body{flex-direction:column;flex:1;min-height:0;display:flex}._3XaZHa_list{flex-direction:column;flex:1;gap:6px;min-height:0;padding:2px 4px 4px 2px;display:flex;overflow-y:auto}._3XaZHa_card{border:1px solid var(--hub-border-2);background:var(--hub-bg-1);border-radius:8px;justify-content:space-between;align-items:stretch;gap:12px;padding:9px 12px;transition:border-color .12s,background .12s;display:flex}._3XaZHa_card:hover{border-color:var(--hub-brand);background:var(--hub-bg-2)}._3XaZHa_cardMain{flex-direction:column;gap:4px;min-width:0;display:flex}._3XaZHa_cardHead{align-items:center;gap:6px;min-width:0;display:flex}._3XaZHa_cardTitle{white-space:nowrap;text-overflow:ellipsis;font-size:13px;font-weight:600;line-height:18px;overflow:hidden}._3XaZHa_categoryBadge,._3XaZHa_verified,._3XaZHa_versionBadge,._3XaZHa_updateBadge{border:1px solid #0000;border-radius:4px;flex-shrink:0;padding:0 6px;font-size:10px;line-height:16px}._3XaZHa_categoryBadge{color:var(--hub-brand);border-color:var(--hub-border-ghost)}._3XaZHa_verified{color:var(--hub-success);border-color:var(--hub-success-border)}._3XaZHa_versionBadge{color:var(--hub-text-secondary);border-color:var(--hub-border-input)}._3XaZHa_updateBadge{color:var(--hub-warn);border-color:var(--hub-warn-border);background:var(--hub-warn-tint)}._3XaZHa_desc{color:var(--hub-text-secondary);-webkit-line-clamp:2;-webkit-box-orient:vertical;margin:0;font-size:12px;line-height:18px;display:-webkit-box;overflow:hidden}._3XaZHa_topics{flex-wrap:wrap;align-items:center;gap:4px;min-width:0;display:flex}._3XaZHa_topic{color:var(--hub-text-tertiary);background:var(--hub-bg-3);white-space:nowrap;border-radius:4px;flex-shrink:0;padding:0 6px;font-size:10px;line-height:16px}._3XaZHa_cardSide{flex-direction:column;flex-shrink:0;justify-content:space-between;align-items:flex-end;gap:6px;display:flex}._3XaZHa_stats{flex-direction:column;align-items:flex-end;gap:1px;display:flex}._3XaZHa_star{color:var(--hub-warn);white-space:nowrap;font-size:11px;line-height:16px}._3XaZHa_fork{color:var(--hub-text-tertiary);white-space:nowrap;font-size:11px;line-height:16px}._3XaZHa_date{color:var(--hub-text-tertiary);white-space:nowrap;font-size:10px;line-height:14px}._3XaZHa_installBtn,._3XaZHa_installBtnCopied,._3XaZHa_installBtnInstalled,._3XaZHa_installBtnUpdate,._3XaZHa_uninstallBtn,._3XaZHa_detailBtn{cursor:pointer;border-radius:6px;padding:2px 10px;font-size:11px;line-height:18px;transition:color .12s,border-color .12s,background .12s}._3XaZHa_installBtnInstalled{color:var(--hub-success);background:var(--hub-success-tint);cursor:default;-webkit-user-select:none;user-select:none;border:none}._3XaZHa_installBtnUpdate{color:#fff;background:var(--hub-warn);-webkit-user-select:none;user-select:none;border:none;font-weight:500}._3XaZHa_installBtnUpdate:hover{background:var(--hub-warn-strong)}._3XaZHa_uninstallBtn{color:var(--hub-danger);-webkit-user-select:none;user-select:none;background:0 0;border:none}._3XaZHa_uninstallBtn:hover{color:#fff;background:var(--hub-danger)}._3XaZHa_installBtn{color:var(--hub-text-on-fill);background:var(--hub-btn-fill);-webkit-user-select:none;user-select:none;border:none;font-weight:500}._3XaZHa_installBtn:hover{background:var(--hub-btn-hover)}._3XaZHa_detailBtn{color:var(--hub-text-secondary);-webkit-user-select:none;user-select:none;background:0 0;border:none;align-items:center;text-decoration:none;display:inline-flex}._3XaZHa_detailBtn:hover{color:var(--hub-text-primary);background:var(--hub-bg-hover)}._3XaZHa_actions{align-items:center;gap:6px;display:flex}._3XaZHa_installBtnCopied{color:var(--hub-success);background:var(--hub-success-tint);-webkit-user-select:none;user-select:none;border:none}._3XaZHa_state{text-align:center;min-height:160px;color:var(--hub-text-tertiary);flex-direction:column;flex:1;justify-content:center;align-items:center;gap:8px;padding:24px;font-size:12px;line-height:18px;display:flex}._3XaZHa_stateTitle{color:var(--hub-text-primary);font-size:13px;font-weight:600}._3XaZHa_stateDesc{max-width:420px}._3XaZHa_retryBtn{color:var(--hub-text-secondary);cursor:pointer;-webkit-user-select:none;user-select:none;background:0 0;border:none;border-radius:6px;margin-top:4px;padding:4px 12px;font-size:12px;line-height:18px}._3XaZHa_retryBtn:hover{color:var(--hub-text-primary);background:var(--hub-bg-hover)}._3XaZHa_footer{border-top:1px solid var(--hub-border-1);flex-shrink:0;justify-content:flex-end;align-items:center;gap:8px;padding:6px 4px 0;display:flex}._3XaZHa_footLink{color:var(--hub-brand);white-space:nowrap;-webkit-user-select:none;user-select:none;font-size:11px;line-height:16px;text-decoration:none}._3XaZHa_footLink:hover{text-decoration:underline}";
2384
+ const css$1 = "._3XaZHa_body{flex-direction:column;flex:1;min-height:0;display:flex}._3XaZHa_list{flex-direction:column;flex:1;gap:6px;min-height:0;padding:2px 4px 4px 2px;display:flex;overflow-y:auto}._3XaZHa_card{border:1px solid var(--hub-border-2);background:var(--hub-bg-1);border-radius:8px;justify-content:space-between;align-items:stretch;gap:12px;padding:9px 12px;transition:border-color .12s,background .12s;display:flex}._3XaZHa_card:hover{border-color:var(--hub-brand);background:var(--hub-bg-2)}._3XaZHa_cardMain{flex-direction:column;gap:4px;min-width:0;display:flex}._3XaZHa_cardHead{align-items:center;gap:6px;min-width:0;display:flex}._3XaZHa_cardTitle{white-space:nowrap;text-overflow:ellipsis;font-size:13px;font-weight:600;line-height:18px;overflow:hidden}._3XaZHa_categoryBadge,._3XaZHa_verified,._3XaZHa_versionBadge,._3XaZHa_updateBadge{border:1px solid #0000;border-radius:4px;flex-shrink:0;padding:0 6px;font-size:10px;line-height:16px}._3XaZHa_categoryBadge{color:var(--hub-brand);border-color:var(--hub-border-ghost)}._3XaZHa_verified{color:var(--hub-success);border-color:var(--hub-success-border)}._3XaZHa_versionBadge{color:var(--hub-text-secondary);border-color:var(--hub-border-input)}._3XaZHa_updateBadge{color:var(--hub-warn);border-color:var(--hub-warn-border);background:var(--hub-warn-tint)}._3XaZHa_desc{color:var(--hub-text-secondary);-webkit-line-clamp:2;-webkit-box-orient:vertical;margin:0;font-size:12px;line-height:18px;display:-webkit-box;overflow:hidden}._3XaZHa_topics{flex-wrap:wrap;align-items:center;gap:4px;min-width:0;display:flex}._3XaZHa_topic{color:var(--hub-text-tertiary);background:var(--hub-bg-3);white-space:nowrap;border-radius:4px;flex-shrink:0;padding:0 6px;font-size:10px;line-height:16px}._3XaZHa_cardSide{flex-direction:column;flex-shrink:0;justify-content:space-between;align-items:flex-end;gap:6px;display:flex}._3XaZHa_stats{flex-direction:column;align-items:flex-end;gap:1px;display:flex}._3XaZHa_star{color:var(--hub-warn);white-space:nowrap;font-size:11px;line-height:16px}._3XaZHa_fork{color:var(--hub-text-tertiary);white-space:nowrap;font-size:11px;line-height:16px}._3XaZHa_date{color:var(--hub-text-tertiary);white-space:nowrap;font-size:10px;line-height:14px}._3XaZHa_installBtn,._3XaZHa_installBtnCopied,._3XaZHa_installBtnInstalled,._3XaZHa_installBtnUpdate,._3XaZHa_uninstallBtn,._3XaZHa_detailBtn{cursor:pointer;border-radius:6px;padding:2px 10px;font-size:11px;line-height:18px;transition:color .12s,border-color .12s,background .12s}._3XaZHa_installBtnInstalled{color:var(--hub-success);background:var(--hub-success-tint);cursor:default;-webkit-user-select:none;user-select:none;border:none}._3XaZHa_installBtnUpdate{color:#fff;background:var(--hub-warn);-webkit-user-select:none;user-select:none;border:none;font-weight:500}._3XaZHa_installBtnUpdate:hover{background:var(--hub-warn-strong)}._3XaZHa_uninstallBtn{color:var(--hub-danger);background:var(--hub-danger-tint);-webkit-user-select:none;user-select:none;border:none}._3XaZHa_uninstallBtn:hover{color:#fff;background:var(--hub-danger-hover)}._3XaZHa_installBtn{color:var(--hub-text-on-fill);background:var(--hub-btn-fill);-webkit-user-select:none;user-select:none;border:none;font-weight:500}._3XaZHa_installBtn:hover{background:var(--hub-btn-hover)}._3XaZHa_detailBtn{color:var(--hub-text-secondary);-webkit-user-select:none;user-select:none;background:0 0;border:none;align-items:center;text-decoration:none;display:inline-flex}._3XaZHa_detailBtn:hover{color:var(--hub-text-primary);background:var(--hub-bg-hover)}._3XaZHa_actions{align-items:center;gap:6px;display:flex}._3XaZHa_installBtnCopied{color:var(--hub-success);background:var(--hub-success-tint);-webkit-user-select:none;user-select:none;border:none}._3XaZHa_state{text-align:center;min-height:160px;color:var(--hub-text-tertiary);flex-direction:column;flex:1;justify-content:center;align-items:center;gap:8px;padding:24px;font-size:12px;line-height:18px;display:flex}._3XaZHa_stateTitle{color:var(--hub-text-primary);font-size:13px;font-weight:600}._3XaZHa_stateDesc{max-width:420px}._3XaZHa_retryBtn{color:var(--hub-text-secondary);cursor:pointer;-webkit-user-select:none;user-select:none;background:0 0;border:none;border-radius:6px;margin-top:4px;padding:4px 12px;font-size:12px;line-height:18px}._3XaZHa_retryBtn:hover{color:var(--hub-text-primary);background:var(--hub-bg-hover)}._3XaZHa_footer{border-top:1px solid var(--hub-border-1);flex-shrink:0;justify-content:flex-end;align-items:center;gap:8px;padding:6px 4px 0;display:flex}._3XaZHa_footLink{color:var(--hub-brand);white-space:nowrap;-webkit-user-select:none;user-select:none;font-size:11px;line-height:16px;text-decoration:none}._3XaZHa_footLink:hover{text-decoration:underline}";
1931
2385
  const tagId$1 = "dsh-plugin/List.module.css";
1932
2386
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
1933
2387
  const tag = document.createElement("style");
@@ -1937,36 +2391,36 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
1937
2391
  document.head.appendChild(tag);
1938
2392
  }
1939
2393
  var List_module_css_default = {
1940
- "footer": "_3XaZHa_footer",
1941
- "state": "_3XaZHa_state",
2394
+ "star": "_3XaZHa_star",
2395
+ "cardTitle": "_3XaZHa_cardTitle",
1942
2396
  "actions": "_3XaZHa_actions",
1943
- "updateBadge": "_3XaZHa_updateBadge",
1944
- "installBtnInstalled": "_3XaZHa_installBtnInstalled",
1945
- "stateDesc": "_3XaZHa_stateDesc",
2397
+ "stateTitle": "_3XaZHa_stateTitle",
1946
2398
  "body": "_3XaZHa_body",
1947
- "verified": "_3XaZHa_verified",
1948
- "topics": "_3XaZHa_topics",
1949
- "desc": "_3XaZHa_desc",
1950
2399
  "installBtn": "_3XaZHa_installBtn",
2400
+ "desc": "_3XaZHa_desc",
2401
+ "footer": "_3XaZHa_footer",
2402
+ "topic": "_3XaZHa_topic",
2403
+ "installBtnCopied": "_3XaZHa_installBtnCopied",
2404
+ "verified": "_3XaZHa_verified",
2405
+ "versionBadge": "_3XaZHa_versionBadge",
2406
+ "installBtnInstalled": "_3XaZHa_installBtnInstalled",
2407
+ "stateDesc": "_3XaZHa_stateDesc",
2408
+ "state": "_3XaZHa_state",
2409
+ "stats": "_3XaZHa_stats",
2410
+ "categoryBadge": "_3XaZHa_categoryBadge",
2411
+ "updateBadge": "_3XaZHa_updateBadge",
2412
+ "footLink": "_3XaZHa_footLink",
2413
+ "retryBtn": "_3XaZHa_retryBtn",
1951
2414
  "uninstallBtn": "_3XaZHa_uninstallBtn",
1952
2415
  "detailBtn": "_3XaZHa_detailBtn",
2416
+ "date": "_3XaZHa_date",
1953
2417
  "cardMain": "_3XaZHa_cardMain",
1954
- "stats": "_3XaZHa_stats",
1955
- "installBtnCopied": "_3XaZHa_installBtnCopied",
1956
- "stateTitle": "_3XaZHa_stateTitle",
2418
+ "topics": "_3XaZHa_topics",
1957
2419
  "list": "_3XaZHa_list",
1958
- "footLink": "_3XaZHa_footLink",
1959
- "versionBadge": "_3XaZHa_versionBadge",
1960
- "installBtnUpdate": "_3XaZHa_installBtnUpdate",
1961
- "cardHead": "_3XaZHa_cardHead",
1962
- "star": "_3XaZHa_star",
1963
2420
  "fork": "_3XaZHa_fork",
2421
+ "cardHead": "_3XaZHa_cardHead",
2422
+ "installBtnUpdate": "_3XaZHa_installBtnUpdate",
1964
2423
  "cardSide": "_3XaZHa_cardSide",
1965
- "cardTitle": "_3XaZHa_cardTitle",
1966
- "date": "_3XaZHa_date",
1967
- "retryBtn": "_3XaZHa_retryBtn",
1968
- "topic": "_3XaZHa_topic",
1969
- "categoryBadge": "_3XaZHa_categoryBadge",
1970
2424
  "card": "_3XaZHa_card"
1971
2425
  };
1972
2426
  //#endregion
@@ -2101,14 +2555,22 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
2101
2555
  const [confirmPlugin, setConfirmPlugin] = (0, react.useState)(null);
2102
2556
  /** 弹窗动作是否为「更新」:已安装插件点「更新」→ 走同一条 add 命令原位覆盖重装,文案区分安装/更新 */
2103
2557
  const [confirmIsUpdate, setConfirmIsUpdate] = (0, react.useState)(false);
2558
+ /** Hub 自我更新说明弹窗:点「可更新」徽标先展示版本 + Markdown 变更记录,确认后再进安装弹窗 */
2559
+ const [showHubUpdate, setShowHubUpdate] = (0, react.useState)(false);
2560
+ /** 「关注我们」弹窗:GitHub 图标后按钮点击打开,展示平台介绍 + 用户反馈群二维码(Worker /about 推送) */
2561
+ const [showAbout, setShowAbout] = (0, react.useState)(false);
2104
2562
  /** 卸载确认弹窗:记录待卸载的插件 */
2105
2563
  const [uninstallPlugin, setUninstallPlugin] = (0, react.useState)(null);
2106
2564
  /** 安装/卸载完成后的结果视图:停留弹窗内,点「完成」关闭 */
2107
2565
  const [installDone, setInstallDone] = (0, react.useState)(false);
2108
2566
  const [uninstallDone, setUninstallDone] = (0, react.useState)(false);
2567
+ /** 结果视图是否给「立即重启」:服务端任务终态带出(卸载时 loader 已即时移除 → false 只给「完成」;
2568
+ * true 时通知中心待重启条目同步常驻,直到用户点「立即重启」真正重启后才消失) */
2569
+ const [installNeedsRestart, setInstallNeedsRestart] = (0, react.useState)(true);
2570
+ const [uninstallNeedsRestart, setUninstallNeedsRestart] = (0, react.useState)(true);
2109
2571
  /** 结果视图「立即重启」:请求宿主重启后进入等待,服务回来后整页刷新 */
2110
2572
  const [restarting, setRestarting] = (0, react.useState)(false);
2111
- /** 操作失败完整信息 + 所属插件仓库 + 失败类型(决定弹窗标题「安装失败/卸载失败」) */
2573
+ /** 操作失败完整信息 + 所属插件仓库 + 失败类型(决定弹窗标题「安装失败/卸载失败」)+ 实际执行的安装命令 + 尝试过的安装方式(issue 预填用) */
2112
2574
  const [errorMsg, setErrorMsg] = (0, react.useState)(null);
2113
2575
  /** 安装/卸载任务通知记录:localStorage 持久化,成败即落盘,即使错过弹窗也能回来查看 */
2114
2576
  const [notifications, setNotifications] = (0, react.useState)(() => loadNotifications());
@@ -2127,7 +2589,8 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
2127
2589
  const queue = useTaskQueue({
2128
2590
  t,
2129
2591
  refreshInstalled: catalog.refreshInstalled,
2130
- onInstallDone: (viaModal, repo) => {
2592
+ onInstallDone: (viaModal, repo, needsRestart) => {
2593
+ setInstallNeedsRestart(needsRestart);
2131
2594
  if (viaModal) setInstallDone(true);
2132
2595
  else setToast({
2133
2596
  id: Date.now(),
@@ -2138,7 +2601,8 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
2138
2601
  repo: repo ?? ""
2139
2602
  }));
2140
2603
  },
2141
- onUninstallDone: (viaModal, repo) => {
2604
+ onUninstallDone: (viaModal, repo, needsRestart) => {
2605
+ setUninstallNeedsRestart(needsRestart);
2142
2606
  if (viaModal) setUninstallDone(true);
2143
2607
  else setToast({
2144
2608
  id: Date.now(),
@@ -2149,16 +2613,20 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
2149
2613
  repo: repo ?? ""
2150
2614
  }));
2151
2615
  },
2152
- onError: (message, repo, kind) => {
2616
+ onError: (message, repo, kind, command, attempts) => {
2153
2617
  setErrorMsg({
2154
2618
  message,
2155
2619
  repo,
2156
- kind
2620
+ kind,
2621
+ command,
2622
+ attempts
2157
2623
  });
2158
2624
  setNotifications(addFailure({
2159
2625
  kind,
2160
2626
  repo: repo ?? "",
2161
- message
2627
+ message,
2628
+ command,
2629
+ attempts
2162
2630
  }));
2163
2631
  },
2164
2632
  installPlugin: confirmPlugin,
@@ -2173,12 +2641,14 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
2173
2641
  }
2174
2642
  });
2175
2643
  (0, react.useEffect)(() => {
2176
- if (!confirmPlugin && !uninstallPlugin && !showNotifications) return;
2644
+ if (!confirmPlugin && !uninstallPlugin && !showNotifications && !showHubUpdate && !showAbout) return;
2177
2645
  const onKey = (e) => {
2178
2646
  if (e.key === "Escape") {
2179
2647
  setConfirmPlugin(null);
2180
2648
  setUninstallPlugin(null);
2181
2649
  setUninstallDone(false);
2650
+ setShowHubUpdate(false);
2651
+ setShowAbout(false);
2182
2652
  setShowNotifications(false);
2183
2653
  }
2184
2654
  };
@@ -2187,7 +2657,9 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
2187
2657
  }, [
2188
2658
  confirmPlugin,
2189
2659
  uninstallPlugin,
2190
- showNotifications
2660
+ showNotifications,
2661
+ showHubUpdate,
2662
+ showAbout
2191
2663
  ]);
2192
2664
  (0, react.useEffect)(() => {
2193
2665
  if (!toast) return;
@@ -2216,10 +2688,10 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
2216
2688
  return ok;
2217
2689
  }
2218
2690
  };
2219
- /** 弹窗动作一:复制安装命令到剪贴板,引导去终端粘贴执行。 */
2691
+ /** 弹窗动作一:复制安装命令到剪贴板,引导去终端粘贴执行(npm 通道显示包名命令)。 */
2220
2692
  const copyCommand = async (p) => {
2221
2693
  const repo = p.source?.repo ?? "";
2222
- if (await doCopy(`dsh plugin add github:${repo}`)) {
2694
+ if (await doCopy(installCommandOf(p))) {
2223
2695
  setCopied(repo);
2224
2696
  setToast({
2225
2697
  id: Date.now(),
@@ -2272,12 +2744,16 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
2272
2744
  const statsTotal = catalog.stats?.total ?? total;
2273
2745
  const statsVerified = catalog.stats?.verified ?? 0;
2274
2746
  const count = catalog.visible.length;
2747
+ const hubEntry = catalog.plugins?.find((p) => p.source?.repo === "dshplugin/dsh-plugin-hub") ?? null;
2275
2748
  return (0, react.createElement)("div", { className: Header_module_css_default.root }, (0, react.createElement)(CatalogHeader, {
2276
2749
  t,
2277
2750
  langPath,
2278
2751
  statsTotal,
2279
2752
  statsVerified,
2280
- onToggleLang: () => setManualLang(lang === "en" ? "zh" : "en")
2753
+ onToggleLang: () => setManualLang(lang === "en" ? "zh" : "en"),
2754
+ hubUpdate: catalog.hubHasUpdate,
2755
+ onVersionClick: () => setShowHubUpdate(true),
2756
+ onAboutClick: () => setShowAbout(true)
2281
2757
  }), (0, react.createElement)(CategoryTabs, {
2282
2758
  category: catalog.category,
2283
2759
  setCategory: catalog.setCategory,
@@ -2323,6 +2799,26 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
2323
2799
  queue.clearModalTask();
2324
2800
  setUninstallPlugin(p);
2325
2801
  }
2802
+ }), showHubUpdate && (0, react.createElement)(HubUpdateModal, {
2803
+ info: catalog.hubUpdateInfo ?? { version: "1.1.1" },
2804
+ lang,
2805
+ t,
2806
+ hasUpdate: catalog.hubHasUpdate,
2807
+ onClose: () => setShowHubUpdate(false),
2808
+ onProceed: () => {
2809
+ setShowHubUpdate(false);
2810
+ if (hubEntry) {
2811
+ setInstallDone(false);
2812
+ setConfirmIsUpdate(true);
2813
+ queue.clearModalTask();
2814
+ setConfirmPlugin(hubEntry);
2815
+ }
2816
+ }
2817
+ }), showAbout && (0, react.createElement)(AboutModal, {
2818
+ info: catalog.hubAboutInfo ?? null,
2819
+ lang,
2820
+ t,
2821
+ onClose: () => setShowAbout(false)
2326
2822
  }), confirmPlugin && (0, react.createElement)(InstallModal, {
2327
2823
  plugin: confirmPlugin,
2328
2824
  done: installDone,
@@ -2331,7 +2827,9 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
2331
2827
  langPath,
2332
2828
  restarting,
2333
2829
  update: confirmIsUpdate,
2830
+ cliOnly: confirmPlugin.install?.webInstallable === false,
2334
2831
  submitting: queue.submitting,
2832
+ needsRestart: installNeedsRestart,
2335
2833
  onClose: () => setConfirmPlugin(null),
2336
2834
  onCopy: () => copyCommand(confirmPlugin),
2337
2835
  onInstall: () => queue.installNow(confirmPlugin, confirmIsUpdate ? { update: true } : void 0),
@@ -2346,6 +2844,7 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
2346
2844
  langPath,
2347
2845
  restarting,
2348
2846
  submitting: queue.submitting,
2847
+ needsRestart: uninstallNeedsRestart,
2349
2848
  onClose: () => {
2350
2849
  setUninstallDone(false);
2351
2850
  setUninstallPlugin(null);
@@ -2363,6 +2862,8 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
2363
2862
  message: errorMsg.message,
2364
2863
  repo: errorMsg.repo,
2365
2864
  kind: errorMsg.kind,
2865
+ command: errorMsg.command,
2866
+ attempts: errorMsg.attempts,
2366
2867
  t,
2367
2868
  env,
2368
2869
  onCopy: (text) => {
@@ -2388,6 +2889,7 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
2388
2889
  });
2389
2890
  },
2390
2891
  onClear: () => setNotifications(clearNotifications()),
2892
+ onRemove: (id) => setNotifications(removeNotification(id)),
2391
2893
  cancelTask: queue.cancelTask,
2392
2894
  restarting,
2393
2895
  onRestart: () => {
@@ -2397,7 +2899,7 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
2397
2899
  }
2398
2900
  //#endregion
2399
2901
  //#region \0dsh-css:src/client/styles/tokens.module.css.mjs
2400
- const css = ":root,body[data-ds-dark-theme]{--hub-text-primary:var(--dsw-alias-label-primary,#1f2328);--hub-text-secondary:var(--dsw-alias-label-secondary,#6b7280);--hub-text-tertiary:var(--dsw-alias-label-tertiary,#8b93a1);--hub-text-disabled:var(--dsw-alias-label-disabled,#aab1bd);--hub-text-on-fill:var(--dsw-alias-label-primary-foreground,#fff);--hub-bg-1:var(--dsw-alias-bg-layer-1,#fff);--hub-bg-2:var(--dsw-alias-bg-layer-2,#80808014);--hub-bg-3:#8080801a;--hub-bg-btn:#8080801f;--hub-bg-hover:var(--dsw-alias-interactive-bg-hover,#80808029);--hub-bg-on-fill:#80808038;--hub-border-1:var(--dsw-alias-border-l1,#eceef1);--hub-border-2:var(--dsw-alias-border-l2,#e5e7eb);--hub-border-ghost:var(--dsw-alias-button-ghost-active-border,#4f6ef740);--hub-border-input:var(--dsw-alias-input-border,#6b728040);--hub-brand:var(--dsw-alias-state-business-primary,#4f6ef7);--hub-brand-hover:#3b5bdb;--hub-brand-tint:#4f6ef70f;--hub-brand-tint-strong:#4f6ef71f;--hub-brand-border:#4f6ef759;--hub-brand-border-soft:#4f6ef738;--hub-purple-1:#4f46e5;--hub-purple-2:#7c3aed;--hub-purple-border:#8b5cf68c;--hub-purple-tint:#4f46e514;--hub-purple-shadow:#4f46e547;--hub-purple-shadow-strong:#7c3aed6b;--hub-btn-fill:var(--dsw-alias-button-primary-fill,#1f2328);--hub-btn-hover:var(--dsw-alias-button-primary-hover,#43454a);--hub-success:var(--dsw-alias-state-success-primary,#1a7f37);--hub-success-tint:#22c55e24;--hub-success-border:var(--dsw-alias-state-success-secondary,#1a7f374d);--hub-warn:var(--dsw-alias-state-warn-primary,#b8860b);--hub-warn-tint:#b8860b14;--hub-warn-border:#b8860b59;--hub-warn-strong:#b8860bd9;--hub-warning:var(--dsw-alias-state-warning-primary,#b45309);--hub-warning-tint:#b4530914;--hub-warning-border:#b453094d;--hub-danger:var(--dsw-alias-state-danger-primary,#d1242f);--hub-danger-hover:var(--dsw-alias-state-danger-hover,#b91c1c);--hub-danger-strong:#b0202a;--hub-danger-text:#e5484d;--hub-danger-tint:#e5484d14;--hub-danger-tint-weak:#e5484d0d;--hub-danger-border:#e5484d59;--hub-danger-border-soft:#e5484d47;--hub-danger-soft:#d1242f14}body[data-ds-dark-theme]{--hub-bg-btn:#ffffff1f}";
2902
+ const css = ":root,body[data-ds-dark-theme]{--hub-text-primary:var(--dsw-alias-label-primary,#1f2328);--hub-text-secondary:var(--dsw-alias-label-secondary,#6b7280);--hub-text-tertiary:var(--dsw-alias-label-tertiary,#8b93a1);--hub-text-disabled:var(--dsw-alias-label-disabled,#aab1bd);--hub-text-on-fill:var(--dsw-alias-label-primary-foreground,#fff);--hub-bg-1:var(--dsw-alias-bg-layer-1,#fff);--hub-bg-2:var(--dsw-alias-bg-layer-2,#80808014);--hub-bg-3:#8080801a;--hub-bg-btn:#8080801f;--hub-bg-hover:var(--dsw-alias-interactive-bg-hover,#80808029);--hub-bg-on-fill:#80808038;--hub-border-1:var(--dsw-alias-border-l1,#eceef1);--hub-border-2:var(--dsw-alias-border-l2,#e5e7eb);--hub-border-ghost:var(--dsw-alias-button-ghost-active-border,#4f6ef740);--hub-border-input:var(--dsw-alias-input-border,#6b728040);--hub-brand:var(--dsw-alias-state-business-primary,#4f6ef7);--hub-brand-hover:#3b5bdb;--hub-brand-tint:#4f6ef70f;--hub-brand-tint-strong:#4f6ef71f;--hub-brand-border:#4f6ef759;--hub-brand-border-soft:#4f6ef738;--hub-purple-1:#4f46e5;--hub-purple-2:#7c3aed;--hub-purple-border:#8b5cf68c;--hub-purple-tint:#4f46e514;--hub-purple-shadow:#4f46e547;--hub-purple-shadow-strong:#7c3aed6b;--hub-btn-fill:var(--dsw-alias-button-primary-fill,#1f2328);--hub-btn-hover:var(--dsw-alias-button-primary-hover,#43454a);--hub-success:#16a34a;--hub-success-tint:#16a34a1f;--hub-success-border:#16a34a4d;--hub-warn:var(--dsw-alias-state-warn-primary,#b8860b);--hub-warn-tint:#b8860b14;--hub-warn-border:#b8860b59;--hub-warn-strong:#b8860bd9;--hub-warning:var(--dsw-alias-state-warning-primary,#b45309);--hub-warning-tint:#b4530914;--hub-warning-border:#b453094d;--hub-danger:var(--dsw-alias-state-danger-primary,#d1242f);--hub-danger-hover:var(--dsw-alias-state-danger-hover,#b91c1c);--hub-danger-strong:#b0202a;--hub-danger-text:#e5484d;--hub-danger-tint:#e5484d14;--hub-danger-tint-weak:#e5484d0d;--hub-danger-border:#e5484d59;--hub-danger-border-soft:#e5484d47;--hub-danger-soft:#d1242f14}body[data-ds-dark-theme]{--hub-bg-btn:#ffffff1f;--hub-success:#22c55e;--hub-success-tint:#22c55e29;--hub-success-border:#22c55e59}";
2401
2903
  const tagId = "dsh-plugin/tokens.module.css";
2402
2904
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
2403
2905
  const tag = document.createElement("style");