dsh-plugin 0.1.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/README.en.md +49 -26
  2. package/README.md +49 -26
  3. package/client/client.js +2148 -229
  4. package/client/client.js.map +1 -1
  5. package/lib/http/routes.js +396 -0
  6. package/lib/index.js +17 -8
  7. package/lib/services/install-types.js +11 -0
  8. package/lib/services/install.js +4 -0
  9. package/lib/services/installed-versions.js +51 -0
  10. package/lib/services/loader.js +98 -0
  11. package/lib/services/pending-restart.js +34 -0
  12. package/lib/services/preflight.js +94 -0
  13. package/lib/services/profile.js +88 -0
  14. package/lib/services/progress.js +50 -0
  15. package/lib/services/task-queue.js +417 -0
  16. package/lib/types/http/routes.d.ts +23 -0
  17. package/lib/types/index.d.ts +27 -0
  18. package/lib/types/services/install-types.d.ts +63 -0
  19. package/lib/types/services/install.d.ts +18 -0
  20. package/lib/types/services/installed-versions.d.ts +14 -0
  21. package/lib/types/services/loader.d.ts +43 -0
  22. package/lib/types/services/pending-restart.d.ts +13 -0
  23. package/lib/types/services/preflight.d.ts +6 -0
  24. package/lib/types/services/profile.d.ts +24 -0
  25. package/lib/types/services/progress.d.ts +17 -0
  26. package/lib/types/services/task-queue.d.ts +76 -0
  27. package/package.json +27 -6
  28. package/src/client/components/CatalogControls.tsx +104 -0
  29. package/src/client/components/CatalogHeader.tsx +78 -0
  30. package/src/client/components/CatalogList.tsx +75 -0
  31. package/src/client/components/CategoryTabs.tsx +35 -0
  32. package/src/client/components/Dropdown.tsx +80 -0
  33. package/src/client/components/NotificationsModal.tsx +230 -0
  34. package/src/client/components/PluginCard.tsx +104 -0
  35. package/src/client/components/PluginHubSection.tsx +339 -0
  36. package/src/client/components/ProgressView.tsx +23 -0
  37. package/src/client/components/icons.tsx +125 -0
  38. package/src/client/components/modals.tsx +365 -0
  39. package/src/client/hooks/useCatalog.ts +192 -0
  40. package/src/client/hooks/useTaskQueue.ts +405 -0
  41. package/src/client/index.tsx +8 -422
  42. package/src/client/lib/catalog.ts +204 -0
  43. package/src/client/lib/env.ts +16 -0
  44. package/src/client/lib/failures.ts +153 -0
  45. package/src/client/lib/format.ts +22 -0
  46. package/src/client/locales.ts +186 -18
  47. package/src/client/styles/Dropdown.module.css +123 -0
  48. package/src/client/styles/Header.module.css +432 -0
  49. package/src/client/styles/List.module.css +324 -0
  50. package/src/client/styles/Modal.module.css +965 -0
  51. package/src/client/styles/tokens.module.css +86 -0
  52. package/src/client/types.ts +80 -0
  53. package/src/server/http/routes.ts +397 -0
  54. package/src/server/index.ts +35 -0
  55. package/src/server/services/install-types.ts +63 -0
  56. package/src/server/services/install.ts +18 -0
  57. package/src/server/services/installed-versions.ts +65 -0
  58. package/src/server/services/loader.ts +110 -0
  59. package/src/server/services/pending-restart.ts +36 -0
  60. package/src/server/services/preflight.ts +97 -0
  61. package/src/server/services/profile.ts +89 -0
  62. package/src/server/services/progress.ts +48 -0
  63. package/src/server/services/task-queue.ts +450 -0
  64. package/src/client/Section.module.css +0 -589
@@ -0,0 +1,432 @@
1
+ /* Plugin Hub section header: brand row, tagline, ad banner, search input,
2
+ * filter toolbar (installed / not installed, failures entry) and category
3
+ * tabs. Colors resolve through the --hub-* design tokens. */
4
+
5
+ .root {
6
+ height: 100%;
7
+ min-width: 0;
8
+ display: flex;
9
+ flex-direction: column;
10
+ gap: 8px;
11
+ color: var(--hub-text-primary);
12
+ }
13
+
14
+ /* —— header —— */
15
+
16
+ .header {
17
+ display: flex;
18
+ flex-direction: column;
19
+ gap: 2px;
20
+ padding: 2px 2px 0;
21
+ }
22
+
23
+ /* 头部右上角控件组:语言切换 + GitHub 源码图标,右侧一列对齐 */
24
+ .headerRight {
25
+ flex-shrink: 0;
26
+ display: inline-flex;
27
+ align-items: center;
28
+ gap: 2px;
29
+ }
30
+
31
+ /* 语言切换按钮:与 GitHub 图标同风格(次级灰低调、悬浮品牌紫),文字显示目标语言 */
32
+ .langBtn {
33
+ flex-shrink: 0;
34
+ display: inline-flex;
35
+ align-items: center;
36
+ justify-content: center;
37
+ height: 24px;
38
+ padding: 0 8px;
39
+ font-size: 12px;
40
+ font-weight: 600;
41
+ color: var(--hub-text-tertiary);
42
+ background: transparent;
43
+ border: none;
44
+ border-radius: 6px;
45
+ cursor: pointer;
46
+ user-select: none;
47
+ transition: color 0.15s ease, background-color 0.15s ease;
48
+ }
49
+
50
+ .langBtn:hover {
51
+ color: var(--hub-purple-1);
52
+ background-color: var(--hub-purple-tint);
53
+ }
54
+
55
+ /* 头部右上角 GitHub 源码图标:次级灰低调呈现,悬浮转品牌紫;纯图标不带按钮边框,保持品牌行克制 */
56
+ .githubLink {
57
+ flex-shrink: 0;
58
+ display: inline-flex;
59
+ align-items: center;
60
+ color: var(--hub-text-tertiary);
61
+ text-decoration: none;
62
+ cursor: pointer;
63
+ padding: 3px;
64
+ border-radius: 6px;
65
+ transition: color 0.15s ease, background-color 0.15s ease;
66
+ }
67
+
68
+ .githubLink:hover {
69
+ color: var(--hub-purple-1);
70
+ background-color: var(--hub-purple-tint);
71
+ }
72
+
73
+ .githubIcon {
74
+ flex-shrink: 0;
75
+ display: block;
76
+ }
77
+
78
+ /* 头部第一行:logo + 标题(官网链接)与 GitHub 图标同行,space-between 把 GitHub 顶到标题行末尾 */
79
+ .headerTitleRow {
80
+ display: flex;
81
+ align-items: center;
82
+ justify-content: space-between;
83
+ gap: 8px;
84
+ min-width: 0;
85
+ }
86
+
87
+ /* 标题行官网链接:logo + 短标题,点击跳官网;标题/描述保持各自默认色不变蓝 */
88
+ .brandTitle {
89
+ display: flex;
90
+ align-items: center;
91
+ gap: 10px;
92
+ min-width: 0;
93
+ color: inherit;
94
+ text-decoration: none;
95
+ cursor: pointer;
96
+ }
97
+
98
+ /* 副标题行独立成行:仍为官网链接;与最左侧 logo 左对齐(顶格),不与标题文字对齐 */
99
+ .taglineLink {
100
+ align-self: flex-start;
101
+ max-width: 100%;
102
+ min-width: 0;
103
+ color: inherit;
104
+ text-decoration: none;
105
+ cursor: pointer;
106
+ }
107
+
108
+ .title {
109
+ font-size: 14px;
110
+ line-height: 20px;
111
+ font-weight: 600;
112
+ margin: 0;
113
+ }
114
+
115
+ /* 标题里的版本号:非标题主内容,颜色弱化为次级灰 */
116
+ .version {
117
+ color: var(--hub-text-secondary);
118
+ }
119
+
120
+ /* 品牌 logo 图标:与标题文字垂直居中对齐 */
121
+ .logoIcon {
122
+ flex-shrink: 0;
123
+ display: block;
124
+ }
125
+
126
+ /* 复制图标:跟随父元素 currentColor 变色 */
127
+ .copyIcon {
128
+ flex-shrink: 0;
129
+ }
130
+
131
+ .tagline {
132
+ font-size: 12px;
133
+ line-height: 18px;
134
+ color: var(--hub-text-tertiary);
135
+ margin: 0;
136
+ /* 品牌副标题强制单行:中文/英文都不换行,超宽时省略号截断兜底 */
137
+ white-space: nowrap;
138
+ overflow: hidden;
139
+ text-overflow: ellipsis;
140
+ }
141
+
142
+ .controls {
143
+ display: flex;
144
+ align-items: center;
145
+ gap: 4px;
146
+ flex-shrink: 0;
147
+ /* 左右分区:左侧操作控件靠左,通知入口经 margin-left:auto 独立顶到最右 */
148
+ }
149
+
150
+ /* 列表头部筛选结果数:位于排序下拉之前,弱化灰字提示「筛选出 N 条插件」,字号与相邻按钮一致 */
151
+ .filterResults {
152
+ flex-shrink: 0;
153
+ display: inline-flex;
154
+ align-items: baseline;
155
+ font-size: 12px;
156
+ line-height: 18px;
157
+ color: var(--hub-text-tertiary);
158
+ white-space: nowrap;
159
+ }
160
+
161
+ /* 结果数里的数字:等宽数字 + 固定 5 位宽度;数字在预留位中始终居中,两侧位置都固定不跳动 */
162
+ .resultCount {
163
+ font-variant-numeric: tabular-nums;
164
+ min-width: 5ch;
165
+ text-align: center;
166
+ }
167
+
168
+ /* —— ad banner —— */
169
+
170
+ /* 广告条:固定蓝紫渐变 + 白字,不随主题变量漂移,两种主题下都醒目。
171
+ 与 toast 相同思路:固定颜色保证可读性。 */
172
+ .adBanner {
173
+ display: flex;
174
+ align-items: center;
175
+ gap: 8px;
176
+ padding: 7px 12px;
177
+ border: 1px solid var(--hub-purple-border);
178
+ border-radius: 8px;
179
+ background: linear-gradient(90deg, var(--hub-purple-1) 0%, var(--hub-purple-2) 100%);
180
+ color: #ffffff;
181
+ text-decoration: none;
182
+ cursor: pointer;
183
+ user-select: none;
184
+ box-shadow: 0 2px 10px var(--hub-purple-shadow);
185
+ transition: filter 0.15s ease, box-shadow 0.15s ease;
186
+ }
187
+
188
+ .adBanner:hover {
189
+ filter: brightness(1.1);
190
+ box-shadow: 0 4px 16px var(--hub-purple-shadow-strong);
191
+ }
192
+
193
+ .adBadge {
194
+ flex-shrink: 0;
195
+ font-size: 10px;
196
+ font-weight: 700;
197
+ letter-spacing: 0.04em;
198
+ line-height: 1;
199
+ color: #ffffff;
200
+ background: rgba(255, 255, 255, 0.16);
201
+ border: 1px solid rgba(255, 255, 255, 0.55);
202
+ border-radius: 4px;
203
+ padding: 3px 6px;
204
+ white-space: nowrap;
205
+ }
206
+
207
+ .adText {
208
+ min-width: 0;
209
+ overflow: hidden;
210
+ text-overflow: ellipsis;
211
+ white-space: nowrap;
212
+ font-size: 12px;
213
+ line-height: 18px;
214
+ color: #ffffff;
215
+ }
216
+
217
+ .adArrow {
218
+ flex-shrink: 0;
219
+ margin-left: auto;
220
+ font-size: 13px;
221
+ color: #ffffff;
222
+ }
223
+
224
+ .searchRow {
225
+ display: flex;
226
+ padding: 0 2px;
227
+ }
228
+
229
+ /* 搜索框:用户指定只占整行一半宽,右侧留白给排序/筛选/通知控件组 */
230
+ .search {
231
+ width: 50%;
232
+ font-size: 12px;
233
+ line-height: 18px;
234
+ color: inherit;
235
+ background: transparent;
236
+ border: 1px solid var(--hub-border-2);
237
+ border-radius: 6px;
238
+ padding: 4px 9px;
239
+ outline: none;
240
+ transition: border-color 120ms ease;
241
+ }
242
+
243
+ .search::placeholder {
244
+ color: var(--hub-text-tertiary);
245
+ }
246
+
247
+ .search:focus {
248
+ border-color: var(--hub-brand);
249
+ }
250
+
251
+ /* —— install-state filter —— */
252
+
253
+ /* 已安装 / 未安装:两个独立按钮,外观尺寸与「安装失败记录」按钮一致(高 24px);
254
+ 各自点击筛选对应安装状态,再点一次恢复全部。公共规则统一无边框,
255
+ 避免激活态回落到浏览器默认 2px 边框导致点击时按钮变粗跳动。 */
256
+ .installedBtn,
257
+ .installedBtnActive,
258
+ .installedBtnDisabled {
259
+ flex-shrink: 0;
260
+ display: inline-flex;
261
+ align-items: center;
262
+ gap: 5px;
263
+ height: 24px;
264
+ padding: 0 8px;
265
+ border: none;
266
+ border-radius: 6px;
267
+ font-size: 12px;
268
+ line-height: 22px;
269
+ cursor: pointer;
270
+ user-select: none;
271
+ transition: color 120ms ease, background 120ms ease;
272
+ }
273
+
274
+ .installedBtn {
275
+ color: var(--hub-text-primary);
276
+ background: transparent;
277
+ }
278
+
279
+ /* 悬浮:明显加重底色,保证「看得见、点得准」。 */
280
+ .installedBtn:hover {
281
+ color: var(--hub-text-primary);
282
+ background: var(--hub-bg-hover);
283
+ }
284
+
285
+ /* 置灰:当前分类下无对应安装状态的插件时不可点;尺寸保持 24px 不塌陷,
286
+ 文字弱化,配合 disabled 属性天然屏蔽 hover/点击。 */
287
+ .installedBtnDisabled,
288
+ .installedBtnDisabled:hover {
289
+ color: var(--hub-text-disabled);
290
+ background: transparent;
291
+ cursor: not-allowed;
292
+ }
293
+
294
+ .installedBtnDisabled .segCount {
295
+ color: var(--hub-text-disabled);
296
+ background: rgba(128, 128, 128, 0.06);
297
+ }
298
+
299
+ .installedBtnActive {
300
+ color: var(--hub-text-on-fill);
301
+ background: var(--hub-btn-fill);
302
+ }
303
+
304
+ .segCount,
305
+ .segCountActive {
306
+ font-size: 10px;
307
+ line-height: 14px;
308
+ border-radius: 999px;
309
+ padding: 0 4px;
310
+ min-width: 14px;
311
+ text-align: center;
312
+ }
313
+
314
+ .segCount {
315
+ color: var(--hub-text-tertiary);
316
+ background: var(--hub-bg-btn);
317
+ }
318
+
319
+ .installedBtnActive .segCountActive {
320
+ color: var(--hub-text-on-fill);
321
+ background: var(--hub-bg-on-fill);
322
+ }
323
+
324
+ /* —— category chips —— */
325
+
326
+ .tabs {
327
+ display: flex;
328
+ align-items: center;
329
+ flex-wrap: wrap;
330
+ gap: 6px;
331
+ padding: 2px 2px 8px;
332
+ border-bottom: 1px solid var(--hub-border-1);
333
+ }
334
+
335
+ .tab,
336
+ .tabActive {
337
+ display: inline-flex;
338
+ align-items: center;
339
+ gap: 6px;
340
+ flex-shrink: 0;
341
+ font-size: 12px;
342
+ line-height: 18px;
343
+ border: none;
344
+ border-radius: 999px;
345
+ padding: 3px 10px;
346
+ cursor: pointer;
347
+ user-select: none;
348
+ transition: color 120ms ease, background 120ms ease;
349
+ }
350
+
351
+ .tab {
352
+ color: var(--hub-text-secondary);
353
+ background: transparent;
354
+ }
355
+
356
+ .tab:hover {
357
+ color: var(--hub-text-primary);
358
+ background: var(--hub-bg-hover);
359
+ }
360
+
361
+ .tabActive {
362
+ color: var(--hub-text-on-fill);
363
+ background: var(--hub-btn-fill);
364
+ }
365
+
366
+ /* 「全部」标签上的总数徽标:浅灰胶囊,选中态转深色底白字胶囊 */
367
+ .tabCount {
368
+ font-size: 10px;
369
+ line-height: 14px;
370
+ border-radius: 999px;
371
+ padding: 0 5px;
372
+ min-width: 16px;
373
+ text-align: center;
374
+ color: var(--hub-text-tertiary);
375
+ background: var(--hub-bg-btn);
376
+ }
377
+
378
+ .tabActive .tabCount {
379
+ color: var(--hub-text-on-fill);
380
+ background: var(--hub-bg-on-fill);
381
+ }
382
+
383
+ /* —— notifications entry —— */
384
+
385
+ /* 通知中心入口:中性次级按钮(浅灰底 + 细边框),与左侧操作按钮区分、常态可辨;
386
+ box-sizing: border-box 让 1px 边框包含在 24px 高度内,与「已安装/未安装」无边框按钮外尺寸一致;
387
+ 有通知时右上角悬浮的红圈白字徽标成为提醒焦点 */
388
+ .failBtn {
389
+ position: relative;
390
+ flex-shrink: 0;
391
+ /* 与左侧操作按钮分开:自动吃掉剩余空间,把按钮顶到控件行最右 */
392
+ margin-left: auto;
393
+ display: inline-flex;
394
+ align-items: center;
395
+ box-sizing: border-box;
396
+ height: 24px;
397
+ padding: 0 10px;
398
+ border: 1px solid var(--hub-border-2);
399
+ border-radius: 6px;
400
+ font-size: 12px;
401
+ line-height: 22px;
402
+ color: var(--hub-text-primary);
403
+ background: var(--hub-bg-hover);
404
+ cursor: pointer;
405
+ user-select: none;
406
+ transition: color 120ms ease, background 120ms ease;
407
+ }
408
+
409
+ .failBtn:hover {
410
+ color: var(--hub-text-primary);
411
+ background: rgba(128, 128, 128, 0.28);
412
+ }
413
+
414
+ /* 计数徽标:红色圆形 + 白色数字,整体浮在按钮右上角上沿之外(16px 徽章 + top:-16px
415
+ 完全悬于按钮灰底之上,不遮盖灰底),只在通知数 >0 时出现 */
416
+ .failBadge {
417
+ position: absolute;
418
+ top: -16px;
419
+ right: -7px;
420
+ z-index: 1;
421
+ min-width: 16px;
422
+ height: 16px;
423
+ padding: 0 5px;
424
+ border-radius: 999px;
425
+ font-size: 10px;
426
+ font-weight: 600;
427
+ line-height: 16px;
428
+ text-align: center;
429
+ color: #ffffff;
430
+ background: var(--hub-danger);
431
+ box-shadow: 0 1px 4px rgba(209, 36, 47, 0.4);
432
+ }