dsh-plugin 1.1.0 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/README.en.md +5 -5
  2. package/README.md +5 -5
  3. package/client/client.js +648 -190
  4. package/client/client.js.map +1 -1
  5. package/lib/http/routes.js +15 -8
  6. package/lib/index.js +4 -0
  7. package/lib/services/install-types.js +8 -2
  8. package/lib/services/install.js +1 -1
  9. package/lib/services/installed-versions.js +5 -1
  10. package/lib/services/loader.js +16 -5
  11. package/lib/services/npm-resolve.js +4 -0
  12. package/lib/services/pending-restart.js +1 -1
  13. package/lib/services/preflight.js +9 -4
  14. package/lib/services/profile.js +30 -3
  15. package/lib/services/progress.js +4 -0
  16. package/lib/services/task-queue.js +21 -10
  17. package/lib/types/index.d.ts +4 -0
  18. package/lib/types/services/install-types.d.ts +12 -4
  19. package/lib/types/services/install.d.ts +5 -1
  20. package/lib/types/services/loader.d.ts +10 -4
  21. package/lib/types/services/pending-restart.d.ts +8 -2
  22. package/lib/types/services/profile.d.ts +4 -2
  23. package/lib/types/services/progress.d.ts +4 -0
  24. package/package.json +1 -1
  25. package/src/client/components/AboutModal.tsx +76 -0
  26. package/src/client/components/CatalogControls.tsx +4 -0
  27. package/src/client/components/CatalogHeader.tsx +34 -5
  28. package/src/client/components/CatalogList.tsx +4 -0
  29. package/src/client/components/CategoryTabs.tsx +4 -0
  30. package/src/client/components/Dropdown.tsx +4 -0
  31. package/src/client/components/HubUpdateModal.tsx +88 -0
  32. package/src/client/components/NotificationsModal.tsx +25 -8
  33. package/src/client/components/PluginCard.tsx +8 -1
  34. package/src/client/components/PluginHubSection.tsx +67 -7
  35. package/src/client/components/ProgressView.tsx +4 -0
  36. package/src/client/components/icons.tsx +4 -0
  37. package/src/client/components/modals.tsx +35 -10
  38. package/src/client/hooks/useCatalog.ts +89 -17
  39. package/src/client/hooks/useTaskQueue.ts +52 -35
  40. package/src/client/index.tsx +4 -0
  41. package/src/client/lib/catalog.ts +59 -6
  42. package/src/client/lib/env.ts +4 -0
  43. package/src/client/lib/failures.ts +5 -1
  44. package/src/client/lib/format.ts +4 -0
  45. package/src/client/lib/renderMarkdown.ts +120 -0
  46. package/src/client/locales.ts +48 -1
  47. package/src/client/styles/Dropdown.module.css +5 -1
  48. package/src/client/styles/Header.module.css +88 -4
  49. package/src/client/styles/List.module.css +5 -1
  50. package/src/client/styles/Modal.module.css +279 -1
  51. package/src/client/styles/tokens.module.css +5 -1
  52. package/src/client/types.ts +7 -1
  53. package/src/server/http/routes.ts +14 -8
  54. package/src/server/index.ts +4 -0
  55. package/src/server/services/install-types.ts +12 -4
  56. package/src/server/services/install.ts +5 -1
  57. package/src/server/services/installed-versions.ts +5 -1
  58. package/src/server/services/loader.ts +16 -5
  59. package/src/server/services/npm-resolve.ts +4 -0
  60. package/src/server/services/pending-restart.ts +8 -2
  61. package/src/server/services/preflight.ts +9 -4
  62. package/src/server/services/profile.ts +27 -3
  63. package/src/server/services/progress.ts +4 -0
  64. package/src/server/services/task-queue.ts +21 -10
package/client/client.js CHANGED
@@ -6,7 +6,13 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
6
6
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
7
  let react = require("react");
8
8
  //#region src/client/locales.ts
9
- /** zh/en dictionaries for the DSH-Plugin Hub settings section. */
9
+ /**
10
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
11
+ * Website: https://dsh-plugin.org
12
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
13
+ *
14
+ * zh/en dictionaries for the DSH-Plugin Hub settings section.
15
+ */
10
16
  const zh = {
11
17
  nav: "插件中心",
12
18
  title: "DSH-Plugin Hub",
@@ -27,6 +33,8 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
27
33
  failedDesc: "请稍后重试,或点击右上角按钮在浏览器中打开插件中心。",
28
34
  retry: "重试",
29
35
  install: "安装",
36
+ cliOnlyHint: "此插件可能需命令行辅助,失败时复制上方命令到 dsh 终端",
37
+ failedCopyHint: "安装失败。请复制上方安装命令到 dsh 终端手动执行(原生依赖构建脚本可能需要人工放行,如 pnpm approve-builds)",
30
38
  toastCopied: "安装命令已复制,去 dsh 终端粘贴即可安装",
31
39
  installed: "已安装",
32
40
  notInstalled: "未安装",
@@ -69,6 +77,7 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
69
77
  restartLater: "稍后重启",
70
78
  restarting: "正在重启…",
71
79
  restartHint: "部分插件(如插件市场、顶层 bundle)需重启后才会生效。",
80
+ done: "完成",
72
81
  restartPendingHint: "安装成功,重启后生效",
73
82
  restartPendingHintUninstall: "卸载成功,重启后移除",
74
83
  sectionPendingRestart: "待重启",
@@ -115,6 +124,21 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
115
124
  update: "可更新",
116
125
  updateAvailable: "有更新",
117
126
  updateAvailableHint: "检测到更新:有版本号的仓库比对版本号,无版本号的仓库比对仓库最近更新时间",
127
+ hubUpdateHint: "检测到 DSH-Plugin Hub 新版本,点击查看更新内容并更新",
128
+ hubUpdateTitle: "DSH-Plugin Hub 有新版本",
129
+ hubUpdateDesc: "v{version} 已发布,点击下方按钮即可更新,完成后需重启宿主生效。",
130
+ hubUpdatePublished: "发布于",
131
+ hubUpdateNotes: "更新记录",
132
+ hubUpdateLater: "稍后再说",
133
+ versionHint: "查看版本信息与更新记录",
134
+ hubCurrentTitle: "当前版本",
135
+ hubCurrentDesc: "你正在使用 v{version},已是最新版本。",
136
+ hubUpToDate: "已是最新版本",
137
+ followUs: "关注我们",
138
+ aboutTitle: "关注我们",
139
+ aboutDesc: "了解 DSH-Plugin Hub 是做什么的,加入用户反馈群随时交流。",
140
+ aboutEmpty: "内容暂未发布,敬请期待。",
141
+ aboutUpdated: "更新于",
118
142
  empty: "该分类暂无插件",
119
143
  dataFrom: "数据源 dsh-plugin.org · 每日人工更新"
120
144
  };
@@ -139,6 +163,8 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
139
163
  failedDesc: "Please retry, or open the hub in your browser with the button above.",
140
164
  retry: "Retry",
141
165
  install: "Install",
166
+ cliOnlyHint: "This plugin may need command-line help; if it fails, copy the command above and run it in your dsh terminal",
167
+ 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)",
142
168
  toastCopied: "Install command copied — paste it in your dsh terminal",
143
169
  installed: "Installed",
144
170
  notInstalled: "Not installed",
@@ -181,6 +207,7 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
181
207
  restartLater: "Later",
182
208
  restarting: "Restarting…",
183
209
  restartHint: "Some plugins (such as plugin markets / top-level bundles) only take effect after a restart.",
210
+ done: "Done",
184
211
  restartPendingHint: "Installed — takes effect after restart",
185
212
  restartPendingHintUninstall: "Removed — cleaned up after restart",
186
213
  sectionPendingRestart: "Pending restart",
@@ -227,12 +254,27 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
227
254
  update: "Update",
228
255
  updateAvailable: "Update available",
229
256
  updateAvailableHint: "An update was detected: repos with a release are compared by tag, repos without one by last push time",
257
+ hubUpdateHint: "A new DSH-Plugin Hub version is available — click to view the changelog and update",
258
+ hubUpdateTitle: "A new DSH-Plugin Hub version is available",
259
+ hubUpdateDesc: "v{version} has been released — click below to update, then restart the host to apply it.",
260
+ hubUpdatePublished: "Published",
261
+ hubUpdateNotes: "What's new",
262
+ hubUpdateLater: "Later",
263
+ versionHint: "View version info and changelog",
264
+ hubCurrentTitle: "Current version",
265
+ hubCurrentDesc: "You are on v{version} — this is the latest version.",
266
+ hubUpToDate: "Up to date",
267
+ followUs: "Follow us",
268
+ aboutTitle: "Follow us",
269
+ aboutDesc: "Learn what DSH-Plugin Hub does and join our user feedback group.",
270
+ aboutEmpty: "Content not published yet — stay tuned.",
271
+ aboutUpdated: "Updated",
230
272
  empty: "No plugins in this category yet",
231
273
  dataFrom: "Data from dsh-plugin.org · curated daily"
232
274
  };
233
275
  //#endregion
234
276
  //#region \0dsh-css:src/client/styles/Header.module.css.mjs
235
- 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}";
277
+ 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}";
236
278
  const tagId$4 = "dsh-plugin/Header.module.css";
237
279
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$4) + "]") === null) {
238
280
  const tag = document.createElement("style");
@@ -242,40 +284,43 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
242
284
  document.head.appendChild(tag);
243
285
  }
244
286
  var Header_module_css_default = {
245
- "adText": "qikqja_adText",
246
- "tab": "qikqja_tab",
247
- "copyIcon": "qikqja_copyIcon",
287
+ "tagline": "qikqja_tagline",
248
288
  "filterResults": "qikqja_filterResults",
249
- "failBadge": "qikqja_failBadge",
250
- "headerRight": "qikqja_headerRight",
251
- "brandTitle": "qikqja_brandTitle",
252
- "taglineLink": "qikqja_taglineLink",
253
- "search": "qikqja_search",
289
+ "adBanner": "qikqja_adBanner",
254
290
  "installedBtn": "qikqja_installedBtn",
255
- "tabCount": "qikqja_tabCount",
256
- "resultCount": "qikqja_resultCount",
257
- "segCountActive": "qikqja_segCountActive",
291
+ "version": "qikqja_version",
292
+ "brandTitle": "qikqja_brandTitle",
293
+ "hubUpdateBadge": "qikqja_hubUpdateBadge",
294
+ "title": "qikqja_title",
295
+ "segCount": "qikqja_segCount",
296
+ "headerTitleRow": "qikqja_headerTitleRow",
297
+ "githubLink": "qikqja_githubLink",
298
+ "headerRight": "qikqja_headerRight",
258
299
  "adArrow": "qikqja_adArrow",
300
+ "tabActive": "qikqja_tabActive",
301
+ "copyIcon": "qikqja_copyIcon",
302
+ "tab": "qikqja_tab",
259
303
  "controls": "qikqja_controls",
260
304
  "installedBtnDisabled": "qikqja_installedBtnDisabled",
261
- "root": "qikqja_root",
262
- "githubLink": "qikqja_githubLink",
305
+ "failBadge": "qikqja_failBadge",
306
+ "taglineLink": "qikqja_taglineLink",
307
+ "search": "qikqja_search",
263
308
  "header": "qikqja_header",
264
- "logoIcon": "qikqja_logoIcon",
265
- "tabs": "qikqja_tabs",
266
- "adBanner": "qikqja_adBanner",
267
- "failBtn": "qikqja_failBtn",
268
309
  "githubIcon": "qikqja_githubIcon",
310
+ "tabCount": "qikqja_tabCount",
311
+ "failBtn": "qikqja_failBtn",
269
312
  "adBadge": "qikqja_adBadge",
270
- "headerTitleRow": "qikqja_headerTitleRow",
271
- "tagline": "qikqja_tagline",
272
- "title": "qikqja_title",
313
+ "logoIcon": "qikqja_logoIcon",
314
+ "searchRow": "qikqja_searchRow",
273
315
  "installedBtnActive": "qikqja_installedBtnActive",
274
- "segCount": "qikqja_segCount",
316
+ "adText": "qikqja_adText",
317
+ "root": "qikqja_root",
318
+ "tabs": "qikqja_tabs",
319
+ "aboutBtn": "qikqja_aboutBtn",
320
+ "segCountActive": "qikqja_segCountActive",
275
321
  "langBtn": "qikqja_langBtn",
276
- "version": "qikqja_version",
277
- "tabActive": "qikqja_tabActive",
278
- "searchRow": "qikqja_searchRow"
322
+ "resultCount": "qikqja_resultCount",
323
+ "versionBtn": "qikqja_versionBtn"
279
324
  };
280
325
  //#endregion
281
326
  //#region src/client/lib/failures.ts
@@ -409,18 +454,28 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
409
454
  /** 插件中心源码仓库:头部右上角 GitHub 图标的跳转地址 */
410
455
  const GITHUB_URL = "https://github.com/dshplugin/dsh-plugin-hub";
411
456
  /**
457
+ * Hub 自我更新版本控制中心(Cloudflare Worker:dsh-update)。
458
+ * 发新版后向 Worker 管理接口写入最新版本 + Markdown 变更记录(见
459
+ * dsh-update-worker 的 POST /admin/hub),所有已装用户的「可更新」徽标即时出现,
460
+ * 不再依赖主站目录数据管道(每日刷新 + 重建站点)。
461
+ * 响应:{ version: string | null, publishedAt: string | null, notes: string | {zh,en} | null }
462
+ */
463
+ const HUB_UPDATE_URL = "https://hub.dsh-plugin.org/hub";
464
+ /** Worker「关注我们」内容接口(同款 Worker,key = hub:about,Markdown 推送非写死)。 */
465
+ const HUB_ABOUT_URL = "https://hub.dsh-plugin.org/about";
466
+ /**
412
467
  * 插件当前版本号,供头部标题展示「DSH-Plugin Hub v0.1.1」。
413
468
  * tsdown 构建时用 define 把 __PLUGIN_VERSION__ 替换成 package.json 的版本号;
414
469
  * node --test 直接 import 本模块时该标识符不存在,typeof 守卫兜底为空串。
415
470
  */
416
- const PLUGIN_VERSION = "1.1.0";
471
+ const PLUGIN_VERSION = "1.1.2";
417
472
  /** dsh-plugin.org 中文页挂在 /zh/ 前缀下,英文在根路径。 */
418
473
  function langPathOf(lang) {
419
474
  return lang === "zh" ? "zh/" : "";
420
475
  }
421
476
  /**
422
477
  * 官网详情页两级路径:/plugins/{ownerSlug}/{slug};
423
- * 旧数据缺 ownerSlug 时从 repo 推导(卡片详情按钮与弹窗来源行共用)。
478
+ * 缺 ownerSlug 时从 repo 推导(卡片详情按钮与弹窗来源行共用)。
424
479
  */
425
480
  function pluginDetailUrl(plugin, langPath) {
426
481
  const repo = plugin.source?.repo ?? "";
@@ -455,7 +510,7 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
455
510
  "",
456
511
  `## [DSH-Plugin 插件中心](${SITE_URL}) · 安装 Plugin 失败错误信息`,
457
512
  `本错误信息由 [dsh-plugin-hub](${GITHUB_URL}) 插件中心的安装程序自动生成,随本次安装失败一并提交。`,
458
- `- 实际执行的安装命令:\`${command ?? `dsh plugin${env?.profile ? ` --profile ${env.profile}` : ""} add github:${repo}`}\``,
513
+ `- 实际执行的安装命令:\`${command ?? `dsh plugin${env?.profile ? ` --profile ${env.profile}` : ""} add git+https://github.com/${repo}.git`}\``,
459
514
  `- 执行结果:安装失败,未能安装该插件。`,
460
515
  ...attempts && attempts.length > 0 ? [
461
516
  "",
@@ -465,7 +520,7 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
465
520
  .../\[packaging\]/i.test(message) ? [
466
521
  "",
467
522
  "## 安装方式说明",
468
- "DSH 插件支持两种官方安装通道:`dsh plugin add <npm-package>`(npm 分发,需发布完整构建产物)与 `dsh plugin add github:owner/repo`(git 直装,仓库需提交构建产物或在 package.json 提供 `prepare` 脚本)。当前插件的分发物缺少 package.json 声明的入口文件,请按所用通道补齐后重新发布。"
523
+ "DSH 插件支持两种官方安装通道:`dsh plugin add <npm-package>`(npm 分发,需发布完整构建产物)与 `dsh plugin add git+https://github.com/owner/repo.git`(Git 直装,仓库需提交构建产物或在 package.json 提供 `prepare` 脚本)。当前插件的分发物缺少 package.json 声明的入口文件,请按所用通道补齐后重新发布。"
469
524
  ] : [],
470
525
  "",
471
526
  "## Environment",
@@ -540,6 +595,7 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
540
595
  repo: typeof raw.r.repo === "string" ? raw.r.repo : void 0,
541
596
  npmPackage: typeof raw.r.npmPackage === "string" ? raw.r.npmPackage : void 0
542
597
  } : void 0,
598
+ install: raw.wi === false ? { webInstallable: false } : void 0,
543
599
  compatibility: typeof raw.v === "string" ? { status: raw.v } : void 0,
544
600
  dates: {
545
601
  repoUpdatedAt: typeof raw.u === "string" ? raw.u : void 0,
@@ -569,10 +625,25 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
569
625
  via: "github"
570
626
  };
571
627
  }
572
- /** 展示用安装命令(复制/弹窗):npm 通道显示包名,git 通道显示 github: 源。 */
628
+ /** 展示用安装命令(复制/弹窗):npm 通道显示包名,git 通道显示显式 HTTPS URL。 */
573
629
  function installCommandOf(p, withProfile = false) {
574
630
  const { target, via } = installTargetOf(p);
575
- return via === "npm" ? `dsh plugin${withProfile ? " --profile web" : ""} add ${target}` : `dsh plugin${withProfile ? " --profile web" : ""} add github:${target}`;
631
+ return via === "npm" ? `dsh plugin${withProfile ? " --profile web" : ""} add ${target}` : `dsh plugin${withProfile ? " --profile web" : ""} add git+https://github.com/${target}.git`;
632
+ }
633
+ /** Normalize a task/install target to its owner/repo display identity. */
634
+ function repoFromInstallTarget(value) {
635
+ const input = value.trim();
636
+ if (/^[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+$/.test(input)) return input;
637
+ for (const pattern of [
638
+ /^github:([^/]+)\/([^/]+)$/i,
639
+ /^(?:git\+)?https:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?(?:[?#].*)?$/i,
640
+ /^(?:git\+)?ssh:\/\/(?:git@)?github\.com\/([^/]+)\/([^/]+?)(?:\.git)?(?:[?#].*)?$/i,
641
+ /^git@github\.com:([^/]+)\/([^/]+?)(?:\.git)?(?:[?#].*)?$/i
642
+ ]) {
643
+ const match = pattern.exec(input);
644
+ if (match !== null) return `${match[1]}/${match[2]}`;
645
+ }
646
+ return value;
576
647
  }
577
648
  const CATEGORY_LABELS = {
578
649
  interface: {
@@ -683,6 +754,10 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
683
754
  //#endregion
684
755
  //#region src/client/hooks/useCatalog.ts
685
756
  /**
757
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
758
+ * Website: https://dsh-plugin.org
759
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
760
+ *
686
761
  * Catalog data + view state for the Plugin Hub section.
687
762
  *
688
763
  * Owns the online-data pipeline (live fetch from dsh-plugin.org), the local
@@ -691,6 +766,8 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
691
766
  */
692
767
  const PLUGINS_URL = (lang) => `https://dsh-plugin.org/api/plugins.${lang}.json`;
693
768
  const STATS_URL = "https://dsh-plugin.org/api/stats.json";
769
+ /** 插件市场自身仓库:DSH-Plugin Hub 不显示在目录里(自己不进自己的插件列表) */
770
+ const SELF_REPO = "dshplugin/dsh-plugin-hub";
694
771
  function useCatalog(lang) {
695
772
  /** 目录插件(仅保留人工验证通过的条目) */
696
773
  const [plugins, setPlugins] = (0, react.useState)(null);
@@ -708,6 +785,10 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
708
785
  /** 安装时记录的目录信号:repo(小写) -> { version, updatedAt }(来自宿主本地路由);
709
786
  * npmPackage 为 npm 优先通道反查命中的包名映射(目录数据未下发时客户端靠它把依赖 key 匹配回仓库) */
710
787
  const [versions, setVersions] = (0, react.useState)({});
788
+ /** Hub 自我更新信息:来自 CF Worker 版本控制中心(hub.dsh-plugin.org),与目录数据解耦 */
789
+ const [hubUpdateInfo, setHubUpdateInfo] = (0, react.useState)(null);
790
+ /** 头部「关注我们」弹窗内容(平台介绍 + 反馈群二维码):来自同款 Worker /about,Markdown 推送非写死 */
791
+ const [hubAboutInfo, setHubAboutInfo] = (0, react.useState)(null);
711
792
  (0, react.useEffect)(() => {
712
793
  let cancelled = false;
713
794
  setPlugins(null);
@@ -717,20 +798,23 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
717
798
  if (!res.ok) throw new Error(String(res.status));
718
799
  return res.json();
719
800
  });
720
- fetchData(PLUGINS_URL(lang)).then((data) => {
801
+ const load = () => Promise.all([fetchData(PLUGINS_URL(lang)), fetchData(STATS_URL)]);
802
+ load().then(([data, s]) => {
721
803
  if (cancelled) return;
722
804
  const list = (Array.isArray(data) ? data : []).map((item) => normalize(item));
723
- setPlugins(list.filter((p) => p.compatibility?.status === "verified"));
724
- }).catch(() => {
725
- if (!cancelled) setFailed(true);
726
- });
727
- fetchData(STATS_URL).then((s) => {
805
+ const hadSelf = list.some((p) => p.source?.repo === SELF_REPO);
806
+ setPlugins(list.filter((p) => p.compatibility?.status === "verified" && p.source?.repo !== SELF_REPO));
728
807
  const stats = s;
729
- if (!cancelled && stats && typeof stats.total === "number" && typeof stats.verified === "number") setStats({
808
+ if (stats && typeof stats.total === "number" && typeof stats.verified === "number") setStats(hadSelf ? {
809
+ total: stats.total - 1,
810
+ verified: stats.verified - 1
811
+ } : {
730
812
  total: stats.total,
731
813
  verified: stats.verified
732
814
  });
733
- }).catch(() => {});
815
+ }).catch(() => {
816
+ if (!cancelled) setFailed(true);
817
+ });
734
818
  return () => {
735
819
  cancelled = true;
736
820
  };
@@ -748,12 +832,52 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
748
832
  (0, react.useEffect)(() => {
749
833
  refreshInstalled();
750
834
  }, []);
751
- /** 插件是否已安装:匹配 installed spec 中的 `github:<owner>/<repo>`,或 npm 通道安装的依赖包名;命中返回 npm 包名。 */
835
+ (0, react.useEffect)(() => {
836
+ let cancelled = false;
837
+ fetch(HUB_UPDATE_URL, { cache: "no-store" }).then((res) => res.ok ? res.json() : Promise.resolve(null)).then((data) => {
838
+ if (cancelled || !data) {
839
+ if (!cancelled) setHubUpdateInfo(null);
840
+ return;
841
+ }
842
+ const version = typeof data.version === "string" && data.version.length > 0 ? data.version : null;
843
+ setHubUpdateInfo(version === null ? null : {
844
+ version,
845
+ publishedAt: data.publishedAt ?? null,
846
+ notes: data.notes ?? null
847
+ });
848
+ }).catch(() => {
849
+ if (!cancelled) setHubUpdateInfo(null);
850
+ });
851
+ return () => {
852
+ cancelled = true;
853
+ };
854
+ }, [reloadKey]);
855
+ (0, react.useEffect)(() => {
856
+ let cancelled = false;
857
+ fetch(HUB_ABOUT_URL, { cache: "no-store" }).then((res) => res.ok ? res.json() : Promise.resolve(null)).then((data) => {
858
+ if (cancelled) return;
859
+ if (!data || data.content === null || data.content === void 0) {
860
+ setHubAboutInfo(null);
861
+ return;
862
+ }
863
+ const content = typeof data.content === "string" || typeof data.content === "object" ? data.content : null;
864
+ setHubAboutInfo(content === null ? null : {
865
+ content,
866
+ updatedAt: data.updatedAt ?? null
867
+ });
868
+ }).catch(() => {
869
+ if (!cancelled) setHubAboutInfo(null);
870
+ });
871
+ return () => {
872
+ cancelled = true;
873
+ };
874
+ }, [reloadKey]);
875
+ /** 插件是否已安装:匹配 Git spec 中的 owner/repo,或 npm 通道安装的依赖包名;命中返回 npm 包名。 */
752
876
  const installedName = (p) => {
753
877
  const repo = p.source?.repo;
754
878
  if (!repo) return null;
755
- const needle = `github:${repo.toLowerCase()}`;
756
- for (const [name, spec] of Object.entries(installed)) if (spec.toLowerCase().includes(needle)) return name;
879
+ const needle = repo.toLowerCase();
880
+ for (const [name, spec] of Object.entries(installed)) if (repoFromInstallTarget(spec).toLowerCase() === needle) return name;
757
881
  const pkg = ((p.source?.npmPackage || versions[repo.toLowerCase()]?.npmPackage) ?? "").toLowerCase();
758
882
  if (pkg) {
759
883
  for (const name of Object.keys(installed)) if (name.toLowerCase() === pkg) return name;
@@ -781,6 +905,16 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
781
905
  const current = p.dates?.repoUpdatedAt;
782
906
  return Boolean(current && rec.updatedAt && current > rec.updatedAt);
783
907
  };
908
+ /**
909
+ * Hub 自身是否有可用更新:Hub 就是当前运行的应用(始终已安装,无需 installedName 命中)。
910
+ * 以 Worker 版本控制中心返回的「最新版本」为准,与当前运行的版本号比对——
911
+ * 构建时注入的 PLUGIN_VERSION 是运行 bundle 的真实版本;测试/异常场景缺失时
912
+ * 回退到安装时记录的版本。版本号不等即新版(版本号只在发版时变更,绝无降级场景)。
913
+ */
914
+ const hubHasUpdate = (() => {
915
+ if (!hubUpdateInfo) return false;
916
+ return hubUpdateInfo.version !== "1.1.2";
917
+ })();
784
918
  /** 当前分类下的插件(「全部」时为整个目录)。 */
785
919
  const categoryPlugins = (0, react.useMemo)(() => {
786
920
  if (!plugins) return [];
@@ -839,6 +973,9 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
839
973
  installedName,
840
974
  installedVersion,
841
975
  hasUpdate,
976
+ hubHasUpdate,
977
+ hubUpdateInfo,
978
+ hubAboutInfo,
842
979
  refreshInstalled,
843
980
  category,
844
981
  setCategory,
@@ -858,6 +995,10 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
858
995
  //#endregion
859
996
  //#region src/client/hooks/useTaskQueue.ts
860
997
  /**
998
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
999
+ * Website: https://dsh-plugin.org
1000
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
1001
+ *
861
1002
  * Background install/remove task queue for the Plugin Hub.
862
1003
  *
863
1004
  * Mirrors the server-side FIFO active queue by polling /active, settles
@@ -908,7 +1049,7 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
908
1049
  queueRef.current = next;
909
1050
  setQueue(next);
910
1051
  };
911
- /** 解析服务端待重启列表(`github:` 前缀已去掉,与安装任务的 target 对齐)。 */
1052
+ /** 解析服务端待重启列表(安装目标归一化为 owner/repo,与安装任务对齐)。 */
912
1053
  const parsePendingRestarts = (value) => {
913
1054
  if (!Array.isArray(value)) return [];
914
1055
  const out = [];
@@ -918,7 +1059,7 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
918
1059
  if (typeof target !== "string" || target === "") continue;
919
1060
  const at = x.at;
920
1061
  out.push({
921
- target: target.replace(/^github:/, ""),
1062
+ target: repoFromInstallTarget(target),
922
1063
  kind: x.kind === "uninstall" ? "uninstall" : "install",
923
1064
  at: typeof at === "number" ? at : 0
924
1065
  });
@@ -967,14 +1108,14 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
967
1108
  refreshInstalled();
968
1109
  if (q.kind === "uninstall") {
969
1110
  syncInstalledVersion(q.repo, void 0, void 0);
970
- onUninstallDone(modalTaskRef.current === q.id, q.repo);
1111
+ onUninstallDone(modalTaskRef.current === q.id, q.repo, q.needsRestart);
971
1112
  } else {
972
1113
  syncInstalledVersion(q.repo, q.version, q.updatedAt);
973
1114
  pendingInfoRef.current.set(q.target, {
974
1115
  desc: q.desc,
975
1116
  version: q.version
976
1117
  });
977
- onInstallDone(modalTaskRef.current === q.id, q.repo);
1118
+ onInstallDone(modalTaskRef.current === q.id, q.repo, q.needsRestart);
978
1119
  }
979
1120
  } else {
980
1121
  const detail = lines.length > 0 ? [...lines].reverse().join("\n") : q.kind === "uninstall" ? t("uninstallFail") : t("installFail");
@@ -1017,23 +1158,33 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
1017
1158
  try {
1018
1159
  const res = await fetch(`/dsh-plugin-hub/status?task=${q.id}`, { cache: "no-store" });
1019
1160
  if (res.ok) {
1020
- const data = await res.json();
1021
- status = data.task?.status ?? "failed";
1022
- lines = data.task?.lines ?? [];
1023
- attempts = Array.isArray(data.task?.attempts) ? data.task.attempts : void 0;
1161
+ const task = (await res.json()).task;
1162
+ if (task !== void 0) {
1163
+ status = task.status ?? "failed";
1164
+ lines = task.lines ?? [];
1165
+ attempts = Array.isArray(task.attempts) ? task.attempts : void 0;
1166
+ const needsRestart = typeof task.needsRestart === "boolean" ? task.needsRestart : q.needsRestart;
1167
+ if (status === "done") {
1168
+ const settled = {
1169
+ ...q,
1170
+ needsRestart
1171
+ };
1172
+ if (q.kind === "uninstall") settleDone(settled, lines);
1173
+ else finishQueueTask(true, settled, lines);
1174
+ return;
1175
+ }
1176
+ if (status === "failed") {
1177
+ finishQueueTask(false, {
1178
+ ...q,
1179
+ attempts
1180
+ }, lines);
1181
+ return;
1182
+ }
1183
+ }
1024
1184
  }
1025
1185
  } catch {}
1026
- if (status === "done") {
1027
- if (q.kind === "uninstall") settleDone(q, lines);
1028
- else finishQueueTask(true, q, lines);
1029
- } else if (status === "failed") finishQueueTask(false, {
1030
- ...q,
1031
- attempts
1032
- }, lines);
1033
- else {
1034
- applyQueue((prev) => prev.filter((x) => x.id !== q.id));
1035
- maybeStopPoll();
1036
- }
1186
+ applyQueue((prev) => prev.filter((x) => x.id !== q.id));
1187
+ maybeStopPoll();
1037
1188
  };
1038
1189
  /** 轮询整个队列:合并服务端 /active(running 在前、pending 在后),
1039
1190
  * 消失的任务查终态收尾;队列清空即停止轮询。 */
@@ -1057,15 +1208,16 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
1057
1208
  next.push({
1058
1209
  id,
1059
1210
  kind: isRemove ? "uninstall" : "install",
1060
- target: typeof a.target === "string" ? a.target.replace(/^github:/, "") : prevTask?.target ?? "",
1211
+ target: typeof a.target === "string" ? repoFromInstallTarget(a.target) : prevTask?.target ?? "",
1061
1212
  desc: prevTask?.desc,
1062
- repo: prevTask?.repo ?? (isRemove ? null : typeof a.target === "string" ? a.target.replace(/^github:/, "") : null),
1213
+ repo: prevTask?.repo ?? (isRemove ? null : typeof a.target === "string" ? repoFromInstallTarget(a.target) : null),
1063
1214
  version: prevTask?.version,
1064
1215
  updatedAt: prevTask?.updatedAt,
1065
1216
  status: a.status === "running" ? "running" : "pending",
1066
1217
  progress: typeof a.progress === "number" ? a.progress : prevTask?.progress ?? 0,
1067
1218
  lines: Array.isArray(a.lines) ? a.lines : prevTask?.lines ?? [],
1068
- attempts: Array.isArray(a.attempts) ? a.attempts : prevTask?.attempts ?? []
1219
+ attempts: Array.isArray(a.attempts) ? a.attempts : prevTask?.attempts ?? [],
1220
+ needsRestart: typeof a.needsRestart === "boolean" ? a.needsRestart : prevTask?.needsRestart ?? true
1069
1221
  });
1070
1222
  }
1071
1223
  const keepOpt = prev.filter((q) => q.optimistic && !next.some((x) => x.id === q.id));
@@ -1098,8 +1250,8 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
1098
1250
  applyPending(parsePendingRestarts(data.pendingRestarts));
1099
1251
  const items = (data.tasks ?? []).filter((a) => typeof a.id === "number").map((a) => {
1100
1252
  const isRemove = a.action === "remove";
1101
- const display = typeof a.displayTarget === "string" ? a.displayTarget.replace(/^github:/, "") : "";
1102
- const fallback = typeof a.target === "string" ? a.target.replace(/^github:/, "") : "";
1253
+ const display = typeof a.displayTarget === "string" ? repoFromInstallTarget(a.displayTarget) : "";
1254
+ const fallback = typeof a.target === "string" ? repoFromInstallTarget(a.target) : "";
1103
1255
  return {
1104
1256
  id: a.id,
1105
1257
  kind: isRemove ? "uninstall" : "install",
@@ -1108,7 +1260,8 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
1108
1260
  status: a.status === "running" ? "running" : "pending",
1109
1261
  progress: typeof a.progress === "number" ? a.progress : 0,
1110
1262
  lines: Array.isArray(a.lines) ? a.lines : [],
1111
- attempts: Array.isArray(a.attempts) ? a.attempts : []
1263
+ attempts: Array.isArray(a.attempts) ? a.attempts : [],
1264
+ needsRestart: typeof a.needsRestart === "boolean" ? a.needsRestart : true
1112
1265
  };
1113
1266
  });
1114
1267
  if (items.length > 0 || pendingRef.current.length > 0) {
@@ -1147,7 +1300,8 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
1147
1300
  lines: [],
1148
1301
  optimistic: true,
1149
1302
  command: installCommandOf(p, true),
1150
- attempts: []
1303
+ attempts: [],
1304
+ needsRestart: true
1151
1305
  }]);
1152
1306
  setSubmitting(true);
1153
1307
  try {
@@ -1219,7 +1373,8 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
1219
1373
  repo,
1220
1374
  status: "pending",
1221
1375
  progress: 0,
1222
- lines: []
1376
+ lines: [],
1377
+ needsRestart: true
1223
1378
  }]);
1224
1379
  pollQueue();
1225
1380
  } catch {
@@ -1266,7 +1421,7 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
1266
1421
  }
1267
1422
  //#endregion
1268
1423
  //#region \0dsh-css:src/client/styles/Modal.module.css.mjs
1269
- 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;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_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}";
1424
+ 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}";
1270
1425
  const tagId$3 = "dsh-plugin/Modal.module.css";
1271
1426
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$3) + "]") === null) {
1272
1427
  const tag = document.createElement("style");
@@ -1276,97 +1431,111 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
1276
1431
  document.head.appendChild(tag);
1277
1432
  }
1278
1433
  var Modal_module_css_default = {
1434
+ "resultDesc": "BiQ1zG_resultDesc",
1435
+ "hubUpdateMetaItem": "BiQ1zG_hubUpdateMetaItem",
1436
+ "modalBody": "BiQ1zG_modalBody",
1279
1437
  "queueRowTarget": "BiQ1zG_queueRowTarget",
1280
- "failClear": "BiQ1zG_failClear",
1281
- "modalValue": "BiQ1zG_modalValue",
1438
+ "queueRowDesc": "BiQ1zG_queueRowDesc",
1439
+ "trustHint": "BiQ1zG_trustHint",
1440
+ "uninstallConfirm": "BiQ1zG_uninstallConfirm",
1441
+ "progressText": "BiQ1zG_progressText",
1442
+ "noticeList": "BiQ1zG_noticeList",
1443
+ "noticeRowMain": "BiQ1zG_noticeRowMain",
1444
+ "queueRowHead": "BiQ1zG_queueRowHead",
1445
+ "result": "BiQ1zG_result",
1446
+ "restartNow": "BiQ1zG_restartNow",
1447
+ "modalIn": "BiQ1zG_modalIn",
1448
+ "modalInstall": "BiQ1zG_modalInstall",
1449
+ "errorBox": "BiQ1zG_errorBox",
1450
+ "toastIn": "BiQ1zG_toastIn",
1451
+ "noticeRemove": "BiQ1zG_noticeRemove",
1452
+ "resultTitle": "BiQ1zG_resultTitle",
1453
+ "progressHead": "BiQ1zG_progressHead",
1454
+ "modal": "BiQ1zG_modal",
1455
+ "modalCmd": "BiQ1zG_modalCmd",
1456
+ "pendingRowActions": "BiQ1zG_pendingRowActions",
1457
+ "modalCmdText": "BiQ1zG_modalCmdText",
1282
1458
  "modalTitleQueued": "BiQ1zG_modalTitleQueued",
1283
- "overlay": "BiQ1zG_overlay",
1459
+ "queuedHint": "BiQ1zG_queuedHint",
1460
+ "noticeBadgeIcon": "BiQ1zG_noticeBadgeIcon",
1461
+ "progressFill": "BiQ1zG_progressFill",
1462
+ "noticeTextFail": "BiQ1zG_noticeTextFail",
1463
+ "aboutModal": "BiQ1zG_aboutModal",
1464
+ "failList": "BiQ1zG_failList",
1465
+ "modalHead": "BiQ1zG_modalHead",
1284
1466
  "noticeRow": "BiQ1zG_noticeRow",
1285
1467
  "toast": "BiQ1zG_toast",
1286
- "restartLater": "BiQ1zG_restartLater",
1287
- "resultCheckIcon": "BiQ1zG_resultCheckIcon",
1288
- "linkIcon": "BiQ1zG_linkIcon",
1289
- "failKindUninstall": "BiQ1zG_failKindUninstall",
1290
- "failRepo": "BiQ1zG_failRepo",
1291
- "modalCmd": "BiQ1zG_modalCmd",
1292
- "modal": "BiQ1zG_modal",
1293
- "queueRowPct": "BiQ1zG_queueRowPct",
1294
- "failKind": "BiQ1zG_failKind",
1295
- "modalTitle": "BiQ1zG_modalTitle",
1296
- "modalTitleBusy": "BiQ1zG_modalTitleBusy",
1297
- "modalBody": "BiQ1zG_modalBody",
1298
1468
  "modalCloseIcon": "BiQ1zG_modalCloseIcon",
1299
- "errorCopySoft": "BiQ1zG_errorCopySoft",
1469
+ "failEmpty": "BiQ1zG_failEmpty",
1470
+ "noticeRowOk": "BiQ1zG_noticeRowOk",
1471
+ "aboutMeta": "BiQ1zG_aboutMeta",
1472
+ "resultRestarting": "BiQ1zG_resultRestarting",
1300
1473
  "modalCancel": "BiQ1zG_modalCancel",
1301
- "overlayIn": "BiQ1zG_overlayIn",
1302
- "modalClose": "BiQ1zG_modalClose",
1303
- "modalCopy": "BiQ1zG_modalCopy",
1304
- "modalCmdText": "BiQ1zG_modalCmdText",
1474
+ "restartLater": "BiQ1zG_restartLater",
1475
+ "noticeTime": "BiQ1zG_noticeTime",
1305
1476
  "queueSection": "BiQ1zG_queueSection",
1306
- "queueRow": "BiQ1zG_queueRow",
1307
- "errorHint": "BiQ1zG_errorHint",
1308
- "noticeMain": "BiQ1zG_noticeMain",
1309
- "noticeFoot": "BiQ1zG_noticeFoot",
1310
- "failCopy": "BiQ1zG_failCopy",
1311
- "failBigIssue": "BiQ1zG_failBigIssue",
1477
+ "modalTitleBusy": "BiQ1zG_modalTitleBusy",
1312
1478
  "modalRow": "BiQ1zG_modalRow",
1313
- "queueRowBody": "BiQ1zG_queueRowBody",
1314
- "queueRowTrack": "BiQ1zG_queueRowTrack",
1315
- "errorBox": "BiQ1zG_errorBox",
1316
- "progressHead": "BiQ1zG_progressHead",
1479
+ "failBigIssue": "BiQ1zG_failBigIssue",
1480
+ "resultCheckIcon": "BiQ1zG_resultCheckIcon",
1481
+ "queueSectionTitle": "BiQ1zG_queueSectionTitle",
1482
+ "hubUpdateModal": "BiQ1zG_hubUpdateModal",
1483
+ "modalCmdCopy": "BiQ1zG_modalCmdCopy",
1484
+ "overlayIn": "BiQ1zG_overlayIn",
1485
+ "modalClose": "BiQ1zG_modalClose",
1486
+ "failRow": "BiQ1zG_failRow",
1487
+ "noticeBadgeFail": "BiQ1zG_noticeBadgeFail",
1488
+ "failRepo": "BiQ1zG_failRepo",
1317
1489
  "noticeHead": "BiQ1zG_noticeHead",
1318
- "progressFillFail": "BiQ1zG_progressFillFail",
1319
- "noticeTextFail": "BiQ1zG_noticeTextFail",
1320
- "noticeRemove": "BiQ1zG_noticeRemove",
1321
- "modalLabel": "BiQ1zG_modalLabel",
1322
- "pendingRowActions": "BiQ1zG_pendingRowActions",
1323
- "resultTitle": "BiQ1zG_resultTitle",
1324
- "restartNow": "BiQ1zG_restartNow",
1325
- "noticeBadgeIcon": "BiQ1zG_noticeBadgeIcon",
1326
- "failPrepareHint": "BiQ1zG_failPrepareHint",
1327
- "noticeBadgeOk": "BiQ1zG_noticeBadgeOk",
1328
- "failEmpty": "BiQ1zG_failEmpty",
1329
- "result": "BiQ1zG_result",
1330
- "modalInstall": "BiQ1zG_modalInstall",
1490
+ "noticeFoot": "BiQ1zG_noticeFoot",
1491
+ "aboutContent": "BiQ1zG_aboutContent",
1492
+ "errorModal": "BiQ1zG_errorModal",
1493
+ "failKindUninstall": "BiQ1zG_failKindUninstall",
1331
1494
  "noticeTextOk": "BiQ1zG_noticeTextOk",
1332
- "noticeList": "BiQ1zG_noticeList",
1333
- "queueSectionTitle": "BiQ1zG_queueSectionTitle",
1334
- "stripCancel": "BiQ1zG_stripCancel",
1495
+ "progressTrack": "BiQ1zG_progressTrack",
1496
+ "noticeBadgeOk": "BiQ1zG_noticeBadgeOk",
1335
1497
  "progress": "BiQ1zG_progress",
1336
- "modalDesc": "BiQ1zG_modalDesc",
1337
- "modalIn": "BiQ1zG_modalIn",
1338
- "errorTitle": "BiQ1zG_errorTitle",
1339
- "failList": "BiQ1zG_failList",
1340
- "noticeTime": "BiQ1zG_noticeTime",
1341
- "queuedHint": "BiQ1zG_queuedHint",
1342
- "resultDesc": "BiQ1zG_resultDesc",
1343
- "trustHint": "BiQ1zG_trustHint",
1344
- "noticeRowMain": "BiQ1zG_noticeRowMain",
1345
- "queueRowDesc": "BiQ1zG_queueRowDesc",
1346
- "modalHead": "BiQ1zG_modalHead",
1347
- "queueRowStatus": "BiQ1zG_queueRowStatus",
1348
- "uninstallConfirm": "BiQ1zG_uninstallConfirm",
1349
- "failKindInstall": "BiQ1zG_failKindInstall",
1498
+ "linkIcon": "BiQ1zG_linkIcon",
1350
1499
  "toastFail": "BiQ1zG_toastFail",
1351
- "queueRowHead": "BiQ1zG_queueRowHead",
1352
- "resultRestarting": "BiQ1zG_resultRestarting",
1353
- "resultCheck": "BiQ1zG_resultCheck",
1354
- "failRow": "BiQ1zG_failRow",
1500
+ "modalDesc": "BiQ1zG_modalDesc",
1355
1501
  "pendingRowStatus": "BiQ1zG_pendingRowStatus",
1356
- "failHead": "BiQ1zG_failHead",
1357
- "progressFill": "BiQ1zG_progressFill",
1358
- "progressTrack": "BiQ1zG_progressTrack",
1502
+ "queueRowPct": "BiQ1zG_queueRowPct",
1503
+ "resultCheck": "BiQ1zG_resultCheck",
1504
+ "queueRowTrack": "BiQ1zG_queueRowTrack",
1505
+ "failPrepareHint": "BiQ1zG_failPrepareHint",
1506
+ "queueRow": "BiQ1zG_queueRow",
1507
+ "failKindInstall": "BiQ1zG_failKindInstall",
1508
+ "failCopy": "BiQ1zG_failCopy",
1509
+ "cliOnlyHint": "BiQ1zG_cliOnlyHint",
1510
+ "modalLabel": "BiQ1zG_modalLabel",
1511
+ "overlay": "BiQ1zG_overlay",
1359
1512
  "modalActions": "BiQ1zG_modalActions",
1360
- "noticeBadgeFail": "BiQ1zG_noticeBadgeFail",
1513
+ "queueRowBody": "BiQ1zG_queueRowBody",
1361
1514
  "modalLink": "BiQ1zG_modalLink",
1362
- "errorModal": "BiQ1zG_errorModal",
1363
- "progressText": "BiQ1zG_progressText",
1364
- "modalCmdCopy": "BiQ1zG_modalCmdCopy",
1365
- "toastIn": "BiQ1zG_toastIn"
1515
+ "queueRowStatus": "BiQ1zG_queueRowStatus",
1516
+ "stripCancel": "BiQ1zG_stripCancel",
1517
+ "errorTitle": "BiQ1zG_errorTitle",
1518
+ "failedCopyHint": "BiQ1zG_failedCopyHint",
1519
+ "modalValue": "BiQ1zG_modalValue",
1520
+ "errorCopySoft": "BiQ1zG_errorCopySoft",
1521
+ "errorHint": "BiQ1zG_errorHint",
1522
+ "modalCopy": "BiQ1zG_modalCopy",
1523
+ "failClear": "BiQ1zG_failClear",
1524
+ "hubUpdateMeta": "BiQ1zG_hubUpdateMeta",
1525
+ "noticeMain": "BiQ1zG_noticeMain",
1526
+ "progressFillFail": "BiQ1zG_progressFillFail",
1527
+ "modalTitle": "BiQ1zG_modalTitle",
1528
+ "failHead": "BiQ1zG_failHead",
1529
+ "failKind": "BiQ1zG_failKind",
1530
+ "hubUpdateNotes": "BiQ1zG_hubUpdateNotes"
1366
1531
  };
1367
1532
  //#endregion
1368
1533
  //#region src/client/components/icons.tsx
1369
1534
  /**
1535
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
1536
+ * Website: https://dsh-plugin.org
1537
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
1538
+ *
1370
1539
  * Inline SVG icons used across the client. Icons inherit currentColor so
1371
1540
  * they adapt to themes and disabled states automatically.
1372
1541
  */
@@ -1522,6 +1691,10 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
1522
1691
  //#endregion
1523
1692
  //#region src/client/components/ProgressView.tsx
1524
1693
  /**
1694
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
1695
+ * Website: https://dsh-plugin.org
1696
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
1697
+ *
1525
1698
  * Inline progress block shared by the install and uninstall dialogs:
1526
1699
  * a real percentage bar (server-estimated, client-polled). No live terminal
1527
1700
  * window — pnpm emits nothing during git clone/build, so a log pane would be
@@ -1536,13 +1709,20 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
1536
1709
  //#endregion
1537
1710
  //#region src/client/components/modals.tsx
1538
1711
  /**
1712
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
1713
+ * Website: https://dsh-plugin.org
1714
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
1715
+ *
1539
1716
  * Dialog layer for the Plugin Hub: the install-confirm dialog, the uninstall
1540
1717
  * dialog and the global toast. Both dialogs lock themselves while a mutation
1541
1718
  * or restart is running, then switch to a result view offering an immediate
1542
1719
  * restart or a "later" deferral.
1543
1720
  */
1544
- /** 完成结果视图:绿色对勾 + 标题/描述 + 「稍后重启 / 立即重启」按钮对(部分插件需重启后才会挂载) */
1545
- function ResultView({ title, desc, t, restarting, onRestart, onClose }) {
1721
+ /** 完成结果视图:绿色对勾 + 标题/描述;
1722
+ * needsRestart=true(插件需重启才生效)→ 「稍后重启 / 立即重启」按钮对,点稍后重启后
1723
+ * 通知中心待重启条目常驻(服务端登记,内存态),直到用户点「立即重启」真正重启后才消失;
1724
+ * needsRestart=false(卸载已即时生效)→ 仅「完成」关闭。 */
1725
+ function ResultView({ title, desc, t, restarting, needsRestart, onRestart, onClose }) {
1546
1726
  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", {
1547
1727
  className: Modal_module_css_default.resultCheckIcon,
1548
1728
  viewBox: "0 0 16 16",
@@ -1556,7 +1736,7 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
1556
1736
  strokeWidth: 2,
1557
1737
  strokeLinecap: "round",
1558
1738
  strokeLinejoin: "round"
1559
- }))), (0, react.createElement)("div", { className: Modal_module_css_default.resultTitle }, title), desc ? (0, react.createElement)("div", { className: Modal_module_css_default.resultDesc }, desc) : 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", {
1739
+ }))), (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", {
1560
1740
  className: Modal_module_css_default.restartLater,
1561
1741
  onClick: onClose,
1562
1742
  disabled: restarting
@@ -1564,14 +1744,17 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
1564
1744
  className: Modal_module_css_default.restartNow,
1565
1745
  onClick: onRestart,
1566
1746
  disabled: restarting
1567
- }, restarting ? t("restarting") : t("restartNow"))));
1747
+ }, restarting ? t("restarting") : t("restartNow")))]) : (0, react.createElement)("div", { className: Modal_module_css_default.modalActions }, (0, react.createElement)("button", {
1748
+ className: Modal_module_css_default.restartNow,
1749
+ onClick: onClose
1750
+ }, t("done"))));
1568
1751
  }
1569
1752
  /**
1570
1753
  * 信任确认弹窗:安装进入后台队列后弹窗仍可关闭(任务继续),
1571
1754
  * 只在本任务执行中展示实时进度;完成后切换为结果视图,与卸载一致。
1572
1755
  */
1573
1756
  function InstallModal(props) {
1574
- const { plugin, done, task, t, langPath, restarting, submitting, update, onClose, onCopy, onInstall, onRestart } = props;
1757
+ const { plugin, done, task, t, langPath, restarting, submitting, update, cliOnly, needsRestart, onClose, onCopy, onInstall, onRestart } = props;
1575
1758
  const busy = submitting || task !== null && (task.status === "pending" || task.status === "running");
1576
1759
  const name = plugin.displayName ?? plugin.slug;
1577
1760
  const busyTitle = (label) => langPath === "zh/" ? `${name} 插件${label}` : `${label} ${name}`;
@@ -1594,6 +1777,7 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
1594
1777
  desc: update ? t("updateResultDesc") : t("installResultDesc"),
1595
1778
  t,
1596
1779
  restarting,
1780
+ needsRestart,
1597
1781
  onRestart,
1598
1782
  onClose
1599
1783
  }) : (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", {
@@ -1611,7 +1795,7 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
1611
1795
  title: t("copyInstallCommand"),
1612
1796
  role: "button",
1613
1797
  tabIndex: 0
1614
- }, (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"))), 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", {
1798
+ }, (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", {
1615
1799
  className: Modal_module_css_default.modalCopy,
1616
1800
  disabled: busy,
1617
1801
  onClick: onCopy
@@ -1623,7 +1807,7 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
1623
1807
  }
1624
1808
  /** 卸载确认弹窗:确认/进行中(后台队列,可关闭);完成后切换为结果视图(成功即生效,仅「完成」关闭)。 */
1625
1809
  function UninstallModal(props) {
1626
- const { plugin, done, task, t, langPath, restarting, submitting, onClose, onCancel, onCopyCommand, onConfirm, onRestart } = props;
1810
+ const { plugin, done, task, t, langPath, restarting, submitting, needsRestart, onClose, onCancel, onCopyCommand, onConfirm, onRestart } = props;
1627
1811
  const busy = submitting || task !== null && (task.status === "pending" || task.status === "running");
1628
1812
  const name = plugin.displayName ?? plugin.slug;
1629
1813
  const busyTitle = (label) => langPath === "zh/" ? `${name} 插件${label}` : `${label} ${name}`;
@@ -1646,6 +1830,7 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
1646
1830
  desc: t("uninstallResultDesc"),
1647
1831
  t,
1648
1832
  restarting,
1833
+ needsRestart,
1649
1834
  onRestart,
1650
1835
  onClose
1651
1836
  }) : (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", {
@@ -1723,8 +1908,193 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
1723
1908
  }, text);
1724
1909
  }
1725
1910
  //#endregion
1911
+ //#region src/client/lib/renderMarkdown.ts
1912
+ /**
1913
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
1914
+ * Website: https://dsh-plugin.org
1915
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
1916
+ *
1917
+ * 轻量 Markdown 渲染器:仅用于 Hub 自我更新的变更记录(Worker 下发的 notes)。
1918
+ *
1919
+ * 内容由插件作者自己编写、属可信内容,但渲染端仍先整体 HTML 转义,再按
1920
+ * Markdown 语法输出结构,杜绝任何未转义的原始 HTML 注入;链接仅放行 http(s)。
1921
+ *
1922
+ * 支持:标题(#~####)、有序/无序列表、代码块(```)、行内代码、加粗、斜体、
1923
+ * 链接、图片(![alt](url),如反馈群二维码)、引用(>)、段落;其余语法一律按
1924
+ * 纯文本处理,不引入外部依赖。图片仅放行 http(s),限定最大宽度不撑破弹窗。
1925
+ */
1926
+ /** HTML 转义:所有文本先进这里,再进入结构变换,保证输出里没有未转义的用户输入。 */
1927
+ function escapeHtml(s) {
1928
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
1929
+ }
1930
+ /** 行内变换:链接 / 加粗 / 斜体 / 行内代码(代码先占位保护,最后还原)。 */
1931
+ function inline(s) {
1932
+ let out = escapeHtml(s);
1933
+ const codeSpans = [];
1934
+ out = out.replace(/`([^`]+)`/g, (_m, code) => {
1935
+ codeSpans.push(String(code));
1936
+ return `\u0000${codeSpans.length - 1}\u0000`;
1937
+ });
1938
+ 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);
1939
+ 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);
1940
+ out = out.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>");
1941
+ out = out.replace(/(^|[^*])\*([^*]+)\*/g, "$1<em>$2</em>");
1942
+ return out.replace(/\u0000(\d+)\u0000/g, (_m, idx) => `<code>${codeSpans[Number(idx)]}</code>`);
1943
+ }
1944
+ /** 把变更记录 Markdown 渲染为安全的 HTML 字符串(由弹窗用 dangerouslySetInnerHTML 注入)。 */
1945
+ function renderMarkdown(md) {
1946
+ const lines = md.replace(/\r\n?/g, "\n").split("\n");
1947
+ const blocks = [];
1948
+ let i = 0;
1949
+ while (i < lines.length) {
1950
+ const line = lines[i];
1951
+ if (/^\s*```/.test(line)) {
1952
+ const buf = [];
1953
+ i++;
1954
+ while (i < lines.length && !/^\s*```/.test(lines[i])) {
1955
+ buf.push(lines[i]);
1956
+ i++;
1957
+ }
1958
+ i++;
1959
+ blocks.push(`<pre><code>${escapeHtml(buf.join("\n"))}</code></pre>`);
1960
+ continue;
1961
+ }
1962
+ if (line.trim() === "") {
1963
+ i++;
1964
+ continue;
1965
+ }
1966
+ const heading = line.match(/^(#{1,4})\s+(.*)/);
1967
+ if (heading) {
1968
+ const level = heading[1].length + 1;
1969
+ blocks.push(`<h${level}>${inline(heading[2])}</h${level}>`);
1970
+ i++;
1971
+ continue;
1972
+ }
1973
+ if (/^\s*[-*+]\s+/.test(line) || /^\s*\d+[.)]\s+/.test(line)) {
1974
+ const ordered = /^\s*\d+[.)]\s+/.test(line);
1975
+ const items = [];
1976
+ while (i < lines.length && (/^\s*[-*+]\s+/.test(lines[i]) || /^\s*\d+[.)]\s+/.test(lines[i]))) {
1977
+ items.push(`<li>${inline(lines[i].replace(/^\s*[-*+]|\s*\d+[.)]\s*/, "").trim())}</li>`);
1978
+ i++;
1979
+ }
1980
+ blocks.push(`<${ordered ? "ol" : "ul"}>${items.join("")}</${ordered ? "ol" : "ul"}>`);
1981
+ continue;
1982
+ }
1983
+ if (/^\s*>\s?/.test(line)) {
1984
+ const buf = [];
1985
+ while (i < lines.length && /^\s*>\s?/.test(lines[i])) {
1986
+ buf.push(inline(lines[i].replace(/^\s*>\s?/, "")));
1987
+ i++;
1988
+ }
1989
+ blocks.push(`<blockquote>${buf.join("<br>")}</blockquote>`);
1990
+ continue;
1991
+ }
1992
+ const buf = [];
1993
+ while (i < lines.length && lines[i].trim() !== "" && !/^\s*```/.test(lines[i]) && !/^#{1,4}\s+/.test(lines[i]) && !/^\s*([-*+]|\d+[.)])\s+/.test(lines[i])) {
1994
+ buf.push(inline(lines[i]));
1995
+ i++;
1996
+ }
1997
+ blocks.push(`<p>${buf.join("<br>")}</p>`);
1998
+ }
1999
+ return blocks.join("");
2000
+ }
2001
+ //#endregion
2002
+ //#region src/client/components/AboutModal.tsx
2003
+ /**
2004
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
2005
+ * Website: https://dsh-plugin.org
2006
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
2007
+ *
2008
+ * 头部「关注我们」弹窗:介绍 DSH-Plugin Hub 是干什么的 + 用户反馈群二维码。
2009
+ * 内容以 Markdown 形式由 dsh-update Worker 的 /about 接口下发(key = hub:about),
2010
+ * 作者想怎么写就怎么写、随写随推,客户端只负责渲染 —— 非写死。
2011
+ * 图片(反馈群二维码)经 renderMarkdown 的 ![](url) 语法嵌入,居中、最大高度受控。
2012
+ */
2013
+ function AboutModal({ info, lang, t, onClose }) {
2014
+ 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 ?? "" : "" : "";
2015
+ const contentHtml = contentRaw.trim() ? renderMarkdown(contentRaw) : null;
2016
+ let updated = null;
2017
+ if (info?.updatedAt) {
2018
+ const d = new Date(info.updatedAt);
2019
+ if (!Number.isNaN(d.getTime())) updated = d.toLocaleString(lang === "en" ? "en-US" : "zh-CN");
2020
+ }
2021
+ return (0, react.createElement)("div", {
2022
+ className: Modal_module_css_default.overlay,
2023
+ onClick: (e) => {
2024
+ if (e.target === e.currentTarget) onClose();
2025
+ }
2026
+ }, (0, react.createElement)("div", {
2027
+ className: `${Modal_module_css_default.modal} ${Modal_module_css_default.aboutModal}`,
2028
+ role: "dialog",
2029
+ "aria-modal": "true"
2030
+ }, (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", {
2031
+ className: Modal_module_css_default.modalClose,
2032
+ "aria-label": t("confirmCancel"),
2033
+ onClick: () => onClose()
2034
+ }, (0, react.createElement)(CloseIcon))), (0, react.createElement)("div", { className: Modal_module_css_default.modalDesc }, t("aboutDesc")), contentHtml ? (0, react.createElement)("div", {
2035
+ className: Modal_module_css_default.aboutContent,
2036
+ dangerouslySetInnerHTML: { __html: contentHtml }
2037
+ }) : (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", {
2038
+ className: Modal_module_css_default.modalInstall,
2039
+ onClick: onClose
2040
+ }, t("doneBtn")))));
2041
+ }
2042
+ //#endregion
2043
+ //#region src/client/components/HubUpdateModal.tsx
2044
+ /**
2045
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
2046
+ * Website: https://dsh-plugin.org
2047
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
2048
+ *
2049
+ * Hub 版本信息 / 自我更新说明弹窗:
2050
+ * - 有更新(hasUpdate=true):标题「有新版本」,展示新版本号、发布时间与 Worker 下发的
2051
+ * Markdown 变更记录(renderMarkdown 渲染,双语言按界面语言取 {zh,en} 对象),
2052
+ * 按钮「稍后再说 / 直接更新」,确认后进入安装弹窗执行覆盖重装。
2053
+ * - 无更新(hasUpdate=false):标题「当前版本」,展示当前版本信息与更新记录,已是最新。
2054
+ * 点击头部版本号(或「可更新」徽标)打开 —— 无论有无更新都能看到更新内容。
2055
+ */
2056
+ function HubUpdateModal({ info, lang, t, hasUpdate, onProceed, onClose }) {
2057
+ 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 ?? "" : "";
2058
+ const notesHtml = notesRaw.trim() ? renderMarkdown(notesRaw) : null;
2059
+ let published = null;
2060
+ if (info.publishedAt) {
2061
+ const d = new Date(info.publishedAt);
2062
+ if (!Number.isNaN(d.getTime())) published = d.toLocaleString(lang === "en" ? "en-US" : "zh-CN");
2063
+ }
2064
+ return (0, react.createElement)("div", {
2065
+ className: Modal_module_css_default.overlay,
2066
+ onClick: (e) => {
2067
+ if (e.target === e.currentTarget) onClose();
2068
+ }
2069
+ }, (0, react.createElement)("div", {
2070
+ className: `${Modal_module_css_default.modal} ${Modal_module_css_default.hubUpdateModal}`,
2071
+ role: "dialog",
2072
+ "aria-modal": "true"
2073
+ }, (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", {
2074
+ className: Modal_module_css_default.modalClose,
2075
+ "aria-label": t("confirmCancel"),
2076
+ onClick: () => onClose()
2077
+ }, (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", {
2078
+ className: Modal_module_css_default.hubUpdateNotes,
2079
+ dangerouslySetInnerHTML: { __html: notesHtml }
2080
+ }) : null, (0, react.createElement)("div", { className: Modal_module_css_default.modalActions }, hasUpdate ? [(0, react.createElement)("button", {
2081
+ className: Modal_module_css_default.restartLater,
2082
+ onClick: onClose
2083
+ }, t("hubUpdateLater")), (0, react.createElement)("button", {
2084
+ className: Modal_module_css_default.modalInstall,
2085
+ onClick: onProceed
2086
+ }, t("updateNow"))] : (0, react.createElement)("button", {
2087
+ className: Modal_module_css_default.modalInstall,
2088
+ onClick: onClose
2089
+ }, t("hubUpToDate")))));
2090
+ }
2091
+ //#endregion
1726
2092
  //#region src/client/components/NotificationsModal.tsx
1727
2093
  /**
2094
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
2095
+ * Website: https://dsh-plugin.org
2096
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
2097
+ *
1728
2098
  * Notification-center dialog: a persistent log of every settled install /
1729
2099
  * remove task — successes and failures alike.
1730
2100
  *
@@ -1816,7 +2186,7 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
1816
2186
  })), (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) => {
1817
2187
  return (0, react.createElement)("div", {
1818
2188
  key: r.id,
1819
- className: Modal_module_css_default.noticeRow
2189
+ className: r.ok ? `${Modal_module_css_default.noticeRow} ${Modal_module_css_default.noticeRowOk}` : Modal_module_css_default.noticeRow
1820
2190
  }, (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", {
1821
2191
  className: Modal_module_css_default.failRepo,
1822
2192
  href: pluginSiteUrl(r.repo),
@@ -1826,7 +2196,10 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
1826
2196
  }, r.repo) : null, !r.ok && (0, react.createElement)("button", {
1827
2197
  className: Modal_module_css_default.failCopy,
1828
2198
  onClick: () => onCopy(r.message)
1829
- }, t("failCopy")), (0, react.createElement)("button", {
2199
+ }, t("failCopy")), r.ok ? (0, react.createElement)("span", {
2200
+ className: Modal_module_css_default.noticeTime,
2201
+ title: new Date(r.at).toLocaleString()
2202
+ }, fmtTime(r.at)) : null, (0, react.createElement)("button", {
1830
2203
  className: Modal_module_css_default.noticeRemove,
1831
2204
  "aria-label": t("removeNotification"),
1832
2205
  title: t("removeNotification"),
@@ -1850,10 +2223,10 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
1850
2223
  rel: "noopener noreferrer",
1851
2224
  title: t("failIssueHint")
1852
2225
  }, t("failIssueBig")) : null;
1853
- })())), (0, react.createElement)("div", { className: Modal_module_css_default.noticeFoot }, (0, react.createElement)("span", {
2226
+ })())), !r.ok ? (0, react.createElement)("div", { className: Modal_module_css_default.noticeFoot }, (0, react.createElement)("span", {
1854
2227
  className: Modal_module_css_default.noticeTime,
1855
2228
  title: new Date(r.at).toLocaleString()
1856
- }, fmtTime(r.at))));
2229
+ }, fmtTime(r.at))) : null);
1857
2230
  })), (0, react.createElement)("div", { className: Modal_module_css_default.modalActions }, records.length > 0 ? (0, react.createElement)("button", {
1858
2231
  className: Modal_module_css_default.failClear,
1859
2232
  onClick: onClear
@@ -1865,10 +2238,14 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
1865
2238
  //#endregion
1866
2239
  //#region src/client/components/CatalogHeader.tsx
1867
2240
  /**
2241
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
2242
+ * Website: https://dsh-plugin.org
2243
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
2244
+ *
1868
2245
  * Section header: brand title row (H1 + open-site button) + tagline, followed
1869
2246
  * by the purple ad banner that promotes the catalog stats.
1870
2247
  */
1871
- function CatalogHeader({ t, langPath, statsTotal, statsVerified, onToggleLang }) {
2248
+ function CatalogHeader({ t, langPath, statsTotal, statsVerified, onToggleLang, hubUpdate, onVersionClick, onAboutClick }) {
1872
2249
  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", {
1873
2250
  className: Header_module_css_default.brandTitle,
1874
2251
  href: `${SITE_URL}${langPath}`,
@@ -1876,13 +2253,25 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
1876
2253
  rel: "noopener noreferrer",
1877
2254
  title: t("openHint"),
1878
2255
  "aria-label": t("openHint")
1879
- }, (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", {
2256
+ }, (0, react.createElement)(LogoIcon), (0, react.createElement)("h1", { className: Header_module_css_default.title }, t("title"))), (0, react.createElement)("button", {
2257
+ className: Header_module_css_default.versionBtn,
2258
+ type: "button",
2259
+ onClick: onVersionClick,
2260
+ title: t(hubUpdate ? "hubUpdateHint" : "versionHint"),
2261
+ "aria-label": t(hubUpdate ? "hubUpdateHint" : "versionHint")
2262
+ }, `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", {
1880
2263
  className: Header_module_css_default.langBtn,
1881
2264
  type: "button",
1882
2265
  onClick: onToggleLang,
1883
2266
  title: t("toggleLangHint"),
1884
2267
  "aria-label": t("toggleLangHint")
1885
- }, langPath === "zh/" ? "EN" : "中文"), (0, react.createElement)("a", {
2268
+ }, langPath === "zh/" ? "EN" : "中文"), (0, react.createElement)("button", {
2269
+ className: Header_module_css_default.aboutBtn,
2270
+ type: "button",
2271
+ onClick: onAboutClick,
2272
+ title: t("aboutDesc"),
2273
+ "aria-label": t("aboutTitle")
2274
+ }, t("followUs")), (0, react.createElement)("a", {
1886
2275
  className: Header_module_css_default.githubLink,
1887
2276
  href: GITHUB_URL,
1888
2277
  target: "_blank",
@@ -1911,6 +2300,10 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
1911
2300
  //#endregion
1912
2301
  //#region src/client/components/CategoryTabs.tsx
1913
2302
  /**
2303
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
2304
+ * Website: https://dsh-plugin.org
2305
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
2306
+ *
1914
2307
  * Category tabs: an "all" chip followed by one chip per catalog category.
1915
2308
  * The "all" chip carries the total plugin count; the per-category chips
1916
2309
  * carry no counts so their widths stay uniform.
@@ -1938,21 +2331,25 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
1938
2331
  document.head.appendChild(tag);
1939
2332
  }
1940
2333
  var Dropdown_module_css_default = {
1941
- "dropdownPanel": "B_Gxsq_dropdownPanel",
1942
- "dropdownArrowOpen": "B_Gxsq_dropdownArrowOpen",
1943
- "dropdownItemActive": "B_Gxsq_dropdownItemActive",
1944
- "dropdownCountActive": "B_Gxsq_dropdownCountActive",
1945
2334
  "dropdownLabel": "B_Gxsq_dropdownLabel",
2335
+ "dropdownItemActive": "B_Gxsq_dropdownItemActive",
1946
2336
  "dropdown": "B_Gxsq_dropdown",
1947
- "dropdownArrow": "B_Gxsq_dropdownArrow",
1948
2337
  "dropdownBtn": "B_Gxsq_dropdownBtn",
1949
2338
  "dropdownItem": "B_Gxsq_dropdownItem",
1950
- "dropdownItemLabel": "B_Gxsq_dropdownItemLabel",
1951
- "dropdownCount": "B_Gxsq_dropdownCount"
2339
+ "dropdownPanel": "B_Gxsq_dropdownPanel",
2340
+ "dropdownCount": "B_Gxsq_dropdownCount",
2341
+ "dropdownCountActive": "B_Gxsq_dropdownCountActive",
2342
+ "dropdownArrow": "B_Gxsq_dropdownArrow",
2343
+ "dropdownArrowOpen": "B_Gxsq_dropdownArrowOpen",
2344
+ "dropdownItemLabel": "B_Gxsq_dropdownItemLabel"
1952
2345
  };
1953
2346
  //#endregion
1954
2347
  //#region src/client/components/Dropdown.tsx
1955
2348
  /**
2349
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
2350
+ * Website: https://dsh-plugin.org
2351
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
2352
+ *
1956
2353
  * Custom dropdown (native <select> replacement).
1957
2354
  *
1958
2355
  * macOS/浏览器原生下拉无法跟随宿主主题,这里自绘:触发器按钮 + 绝对定位面板,
@@ -2005,6 +2402,10 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
2005
2402
  //#endregion
2006
2403
  //#region src/client/components/CatalogControls.tsx
2007
2404
  /**
2405
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
2406
+ * Website: https://dsh-plugin.org
2407
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
2408
+ *
2008
2409
  * Toolbar above the list: search input + sort dropdown + installed / not
2009
2410
  * installed filter buttons + the notifications entry. All buttons share the
2010
2411
  * same size (24px high, like the notification entry button).
@@ -2063,37 +2464,37 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
2063
2464
  document.head.appendChild(tag);
2064
2465
  }
2065
2466
  var List_module_css_default = {
2066
- "topic": "_3XaZHa_topic",
2067
- "verified": "_3XaZHa_verified",
2068
- "uninstallBtn": "_3XaZHa_uninstallBtn",
2069
- "actions": "_3XaZHa_actions",
2070
- "stateTitle": "_3XaZHa_stateTitle",
2071
- "cardMain": "_3XaZHa_cardMain",
2072
- "installBtn": "_3XaZHa_installBtn",
2073
- "cardTitle": "_3XaZHa_cardTitle",
2467
+ "body": "_3XaZHa_body",
2074
2468
  "star": "_3XaZHa_star",
2075
- "card": "_3XaZHa_card",
2076
- "categoryBadge": "_3XaZHa_categoryBadge",
2077
- "fork": "_3XaZHa_fork",
2078
- "stateDesc": "_3XaZHa_stateDesc",
2079
- "retryBtn": "_3XaZHa_retryBtn",
2469
+ "footer": "_3XaZHa_footer",
2470
+ "footLink": "_3XaZHa_footLink",
2471
+ "installBtnUpdate": "_3XaZHa_installBtnUpdate",
2080
2472
  "updateBadge": "_3XaZHa_updateBadge",
2081
- "detailBtn": "_3XaZHa_detailBtn",
2473
+ "stateTitle": "_3XaZHa_stateTitle",
2474
+ "fork": "_3XaZHa_fork",
2082
2475
  "versionBadge": "_3XaZHa_versionBadge",
2476
+ "topic": "_3XaZHa_topic",
2083
2477
  "state": "_3XaZHa_state",
2084
2478
  "date": "_3XaZHa_date",
2479
+ "cardSide": "_3XaZHa_cardSide",
2480
+ "verified": "_3XaZHa_verified",
2085
2481
  "stats": "_3XaZHa_stats",
2086
- "body": "_3XaZHa_body",
2482
+ "installBtn": "_3XaZHa_installBtn",
2483
+ "topics": "_3XaZHa_topics",
2484
+ "stateDesc": "_3XaZHa_stateDesc",
2485
+ "categoryBadge": "_3XaZHa_categoryBadge",
2486
+ "uninstallBtn": "_3XaZHa_uninstallBtn",
2087
2487
  "cardHead": "_3XaZHa_cardHead",
2488
+ "detailBtn": "_3XaZHa_detailBtn",
2088
2489
  "list": "_3XaZHa_list",
2089
2490
  "installBtnCopied": "_3XaZHa_installBtnCopied",
2090
- "cardSide": "_3XaZHa_cardSide",
2091
- "footLink": "_3XaZHa_footLink",
2092
- "topics": "_3XaZHa_topics",
2491
+ "actions": "_3XaZHa_actions",
2492
+ "cardTitle": "_3XaZHa_cardTitle",
2093
2493
  "installBtnInstalled": "_3XaZHa_installBtnInstalled",
2494
+ "cardMain": "_3XaZHa_cardMain",
2094
2495
  "desc": "_3XaZHa_desc",
2095
- "installBtnUpdate": "_3XaZHa_installBtnUpdate",
2096
- "footer": "_3XaZHa_footer"
2496
+ "retryBtn": "_3XaZHa_retryBtn",
2497
+ "card": "_3XaZHa_card"
2097
2498
  };
2098
2499
  //#endregion
2099
2500
  //#region src/client/lib/format.ts
@@ -2116,6 +2517,10 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
2116
2517
  //#endregion
2117
2518
  //#region src/client/components/PluginCard.tsx
2118
2519
  /**
2520
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
2521
+ * Website: https://dsh-plugin.org
2522
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
2523
+ *
2119
2524
  * One plugin card in the catalog list: name/category/verified badge,
2120
2525
  * description + topics, star/fork/date stats, and the detail/install/
2121
2526
  * uninstall actions.
@@ -2163,6 +2568,10 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
2163
2568
  //#endregion
2164
2569
  //#region src/client/components/CatalogList.tsx
2165
2570
  /**
2571
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
2572
+ * Website: https://dsh-plugin.org
2573
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
2574
+ *
2166
2575
  * Catalog list: the scrollable container with the loading / failed / empty
2167
2576
  * states, the plugin cards, and the count footer. Resets scroll position when
2168
2577
  * the category or install-status filter changes so the replaced content is
@@ -2202,10 +2611,14 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
2202
2611
  //#endregion
2203
2612
  //#region src/client/components/PluginHubSection.tsx
2204
2613
  /**
2614
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
2615
+ * Website: https://dsh-plugin.org
2616
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
2617
+ *
2205
2618
  * Plugin Hub section: wires the catalog data pipeline, the background task
2206
2619
  * queue and the feedback state together. Data/queue logic lives in hooks/,
2207
2620
  * rendering is delegated to the small presentational components in this
2208
- * folder; only the dialogs/toast and the section-level copy actions remain.
2621
+ * folder; it hosts the dialogs/toast and the section-level copy actions.
2209
2622
  */
2210
2623
  function PluginHubSection({ t: _hostT, locale }) {
2211
2624
  /** 界面语言:默认跟随宿主(系统)语言;右上角按钮可手动切换,切换后以手动选择为准 */
@@ -2227,11 +2640,19 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
2227
2640
  const [confirmPlugin, setConfirmPlugin] = (0, react.useState)(null);
2228
2641
  /** 弹窗动作是否为「更新」:已安装插件点「更新」→ 走同一条 add 命令原位覆盖重装,文案区分安装/更新 */
2229
2642
  const [confirmIsUpdate, setConfirmIsUpdate] = (0, react.useState)(false);
2643
+ /** Hub 自我更新说明弹窗:点「可更新」徽标先展示版本 + Markdown 变更记录,确认后再进安装弹窗 */
2644
+ const [showHubUpdate, setShowHubUpdate] = (0, react.useState)(false);
2645
+ /** 「关注我们」弹窗:GitHub 图标后按钮点击打开,展示平台介绍 + 用户反馈群二维码(Worker /about 推送) */
2646
+ const [showAbout, setShowAbout] = (0, react.useState)(false);
2230
2647
  /** 卸载确认弹窗:记录待卸载的插件 */
2231
2648
  const [uninstallPlugin, setUninstallPlugin] = (0, react.useState)(null);
2232
2649
  /** 安装/卸载完成后的结果视图:停留弹窗内,点「完成」关闭 */
2233
2650
  const [installDone, setInstallDone] = (0, react.useState)(false);
2234
2651
  const [uninstallDone, setUninstallDone] = (0, react.useState)(false);
2652
+ /** 结果视图是否给「立即重启」:服务端任务终态带出(卸载时 loader 已即时移除 → false 只给「完成」;
2653
+ * true 时通知中心待重启条目同步常驻,直到用户点「立即重启」真正重启后才消失) */
2654
+ const [installNeedsRestart, setInstallNeedsRestart] = (0, react.useState)(true);
2655
+ const [uninstallNeedsRestart, setUninstallNeedsRestart] = (0, react.useState)(true);
2235
2656
  /** 结果视图「立即重启」:请求宿主重启后进入等待,服务回来后整页刷新 */
2236
2657
  const [restarting, setRestarting] = (0, react.useState)(false);
2237
2658
  /** 操作失败完整信息 + 所属插件仓库 + 失败类型(决定弹窗标题「安装失败/卸载失败」)+ 实际执行的安装命令 + 尝试过的安装方式(issue 预填用) */
@@ -2253,7 +2674,8 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
2253
2674
  const queue = useTaskQueue({
2254
2675
  t,
2255
2676
  refreshInstalled: catalog.refreshInstalled,
2256
- onInstallDone: (viaModal, repo) => {
2677
+ onInstallDone: (viaModal, repo, needsRestart) => {
2678
+ setInstallNeedsRestart(needsRestart);
2257
2679
  if (viaModal) setInstallDone(true);
2258
2680
  else setToast({
2259
2681
  id: Date.now(),
@@ -2264,7 +2686,8 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
2264
2686
  repo: repo ?? ""
2265
2687
  }));
2266
2688
  },
2267
- onUninstallDone: (viaModal, repo) => {
2689
+ onUninstallDone: (viaModal, repo, needsRestart) => {
2690
+ setUninstallNeedsRestart(needsRestart);
2268
2691
  if (viaModal) setUninstallDone(true);
2269
2692
  else setToast({
2270
2693
  id: Date.now(),
@@ -2303,12 +2726,14 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
2303
2726
  }
2304
2727
  });
2305
2728
  (0, react.useEffect)(() => {
2306
- if (!confirmPlugin && !uninstallPlugin && !showNotifications) return;
2729
+ if (!confirmPlugin && !uninstallPlugin && !showNotifications && !showHubUpdate && !showAbout) return;
2307
2730
  const onKey = (e) => {
2308
2731
  if (e.key === "Escape") {
2309
2732
  setConfirmPlugin(null);
2310
2733
  setUninstallPlugin(null);
2311
2734
  setUninstallDone(false);
2735
+ setShowHubUpdate(false);
2736
+ setShowAbout(false);
2312
2737
  setShowNotifications(false);
2313
2738
  }
2314
2739
  };
@@ -2317,7 +2742,9 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
2317
2742
  }, [
2318
2743
  confirmPlugin,
2319
2744
  uninstallPlugin,
2320
- showNotifications
2745
+ showNotifications,
2746
+ showHubUpdate,
2747
+ showAbout
2321
2748
  ]);
2322
2749
  (0, react.useEffect)(() => {
2323
2750
  if (!toast) return;
@@ -2402,12 +2829,16 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
2402
2829
  const statsTotal = catalog.stats?.total ?? total;
2403
2830
  const statsVerified = catalog.stats?.verified ?? 0;
2404
2831
  const count = catalog.visible.length;
2832
+ const hubEntry = catalog.plugins?.find((p) => p.source?.repo === "dshplugin/dsh-plugin-hub") ?? null;
2405
2833
  return (0, react.createElement)("div", { className: Header_module_css_default.root }, (0, react.createElement)(CatalogHeader, {
2406
2834
  t,
2407
2835
  langPath,
2408
2836
  statsTotal,
2409
2837
  statsVerified,
2410
- onToggleLang: () => setManualLang(lang === "en" ? "zh" : "en")
2838
+ onToggleLang: () => setManualLang(lang === "en" ? "zh" : "en"),
2839
+ hubUpdate: catalog.hubHasUpdate,
2840
+ onVersionClick: () => setShowHubUpdate(true),
2841
+ onAboutClick: () => setShowAbout(true)
2411
2842
  }), (0, react.createElement)(CategoryTabs, {
2412
2843
  category: catalog.category,
2413
2844
  setCategory: catalog.setCategory,
@@ -2453,6 +2884,26 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
2453
2884
  queue.clearModalTask();
2454
2885
  setUninstallPlugin(p);
2455
2886
  }
2887
+ }), showHubUpdate && (0, react.createElement)(HubUpdateModal, {
2888
+ info: catalog.hubUpdateInfo ?? { version: "1.1.2" },
2889
+ lang,
2890
+ t,
2891
+ hasUpdate: catalog.hubHasUpdate,
2892
+ onClose: () => setShowHubUpdate(false),
2893
+ onProceed: () => {
2894
+ setShowHubUpdate(false);
2895
+ if (hubEntry) {
2896
+ setInstallDone(false);
2897
+ setConfirmIsUpdate(true);
2898
+ queue.clearModalTask();
2899
+ setConfirmPlugin(hubEntry);
2900
+ }
2901
+ }
2902
+ }), showAbout && (0, react.createElement)(AboutModal, {
2903
+ info: catalog.hubAboutInfo ?? null,
2904
+ lang,
2905
+ t,
2906
+ onClose: () => setShowAbout(false)
2456
2907
  }), confirmPlugin && (0, react.createElement)(InstallModal, {
2457
2908
  plugin: confirmPlugin,
2458
2909
  done: installDone,
@@ -2461,7 +2912,9 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
2461
2912
  langPath,
2462
2913
  restarting,
2463
2914
  update: confirmIsUpdate,
2915
+ cliOnly: confirmPlugin.install?.webInstallable === false,
2464
2916
  submitting: queue.submitting,
2917
+ needsRestart: installNeedsRestart,
2465
2918
  onClose: () => setConfirmPlugin(null),
2466
2919
  onCopy: () => copyCommand(confirmPlugin),
2467
2920
  onInstall: () => queue.installNow(confirmPlugin, confirmIsUpdate ? { update: true } : void 0),
@@ -2476,6 +2929,7 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
2476
2929
  langPath,
2477
2930
  restarting,
2478
2931
  submitting: queue.submitting,
2932
+ needsRestart: uninstallNeedsRestart,
2479
2933
  onClose: () => {
2480
2934
  setUninstallDone(false);
2481
2935
  setUninstallPlugin(null);
@@ -2542,6 +2996,10 @@ window.__ModuleLoader__.load({ id: "dsh-plugin", factory: (require) => {
2542
2996
  //#endregion
2543
2997
  //#region src/client/index.tsx
2544
2998
  /**
2999
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
3000
+ * Website: https://dsh-plugin.org
3001
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
3002
+ *
2545
3003
  * dsh-plugin client entry: wires the Plugin Hub into the host settings via
2546
3004
  * the module-loader bundle (see tsdown.config.ts). All UI logic lives in
2547
3005
  * PluginHubSection; this file only performs the cordis apply wiring.