dsh-plugin-panel 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client.js +727 -182
- package/index.js +71 -2
- package/package.json +4 -2
- package/trend.js +191 -4
- package/docs/promo-3x4.png +0 -0
- package/docs/promo-wechat.png +0 -0
package/client.js
CHANGED
|
@@ -6,7 +6,7 @@ window.__ModuleLoader__.load({
|
|
|
6
6
|
const PANEL_ID = 'dsh-plugin-panel'
|
|
7
7
|
const TAB_KIND = 'plugin-canvas'
|
|
8
8
|
const NS = 'dsh-plugin-panel'
|
|
9
|
-
const VERSION = 'v0.1.
|
|
9
|
+
const VERSION = 'v0.1.3'
|
|
10
10
|
const PREF_KEY = 'dsh.plugin-list.pref.v2'
|
|
11
11
|
|
|
12
12
|
const COPY = {
|
|
@@ -16,7 +16,6 @@ window.__ModuleLoader__.load({
|
|
|
16
16
|
'guide.description': '查看并打开右侧栏插件。',
|
|
17
17
|
'list.installed': '用户安装',
|
|
18
18
|
'list.builtin': '系统内置',
|
|
19
|
-
'list.all': '全部',
|
|
20
19
|
'list.refresh': '刷新',
|
|
21
20
|
'list.market': '市场',
|
|
22
21
|
'list.back': '返回',
|
|
@@ -28,6 +27,34 @@ window.__ModuleLoader__.load({
|
|
|
28
27
|
'list.countFiltered': '已过滤',
|
|
29
28
|
'list.perDay': '日均新增',
|
|
30
29
|
'list.spanDays': '覆盖天数',
|
|
30
|
+
'list.marketFirstNote': '首日已含此前录入的存量,不是从 0 开始',
|
|
31
|
+
'list.install': '安装',
|
|
32
|
+
'list.uninstall': '卸载',
|
|
33
|
+
'list.uninstallConfirm': '确认卸载',
|
|
34
|
+
'list.installing': '安装中…',
|
|
35
|
+
'list.removing': '卸载中…',
|
|
36
|
+
'list.hotDelta': '7 天增长',
|
|
37
|
+
'list.hotStars': '仓库 star',
|
|
38
|
+
'list.hotCategory': '分类',
|
|
39
|
+
'list.hotRepo': '仓库',
|
|
40
|
+
'list.installedState': '已安装',
|
|
41
|
+
'list.notInstalled': '未安装',
|
|
42
|
+
'list.chartTitle': '插件市场的增长趋势',
|
|
43
|
+
'list.chartCumulative': '累计',
|
|
44
|
+
'list.chartDaily': '每日增量',
|
|
45
|
+
'list.chartDailyHint': '每天新增多少,比累计更能看出快慢',
|
|
46
|
+
'list.marketSearch': '搜索整个市场的插件…',
|
|
47
|
+
'list.marketSearching': '搜索中…',
|
|
48
|
+
'list.searchError': '搜索暂时不可用。如果刚更新过插件,在插件管理里停用再启用一次即可。',
|
|
49
|
+
'list.marketNoMatch': '没有找到匹配的插件',
|
|
50
|
+
'list.marketHits': '找到 {n} 个',
|
|
51
|
+
'list.hotSearch': '搜索插件名、说明或分类…',
|
|
52
|
+
'list.hotNoMatch': '没有匹配的插件',
|
|
53
|
+
'list.hotTitle': '最近 7 天增长最快',
|
|
54
|
+
'list.hotHint': '按 GitHub 仓库 star 的 7 天增量排序,同一仓库只算一次。',
|
|
55
|
+
'list.hotLoading': '正在算增长…',
|
|
56
|
+
'list.hotError': '取不到增长数据。如果刚更新插件,需要重启一次 DeepSeek Harness 才会启用这个榜单。',
|
|
57
|
+
'list.hotEmpty': '这 7 天没有增长记录。',
|
|
31
58
|
'list.viewList': '列表',
|
|
32
59
|
'list.viewCard': '卡片',
|
|
33
60
|
'list.open': '打开',
|
|
@@ -89,7 +116,6 @@ window.__ModuleLoader__.load({
|
|
|
89
116
|
'guide.description': 'Browse and open right-sidebar plugins.',
|
|
90
117
|
'list.installed': 'Installed',
|
|
91
118
|
'list.builtin': 'Built-in',
|
|
92
|
-
'list.all': 'All',
|
|
93
119
|
'list.refresh': 'Refresh',
|
|
94
120
|
'list.market': 'Market',
|
|
95
121
|
'list.back': 'Back',
|
|
@@ -101,6 +127,34 @@ window.__ModuleLoader__.load({
|
|
|
101
127
|
'list.countFiltered': 'Filtered',
|
|
102
128
|
'list.perDay': 'Per day',
|
|
103
129
|
'list.spanDays': 'Days',
|
|
130
|
+
'list.marketFirstNote': 'First point already includes earlier stock; not growth from zero',
|
|
131
|
+
'list.install': 'Install',
|
|
132
|
+
'list.uninstall': 'Remove',
|
|
133
|
+
'list.uninstallConfirm': 'Confirm',
|
|
134
|
+
'list.installing': 'Installing…',
|
|
135
|
+
'list.removing': 'Removing…',
|
|
136
|
+
'list.hotDelta': '7-day growth',
|
|
137
|
+
'list.hotStars': 'Repo stars',
|
|
138
|
+
'list.hotCategory': 'Category',
|
|
139
|
+
'list.hotRepo': 'Repository',
|
|
140
|
+
'list.installedState': 'Installed',
|
|
141
|
+
'list.notInstalled': 'Not installed',
|
|
142
|
+
'list.chartTitle': 'Plugin market growth',
|
|
143
|
+
'list.chartCumulative': 'Total',
|
|
144
|
+
'list.chartDaily': 'Per day',
|
|
145
|
+
'list.chartDailyHint': 'New plugins per day, which shows pace better than a total',
|
|
146
|
+
'list.marketSearch': 'Search the whole market…',
|
|
147
|
+
'list.marketSearching': 'Searching…',
|
|
148
|
+
'list.searchError': 'Search is unavailable. If you just updated the plugin, disable and re-enable it once.',
|
|
149
|
+
'list.marketNoMatch': 'No matching plugin',
|
|
150
|
+
'list.marketHits': '{n} found',
|
|
151
|
+
'list.hotSearch': 'Search by name, description or category…',
|
|
152
|
+
'list.hotNoMatch': 'No matching plugin',
|
|
153
|
+
'list.hotTitle': 'Fastest growing, last 7 days',
|
|
154
|
+
'list.hotHint': 'Ranked by 7-day GitHub repository star delta; one row per repository.',
|
|
155
|
+
'list.hotLoading': 'Crunching growth…',
|
|
156
|
+
'list.hotError': 'Could not load growth data. Restart DeepSeek Harness once to enable this ranking.',
|
|
157
|
+
'list.hotEmpty': 'No growth recorded in the last 7 days.',
|
|
104
158
|
'list.viewList': 'List',
|
|
105
159
|
'list.viewCard': 'Cards',
|
|
106
160
|
'list.open': 'Open',
|
|
@@ -171,7 +225,7 @@ window.__ModuleLoader__.load({
|
|
|
171
225
|
try {
|
|
172
226
|
const parsed = JSON.parse(window.localStorage.getItem(PREF_KEY) || '{}')
|
|
173
227
|
return {
|
|
174
|
-
filter:
|
|
228
|
+
filter: parsed.filter === 'builtin' ? 'builtin' : 'installed',
|
|
175
229
|
pins: Array.isArray(parsed.pins) ? parsed.pins.filter((id) => typeof id === 'string') : [],
|
|
176
230
|
layout: parsed.layout === 'card' ? 'card' : 'list',
|
|
177
231
|
}
|
|
@@ -273,15 +327,97 @@ window.__ModuleLoader__.load({
|
|
|
273
327
|
return ''
|
|
274
328
|
}
|
|
275
329
|
|
|
276
|
-
|
|
330
|
+
/** Bars for the day-over-day change; the first day has no previous snapshot. */
|
|
331
|
+
function barChart(days, hoverIndex, onHover) {
|
|
277
332
|
const W = 1000
|
|
278
|
-
const H =
|
|
279
|
-
const L =
|
|
280
|
-
const R =
|
|
281
|
-
const T =
|
|
282
|
-
const B =
|
|
333
|
+
const H = 250
|
|
334
|
+
const L = 68
|
|
335
|
+
const R = 24
|
|
336
|
+
const T = 18
|
|
337
|
+
const B = 42
|
|
338
|
+
const deltas = days.map((day, index) => (index === 0 ? null : day.count - days[index - 1].count))
|
|
339
|
+
const usable = deltas.filter((value) => typeof value === 'number')
|
|
340
|
+
const peak = Math.max(...usable, 1) * 1.15
|
|
341
|
+
const n = days.length
|
|
342
|
+
const slot = (W - L - R) / Math.max(n, 1)
|
|
343
|
+
const barW = Math.max(2, Math.min(slot * 0.62, 14))
|
|
344
|
+
const x = (i) => L + slot * i + slot / 2
|
|
345
|
+
const zeroY = T + (H - T - B)
|
|
346
|
+
const y = (v) => zeroY - ((H - T - B) * v) / Math.max(peak, 1)
|
|
347
|
+
const ticks = [0, 0.5, 1].map((r) => Math.round((peak * r) / 500) * 500)
|
|
348
|
+
const labelIndexes = n <= 1 ? [0] : [0, Math.floor((n - 1) / 2), n - 1]
|
|
349
|
+
|
|
350
|
+
const pick = (event) => {
|
|
351
|
+
const rect = event.currentTarget.getBoundingClientRect()
|
|
352
|
+
if (!rect.width) return
|
|
353
|
+
const px = ((event.clientX - rect.left) / rect.width) * W
|
|
354
|
+
const index = Math.round((px - L - slot / 2) / Math.max(slot, 1))
|
|
355
|
+
onHover(Math.max(0, Math.min(n - 1, index)))
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const hovered = hoverIndex >= 0 && hoverIndex < n ? hoverIndex : -1
|
|
359
|
+
const hx = hovered >= 0 ? x(hovered) : 0
|
|
360
|
+
const value = hovered >= 0 ? deltas[hovered] : null
|
|
361
|
+
const boxW = 200
|
|
362
|
+
const boxH = 64
|
|
363
|
+
const boxX = hovered >= 0 ? Math.min(Math.max(hx - boxW / 2, L), W - R - boxW) : 0
|
|
364
|
+
const boxY = hovered >= 0 ? Math.max(T + 8, y(typeof value === 'number' ? value : 0) - boxH - 14) : 0
|
|
365
|
+
const shown = typeof value === 'number' ? `${value >= 0 ? '+' : ''}${value.toLocaleString()}` : '—'
|
|
366
|
+
|
|
367
|
+
return h('svg', {
|
|
368
|
+
className: 'dsh-pl-chart',
|
|
369
|
+
viewBox: `0 0 ${W} ${H}`,
|
|
370
|
+
role: 'img',
|
|
371
|
+
onMouseMove: pick,
|
|
372
|
+
onMouseLeave: () => onHover(-1),
|
|
373
|
+
},
|
|
374
|
+
ticks.map((tick, i) => h('line', {
|
|
375
|
+
key: `g${i}`, x1: L, x2: W - R, y1: y(tick), y2: y(tick),
|
|
376
|
+
stroke: 'var(--dsw-alias-border-l1)', strokeWidth: 1,
|
|
377
|
+
})),
|
|
378
|
+
ticks.map((tick, i) => h('text', {
|
|
379
|
+
key: `t${i}`, x: L - 12, y: y(tick) + 6, textAnchor: 'end',
|
|
380
|
+
fill: 'var(--dsw-alias-label-secondary)', fontSize: 16,
|
|
381
|
+
}, `+${Math.round(tick / 1000)}k`)),
|
|
382
|
+
deltas.map((delta, i) => {
|
|
383
|
+
if (typeof delta !== 'number') return null
|
|
384
|
+
const top = Math.min(y(delta), zeroY)
|
|
385
|
+
const height = Math.max(1, Math.abs(zeroY - y(delta)))
|
|
386
|
+
return h('rect', {
|
|
387
|
+
key: `b${i}`, x: x(i) - barW / 2, y: top, width: barW, height,
|
|
388
|
+
rx: Math.min(2, barW / 2),
|
|
389
|
+
fill: 'var(--dsw-alias-brand-primary)',
|
|
390
|
+
opacity: hovered === i ? 0.95 : 0.55,
|
|
391
|
+
})
|
|
392
|
+
}),
|
|
393
|
+
labelIndexes.map((i) => h('text', {
|
|
394
|
+
key: `d${i}`, x: x(i), y: H - B + 34, textAnchor: i === 0 ? 'start' : i === n - 1 ? 'end' : 'middle',
|
|
395
|
+
fill: 'var(--dsw-alias-label-secondary)', fontSize: 16,
|
|
396
|
+
}, days[i].day.slice(5))),
|
|
397
|
+
hovered >= 0 ? h('g', null,
|
|
398
|
+
h('line', {
|
|
399
|
+
x1: hx, x2: hx, y1: T, y2: H - B,
|
|
400
|
+
stroke: 'var(--dsw-alias-label-secondary)', strokeWidth: 1, strokeDasharray: '4 4', opacity: 0.5,
|
|
401
|
+
}),
|
|
402
|
+
h('rect', {
|
|
403
|
+
x: boxX, y: boxY, width: boxW, height: boxH, rx: 10,
|
|
404
|
+
fill: 'var(--dsw-alias-bg-overlay)', stroke: 'var(--dsw-alias-border-l2)', strokeWidth: 1,
|
|
405
|
+
}),
|
|
406
|
+
h('text', { x: boxX + 14, y: boxY + 24, fill: 'var(--dsw-alias-label-secondary)', fontSize: 16 }, days[hovered].day),
|
|
407
|
+
h('text', { x: boxX + 14, y: boxY + 50, fill: 'var(--dsw-alias-label-primary)', fontSize: 22, fontWeight: 600 }, shown),
|
|
408
|
+
) : null,
|
|
409
|
+
)
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
function trendChart(days, hoverIndex, onHover, firstNote) {
|
|
413
|
+
const W = 1000
|
|
414
|
+
const H = 250
|
|
415
|
+
const L = 68
|
|
416
|
+
const R = 24
|
|
417
|
+
const T = 18
|
|
418
|
+
const B = 42
|
|
283
419
|
const values = days.map((d) => d.count)
|
|
284
|
-
const max = Math.max(...values, 1) * 1.
|
|
420
|
+
const max = Math.max(...values, 1) * 1.2
|
|
285
421
|
const n = days.length
|
|
286
422
|
const x = (i) => L + (W - L - R) * (n <= 1 ? 0.5 : i / (n - 1))
|
|
287
423
|
const y = (v) => T + (H - T - B) * (1 - v / max)
|
|
@@ -289,25 +425,68 @@ window.__ModuleLoader__.load({
|
|
|
289
425
|
const area = `${line} L ${x(n - 1).toFixed(1)} ${H - B} L ${x(0).toFixed(1)} ${H - B} Z`
|
|
290
426
|
const ticks = [0, 0.25, 0.5, 0.75, 1].map((r) => Math.round((max * r) / 1000) * 1000)
|
|
291
427
|
const labelIndexes = n <= 1 ? [0] : [0, Math.floor((n - 1) / 2), n - 1]
|
|
292
|
-
|
|
428
|
+
|
|
429
|
+
const pick = (event) => {
|
|
430
|
+
const rect = event.currentTarget.getBoundingClientRect()
|
|
431
|
+
if (!rect.width) return
|
|
432
|
+
const px = ((event.clientX - rect.left) / rect.width) * W
|
|
433
|
+
const ratio = (px - L) / Math.max(W - L - R, 1)
|
|
434
|
+
const index = Math.round(ratio * Math.max(n - 1, 1))
|
|
435
|
+
onHover(Math.max(0, Math.min(n - 1, index)))
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
const hovered = hoverIndex >= 0 && hoverIndex < n ? hoverIndex : -1
|
|
439
|
+
const hx = hovered >= 0 ? x(hovered) : 0
|
|
440
|
+
const hy = hovered >= 0 ? y(values[hovered]) : 0
|
|
441
|
+
const isFirst = hovered === 0
|
|
442
|
+
const boxW = isFirst ? 246 : 196
|
|
443
|
+
const boxH = isFirst ? 96 : 64
|
|
444
|
+
const boxX = hovered >= 0 ? Math.min(Math.max(hx - boxW / 2, L), W - R - boxW) : 0
|
|
445
|
+
const boxY = hovered >= 0 ? Math.max(hy - boxH - 16, T) : 0
|
|
446
|
+
|
|
447
|
+
return h('svg', {
|
|
448
|
+
className: 'dsh-pl-chart',
|
|
449
|
+
viewBox: `0 0 ${W} ${H}`,
|
|
450
|
+
role: 'img',
|
|
451
|
+
onMouseMove: pick,
|
|
452
|
+
onMouseLeave: () => onHover(-1),
|
|
453
|
+
},
|
|
293
454
|
ticks.map((tick, i) => h('line', {
|
|
294
455
|
key: `g${i}`, x1: L, x2: W - R, y1: y(tick), y2: y(tick),
|
|
295
456
|
stroke: 'var(--dsw-alias-border-l1)', strokeWidth: 1,
|
|
296
457
|
})),
|
|
297
458
|
ticks.map((tick, i) => h('text', {
|
|
298
459
|
key: `t${i}`, x: L - 12, y: y(tick) + 6, textAnchor: 'end',
|
|
299
|
-
fill: 'var(--dsw-alias-label-secondary)', fontSize:
|
|
460
|
+
fill: 'var(--dsw-alias-label-secondary)', fontSize: 16,
|
|
300
461
|
}, `${Math.round(tick / 1000)}k`)),
|
|
301
|
-
h('path', { d: area, fill: 'var(--dsw-alias-brand-primary)', opacity: 0.
|
|
302
|
-
h('path', { d: line, fill: 'none', stroke: 'var(--dsw-alias-brand-primary)', strokeWidth:
|
|
462
|
+
h('path', { d: area, fill: 'var(--dsw-alias-brand-primary)', opacity: 0.08 }),
|
|
463
|
+
h('path', { d: line, fill: 'none', stroke: 'var(--dsw-alias-brand-primary)', strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round' }),
|
|
303
464
|
labelIndexes.map((i) => h('text', {
|
|
304
465
|
key: `d${i}`, x: x(i), y: H - B + 34, textAnchor: i === 0 ? 'start' : i === n - 1 ? 'end' : 'middle',
|
|
305
|
-
fill: 'var(--dsw-alias-label-secondary)', fontSize:
|
|
466
|
+
fill: 'var(--dsw-alias-label-secondary)', fontSize: 16,
|
|
306
467
|
}, days[i].day.slice(5))),
|
|
307
468
|
h('text', {
|
|
308
469
|
x: x(n - 1), y: y(values[n - 1]) - 16, textAnchor: 'end',
|
|
309
|
-
fill: 'var(--dsw-alias-label-primary)', fontSize:
|
|
470
|
+
fill: 'var(--dsw-alias-label-primary)', fontSize: 22, fontWeight: 600,
|
|
310
471
|
}, String(values[n - 1])),
|
|
472
|
+
hovered >= 0 ? h('g', null,
|
|
473
|
+
h('line', {
|
|
474
|
+
x1: hx, x2: hx, y1: T, y2: H - B,
|
|
475
|
+
stroke: 'var(--dsw-alias-label-secondary)', strokeWidth: 1, strokeDasharray: '4 4', opacity: 0.5,
|
|
476
|
+
}),
|
|
477
|
+
h('circle', { cx: hx, cy: hy, r: 4.5, fill: 'var(--dsw-alias-brand-primary)', stroke: '#fff', strokeWidth: 2 }),
|
|
478
|
+
h('rect', {
|
|
479
|
+
x: boxX, y: boxY, width: boxW, height: boxH, rx: 10,
|
|
480
|
+
fill: 'var(--dsw-alias-bg-overlay)', stroke: 'var(--dsw-alias-border-l2)', strokeWidth: 1,
|
|
481
|
+
}),
|
|
482
|
+
h('text', { x: boxX + 14, y: boxY + 24, fill: 'var(--dsw-alias-label-secondary)', fontSize: 16 },
|
|
483
|
+
days[hovered].day),
|
|
484
|
+
h('text', { x: boxX + 14, y: boxY + 50, fill: 'var(--dsw-alias-label-primary)', fontSize: 22, fontWeight: 600 },
|
|
485
|
+
`${values[hovered].toLocaleString()} 个`),
|
|
486
|
+
isFirst && firstNote
|
|
487
|
+
? h('text', { x: boxX + 14, y: boxY + 78, fill: 'var(--dsw-alias-label-secondary)', fontSize: 14 }, firstNote)
|
|
488
|
+
: null,
|
|
489
|
+
) : null,
|
|
311
490
|
)
|
|
312
491
|
}
|
|
313
492
|
|
|
@@ -468,6 +647,49 @@ window.__ModuleLoader__.load({
|
|
|
468
647
|
}) || null
|
|
469
648
|
}
|
|
470
649
|
|
|
650
|
+
/** One card shape, shared by the plugin list and the market ranking. */
|
|
651
|
+
function PluginCard(options) {
|
|
652
|
+
const { icon, title, subtitle, actions, className, onClick, badge } = options
|
|
653
|
+
const classes = ['dsh-pl-item']
|
|
654
|
+
if (className) classes.push(className)
|
|
655
|
+
return h('div', {
|
|
656
|
+
className: classes.join(' '),
|
|
657
|
+
role: 'button',
|
|
658
|
+
tabIndex: 0,
|
|
659
|
+
onClick,
|
|
660
|
+
onKeyDown: (event) => {
|
|
661
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
662
|
+
event.preventDefault()
|
|
663
|
+
if (onClick) onClick()
|
|
664
|
+
}
|
|
665
|
+
},
|
|
666
|
+
},
|
|
667
|
+
icon || h(DefaultPluginIcon),
|
|
668
|
+
h('span', { className: 'dsh-pl-copy' },
|
|
669
|
+
h('strong', null, title),
|
|
670
|
+
subtitle ? h('span', { className: 'dsh-pl-where' }, subtitle) : null),
|
|
671
|
+
badge || null,
|
|
672
|
+
actions && actions.length ? h('div', { className: 'dsh-pl-acts' }, actions) : null)
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
/** One detail shape, shared by the plugin detail and the market detail. */
|
|
676
|
+
function DetailPanel(options) {
|
|
677
|
+
const { icon, title, facts, onClose, closeLabel, actions, note, children } = options
|
|
678
|
+
const rows = (facts || []).filter((entry) => entry && entry.value)
|
|
679
|
+
return h('aside', { className: 'dsh-pl-detail' },
|
|
680
|
+
h('div', { className: 'dsh-pl-detail-head' },
|
|
681
|
+
icon || h(DefaultPluginIcon),
|
|
682
|
+
h('strong', null, title),
|
|
683
|
+
h('button', { type: 'button', onClick: onClose }, closeLabel)),
|
|
684
|
+
h('div', { className: 'dsh-pl-detail-body' },
|
|
685
|
+
rows.map((entry, index) => h('div', { className: 'dsh-pl-fact', key: index },
|
|
686
|
+
h('span', { className: 'dsh-pl-fact-label' }, entry.label),
|
|
687
|
+
h('span', { className: 'dsh-pl-fact-value' }, entry.value))),
|
|
688
|
+
note ? h('p', { className: 'dsh-pl-note' }, note) : null,
|
|
689
|
+
children || null,
|
|
690
|
+
actions && actions.length ? h('div', { className: 'dsh-pl-actions' }, actions) : null))
|
|
691
|
+
}
|
|
692
|
+
|
|
471
693
|
function DefaultPluginIcon() {
|
|
472
694
|
return h('svg', {
|
|
473
695
|
className: 'dsh-pl-fallback',
|
|
@@ -510,6 +732,19 @@ window.__ModuleLoader__.load({
|
|
|
510
732
|
const [surface, setSurface] = React.useState('plugins')
|
|
511
733
|
const [trend, setTrend] = React.useState(null)
|
|
512
734
|
const [trendStatus, setTrendStatus] = React.useState('')
|
|
735
|
+
const [hoverIndex, setHoverIndex] = React.useState(-1)
|
|
736
|
+
const [hot, setHot] = React.useState(null)
|
|
737
|
+
const [hotError, setHotError] = React.useState('')
|
|
738
|
+
const [busyKey, setBusyKey] = React.useState('')
|
|
739
|
+
const [confirmKey, setConfirmKey] = React.useState('')
|
|
740
|
+
const [selectedMarket, setSelectedMarket] = React.useState(null)
|
|
741
|
+
const [chartMode, setChartMode] = React.useState('cumulative')
|
|
742
|
+
const [hotQuery, setHotQuery] = React.useState('')
|
|
743
|
+
const [marketQuery, setMarketQuery] = React.useState('')
|
|
744
|
+
const [searchResult, setSearchResult] = React.useState(null)
|
|
745
|
+
const [searchStatus, setSearchStatus] = React.useState('')
|
|
746
|
+
const [trendError, setTrendError] = React.useState('')
|
|
747
|
+
const [hotStatus, setHotStatus] = React.useState('')
|
|
513
748
|
const [plugins, setPlugins] = React.useState([])
|
|
514
749
|
const [bundles, setBundles] = React.useState([])
|
|
515
750
|
const [status, setStatus] = React.useState(listPlugins ? 'loading' : 'unavailable')
|
|
@@ -635,33 +870,100 @@ window.__ModuleLoader__.load({
|
|
|
635
870
|
return () => { cancelled = true }
|
|
636
871
|
}, [selectedId, plugins, bundles, loadFacts])
|
|
637
872
|
|
|
873
|
+
const MARKET_CACHE_KEY = 'dsh.plugin-list.market.v1'
|
|
874
|
+
|
|
875
|
+
function todayKey() {
|
|
876
|
+
return new Date().toISOString().slice(0, 10)
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
function readMarketCache() {
|
|
880
|
+
try {
|
|
881
|
+
const raw = JSON.parse(window.localStorage.getItem(MARKET_CACHE_KEY) || '{}')
|
|
882
|
+
return raw && raw.day === todayKey() ? raw : null
|
|
883
|
+
} catch {
|
|
884
|
+
return null
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
function writeMarketCache(patch) {
|
|
889
|
+
try {
|
|
890
|
+
const raw = readMarketCache() || { day: todayKey() }
|
|
891
|
+
window.localStorage.setItem(MARKET_CACHE_KEY, JSON.stringify({ ...raw, ...patch }))
|
|
892
|
+
} catch { /* caching is best effort */ }
|
|
893
|
+
}
|
|
894
|
+
|
|
638
895
|
const loadTrend = React.useCallback(async () => {
|
|
639
|
-
|
|
896
|
+
const cached = readMarketCache()
|
|
897
|
+
if (cached && cached.trend) {
|
|
898
|
+
setTrend(cached.trend)
|
|
899
|
+
setTrendStatus('ready')
|
|
900
|
+
return
|
|
901
|
+
}
|
|
902
|
+
const call = props.loadMarketTrend
|
|
903
|
+
if (typeof call !== 'function') return
|
|
640
904
|
setTrendStatus('loading')
|
|
641
905
|
try {
|
|
642
|
-
const result = await
|
|
906
|
+
const result = await call()
|
|
907
|
+
if (result && result.ok === false) {
|
|
908
|
+
setTrendError((result.error && result.error.message) || '')
|
|
909
|
+
setTrendStatus('error')
|
|
910
|
+
return
|
|
911
|
+
}
|
|
643
912
|
const value = result && result.ok === true ? result.value : result
|
|
644
913
|
if (value && Array.isArray(value.days) && value.days.length) {
|
|
645
914
|
setTrend(value)
|
|
915
|
+
writeMarketCache({ trend: value })
|
|
646
916
|
setTrendStatus('ready')
|
|
647
917
|
} else {
|
|
648
918
|
setTrendStatus('error')
|
|
649
919
|
}
|
|
650
|
-
} catch {
|
|
920
|
+
} catch (failure) {
|
|
921
|
+
setTrendError(failure instanceof Error ? failure.message : '')
|
|
651
922
|
setTrendStatus('error')
|
|
652
923
|
}
|
|
653
|
-
}, [loadMarketTrend])
|
|
924
|
+
}, [props.loadMarketTrend])
|
|
925
|
+
|
|
926
|
+
const loadHot = React.useCallback(async () => {
|
|
927
|
+
const cached = readMarketCache()
|
|
928
|
+
if (cached && cached.hot) {
|
|
929
|
+
setHot(cached.hot)
|
|
930
|
+
setHotStatus('ready')
|
|
931
|
+
return
|
|
932
|
+
}
|
|
933
|
+
const call = props.loadMarketHot
|
|
934
|
+
if (typeof call !== 'function') return
|
|
935
|
+
setHotStatus('loading')
|
|
936
|
+
try {
|
|
937
|
+
const result = await call()
|
|
938
|
+
if (result && result.ok === false) {
|
|
939
|
+
setHotError((result.error && result.error.message) || '')
|
|
940
|
+
setHotStatus('error')
|
|
941
|
+
return
|
|
942
|
+
}
|
|
943
|
+
const value = result && result.ok === true ? result.value : result
|
|
944
|
+
if (value && Array.isArray(value.top) && value.top.length) {
|
|
945
|
+
setHot(value)
|
|
946
|
+
writeMarketCache({ hot: value })
|
|
947
|
+
setHotStatus('ready')
|
|
948
|
+
} else {
|
|
949
|
+
setHotStatus('empty')
|
|
950
|
+
}
|
|
951
|
+
} catch (failure) {
|
|
952
|
+
setHotError(failure instanceof Error ? failure.message : '')
|
|
953
|
+
setHotStatus('error')
|
|
954
|
+
}
|
|
955
|
+
}, [props.loadMarketHot])
|
|
654
956
|
|
|
655
957
|
React.useEffect(() => {
|
|
656
|
-
if (surface
|
|
657
|
-
|
|
958
|
+
if (surface !== 'market') return
|
|
959
|
+
if (trendStatus === '') void loadTrend()
|
|
960
|
+
if (hotStatus === '') void loadHot()
|
|
961
|
+
}, [surface, trendStatus, hotStatus, loadTrend, loadHot])
|
|
658
962
|
|
|
659
963
|
const visible = React.useMemo(() => {
|
|
660
964
|
const rows = plugins.filter((plugin) => {
|
|
661
965
|
const origin = originOf(plugin)
|
|
662
|
-
|
|
663
|
-
if (filter === 'builtin') return origin === 'builtin'
|
|
664
|
-
return true
|
|
966
|
+
return filter === 'builtin' ? origin === 'builtin' : origin === 'installed'
|
|
665
967
|
})
|
|
666
968
|
const rank = (plugin) => {
|
|
667
969
|
const places = placesFor(plugin, guide, seats, localeId)
|
|
@@ -681,13 +983,6 @@ window.__ModuleLoader__.load({
|
|
|
681
983
|
const selected = plugins.find((item) => item.entryId === selectedId) || null
|
|
682
984
|
const selectedBundle = selected ? matchBundle(selected, bundles) : null
|
|
683
985
|
|
|
684
|
-
function fact(label, value) {
|
|
685
|
-
if (!value) return null
|
|
686
|
-
return h('div', { className: 'dsh-pl-fact' },
|
|
687
|
-
h('span', { className: 'dsh-pl-fact-label' }, label),
|
|
688
|
-
h('span', { className: 'dsh-pl-fact-value' }, value))
|
|
689
|
-
}
|
|
690
|
-
|
|
691
986
|
function revealHint(text) {
|
|
692
987
|
const raw = String(text || '')
|
|
693
988
|
if (/Unknown detail method|invalid server-response|unaddressable|not found/i.test(raw)) return textOf(t, 'list.revealRestart')
|
|
@@ -722,64 +1017,73 @@ window.__ModuleLoader__.load({
|
|
|
722
1017
|
const componentText = rows.length
|
|
723
1018
|
? `${rows.length}${siblings.length ? ` · ${siblings.join('、')}${rows.length > 7 ? '…' : ''}` : ''}`
|
|
724
1019
|
: ''
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
1020
|
+
const detailFacts = [
|
|
1021
|
+
{ label: textOf(t, 'list.intro'), value: intro || textOf(t, 'list.unknown') },
|
|
1022
|
+
{ label: textOf(t, 'list.module'), value: shortModule(plugin.moduleName) },
|
|
1023
|
+
{ label: textOf(t, 'list.entry'), value: plugin.entryId },
|
|
1024
|
+
{ label: textOf(t, 'list.status'), value: textOf(t, phaseKey(plugin)) },
|
|
1025
|
+
{ label: textOf(t, 'list.package'), value: (selectedBundle && selectedBundle.name) || (manifest && manifest.name) || shortModule(plugin.moduleName) },
|
|
1026
|
+
{ label: textOf(t, 'list.version'), value: (selectedBundle && selectedBundle.version) || (manifest && manifest.version) },
|
|
1027
|
+
{ label: textOf(t, 'list.place'), value: place },
|
|
1028
|
+
{ label: textOf(t, 'list.source'), value: source },
|
|
1029
|
+
{ label: textOf(t, 'list.flags'), value: flags },
|
|
1030
|
+
{ label: textOf(t, 'list.components'), value: componentText },
|
|
1031
|
+
{ label: textOf(t, 'list.license'), value: manifest && manifest.license },
|
|
1032
|
+
{ label: textOf(t, 'list.repo'), value: manifest && manifest.repository },
|
|
1033
|
+
{ label: textOf(t, 'list.engines'), value: manifest && manifest.engines },
|
|
1034
|
+
{ label: textOf(t, 'list.keywords'), value: manifest && Array.isArray(manifest.keywords) ? manifest.keywords.join('、') : '' },
|
|
1035
|
+
{ label: textOf(t, 'list.inject'), value: manifest && Array.isArray(manifest.clientInject) ? manifest.clientInject.join('、') : '' },
|
|
1036
|
+
]
|
|
1037
|
+
|
|
1038
|
+
const detailActions = []
|
|
1039
|
+
const openKind = tabKindFor(plugin, guide, localeId)
|
|
1040
|
+
if (openKind && typeof openPanel === 'function') {
|
|
1041
|
+
detailActions.push(h('button', {
|
|
1042
|
+
key: 'open',
|
|
1043
|
+
type: 'button',
|
|
1044
|
+
onClick: () => openPanel(openKind),
|
|
1045
|
+
}, textOf(t, 'list.open')))
|
|
1046
|
+
}
|
|
1047
|
+
if (typeof props.revealPackage === 'function') {
|
|
1048
|
+
detailActions.push(h('button', {
|
|
1049
|
+
key: 'reveal',
|
|
1050
|
+
type: 'button',
|
|
1051
|
+
onClick: () => {
|
|
1052
|
+
setRevealNote('')
|
|
1053
|
+
const source = selectedBundle && selectedBundle.source
|
|
1054
|
+
Promise.resolve(props.revealPackage({ moduleName: plugin.moduleName || '', source: source || '' })).then((result) => {
|
|
1055
|
+
if (result && result.ok === false) setRevealNote(revealHint(result.error && result.error.message))
|
|
1056
|
+
}).catch((failure) => {
|
|
1057
|
+
setRevealNote(revealHint(failure instanceof Error ? failure.message : ''))
|
|
1058
|
+
})
|
|
1059
|
+
},
|
|
1060
|
+
}, textOf(t, 'list.reveal')))
|
|
1061
|
+
}
|
|
1062
|
+
if (plugin.patchId && plugin.readOnlyReason == null && setPluginEnabled) {
|
|
1063
|
+
detailActions.push(h('button', {
|
|
1064
|
+
key: 'toggle',
|
|
1065
|
+
type: 'button',
|
|
1066
|
+
onClick: () => { void setPluginEnabled(plugin.entryId, plugin.enabled !== true).then(() => load()) },
|
|
1067
|
+
}, plugin.enabled ? textOf(t, 'list.disable') : textOf(t, 'list.enable')))
|
|
1068
|
+
}
|
|
1069
|
+
if (revealNote) detailActions.push(h('span', { key: 'note', className: 'dsh-pl-note' }, revealNote))
|
|
1070
|
+
if (plugin.readOnlyReason) detailActions.push(h('span', { key: 'locked', className: 'dsh-pl-note' }, String(plugin.readOnlyReason)))
|
|
1071
|
+
|
|
1072
|
+
return h(DetailPanel, {
|
|
1073
|
+
icon: icon ? h('img', { src: icon, alt: '' }) : null,
|
|
1074
|
+
title,
|
|
1075
|
+
closeLabel: textOf(t, 'list.close'),
|
|
1076
|
+
onClose: () => setSelectedId(''),
|
|
1077
|
+
facts: detailFacts,
|
|
1078
|
+
actions: detailActions,
|
|
1079
|
+
children: h('section', { className: 'dsh-pl-doc-block' },
|
|
1080
|
+
h('div', { className: 'dsh-pl-fact-label' }, textOf(t, 'list.doc')),
|
|
1081
|
+
readmeStatus === 'loading'
|
|
1082
|
+
? h('p', null, textOf(t, 'list.docLoading'))
|
|
1083
|
+
: (readme || (facts && facts.document))
|
|
1084
|
+
? markdownView(readme || facts.document)
|
|
1085
|
+
: h('p', null, textOf(t, 'list.docEmpty'))),
|
|
1086
|
+
})
|
|
783
1087
|
}
|
|
784
1088
|
|
|
785
1089
|
function togglePin(entryId) {
|
|
@@ -801,6 +1105,168 @@ window.__ModuleLoader__.load({
|
|
|
801
1105
|
}, label)
|
|
802
1106
|
}
|
|
803
1107
|
|
|
1108
|
+
function isInstalled(name) {
|
|
1109
|
+
const target = String(name || '')
|
|
1110
|
+
return plugins.some((plugin) => {
|
|
1111
|
+
const moduleName = String(plugin.moduleName || '')
|
|
1112
|
+
return moduleName === target || plugin.patchId === target || plugin.entryId === `include:${target}`
|
|
1113
|
+
})
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
async function installFromMarket(name) {
|
|
1117
|
+
if (typeof props.installBundle !== 'function' || busyKey) return
|
|
1118
|
+
setBusyKey(name)
|
|
1119
|
+
try {
|
|
1120
|
+
const result = await props.installBundle(String(name))
|
|
1121
|
+
const value = result && result.ok === true ? result.value : result
|
|
1122
|
+
if (value && value.application === 'failed') {
|
|
1123
|
+
setHotError((value.error && (value.error.diagnostic || value.error.code)) || '')
|
|
1124
|
+
} else if (value && Array.isArray(value.pendingBuilds) && value.pendingBuilds.length) {
|
|
1125
|
+
setHotError(`需要批准构建脚本:${value.pendingBuilds.join(', ')}`)
|
|
1126
|
+
}
|
|
1127
|
+
await load()
|
|
1128
|
+
} catch (failure) {
|
|
1129
|
+
setHotError(failure instanceof Error ? failure.message : '')
|
|
1130
|
+
} finally {
|
|
1131
|
+
setBusyKey('')
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
async function removeFromMarket(name) {
|
|
1136
|
+
if (typeof props.removeBundle !== 'function' || busyKey) return
|
|
1137
|
+
setBusyKey(name)
|
|
1138
|
+
setConfirmKey('')
|
|
1139
|
+
try {
|
|
1140
|
+
const result = await props.removeBundle(String(name))
|
|
1141
|
+
const value = result && result.ok === true ? result.value : result
|
|
1142
|
+
if (value && value.application === 'failed') {
|
|
1143
|
+
setHotError((value.error && (value.error.diagnostic || value.error.code)) || '')
|
|
1144
|
+
}
|
|
1145
|
+
await load()
|
|
1146
|
+
} catch (failure) {
|
|
1147
|
+
setHotError(failure instanceof Error ? failure.message : '')
|
|
1148
|
+
} finally {
|
|
1149
|
+
setBusyKey('')
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
function hotAction(row) {
|
|
1154
|
+
const name = row.name
|
|
1155
|
+
if (busyKey === name) {
|
|
1156
|
+
return h('span', { className: 'dsh-pl-busy' }, isInstalled(name) ? textOf(t, 'list.removing') : textOf(t, 'list.installing'))
|
|
1157
|
+
}
|
|
1158
|
+
if (isInstalled(name)) {
|
|
1159
|
+
return confirmKey === name
|
|
1160
|
+
? h('button', {
|
|
1161
|
+
type: 'button',
|
|
1162
|
+
className: 'dsh-pl-card-btn',
|
|
1163
|
+
onClick: (event) => { event.stopPropagation(); void removeFromMarket(name) },
|
|
1164
|
+
}, textOf(t, 'list.uninstallConfirm'))
|
|
1165
|
+
: h('button', {
|
|
1166
|
+
type: 'button',
|
|
1167
|
+
className: 'dsh-pl-card-btn',
|
|
1168
|
+
onClick: (event) => { event.stopPropagation(); setConfirmKey(name) },
|
|
1169
|
+
}, textOf(t, 'list.uninstall'))
|
|
1170
|
+
}
|
|
1171
|
+
return h('button', {
|
|
1172
|
+
type: 'button',
|
|
1173
|
+
className: 'dsh-pl-card-btn',
|
|
1174
|
+
onClick: (event) => { event.stopPropagation(); void installFromMarket(name) },
|
|
1175
|
+
}, textOf(t, 'list.install'))
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
function hotView() {
|
|
1179
|
+
const rows = hot && Array.isArray(hot.top) ? hot.top : []
|
|
1180
|
+
const query = hotQuery.trim().toLowerCase()
|
|
1181
|
+
const shown = query
|
|
1182
|
+
? rows.filter((row) => [row.name, row.summary, row.category]
|
|
1183
|
+
.filter(Boolean)
|
|
1184
|
+
.some((field) => String(field).toLowerCase().includes(query)))
|
|
1185
|
+
: rows
|
|
1186
|
+
return h('section', { className: 'dsh-pl-hot' },
|
|
1187
|
+
h('div', { className: 'dsh-pl-block-head' },
|
|
1188
|
+
h('strong', null, textOf(t, 'list.hotTitle')),
|
|
1189
|
+
hot && hot.fromDay
|
|
1190
|
+
? h('span', null, rows.length === shown.length ? `${hot.fromDay} → ${hot.toDay}` : `${shown.length} / ${rows.length}`)
|
|
1191
|
+
: null),
|
|
1192
|
+
h('p', { className: 'dsh-pl-note' }, textOf(t, 'list.hotHint')),
|
|
1193
|
+
hotStatus === 'loading'
|
|
1194
|
+
? h('p', { className: 'dsh-pl-state' }, textOf(t, 'list.hotLoading'))
|
|
1195
|
+
: hotStatus === 'error'
|
|
1196
|
+
? h('p', { className: 'dsh-pl-state' }, hotError ? `${textOf(t, 'list.hotError')}(${hotError})` : textOf(t, 'list.hotError'))
|
|
1197
|
+
: rows.length === 0
|
|
1198
|
+
? h('p', { className: 'dsh-pl-state' }, textOf(t, 'list.hotEmpty'))
|
|
1199
|
+
: shown.length === 0
|
|
1200
|
+
? h('p', { className: 'dsh-pl-state' }, textOf(t, 'list.hotNoMatch'))
|
|
1201
|
+
: h('div', { className: `dsh-pl-grid${view === 'card' ? ' is-card' : ''}` },
|
|
1202
|
+
shown.map((row, index) => h(PluginCard, {
|
|
1203
|
+
key: row.repo || row.name || index,
|
|
1204
|
+
icon: row.icon ? h('img', { src: row.icon, alt: '', draggable: false }) : null,
|
|
1205
|
+
title: row.name,
|
|
1206
|
+
subtitle: row.summary || row.category || '',
|
|
1207
|
+
className: 'is-market',
|
|
1208
|
+
badge: h('span', { className: 'dsh-pl-delta' }, `+${row.delta.toLocaleString()}`),
|
|
1209
|
+
onClick: () => setSelectedMarket(row),
|
|
1210
|
+
actions: [hotAction(row)],
|
|
1211
|
+
}))))
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
const searchTimer = React.useRef(null)
|
|
1215
|
+
|
|
1216
|
+
const runMarketSearch = React.useCallback((query) => {
|
|
1217
|
+
const call = props.searchCatalog
|
|
1218
|
+
if (typeof call !== 'function') return
|
|
1219
|
+
if (!query.trim()) {
|
|
1220
|
+
setSearchResult(null)
|
|
1221
|
+
setSearchStatus('')
|
|
1222
|
+
return
|
|
1223
|
+
}
|
|
1224
|
+
setSearchStatus('loading')
|
|
1225
|
+
Promise.resolve(call({ query })).then((result) => {
|
|
1226
|
+
if (result && result.ok === false) {
|
|
1227
|
+
setSearchResult(null)
|
|
1228
|
+
setSearchStatus('error')
|
|
1229
|
+
return
|
|
1230
|
+
}
|
|
1231
|
+
const value = result && result.ok === true ? result.value : result
|
|
1232
|
+
setSearchResult(value && Array.isArray(value.items) ? value.items : [])
|
|
1233
|
+
setSearchStatus('ready')
|
|
1234
|
+
}).catch(() => {
|
|
1235
|
+
setSearchResult(null)
|
|
1236
|
+
setSearchStatus('error')
|
|
1237
|
+
})
|
|
1238
|
+
}, [props.searchCatalog])
|
|
1239
|
+
|
|
1240
|
+
function onMarketQuery(value) {
|
|
1241
|
+
setMarketQuery(value)
|
|
1242
|
+
if (searchTimer.current) clearTimeout(searchTimer.current)
|
|
1243
|
+
searchTimer.current = setTimeout(() => runMarketSearch(value), 300)
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
function marketDetail(row) {
|
|
1247
|
+
const installed = isInstalled(row.name)
|
|
1248
|
+
const facts = [
|
|
1249
|
+
typeof row.delta === 'number'
|
|
1250
|
+
? { label: textOf(t, 'list.hotDelta'), value: `+${row.delta.toLocaleString()}` }
|
|
1251
|
+
: null,
|
|
1252
|
+
typeof row.stars === 'number'
|
|
1253
|
+
? { label: textOf(t, 'list.hotStars'), value: row.stars.toLocaleString() }
|
|
1254
|
+
: null,
|
|
1255
|
+
row.version ? { label: textOf(t, 'list.version'), value: row.version } : null,
|
|
1256
|
+
row.category ? { label: textOf(t, 'list.hotCategory'), value: row.category } : null,
|
|
1257
|
+
row.repo ? { label: textOf(t, 'list.hotRepo'), value: row.repo } : null,
|
|
1258
|
+
{ label: textOf(t, 'list.status'), value: installed ? textOf(t, 'list.installedState') : textOf(t, 'list.notInstalled') },
|
|
1259
|
+
].filter(Boolean)
|
|
1260
|
+
return h(DetailPanel, {
|
|
1261
|
+
title: row.name,
|
|
1262
|
+
closeLabel: textOf(t, 'list.close'),
|
|
1263
|
+
onClose: () => setSelectedMarket(null),
|
|
1264
|
+
facts,
|
|
1265
|
+
note: row.summary || '',
|
|
1266
|
+
actions: [hotAction(row)],
|
|
1267
|
+
})
|
|
1268
|
+
}
|
|
1269
|
+
|
|
804
1270
|
function marketView() {
|
|
805
1271
|
const days = trend && Array.isArray(trend.days) ? trend.days : []
|
|
806
1272
|
const first = days[0]
|
|
@@ -809,22 +1275,63 @@ window.__ModuleLoader__.load({
|
|
|
809
1275
|
? Math.max(1, Math.round((Date.parse(last.day) - Date.parse(first.day)) / 86400000))
|
|
810
1276
|
: 0
|
|
811
1277
|
const perDay = first && last ? Math.round((last.count - first.count) / Math.max(span, 1)) : 0
|
|
1278
|
+
const query = marketQuery.trim()
|
|
1279
|
+
const searchBox = h('input', {
|
|
1280
|
+
type: 'search',
|
|
1281
|
+
className: 'dsh-pl-search',
|
|
1282
|
+
placeholder: textOf(t, 'list.marketSearch'),
|
|
1283
|
+
value: marketQuery,
|
|
1284
|
+
onChange: (event) => onMarketQuery(event.target.value),
|
|
1285
|
+
})
|
|
1286
|
+
if (query) {
|
|
1287
|
+
const items = Array.isArray(searchResult) ? searchResult : []
|
|
1288
|
+
return h('div', { className: 'dsh-pl-market' },
|
|
1289
|
+
searchBox,
|
|
1290
|
+
searchStatus === 'loading'
|
|
1291
|
+
? h('p', { className: 'dsh-pl-state' }, textOf(t, 'list.marketSearching'))
|
|
1292
|
+
: searchStatus === 'error'
|
|
1293
|
+
? h('p', { className: 'dsh-pl-state' }, textOf(t, 'list.searchError'))
|
|
1294
|
+
: items.length === 0
|
|
1295
|
+
? h('p', { className: 'dsh-pl-state' }, textOf(t, 'list.marketNoMatch'))
|
|
1296
|
+
: h('div', null,
|
|
1297
|
+
h('div', { className: 'dsh-pl-block-head' },
|
|
1298
|
+
h('strong', null, textOf(t, 'list.marketHits').replace('{n}', String(items.length)))),
|
|
1299
|
+
h('div', { className: `dsh-pl-grid${view === 'card' ? ' is-card' : ''}` },
|
|
1300
|
+
items.map((row, index) => h(PluginCard, {
|
|
1301
|
+
key: row.name || index,
|
|
1302
|
+
icon: null,
|
|
1303
|
+
title: row.name,
|
|
1304
|
+
subtitle: row.summary || row.category || '',
|
|
1305
|
+
className: 'is-market',
|
|
1306
|
+
onClick: () => setSelectedMarket(row),
|
|
1307
|
+
actions: [hotAction(row)],
|
|
1308
|
+
})))))
|
|
1309
|
+
}
|
|
812
1310
|
return h('div', { className: 'dsh-pl-market' },
|
|
813
|
-
|
|
814
|
-
h('strong', null, textOf(t, 'list.marketTitle')),
|
|
815
|
-
h('p', { className: 'dsh-pl-note' }, textOf(t, 'list.marketHint'))),
|
|
1311
|
+
searchBox,
|
|
816
1312
|
trendStatus === 'loading'
|
|
817
1313
|
? h('p', { className: 'dsh-pl-state' }, textOf(t, 'list.marketLoading'))
|
|
818
1314
|
: trendStatus === 'error'
|
|
819
1315
|
? h('p', { className: 'dsh-pl-state' }, textOf(t, 'list.marketError'))
|
|
820
1316
|
: days.length
|
|
821
1317
|
? [
|
|
822
|
-
h('
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
1318
|
+
h('section', { className: 'dsh-pl-block', key: 'trend' },
|
|
1319
|
+
h('div', { className: 'dsh-pl-block-head' },
|
|
1320
|
+
h('strong', null, textOf(t, 'list.chartTitle')),
|
|
1321
|
+
h('div', { className: 'dsh-pl-seg', role: 'group', 'aria-label': 'chart' },
|
|
1322
|
+
chip('cumulative', textOf(t, 'list.chartCumulative'), chartMode, setChartMode),
|
|
1323
|
+
chip('daily', textOf(t, 'list.chartDaily'), chartMode, setChartMode))),
|
|
1324
|
+
h('div', { className: 'dsh-pl-market-stats' },
|
|
1325
|
+
stat(textOf(t, 'list.countNow'), last.count.toLocaleString()),
|
|
1326
|
+
stat(textOf(t, 'list.countFiltered'), (last.rejected || 0).toLocaleString()),
|
|
1327
|
+
stat(textOf(t, 'list.perDay'), `+${perDay.toLocaleString()}`),
|
|
1328
|
+
stat(textOf(t, 'list.spanDays'), String(days.length))),
|
|
1329
|
+
h('div', { className: 'dsh-pl-chart-wrap' },
|
|
1330
|
+
chartMode === 'daily'
|
|
1331
|
+
? barChart(days, hoverIndex, setHoverIndex)
|
|
1332
|
+
: trendChart(days, hoverIndex, setHoverIndex, textOf(t, 'list.marketFirstNote'))),
|
|
1333
|
+
h('p', { className: 'dsh-pl-note' }, chartMode === 'daily' ? textOf(t, 'list.chartDailyHint') : textOf(t, 'list.marketHint'))),
|
|
1334
|
+
hotView(),
|
|
828
1335
|
]
|
|
829
1336
|
: null)
|
|
830
1337
|
}
|
|
@@ -840,21 +1347,28 @@ window.__ModuleLoader__.load({
|
|
|
840
1347
|
h('header', { className: 'dsh-pl-bar' },
|
|
841
1348
|
h('div', { className: 'dsh-pl-tools' },
|
|
842
1349
|
h('div', { className: 'dsh-pl-seg', role: 'group', 'aria-label': 'filter' },
|
|
843
|
-
chip('installed', textOf(t, 'list.installed'), filter, (id) => { setFilter(id); remember({ filter: id }) }),
|
|
844
|
-
chip('builtin', textOf(t, 'list.builtin'), filter, (id) => { setFilter(id); remember({ filter: id }) }),
|
|
845
|
-
chip('
|
|
846
|
-
h('button', { type: 'button', onClick: () => { void load() } }, textOf(t, 'list.refresh')),
|
|
1350
|
+
chip('installed', textOf(t, 'list.installed'), surface === 'market' ? '' : filter, (id) => { setSurface('plugins'); setFilter(id); remember({ filter: id }) }),
|
|
1351
|
+
chip('builtin', textOf(t, 'list.builtin'), surface === 'market' ? '' : filter, (id) => { setSurface('plugins'); setFilter(id); remember({ filter: id }) }),
|
|
1352
|
+
chip('market', textOf(t, 'list.market'), surface === 'market' ? 'market' : '', () => setSurface('market'))),
|
|
847
1353
|
h('button', {
|
|
848
1354
|
type: 'button',
|
|
849
|
-
className:
|
|
850
|
-
onClick: () =>
|
|
851
|
-
|
|
1355
|
+
className: 'dsh-pl-refresh',
|
|
1356
|
+
onClick: () => {
|
|
1357
|
+
if (surface === 'market') {
|
|
1358
|
+
try { window.localStorage.removeItem('dsh.plugin-list.market.v1') } catch { /* ignore */ }
|
|
1359
|
+
setTrendStatus('')
|
|
1360
|
+
setHotStatus('')
|
|
1361
|
+
} else {
|
|
1362
|
+
void load()
|
|
1363
|
+
}
|
|
1364
|
+
},
|
|
1365
|
+
}, textOf(t, 'list.refresh')),
|
|
852
1366
|
h('div', { className: 'dsh-pl-view', role: 'group', 'aria-label': 'view' },
|
|
853
1367
|
chip('list', textOf(t, 'list.viewList'), view, (id) => { setView(id); remember({ layout: id }) }),
|
|
854
1368
|
chip('card', textOf(t, 'list.viewCard'), view, (id) => { setView(id); remember({ layout: id }) })),
|
|
855
1369
|
h('span', { className: 'dsh-pl-version' }, VERSION))),
|
|
856
1370
|
surface === 'market'
|
|
857
|
-
? marketView()
|
|
1371
|
+
? selectedMarket ? marketDetail(selectedMarket) : marketView()
|
|
858
1372
|
: status === 'loading' && plugins.length === 0
|
|
859
1373
|
? h('p', { className: 'dsh-pl-state' }, textOf(t, 'list.loading'))
|
|
860
1374
|
: status === 'unavailable'
|
|
@@ -871,66 +1385,80 @@ window.__ModuleLoader__.load({
|
|
|
871
1385
|
? h('p', { className: 'dsh-pl-state' }, textOf(t, 'list.empty'))
|
|
872
1386
|
: visible.map((plugin) => {
|
|
873
1387
|
const title = pluginTitle(plugin, localeId)
|
|
874
|
-
const
|
|
1388
|
+
const iconUrl = safeIcon(plugin.meta && plugin.meta.icon)
|
|
875
1389
|
const kind = tabKindFor(plugin, guide, localeId)
|
|
876
1390
|
const pinned = pins.includes(plugin.entryId)
|
|
877
|
-
|
|
1391
|
+
const places = placesFor(plugin, guide, seats, localeId)
|
|
1392
|
+
.map((place) => textOf(t, place === 'right' ? 'list.placeRight' : place === 'settings' ? 'list.placeSettings' : 'list.placeLeft'))
|
|
1393
|
+
.join(' · ') || textOf(t, 'list.placeNone')
|
|
1394
|
+
const classes = [
|
|
1395
|
+
selectedId === plugin.entryId ? 'is-on' : '',
|
|
1396
|
+
pinned ? 'is-pinned' : '',
|
|
1397
|
+
plugin.fiberPhase === 'failed' ? 'is-failed' : '',
|
|
1398
|
+
].filter(Boolean).join(' ')
|
|
1399
|
+
return h(PluginCard, {
|
|
878
1400
|
key: plugin.entryId,
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
1401
|
+
icon: iconUrl ? h('img', { src: iconUrl, alt: '', draggable: false }) : null,
|
|
1402
|
+
title,
|
|
1403
|
+
subtitle: places,
|
|
1404
|
+
className: classes,
|
|
882
1405
|
onClick: () => setSelectedId(plugin.entryId),
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
'
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
togglePin(plugin.entryId)
|
|
906
|
-
},
|
|
907
|
-
}, pinned ? textOf(t, 'list.unpin') : textOf(t, 'list.pin')),
|
|
908
|
-
kind ? h('button', {
|
|
909
|
-
type: 'button',
|
|
910
|
-
className: 'dsh-pl-open',
|
|
911
|
-
onClick: (event) => {
|
|
912
|
-
event.stopPropagation()
|
|
913
|
-
if (typeof openPanel === 'function') openPanel(kind)
|
|
914
|
-
},
|
|
915
|
-
}, textOf(t, 'list.open')) : null))
|
|
1406
|
+
actions: [
|
|
1407
|
+
h('button', {
|
|
1408
|
+
key: 'pin',
|
|
1409
|
+
type: 'button',
|
|
1410
|
+
className: 'dsh-pl-card-btn',
|
|
1411
|
+
'aria-pressed': pinned,
|
|
1412
|
+
onClick: (event) => {
|
|
1413
|
+
event.stopPropagation()
|
|
1414
|
+
togglePin(plugin.entryId)
|
|
1415
|
+
},
|
|
1416
|
+
}, pinned ? textOf(t, 'list.unpin') : textOf(t, 'list.pin')),
|
|
1417
|
+
kind ? h('button', {
|
|
1418
|
+
key: 'open',
|
|
1419
|
+
type: 'button',
|
|
1420
|
+
className: 'dsh-pl-card-btn',
|
|
1421
|
+
onClick: (event) => {
|
|
1422
|
+
event.stopPropagation()
|
|
1423
|
+
if (typeof openPanel === 'function') openPanel(kind)
|
|
1424
|
+
},
|
|
1425
|
+
}, textOf(t, 'list.open')) : null,
|
|
1426
|
+
],
|
|
1427
|
+
})
|
|
916
1428
|
}))))
|
|
917
1429
|
}
|
|
918
1430
|
|
|
919
1431
|
const CSS = `
|
|
920
1432
|
.dsh-pl, .dsh-pl * { box-sizing: border-box; }
|
|
921
|
-
.dsh-pl {
|
|
1433
|
+
.dsh-pl {
|
|
1434
|
+
--pl-fs-xs: 11px;
|
|
1435
|
+
--pl-fs-sm: 12px;
|
|
1436
|
+
--pl-fs-md: 13px;
|
|
1437
|
+
--pl-fs-lg: 14px;
|
|
1438
|
+
--pl-fs-title: 16px;
|
|
1439
|
+
--pl-fs-num: 18px;
|
|
1440
|
+
--pl-gap-1: 4px;
|
|
1441
|
+
--pl-gap-2: 8px;
|
|
1442
|
+
--pl-gap-3: 12px;
|
|
1443
|
+
--pl-radius-card: 16px;
|
|
1444
|
+
--pl-radius-inner: 12px;
|
|
1445
|
+
--pl-radius-chip: 10px;
|
|
1446
|
+
--pl-radius-pill: 7px;
|
|
1447
|
+
}
|
|
1448
|
+
.dsh-pl { display: flex; flex-direction: column; flex: 1 1 auto; height: 100%; min-height: 0; min-width: 0; padding-top: 8px; color: var(--dsw-alias-label-primary); background: var(--dsw-alias-bg-base); font-family: inherit; font-size: var(--pl-fs-md); line-height: 1.4; }
|
|
922
1449
|
.dsh-pl-bar { display: flex; flex: none; flex-direction: column; gap: 8px; margin: 0; padding: 12px 12px 10px; border-bottom: 1px solid var(--dsw-alias-border-l1); background: var(--dsw-specific-sidebar-fill, var(--dsw-alias-bg-layer-1)); position: relative; z-index: 1; }
|
|
923
1450
|
.dsh-pl-top { display: flex; align-items: center; justify-content: space-between; }
|
|
924
|
-
.dsh-pl-version, .dsh-pl-hint, .dsh-pl-item span { color: var(--dsw-alias-label-secondary); font-size:
|
|
1451
|
+
.dsh-pl-version, .dsh-pl-hint, .dsh-pl-item span { color: var(--dsw-alias-label-secondary); font-size: var(--pl-fs-xs); }
|
|
925
1452
|
.dsh-pl-hint { margin: 0; }
|
|
926
1453
|
.dsh-pl-tools { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; width: 100%; }
|
|
927
|
-
.dsh-pl-
|
|
928
|
-
.dsh-pl-
|
|
929
|
-
.dsh-pl-view
|
|
1454
|
+
.dsh-pl-refresh { margin-left: auto; }
|
|
1455
|
+
.dsh-pl-version { margin-left: 10px; }
|
|
1456
|
+
.dsh-pl-view { display: flex; gap: 2px; padding: 2px; border-radius: var(--pl-radius-inner); background: var(--dsw-alias-bg-layer-2); }
|
|
1457
|
+
.dsh-pl-view button { border: 0; border-radius: var(--pl-radius-pill); padding: 3px 8px; font-size: var(--pl-fs-xs); background: transparent; color: var(--dsw-alias-label-secondary); }
|
|
930
1458
|
.dsh-pl-view button.is-on { background: var(--dsw-alias-bg-base); color: var(--dsw-alias-label-primary); font-weight: 600; }
|
|
931
|
-
.dsh-pl-seg { display: flex; gap: 2px; padding: 3px; border: 0; border-radius:
|
|
932
|
-
.dsh-pl button { font: inherit; font-size:
|
|
933
|
-
.dsh-pl-seg button { border: 0; border-radius:
|
|
1459
|
+
.dsh-pl-seg { display: flex; gap: 2px; padding: 3px; border: 0; border-radius: var(--pl-radius-inner); background: var(--dsw-alias-bg-layer-2); }
|
|
1460
|
+
.dsh-pl button { font: inherit; font-size: var(--pl-fs-sm); line-height: 1.2; color: var(--dsw-alias-label-primary); background: var(--dsw-alias-bg-base); border: 1px solid var(--dsw-alias-border-l1); border-radius: var(--pl-radius-chip); padding: 6px 12px; cursor: pointer; }
|
|
1461
|
+
.dsh-pl-seg button { border: 0; border-radius: var(--pl-radius-pill); background: transparent; color: var(--dsw-alias-label-secondary); padding: 5px 12px; }
|
|
934
1462
|
.dsh-pl-seg button.is-on { background: var(--dsw-alias-bg-base); color: var(--dsw-alias-label-primary); font-weight: 600; box-shadow: none; }
|
|
935
1463
|
.dsh-pl-item.is-on { border-color: var(--dsw-alias-brand-primary); background: var(--dsw-alias-bg-layer-2); box-shadow: inset 0 0 0 1px var(--dsw-alias-brand-primary); }
|
|
936
1464
|
.dsh-pl-body { display: flex; flex: 1 1 auto; min-height: 0; min-width: 0; overflow: hidden; }
|
|
@@ -942,54 +1470,67 @@ window.__ModuleLoader__.load({
|
|
|
942
1470
|
.dsh-pl-acts { display: flex; gap: 6px; align-items: center; margin-left: auto; flex: none; }
|
|
943
1471
|
.dsh-pl-item { position: relative; }
|
|
944
1472
|
.dsh-pl-grid.is-card .dsh-pl-acts { position: absolute; top: 8px; right: 8px; margin: 0; padding: 0; background: transparent; opacity: 0; pointer-events: none; transition: opacity .12s ease; }
|
|
945
|
-
.dsh-pl-grid.is-card .dsh-pl-acts .dsh-pl-
|
|
1473
|
+
.dsh-pl-grid.is-card .dsh-pl-acts .dsh-pl-card-btn { padding: 4px 8px; font-size: var(--pl-fs-xs); border: 0; background: var(--dsw-alias-bg-layer-2); }
|
|
946
1474
|
.dsh-pl-grid.is-card .dsh-pl-item:hover .dsh-pl-acts, .dsh-pl-grid.is-card .dsh-pl-item:focus-within .dsh-pl-acts { opacity: 1; pointer-events: auto; }
|
|
947
|
-
.dsh-pl-item { display: flex; gap: 10px; align-items: center; width: 100%; min-width: 0; min-height: 64px; padding: 12px; text-align: left; border: 1px solid var(--dsw-alias-border-l2); border-radius:
|
|
948
|
-
.dsh-pl-
|
|
1475
|
+
.dsh-pl-item { display: flex; gap: 10px; align-items: center; width: 100%; min-width: 0; min-height: 64px; padding: 12px; text-align: left; border: 1px solid var(--dsw-alias-border-l2); border-radius: var(--pl-radius-card); background: var(--dsw-alias-bg-layer-1); overflow: hidden; }
|
|
1476
|
+
.dsh-pl-card-btn { margin-left: 0; flex: none; position: static; }
|
|
949
1477
|
.dsh-pl-item.is-pinned { background: color-mix(in srgb, var(--dsw-alias-label-primary) 7%, var(--dsw-alias-bg-layer-1)); }
|
|
950
|
-
.dsh-pl-item img, .dsh-pl-fallback { width: 22px; height: 22px; border-radius:
|
|
1478
|
+
.dsh-pl-item img, .dsh-pl-fallback { width: 22px; height: 22px; border-radius: var(--pl-radius-pill); object-fit: cover; flex: none; }
|
|
951
1479
|
.dsh-pl-copy { min-width: 0; flex: 1 1 auto; display: flex; flex-direction: column; gap: 2px; }
|
|
952
|
-
.dsh-pl-copy strong { font-size:
|
|
953
|
-
.dsh-pl-where { color: var(--dsw-alias-label-secondary); font-size:
|
|
1480
|
+
.dsh-pl-copy strong { font-size: var(--pl-fs-lg); font-weight: 560; color: var(--dsw-alias-label-primary); white-space: normal; overflow-wrap: anywhere; }
|
|
1481
|
+
.dsh-pl-where { color: var(--dsw-alias-label-secondary); font-size: var(--pl-fs-xs); }
|
|
1482
|
+
.dsh-pl-item.is-market .dsh-pl-where { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
1483
|
+
.dsh-pl-item.is-market .dsh-pl-copy strong { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
954
1484
|
.dsh-pl-item.is-failed { border-color: var(--dsw-alias-state-error-primary); }
|
|
955
1485
|
.dsh-pl-state { margin: 16px; color: var(--dsw-alias-label-secondary); }
|
|
956
1486
|
.dsh-pl-detail { flex: 1 1 auto; width: auto; max-width: none; min-width: 0; min-height: 0; overflow: auto; background: var(--dsw-alias-bg-base); padding: 12px 16px 20px; }
|
|
957
1487
|
.dsh-pl-detail-head { display: flex; align-items: center; gap: 10px; margin-bottom: 14px; }
|
|
958
|
-
.dsh-pl-detail-head strong { flex: 1 1 auto; min-width: 0; font-size:
|
|
1488
|
+
.dsh-pl-detail-head strong { flex: 1 1 auto; min-width: 0; font-size: var(--pl-fs-title); line-height: 1.35; overflow-wrap: anywhere; }
|
|
959
1489
|
.dsh-pl-detail-head button { flex: none; white-space: nowrap; align-self: flex-start; }
|
|
960
1490
|
.dsh-pl-detail-head img, .dsh-pl-detail-head .dsh-pl-fallback { width: 32px; height: 32px; }
|
|
961
1491
|
.dsh-pl-fact { display: grid; grid-template-columns: 4.5em minmax(0, 1fr); column-gap: 12px; align-items: start; margin: 0 0 10px; }
|
|
962
|
-
.dsh-pl-fact-label { color: var(--dsw-alias-label-secondary); font-size:
|
|
963
|
-
.dsh-pl-fact-value { color: var(--dsw-alias-label-primary); font-size:
|
|
964
|
-
.dsh-pl-detail h3 { margin: 14px 0 6px; font-size:
|
|
965
|
-
.dsh-pl-detail p, .dsh-pl-doc { margin: 0 0 8px; font-size:
|
|
966
|
-
.dsh-pl-note { color: var(--dsw-alias-label-secondary); }
|
|
1492
|
+
.dsh-pl-fact-label { color: var(--dsw-alias-label-secondary); font-size: var(--pl-fs-sm); line-height: 1.45; }
|
|
1493
|
+
.dsh-pl-fact-value { color: var(--dsw-alias-label-primary); font-size: var(--pl-fs-md); line-height: 1.45; overflow-wrap: anywhere; }
|
|
1494
|
+
.dsh-pl-detail h3 { margin: 14px 0 6px; font-size: var(--pl-fs-sm); color: var(--dsw-alias-label-secondary); }
|
|
1495
|
+
.dsh-pl-detail p, .dsh-pl-doc { margin: 0 0 8px; font-size: var(--pl-fs-md); line-height: 1.45; overflow-wrap: anywhere; }
|
|
1496
|
+
.dsh-pl-note { color: var(--dsw-alias-label-secondary); font-size: var(--pl-fs-sm); line-height: 1.5; }
|
|
967
1497
|
.dsh-pl-doc-block { margin-top: 12px; }
|
|
968
|
-
.dsh-pl-md h3, .dsh-pl-md-h { margin: 14px 0 6px; font-size:
|
|
969
|
-
.dsh-pl-md h4 { margin: 10px 0 4px; font-size:
|
|
970
|
-
.dsh-pl-md p, .dsh-pl-md li { margin: 0 0 8px; font-size:
|
|
1498
|
+
.dsh-pl-md h3, .dsh-pl-md-h { margin: 14px 0 6px; font-size: var(--pl-fs-lg); line-height: 1.4; font-weight: 650; color: var(--dsw-alias-label-primary); }
|
|
1499
|
+
.dsh-pl-md h4 { margin: 10px 0 4px; font-size: var(--pl-fs-md); font-weight: 650; }
|
|
1500
|
+
.dsh-pl-md p, .dsh-pl-md li { margin: 0 0 8px; font-size: var(--pl-fs-md); line-height: 1.55; }
|
|
971
1501
|
.dsh-pl-md ul { margin: 0 0 10px; padding-left: 18px; }
|
|
972
|
-
.dsh-pl-md code { font-size:
|
|
1502
|
+
.dsh-pl-md code { font-size: var(--pl-fs-sm); padding: 0 4px; border-radius: 4px; background: var(--dsw-alias-bg-layer-2); }
|
|
973
1503
|
.dsh-pl-md a { color: var(--dsw-alias-brand-primary); }
|
|
974
|
-
.dsh-pl-table-wrap { margin: 8px 0 14px; overflow-x: auto; border: 1px solid var(--dsw-alias-border-l1); border-radius:
|
|
975
|
-
.dsh-pl-md table { width: 100%; border-collapse: collapse; font-size:
|
|
1504
|
+
.dsh-pl-table-wrap { margin: 8px 0 14px; overflow-x: auto; border: 1px solid var(--dsw-alias-border-l1); border-radius: var(--pl-radius-chip); }
|
|
1505
|
+
.dsh-pl-md table { width: 100%; border-collapse: collapse; font-size: var(--pl-fs-md); }
|
|
976
1506
|
.dsh-pl-md th, .dsh-pl-md td { padding: 8px 10px; text-align: left; vertical-align: top; border-bottom: 1px solid var(--dsw-alias-border-l1); }
|
|
977
1507
|
.dsh-pl-md th { color: var(--dsw-alias-label-secondary); font-weight: 650; background: var(--dsw-alias-bg-layer-1); white-space: nowrap; }
|
|
978
1508
|
.dsh-pl-md td:first-child { white-space: nowrap; }
|
|
979
1509
|
.dsh-pl-md tr:last-child td { border-bottom: 0; }
|
|
980
|
-
.dsh-pl-market
|
|
981
|
-
.dsh-pl-market {
|
|
982
|
-
.dsh-pl-
|
|
983
|
-
.dsh-pl-
|
|
984
|
-
.dsh-pl-stat {
|
|
985
|
-
.dsh-pl-
|
|
986
|
-
.dsh-pl-
|
|
987
|
-
.dsh-pl-
|
|
1510
|
+
.dsh-pl-market { flex: 1 1 auto; min-height: 0; overflow: auto; padding: 12px; }
|
|
1511
|
+
.dsh-pl-market-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 10px; margin-bottom: 12px; }
|
|
1512
|
+
.dsh-pl-stat { display: flex; flex-direction: column; gap: 6px; padding: 12px 14px; border: 1px solid var(--dsw-alias-border-l2); border-radius: var(--pl-radius-card); background: var(--dsw-alias-bg-layer-1); }
|
|
1513
|
+
.dsh-pl-stat span { font-size: var(--pl-fs-xs); color: var(--dsw-alias-label-secondary); }
|
|
1514
|
+
.dsh-pl-stat strong { font-size: var(--pl-fs-num); font-weight: 600; line-height: 1.2; }
|
|
1515
|
+
.dsh-pl-chart-wrap { padding: 10px 12px 4px; border: 1px solid var(--dsw-alias-border-l2); border-radius: var(--pl-radius-card); background: var(--dsw-alias-bg-layer-1); }
|
|
1516
|
+
.dsh-pl-chart { display: block; width: 100%; height: auto; }
|
|
1517
|
+
.dsh-pl-block-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 10px; }
|
|
1518
|
+
.dsh-pl-block-head strong { font-size: var(--pl-fs-lg); }
|
|
1519
|
+
.dsh-pl-block-head span { font-size: var(--pl-fs-xs); color: var(--dsw-alias-label-secondary); }
|
|
1520
|
+
.dsh-pl-block { margin-bottom: 22px; }
|
|
1521
|
+
.dsh-pl-market .dsh-pl-note { margin: 10px 2px 0; }
|
|
1522
|
+
.dsh-pl-hot { margin-top: 4px; }
|
|
1523
|
+
.dsh-pl-search { width: 100%; margin: 2px 0 8px; padding: 8px 12px; border: 1px solid var(--dsw-alias-border-l2); border-radius: var(--pl-radius-inner); background: var(--dsw-alias-bg-layer-1); color: var(--dsw-alias-label-primary); font-family: inherit; font-size: var(--pl-fs-sm); }
|
|
1524
|
+
.dsh-pl-search::placeholder { color: var(--dsw-alias-label-secondary); }
|
|
1525
|
+
.dsh-pl-search:focus { outline: none; border-color: var(--dsw-alias-brand-primary); }
|
|
1526
|
+
.dsh-pl-delta { flex: none; margin-left: auto; font-size: var(--pl-fs-sm); font-weight: 600; color: var(--dsw-alias-brand-primary); }
|
|
1527
|
+
.dsh-pl-busy { flex: none; font-size: var(--pl-fs-xs); color: var(--dsw-alias-label-secondary); }
|
|
1528
|
+
.dsh-pl-hot-busy { flex: none; font-size: var(--pl-fs-xs); color: var(--dsw-alias-label-secondary); }
|
|
988
1529
|
.dsh-pl-actions { display: flex; gap: 8px; align-items: center; margin-top: 16px; }
|
|
989
1530
|
`
|
|
990
1531
|
|
|
991
1532
|
return {
|
|
992
|
-
name: 'dsh-
|
|
1533
|
+
name: 'dsh-plugin-panel',
|
|
993
1534
|
inject: ['slots', 'sidebarRightTabs', 'sidebarRight', 'layout', 'locale', 'remote', 'remote.pluginManager', 'remote.session', 'connection'],
|
|
994
1535
|
apply(ctx) {
|
|
995
1536
|
ctx.effect(() => ctx.locale.register(NS, COPY), 'plugin-list: dictionaries')
|
|
@@ -1002,6 +1543,10 @@ window.__ModuleLoader__.load({
|
|
|
1002
1543
|
const rpcCall = ctx.connection.rpc.call.bind(ctx.connection.rpc)
|
|
1003
1544
|
const loadFacts = (payload) => rpcCall('/dsh-plugins-ui-rpc', 'facts', payload || {})
|
|
1004
1545
|
const loadMarketTrend = (payload) => rpcCall('/dsh-plugins-ui-rpc', 'trend', payload || {})
|
|
1546
|
+
const loadMarketHot = (payload) => rpcCall('/dsh-plugins-ui-rpc', 'hot', payload || {})
|
|
1547
|
+
const searchCatalog = (payload) => rpcCall('/dsh-plugins-ui-rpc', 'search', payload || {})
|
|
1548
|
+
const installBundle = pluginManager.installBundle.bind(pluginManager)
|
|
1549
|
+
const removeBundle = pluginManager.removeBundle.bind(pluginManager)
|
|
1005
1550
|
const revealPackage = async (payload) => {
|
|
1006
1551
|
const input = payload && typeof payload === 'object' ? payload : {}
|
|
1007
1552
|
const dir = directoryFromSource(input.source)
|
|
@@ -1085,7 +1630,7 @@ window.__ModuleLoader__.load({
|
|
|
1085
1630
|
name: 'sidebar.right.pane.tab',
|
|
1086
1631
|
key: PANEL_ID,
|
|
1087
1632
|
locale: NS,
|
|
1088
|
-
inject: () => ({ listPlugins, listBundles, readBundleDetail, setPluginEnabled, loadFacts, revealPackage, loadMarketTrend, listGuide, subscribeGuide, listSeats, subscribeSeats, openPanel, t, readLocale, subscribeLocale }),
|
|
1633
|
+
inject: () => ({ listPlugins, listBundles, readBundleDetail, setPluginEnabled, installBundle, removeBundle, loadFacts, revealPackage, loadMarketTrend, loadMarketHot, searchCatalog, listGuide, subscribeGuide, listSeats, subscribeSeats, openPanel, t, readLocale, subscribeLocale }),
|
|
1089
1634
|
}, ListView)), 'plugin-list: tab body')
|
|
1090
1635
|
},
|
|
1091
1636
|
}
|