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
@@ -0,0 +1,120 @@
1
+ /**
2
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
3
+ * Website: https://dsh-plugin.org
4
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
+ *
6
+ * 轻量 Markdown 渲染器:仅用于 Hub 自我更新的变更记录(Worker 下发的 notes)。
7
+ *
8
+ * 内容由插件作者自己编写、属可信内容,但渲染端仍先整体 HTML 转义,再按
9
+ * Markdown 语法输出结构,杜绝任何未转义的原始 HTML 注入;链接仅放行 http(s)。
10
+ *
11
+ * 支持:标题(#~####)、有序/无序列表、代码块(```)、行内代码、加粗、斜体、
12
+ * 链接、图片(![alt](url),如反馈群二维码)、引用(>)、段落;其余语法一律按
13
+ * 纯文本处理,不引入外部依赖。图片仅放行 http(s),限定最大宽度不撑破弹窗。
14
+ */
15
+
16
+ /** HTML 转义:所有文本先进这里,再进入结构变换,保证输出里没有未转义的用户输入。 */
17
+ function escapeHtml(s: string): string {
18
+ return s
19
+ .replace(/&/g, '&')
20
+ .replace(/</g, '&lt;')
21
+ .replace(/>/g, '&gt;')
22
+ .replace(/"/g, '&quot;')
23
+ .replace(/'/g, '&#39;')
24
+ }
25
+
26
+ /** 行内变换:链接 / 加粗 / 斜体 / 行内代码(代码先占位保护,最后还原)。 */
27
+ function inline(s: string): string {
28
+ let out = escapeHtml(s)
29
+ // 行内代码先占位:避免其中的 * [ ] 被后续链接/加粗/斜体规则破坏
30
+ const codeSpans: string[] = []
31
+ out = out.replace(/`([^`]+)`/g, (_m, code) => {
32
+ codeSpans.push(String(code))
33
+ return `\u0000${codeSpans.length - 1}\u0000`
34
+ })
35
+ // 图片 ![alt](url):仅放行 http(s),用于展示反馈群二维码等;limited by CSS
36
+ out = out.replace(/!\[([^\]]*)\]\(([^)\s]+)\)/g, (m, alt, url) =>
37
+ /^https?:\/\//i.test(String(url)) && !/[\u0000]/.test(String(url))
38
+ ? `<img src="${String(url)}" alt="${String(alt)}" loading="lazy">`
39
+ : m,
40
+ )
41
+ // 链接 [text](url):仅放行 http(s),新窗口打开
42
+ out = out.replace(/\[([^\]]+)\]\(([^)\s]+)\)/g, (m, text, url) =>
43
+ /^https?:\/\//i.test(String(url)) && !/[\u0000]/.test(String(url))
44
+ ? `<a href="${String(url)}" target="_blank" rel="noopener noreferrer">${String(text)}</a>`
45
+ : m,
46
+ )
47
+ out = out.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')
48
+ out = out.replace(/(^|[^*])\*([^*]+)\*/g, '$1<em>$2</em>')
49
+ return out.replace(/\u0000(\d+)\u0000/g, (_m, idx) => `<code>${codeSpans[Number(idx)]}</code>`)
50
+ }
51
+
52
+ /** 把变更记录 Markdown 渲染为安全的 HTML 字符串(由弹窗用 dangerouslySetInnerHTML 注入)。 */
53
+ export function renderMarkdown(md: string): string {
54
+ const lines = md.replace(/\r\n?/g, '\n').split('\n')
55
+ const blocks: string[] = []
56
+ let i = 0
57
+ while (i < lines.length) {
58
+ const line = lines[i]
59
+ // 围栏代码块
60
+ if (/^\s*```/.test(line)) {
61
+ const buf: string[] = []
62
+ i++
63
+ while (i < lines.length && !/^\s*```/.test(lines[i])) {
64
+ buf.push(lines[i])
65
+ i++
66
+ }
67
+ i++ // 跳过闭合围栏
68
+ blocks.push(`<pre><code>${escapeHtml(buf.join('\n'))}</code></pre>`)
69
+ continue
70
+ }
71
+ // 空行:段落分隔符
72
+ if (line.trim() === '') {
73
+ i++
74
+ continue
75
+ }
76
+ // 标题:# 到 #### 映射为 h2~h5(弹窗标题用 modalTitle,不与其冲突)
77
+ const heading = line.match(/^(#{1,4})\s+(.*)/)
78
+ if (heading) {
79
+ const level = heading[1].length + 1
80
+ blocks.push(`<h${level}>${inline(heading[2])}</h${level}>`)
81
+ i++
82
+ continue
83
+ }
84
+ // 有序 / 无序列表(连续收集为一个列表块)
85
+ if (/^\s*[-*+]\s+/.test(line) || /^\s*\d+[.)]\s+/.test(line)) {
86
+ const ordered = /^\s*\d+[.)]\s+/.test(line)
87
+ const items: string[] = []
88
+ while (i < lines.length && (/^\s*[-*+]\s+/.test(lines[i]) || /^\s*\d+[.)]\s+/.test(lines[i]))) {
89
+ items.push(`<li>${inline(lines[i].replace(/^\s*[-*+]|\s*\d+[.)]\s*/, '').trim())}</li>`)
90
+ i++
91
+ }
92
+ blocks.push(`<${ordered ? 'ol' : 'ul'}>${items.join('')}</${ordered ? 'ol' : 'ul'}>`)
93
+ continue
94
+ }
95
+ // 引用
96
+ if (/^\s*>\s?/.test(line)) {
97
+ const buf: string[] = []
98
+ while (i < lines.length && /^\s*>\s?/.test(lines[i])) {
99
+ buf.push(inline(lines[i].replace(/^\s*>\s?/, '')))
100
+ i++
101
+ }
102
+ blocks.push(`<blockquote>${buf.join('<br>')}</blockquote>`)
103
+ continue
104
+ }
105
+ // 普通段落:收集连续非空、非特殊开头的行
106
+ const buf: string[] = []
107
+ while (
108
+ i < lines.length
109
+ && lines[i].trim() !== ''
110
+ && !/^\s*```/.test(lines[i])
111
+ && !/^#{1,4}\s+/.test(lines[i])
112
+ && !/^\s*([-*+]|\d+[.)])\s+/.test(lines[i])
113
+ ) {
114
+ buf.push(inline(lines[i]))
115
+ i++
116
+ }
117
+ blocks.push(`<p>${buf.join('<br>')}</p>`)
118
+ }
119
+ return blocks.join('')
120
+ }
@@ -1,4 +1,10 @@
1
- /** zh/en dictionaries for the DSH-Plugin Hub settings section. */
1
+ /**
2
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
3
+ * Website: https://dsh-plugin.org
4
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
+ *
6
+ * zh/en dictionaries for the DSH-Plugin Hub settings section.
7
+ */
2
8
  export const zh = {
3
9
  nav: '插件中心',
4
10
  title: 'DSH-Plugin Hub',
@@ -21,6 +27,9 @@ export const zh = {
21
27
  failedDesc: '请稍后重试,或点击右上角按钮在浏览器中打开插件中心。',
22
28
  retry: '重试',
23
29
  install: '安装',
30
+ // AI 识别可能需要命令行辅助的插件(webInstallable=false):仍可尝试一键安装,失败时复制命令去 dsh 终端
31
+ cliOnlyHint: '此插件可能需命令行辅助,失败时复制上方命令到 dsh 终端',
32
+ failedCopyHint: '安装失败。请复制上方安装命令到 dsh 终端手动执行(原生依赖构建脚本可能需要人工放行,如 pnpm approve-builds)',
24
33
  toastCopied: '安装命令已复制,去 dsh 终端粘贴即可安装',
25
34
  installed: '已安装',
26
35
  notInstalled: '未安装',
@@ -65,6 +74,7 @@ export const zh = {
65
74
  restartLater: '稍后重启',
66
75
  restarting: '正在重启…',
67
76
  restartHint: '部分插件(如插件市场、顶层 bundle)需重启后才会生效。',
77
+ done: '完成',
68
78
  // 待重启:装完没重启的插件常驻提醒,直到宿主真正重启
69
79
  restartPendingHint: '安装成功,重启后生效',
70
80
  restartPendingHintUninstall: '卸载成功,重启后移除',
@@ -120,6 +130,22 @@ export const zh = {
120
130
  update: '可更新',
121
131
  updateAvailable: '有更新',
122
132
  updateAvailableHint: '检测到更新:有版本号的仓库比对版本号,无版本号的仓库比对仓库最近更新时间',
133
+ hubUpdateHint: '检测到 DSH-Plugin Hub 新版本,点击查看更新内容并更新',
134
+ hubUpdateTitle: 'DSH-Plugin Hub 有新版本',
135
+ hubUpdateDesc: 'v{version} 已发布,点击下方按钮即可更新,完成后需重启宿主生效。',
136
+ hubUpdatePublished: '发布于',
137
+ hubUpdateNotes: '更新记录',
138
+ hubUpdateLater: '稍后再说',
139
+ versionHint: '查看版本信息与更新记录',
140
+ hubCurrentTitle: '当前版本',
141
+ hubCurrentDesc: '你正在使用 v{version},已是最新版本。',
142
+ hubUpToDate: '已是最新版本',
143
+ // 头部「关注我们」弹窗:平台介绍 + 用户反馈群二维码(内容由 Worker /about Markdown 推送,非写死)
144
+ followUs: '关注我们',
145
+ aboutTitle: '关注我们',
146
+ aboutDesc: '了解 DSH-Plugin Hub 是做什么的,加入用户反馈群随时交流。',
147
+ aboutEmpty: '内容暂未发布,敬请期待。',
148
+ aboutUpdated: '更新于',
123
149
  empty: '该分类暂无插件',
124
150
  dataFrom: '数据源 dsh-plugin.org · 每日人工更新',
125
151
  }
@@ -146,6 +172,10 @@ export const en = {
146
172
  failedDesc: 'Please retry, or open the hub in your browser with the button above.',
147
173
  retry: 'Retry',
148
174
  install: 'Install',
175
+ // AI-flagged plugins that may need command-line help (webInstallable=false): one-click install still allowed,
176
+ // on failure the user is guided to copy the command into the dsh terminal
177
+ cliOnlyHint: 'This plugin may need command-line help; if it fails, copy the command above and run it in your dsh terminal',
178
+ 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)',
149
179
  toastCopied: 'Install command copied — paste it in your dsh terminal',
150
180
  installed: 'Installed',
151
181
  notInstalled: 'Not installed',
@@ -190,6 +220,7 @@ export const en = {
190
220
  restartLater: 'Later',
191
221
  restarting: 'Restarting…',
192
222
  restartHint: 'Some plugins (such as plugin markets / top-level bundles) only take effect after a restart.',
223
+ done: 'Done',
193
224
  restartPendingHint: 'Installed — takes effect after restart',
194
225
  restartPendingHintUninstall: 'Removed — cleaned up after restart',
195
226
  sectionPendingRestart: 'Pending restart',
@@ -243,6 +274,22 @@ export const en = {
243
274
  update: 'Update',
244
275
  updateAvailable: 'Update available',
245
276
  updateAvailableHint: 'An update was detected: repos with a release are compared by tag, repos without one by last push time',
277
+ hubUpdateHint: 'A new DSH-Plugin Hub version is available — click to view the changelog and update',
278
+ hubUpdateTitle: 'A new DSH-Plugin Hub version is available',
279
+ hubUpdateDesc: 'v{version} has been released — click below to update, then restart the host to apply it.',
280
+ hubUpdatePublished: 'Published',
281
+ hubUpdateNotes: "What's new",
282
+ hubUpdateLater: 'Later',
283
+ versionHint: 'View version info and changelog',
284
+ hubCurrentTitle: 'Current version',
285
+ hubCurrentDesc: 'You are on v{version} — this is the latest version.',
286
+ hubUpToDate: 'Up to date',
287
+ // Header "Follow us" modal: platform intro + user feedback group QR (content pushed via Worker /about Markdown)
288
+ followUs: 'Follow us',
289
+ aboutTitle: 'Follow us',
290
+ aboutDesc: 'Learn what DSH-Plugin Hub does and join our user feedback group.',
291
+ aboutEmpty: 'Content not published yet — stay tuned.',
292
+ aboutUpdated: 'Updated',
246
293
  empty: 'No plugins in this category yet',
247
294
  dataFrom: 'Data from dsh-plugin.org · curated daily',
248
295
  }
@@ -1,4 +1,8 @@
1
- /* 自绘下拉(替换原生 <select>)。颜色跟随宿主主题:经 --hub-* 设计令牌解析,
1
+ /* DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
2
+ * Website: https://dsh-plugin.org
3
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
4
+ *
5
+ * 自绘下拉(替换原生 <select>)。颜色跟随宿主主题:经 --hub-* 设计令牌解析,
2
6
  * 与 Header 的控件(排序/分段)保持一致的无边框 + 圆角语言。 */
3
7
  .dropdown {
4
8
  position: relative;
@@ -1,4 +1,8 @@
1
- /* Plugin Hub section header: brand row, tagline, ad banner, search input,
1
+ /* DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
2
+ * Website: https://dsh-plugin.org
3
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
4
+ *
5
+ * Plugin Hub section header: brand row, tagline, ad banner, search input,
2
6
  * filter toolbar (installed / not installed, failures entry) and category
3
7
  * tabs. Colors resolve through the --hub-* design tokens. */
4
8
 
@@ -20,12 +24,13 @@
20
24
  padding: 2px 2px 0;
21
25
  }
22
26
 
23
- /* 头部右上角控件组:语言切换 + GitHub 源码图标,右侧一列对齐 */
27
+ /* 头部右上角控件组:语言切换 + GitHub 源码图标,右侧一列对齐,auto 把它顶到标题行最右 */
24
28
  .headerRight {
25
29
  flex-shrink: 0;
26
30
  display: inline-flex;
27
31
  align-items: center;
28
32
  gap: 2px;
33
+ margin-left: auto;
29
34
  }
30
35
 
31
36
  /* 语言切换按钮:与 GitHub 图标同风格(次级灰低调、悬浮品牌紫),文字显示目标语言 */
@@ -75,11 +80,36 @@
75
80
  display: block;
76
81
  }
77
82
 
78
- /* 头部第一行:logo + 标题(官网链接)与 GitHub 图标同行,space-between 把 GitHub 顶到标题行末尾 */
83
+ /* 「关注我们」入口:GitHub 图标后,与语言切换按钮同风格 —— 普通文字、无边框,
84
+ 悬浮才转品牌紫 + 浅紫底,提示可点(打开平台介绍 + 用户反馈群二维码弹窗) */
85
+ .aboutBtn {
86
+ flex-shrink: 0;
87
+ display: inline-flex;
88
+ align-items: center;
89
+ justify-content: center;
90
+ height: 24px;
91
+ padding: 0 8px;
92
+ font-size: 12px;
93
+ font-weight: 600;
94
+ line-height: 1;
95
+ color: var(--hub-text-tertiary);
96
+ background: transparent;
97
+ border: none;
98
+ border-radius: 6px;
99
+ cursor: pointer;
100
+ user-select: none;
101
+ transition: color 0.15s ease, background-color 0.15s ease;
102
+ }
103
+
104
+ .aboutBtn:hover {
105
+ color: var(--hub-purple-1);
106
+ background-color: var(--hub-purple-tint);
107
+ }
108
+
109
+ /* 头部第一行:logo + 标题(官网链接)紧接着版本号入口,右侧控件组靠 auto 顶到最右 */
79
110
  .headerTitleRow {
80
111
  display: flex;
81
112
  align-items: center;
82
- justify-content: space-between;
83
113
  gap: 8px;
84
114
  min-width: 0;
85
115
  }
@@ -117,6 +147,60 @@
117
147
  color: var(--hub-text-secondary);
118
148
  }
119
149
 
150
+ /* 版本号入口按钮:常驻可点(打开版本信息/更新记录弹窗),无边框、字号与标题协调,
151
+ hover 加品牌色下划线提示可点 —— 无论有无更新都可查看更新内容;
152
+ 有更新时版本号后紧跟红色「可更新」徽标(同一按钮内的 span,不分开成两个元素) */
153
+ .versionBtn {
154
+ flex-shrink: 0;
155
+ display: inline-flex;
156
+ align-items: center;
157
+ gap: 5px;
158
+ height: 20px;
159
+ padding: 0 6px;
160
+ border: none;
161
+ border-radius: 5px;
162
+ font-size: 12px;
163
+ font-weight: 600;
164
+ line-height: 1;
165
+ color: var(--hub-text-secondary);
166
+ background: transparent;
167
+ cursor: pointer;
168
+ user-select: none;
169
+ transition: color 120ms ease;
170
+ }
171
+
172
+ /* 悬浮只提示整组可点,但版本号本身颜色保持不变;交互反馈交给「可更新」徽标单独变深红 */
173
+ .versionBtn:hover {
174
+ color: var(--hub-text-secondary);
175
+ }
176
+
177
+ /* 版本号旁的「可更新」徽标:默认红底白字(醒目),悬浮轻微加深 + 投影提升一点反馈感,
178
+ 紧跟版本号,提示可查看并直接升级。作为同一按钮内文字,点击由外层按钮统一处理,本身无需独立事件。 */
179
+ .hubUpdateBadge {
180
+ flex-shrink: 0;
181
+ display: inline-flex;
182
+ align-items: center;
183
+ height: 18px;
184
+ padding: 0 7px;
185
+ border-radius: 999px;
186
+ font-size: 11px;
187
+ font-weight: 700;
188
+ line-height: 1;
189
+ letter-spacing: 0.02em;
190
+ color: #ffffff;
191
+ background: var(--hub-danger);
192
+ box-shadow: 0 1px 4px rgba(209, 36, 47, 0.4);
193
+ user-select: none;
194
+ transition: background 120ms ease, box-shadow 120ms ease, transform 120ms ease;
195
+ }
196
+
197
+ /* 悬浮:轻微变深红 + 一点投影提升,只动徽标本身,版本号不动 */
198
+ .hubUpdateBadge:hover {
199
+ background: var(--hub-danger-hover);
200
+ box-shadow: 0 2px 8px rgba(209, 36, 47, 0.5);
201
+ transform: translateY(-1px);
202
+ }
203
+
120
204
  /* 品牌 logo 图标:与标题文字垂直居中对齐 */
121
205
  .logoIcon {
122
206
  flex-shrink: 0;
@@ -1,4 +1,8 @@
1
- /* Plugin Hub catalog list: the scrollable body, plugin cards (name, badges,
1
+ /* DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
2
+ * Website: https://dsh-plugin.org
3
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
4
+ *
5
+ * Plugin Hub catalog list: the scrollable body, plugin cards (name, badges,
2
6
  * description, topics, stats, actions), loading / empty states and the
3
7
  * browse-all footer. Colors resolve through the --hub-* design tokens. */
4
8
 
@@ -1,4 +1,8 @@
1
- /* Plugin Hub dialogs and overlays: install / uninstall modals, error dialog,
1
+ /* DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
2
+ * Website: https://dsh-plugin.org
3
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
4
+ *
5
+ * Plugin Hub dialogs and overlays: install / uninstall modals, error dialog,
2
6
  * failure-record dialog, task-queue dialog, progress strip, inline progress
3
7
  * and the global toast. Colors resolve through the --hub-* design tokens. */
4
8
 
@@ -113,6 +117,28 @@
113
117
  padding: 6px 10px;
114
118
  }
115
119
 
120
+ /* 可能需要命令行辅助的插件提示(webInstallable=false):中性说明,仍允许尝试一键安装 */
121
+ .cliOnlyHint {
122
+ font-size: 12px;
123
+ line-height: 18px;
124
+ color: var(--hub-text-secondary);
125
+ background: var(--hub-bg-hover);
126
+ border-radius: 6px;
127
+ padding: 6px 10px;
128
+ }
129
+
130
+ /* 安装失败引导:红色醒目,提示复制命令到 dsh 终端手动执行(原生依赖构建脚本需人工放行) */
131
+ .failedCopyHint {
132
+ font-size: 12px;
133
+ line-height: 18px;
134
+ font-weight: 500;
135
+ color: var(--hub-danger-text);
136
+ background: var(--hub-danger-tint);
137
+ border: 1px solid var(--hub-danger-border-soft);
138
+ border-radius: 6px;
139
+ padding: 6px 10px;
140
+ }
141
+
116
142
  .modalRow {
117
143
  display: flex;
118
144
  align-items: baseline;
@@ -916,6 +942,17 @@
916
942
  background: var(--hub-danger-tint);
917
943
  }
918
944
 
945
+ /* 成功通知卡片(时间戳已并入头部行):删除按钮不再用 auto margin 顶到最右,
946
+ 由时间戳的 margin-left:auto 把「时间 + 删除」整体推到行尾 —— 图标文字与
947
+ 时间垂直居中,卡片只有一行内容,整体居中不顶头 */
948
+ .noticeRowOk .noticeRemove {
949
+ margin-left: 0;
950
+ }
951
+
952
+ .noticeRowOk .noticeTime {
953
+ margin-left: auto;
954
+ }
955
+
919
956
  /* 记录内的复制按钮:纯灰文字、无边框,低调隐蔽,点击即可复制该条完整错误日志 */
920
957
  .failCopy {
921
958
  flex-shrink: 0;
@@ -1004,3 +1041,244 @@
1004
1041
  border: 1px solid var(--hub-warning-border);
1005
1042
  border-radius: 6px;
1006
1043
  }
1044
+
1045
+ /* —— 「关注我们」弹窗 —— */
1046
+
1047
+ /* 关注弹窗:比普通弹窗略宽,给 Markdown 介绍 + 反馈群二维码留出阅读空间;
1048
+ max-width 覆盖普通弹窗的 calc(100vw - 32px),窄屏时仍不贴边 */
1049
+ .aboutModal {
1050
+ width: 460px;
1051
+ max-width: calc(100vw - 48px);
1052
+ }
1053
+
1054
+ /* Markdown 内容区:固定最大高度内滚动,介绍文字 + 二维码不把弹窗撑爆;
1055
+ 样式沿用 hubUpdateNotes 的 12px 基线,仅二维码图片单独约束最大尺寸 */
1056
+ .aboutContent {
1057
+ flex: 1 1 auto;
1058
+ min-height: 0;
1059
+ max-height: 340px;
1060
+ overflow-y: auto;
1061
+ font-size: 12px;
1062
+ line-height: 18px;
1063
+ color: var(--hub-text-secondary);
1064
+ background: var(--hub-bg-2);
1065
+ border: 1px solid var(--hub-border-2);
1066
+ border-radius: 8px;
1067
+ padding: 10px 12px;
1068
+ }
1069
+
1070
+ .aboutContent > *:first-child {
1071
+ margin-top: 0;
1072
+ }
1073
+
1074
+ .aboutContent > *:last-child {
1075
+ margin-bottom: 0;
1076
+ }
1077
+
1078
+ .aboutContent h2,
1079
+ .aboutContent h3,
1080
+ .aboutContent h4,
1081
+ .aboutContent h5 {
1082
+ margin: 10px 0 4px;
1083
+ font-size: 13px;
1084
+ font-weight: 600;
1085
+ line-height: 20px;
1086
+ color: var(--hub-text-primary);
1087
+ }
1088
+
1089
+ .aboutContent h2 {
1090
+ font-size: 14px;
1091
+ }
1092
+
1093
+ .aboutContent p {
1094
+ margin: 4px 0;
1095
+ }
1096
+
1097
+ .aboutContent ul,
1098
+ .aboutContent ol {
1099
+ margin: 4px 0;
1100
+ padding-left: 18px;
1101
+ }
1102
+
1103
+ .aboutContent li {
1104
+ margin: 2px 0;
1105
+ }
1106
+
1107
+ .aboutContent code {
1108
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
1109
+ font-size: 11px;
1110
+ background: var(--hub-bg-hover);
1111
+ border-radius: 4px;
1112
+ padding: 1px 4px;
1113
+ color: var(--hub-text-primary);
1114
+ }
1115
+
1116
+ .aboutContent pre {
1117
+ margin: 6px 0;
1118
+ background: var(--hub-bg-1);
1119
+ border: 1px solid var(--hub-border-2);
1120
+ border-radius: 6px;
1121
+ padding: 8px 10px;
1122
+ overflow-x: auto;
1123
+ }
1124
+
1125
+ .aboutContent pre code {
1126
+ background: none;
1127
+ padding: 0;
1128
+ border-radius: 0;
1129
+ }
1130
+
1131
+ .aboutContent blockquote {
1132
+ margin: 6px 0;
1133
+ padding: 2px 10px;
1134
+ border-left: 3px solid var(--hub-brand);
1135
+ background: var(--hub-bg-hover);
1136
+ border-radius: 0 6px 6px 0;
1137
+ color: var(--hub-text-tertiary);
1138
+ }
1139
+
1140
+ .aboutContent a {
1141
+ color: var(--hub-brand);
1142
+ text-decoration: none;
1143
+ }
1144
+
1145
+ .aboutContent a:hover {
1146
+ color: var(--hub-brand-hover);
1147
+ text-decoration: underline;
1148
+ }
1149
+
1150
+ /* 内容里的图片(反馈群二维码):居中、最大高度受控,不撑破弹窗:
1151
+ 当图片自身更窄时收缩到实际宽度(不强行拉满铺平) */
1152
+ .aboutContent img {
1153
+ display: block;
1154
+ margin: 8px auto;
1155
+ max-width: 100%;
1156
+ max-height: 220px;
1157
+ border-radius: 8px;
1158
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
1159
+ }
1160
+
1161
+ /* 更新时间小字:弱化展示在内容区下方 */
1162
+ .aboutMeta {
1163
+ font-size: 11px;
1164
+ line-height: 16px;
1165
+ color: var(--hub-text-tertiary);
1166
+ }
1167
+
1168
+ /* —— Hub 自我更新说明弹窗 —— */
1169
+
1170
+ /* 更新说明弹窗:比普通弹窗略宽,给变更记录留出阅读空间 */
1171
+ .hubUpdateModal {
1172
+ width: 520px;
1173
+ max-width: calc(100vw - 48px);
1174
+ }
1175
+
1176
+ /* 版本 / 发布时间元信息行:小号标签并排,弱化但可读 */
1177
+ .hubUpdateMeta {
1178
+ display: flex;
1179
+ align-items: center;
1180
+ flex-wrap: wrap;
1181
+ gap: 4px 14px;
1182
+ font-size: 12px;
1183
+ line-height: 18px;
1184
+ }
1185
+
1186
+ .hubUpdateMetaItem {
1187
+ color: var(--hub-text-tertiary);
1188
+ }
1189
+
1190
+ /* Markdown 变更记录区:固定最大高度内滚动,长更新记录不把弹窗撑爆;
1191
+ 子元素(标题/列表/代码块/引用)继承 12px 基线,仅标题加大加粗 */
1192
+ .hubUpdateNotes {
1193
+ flex: 1 1 auto;
1194
+ min-height: 0;
1195
+ max-height: 300px;
1196
+ overflow-y: auto;
1197
+ font-size: 12px;
1198
+ line-height: 18px;
1199
+ color: var(--hub-text-secondary);
1200
+ background: var(--hub-bg-2);
1201
+ border: 1px solid var(--hub-border-2);
1202
+ border-radius: 8px;
1203
+ padding: 10px 12px;
1204
+ }
1205
+
1206
+ .hubUpdateNotes > *:first-child {
1207
+ margin-top: 0;
1208
+ }
1209
+
1210
+ .hubUpdateNotes > *:last-child {
1211
+ margin-bottom: 0;
1212
+ }
1213
+
1214
+ .hubUpdateNotes h2,
1215
+ .hubUpdateNotes h3,
1216
+ .hubUpdateNotes h4,
1217
+ .hubUpdateNotes h5 {
1218
+ margin: 10px 0 4px;
1219
+ font-size: 13px;
1220
+ font-weight: 600;
1221
+ line-height: 20px;
1222
+ color: var(--hub-text-primary);
1223
+ }
1224
+
1225
+ .hubUpdateNotes h2 {
1226
+ font-size: 14px;
1227
+ }
1228
+
1229
+ .hubUpdateNotes p {
1230
+ margin: 4px 0;
1231
+ }
1232
+
1233
+ .hubUpdateNotes ul,
1234
+ .hubUpdateNotes ol {
1235
+ margin: 4px 0;
1236
+ padding-left: 18px;
1237
+ }
1238
+
1239
+ .hubUpdateNotes li {
1240
+ margin: 2px 0;
1241
+ }
1242
+
1243
+ .hubUpdateNotes code {
1244
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
1245
+ font-size: 11px;
1246
+ background: var(--hub-bg-hover);
1247
+ border-radius: 4px;
1248
+ padding: 1px 4px;
1249
+ color: var(--hub-text-primary);
1250
+ }
1251
+
1252
+ .hubUpdateNotes pre {
1253
+ margin: 6px 0;
1254
+ background: var(--hub-bg-1);
1255
+ border: 1px solid var(--hub-border-2);
1256
+ border-radius: 6px;
1257
+ padding: 8px 10px;
1258
+ overflow-x: auto;
1259
+ }
1260
+
1261
+ .hubUpdateNotes pre code {
1262
+ background: none;
1263
+ padding: 0;
1264
+ border-radius: 0;
1265
+ }
1266
+
1267
+ .hubUpdateNotes blockquote {
1268
+ margin: 6px 0;
1269
+ padding: 2px 10px;
1270
+ border-left: 3px solid var(--hub-brand);
1271
+ background: var(--hub-bg-hover);
1272
+ border-radius: 0 6px 6px 0;
1273
+ color: var(--hub-text-tertiary);
1274
+ }
1275
+
1276
+ .hubUpdateNotes a {
1277
+ color: var(--hub-brand);
1278
+ text-decoration: none;
1279
+ }
1280
+
1281
+ .hubUpdateNotes a:hover {
1282
+ color: var(--hub-brand-hover);
1283
+ text-decoration: underline;
1284
+ }
@@ -1,4 +1,8 @@
1
- /* Plugin Hub design tokens.
1
+ /* DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
2
+ * Website: https://dsh-plugin.org
3
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
4
+ *
5
+ * Plugin Hub design tokens.
2
6
  *
3
7
  * Semantic aliases over the host's --dsw-alias-* theme tokens, each with the
4
8
  * original light-mode literal as fallback for hosts that do not define the
@@ -1,4 +1,8 @@
1
1
  /**
2
+ * DSH-Plugin Hub — the community plugin marketplace for DeepSeek Harness.
3
+ * Website: https://dsh-plugin.org
4
+ * GitHub: https://github.com/dshplugin/dsh-plugin-hub
5
+ *
2
6
  * Shared types for the dsh-plugin browser client. Kept dependency-free so
3
7
  * any client module can import them without pulling in components or styles.
4
8
  */
@@ -36,7 +40,7 @@ export interface SectionProps {
36
40
 
37
41
  /** Slim projection of the site's DirectoryPlugin entries. */
38
42
  export interface HubPlugin {
39
- /** 作者小写 key,与 slug 共同构成唯一身份(两级路径 /plugins/{ownerSlug}/{slug});旧数据可能缺省 */
43
+ /** 作者小写 key,与 slug 共同构成唯一身份(两级路径 /plugins/{ownerSlug}/{slug});可能缺省 */
40
44
  ownerSlug?: string
41
45
  slug: string
42
46
  displayName?: string
@@ -47,6 +51,8 @@ export interface HubPlugin {
47
51
  features?: string[]
48
52
  description?: string
49
53
  source?: { repo?: string; npmPackage?: string }
54
+ /** 能否网页一键安装:false = 仅支持命令行/git/本地构建安装(缺省视为 true 可安装) */
55
+ install?: { webInstallable?: boolean }
50
56
  compatibility?: { status?: string }
51
57
  dates?: { repoUpdatedAt?: string; addedAt?: string }
52
58
  stats?: { stargazers_count?: number; forks_count?: number }