dsh-plugin 1.2.0 → 1.3.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.
- package/README.en.md +20 -15
- package/README.md +21 -16
- package/client/client.js +684 -446
- package/client/client.js.map +1 -1
- package/cordis.patch.yml +1 -1
- package/lib/http/routes.js +47 -3
- package/lib/index.js +1 -1
- package/lib/services/install/install-types.js +1 -1
- package/lib/services/install/npm-check.js +1 -1
- package/lib/services/install/npm-resolve.js +1 -1
- package/lib/services/install/preflight.js +1 -1
- package/lib/services/install/task-queue.js +1 -1
- package/lib/services/loader.js +1 -1
- package/lib/services/log.js +1 -1
- package/lib/services/probe.js +1 -1
- package/lib/services/profile/installed-versions.js +1 -1
- package/lib/services/profile/profile.js +6 -5
- package/lib/services/profile/progress.js +1 -1
- package/lib/services/settings.js +1 -1
- package/lib/types/index.d.ts +1 -1
- package/lib/types/services/install/install-types.d.ts +1 -1
- package/lib/types/services/install/install.d.ts +1 -1
- package/lib/types/services/profile/pending-restart.d.ts +1 -1
- package/lib/types/services/profile/progress.d.ts +1 -1
- package/package.json +2 -2
- package/src/client/components/PluginHubSection.tsx +83 -13
- package/src/client/components/catalog/CatalogList.tsx +1 -1
- package/src/client/components/catalog/PluginCard.tsx +1 -1
- package/src/client/components/layout/CatalogControls.tsx +1 -1
- package/src/client/components/layout/CatalogHeader.tsx +2 -2
- package/src/client/components/layout/CategoryTabs.tsx +1 -1
- package/src/client/components/layout/SectionTabs.tsx +1 -1
- package/src/client/components/modals/AboutModal.tsx +2 -2
- package/src/client/components/modals/HubUpdateModal.tsx +1 -1
- package/src/client/components/modals/InstalledDetailModal.tsx +1 -1
- package/src/client/components/modals/LogsModal.tsx +1 -1
- package/src/client/components/modals/LogsPathModal.tsx +1 -1
- package/src/client/components/modals/NotificationsModal.tsx +11 -2
- package/src/client/components/modals/ProgressView.tsx +1 -1
- package/src/client/components/modals/modals.tsx +1 -1
- package/src/client/components/ui/Dropdown.tsx +1 -1
- package/src/client/components/ui/Toggle.tsx +1 -1
- package/src/client/components/ui/icons.tsx +22 -1
- package/src/client/components/views/CustomInstallView.tsx +133 -44
- package/src/client/components/views/DiagnosticsView.tsx +2 -2
- package/src/client/components/views/InstalledView.tsx +1 -1
- package/src/client/components/views/LogsView.tsx +1 -1
- package/src/client/components/views/MarketView.tsx +1 -1
- package/src/client/components/views/SettingsView.tsx +1 -1
- package/src/client/data/catalog.ts +1 -1
- package/src/client/data/host.ts +1 -1
- package/src/client/data/hub.ts +12 -3
- package/src/client/hooks/useCatalog.ts +19 -6
- package/src/client/hooks/useIncrementalList.ts +1 -1
- package/src/client/hooks/useLanguage.ts +1 -1
- package/src/client/hooks/useSettings.ts +1 -1
- package/src/client/hooks/useTaskQueue.ts +1 -1
- package/src/client/index.tsx +1 -1
- package/src/client/locales.ts +50 -30
- package/src/client/logic/constants.ts +8 -9
- package/src/client/logic/failures.ts +48 -2
- package/src/client/logic/format.ts +1 -1
- package/src/client/logic/install-command.ts +1 -1
- package/src/client/logic/installed.ts +1 -1
- package/src/client/logic/normalize.ts +1 -1
- package/src/client/logic/renderMarkdown.ts +1 -1
- package/src/client/logic/urls.ts +2 -2
- package/src/client/styles/CustomInstallView.module.css +59 -1
- package/src/client/styles/DiagnosticsView.module.css +1 -1
- package/src/client/styles/Dropdown.module.css +1 -1
- package/src/client/styles/Header.module.css +1 -1
- package/src/client/styles/InstalledView.module.css +19 -6
- package/src/client/styles/List.module.css +11 -1
- package/src/client/styles/LogsView.module.css +1 -1
- package/src/client/styles/Modal.module.css +32 -1
- package/src/client/styles/SectionTabs.module.css +1 -1
- package/src/client/styles/SettingsView.module.css +1 -1
- package/src/client/styles/Toggle.module.css +1 -1
- package/src/client/styles/tokens.module.css +32 -5
- package/src/client/types.ts +1 -1
- package/src/server/http/routes.ts +54 -3
- package/src/server/index.ts +1 -1
- package/src/server/services/install/install-types.ts +1 -1
- package/src/server/services/install/install.ts +1 -1
- package/src/server/services/install/npm-check.ts +1 -1
- package/src/server/services/install/npm-resolve.ts +1 -1
- package/src/server/services/install/preflight.ts +1 -1
- package/src/server/services/install/task-queue.ts +1 -1
- package/src/server/services/loader.ts +1 -1
- package/src/server/services/log.ts +1 -1
- package/src/server/services/probe.ts +1 -1
- package/src/server/services/profile/installed-versions.ts +1 -1
- package/src/server/services/profile/pending-restart.ts +1 -1
- package/src/server/services/profile/profile.ts +6 -5
- package/src/server/services/profile/progress.ts +1 -1
- package/src/server/services/settings.ts +1 -1
package/cordis.patch.yml
CHANGED
package/lib/http/routes.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* DSH
|
|
2
|
+
* DSH Plugin Hub — the community plugin marketplace for DeepSeek Harness.
|
|
3
3
|
* Website: https://dsh-plugin.org
|
|
4
4
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
5
|
*
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* install handler validates the target, then spawns the official dsh CLI
|
|
9
9
|
* (see services/install/install.ts) and reports the captured result back.
|
|
10
10
|
*/
|
|
11
|
-
import { mkdirSync, readFileSync } from 'node:fs';
|
|
11
|
+
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
12
12
|
import { homedir, release } from 'node:os';
|
|
13
13
|
import { dirname, join, resolve } from 'node:path';
|
|
14
14
|
import { spawn } from 'node:child_process';
|
|
@@ -23,6 +23,36 @@ import { appendLog, readLog, logFilePath, defaultLogFilePath, customLogFile } fr
|
|
|
23
23
|
const PROFILE_RE = /^[A-Za-z0-9_-]+$/;
|
|
24
24
|
const BODY_LIMIT_BYTES = 4 * 1024;
|
|
25
25
|
const COMMAND_TIMEOUT_MS = 5 * 60 * 1000;
|
|
26
|
+
/** 目录/统计数据代理的本地缓存时长:1 小时内重复打开插件中心直接读盘,
|
|
27
|
+
* 不重复走 curl 拉远程 —— 重启宿主后首次打开同样秒开(缓存跨重启存活)。 */
|
|
28
|
+
const CATALOG_CACHE_TTL_MS = 60 * 60 * 1000;
|
|
29
|
+
function catalogCacheFile(profile, key) {
|
|
30
|
+
return join(profileDirectory(profile), 'cache', `catalog-${key}.json`);
|
|
31
|
+
}
|
|
32
|
+
/** 读缓存:文件存在、JSON 合法、未过期 → 返回原始响应字符串;否则 null(视为 miss)。 */
|
|
33
|
+
function readCatalogCache(file) {
|
|
34
|
+
try {
|
|
35
|
+
const entry = JSON.parse(readFileSync(file, 'utf8'));
|
|
36
|
+
if (entry && typeof entry.at === 'number' && typeof entry.body === 'string'
|
|
37
|
+
&& Date.now() - entry.at < CATALOG_CACHE_TTL_MS) {
|
|
38
|
+
return entry.body;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
// 文件缺失 / 解析失败 / 字段不完整 → miss,重新拉取覆盖写盘
|
|
43
|
+
}
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
/** 写缓存:目录不存在则创建;写失败静默忽略(缓存只是加速,不影响功能)。 */
|
|
47
|
+
function writeCatalogCache(file, body) {
|
|
48
|
+
try {
|
|
49
|
+
mkdirSync(dirname(file), { recursive: true });
|
|
50
|
+
writeFileSync(file, JSON.stringify({ at: Date.now(), body }));
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
// 磁盘只读/无权限等极端情况:忽略,继续走直连
|
|
54
|
+
}
|
|
55
|
+
}
|
|
26
56
|
function profileDirectory(profile) {
|
|
27
57
|
return join(process.env.DSH_HOME ?? join(homedir(), '.dsh'), 'profiles', profile);
|
|
28
58
|
}
|
|
@@ -439,9 +469,22 @@ export function mountPluginHubRoutes(webServer, profile, loader) {
|
|
|
439
469
|
? settings.proxy
|
|
440
470
|
: (systemProxy() ?? process.env.HTTPS_PROXY ?? process.env.https_proxy ?? '');
|
|
441
471
|
const isStats = url.searchParams.get('stats') === '1';
|
|
472
|
+
const lang = url.searchParams.get('lang') === 'en' ? 'en' : 'zh';
|
|
442
473
|
const target = isStats
|
|
443
474
|
? 'https://api.dsh-plugin.org/stats.json'
|
|
444
|
-
: `https://api.dsh-plugin.org/plugins.${
|
|
475
|
+
: `https://api.dsh-plugin.org/plugins.${lang}.json`;
|
|
476
|
+
// 1 小时本地缓存:命中直接返回(跨重启生效,插件中心秒开),未命中才走 curl 拉远程
|
|
477
|
+
const cacheFile = catalogCacheFile(profile, isStats ? 'stats' : `plugins-${lang}`);
|
|
478
|
+
const cached = readCatalogCache(cacheFile);
|
|
479
|
+
if (cached !== null) {
|
|
480
|
+
try {
|
|
481
|
+
sendJson(response, 200, JSON.parse(cached));
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
catch {
|
|
485
|
+
// 缓存正文损坏:忽略,走重新拉取并覆盖写盘
|
|
486
|
+
}
|
|
487
|
+
}
|
|
445
488
|
const r = await fetchViaCurl(target, proxy, 20000);
|
|
446
489
|
if (!r.ok || r.body === '') {
|
|
447
490
|
sendJson(response, 502, { error: 'catalog fetch failed' });
|
|
@@ -449,6 +492,7 @@ export function mountPluginHubRoutes(webServer, profile, loader) {
|
|
|
449
492
|
}
|
|
450
493
|
try {
|
|
451
494
|
sendJson(response, 200, JSON.parse(r.body));
|
|
495
|
+
writeCatalogCache(cacheFile, r.body);
|
|
452
496
|
}
|
|
453
497
|
catch {
|
|
454
498
|
sendJson(response, 502, { error: 'catalog fetch returned invalid JSON' });
|
package/lib/index.js
CHANGED
package/lib/services/loader.js
CHANGED
package/lib/services/log.js
CHANGED
package/lib/services/probe.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* DSH
|
|
2
|
+
* DSH Plugin Hub — the community plugin marketplace for DeepSeek Harness.
|
|
3
3
|
* Website: https://dsh-plugin.org
|
|
4
4
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
5
|
*
|
|
@@ -27,11 +27,12 @@ export function githubTarget(repo) {
|
|
|
27
27
|
return `git+https://github.com/${repo}.git`;
|
|
28
28
|
}
|
|
29
29
|
/**
|
|
30
|
-
* DSH
|
|
31
|
-
* 非命令输入原样返回。兼容「直接粘贴完整命令」(防用户输错):--profile
|
|
32
|
-
*
|
|
30
|
+
* DSH 命令行前缀(官方唯一形式 `dsh plugin --profile <name> add <target>`)—— 提取其中的安装目标;
|
|
31
|
+
* 非命令输入原样返回。兼容「直接粘贴完整命令」(防用户输错):--profile 必填且只有长选项
|
|
32
|
+
* (官方 CLI 无 -p 简写,省略即报错),支持 `--profile=<name>` 等号形式,命令大小写不敏感。
|
|
33
|
+
* 与客户端 CustomInstallView 口径一致。
|
|
33
34
|
*/
|
|
34
|
-
const DSH_PLUGIN_CMD_RE = /^dsh\s+plugin\s
|
|
35
|
+
const DSH_PLUGIN_CMD_RE = /^dsh\s+plugin\s+--profile(?:\s+|=)\S+\s+add\s+(.+)$/i;
|
|
35
36
|
export function installTargetOf(value) {
|
|
36
37
|
const input = typeof value === 'string' ? value.trim() : '';
|
|
37
38
|
const match = DSH_PLUGIN_CMD_RE.exec(input);
|
package/lib/services/settings.js
CHANGED
package/lib/types/index.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-plugin",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "A community plugin marketplace for DeepSeek Harness, built to the official plugin spec — browse, search and install 4000+ human-curated community plugins without leaving the app. · DeepSeek Harness 社区插件市场(遵循官方开发规范):4000+ 人工精选社区插件,每日更新。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"bugs": {
|
|
15
15
|
"url": "https://github.com/dshplugin/dsh-plugin-hub/issues"
|
|
16
16
|
},
|
|
17
|
-
"author": "DSH
|
|
17
|
+
"author": "DSH Plugin Hub contributors",
|
|
18
18
|
"funding": {
|
|
19
19
|
"type": "custom",
|
|
20
20
|
"url": "https://dsh-plugin.org"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* DSH
|
|
2
|
+
* DSH Plugin Hub — the community plugin marketplace for DeepSeek Harness.
|
|
3
3
|
* Website: https://dsh-plugin.org
|
|
4
4
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
5
|
*
|
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
* folder; it hosts the dialogs/toast and the section-level copy actions.
|
|
10
10
|
*/
|
|
11
11
|
import { createElement as h, useEffect, useRef, useState } from 'react'
|
|
12
|
+
import type { MouseEvent } from 'react'
|
|
12
13
|
import styles from '../styles/Header.module.css'
|
|
14
|
+
import modalStyles from '../styles/Modal.module.css'
|
|
13
15
|
import type { EnvInfo, HubPlugin, SectionProps, ToastState } from '../types.ts'
|
|
14
16
|
import { installCommandOf, repoFromInstallTarget } from '../logic/install-command.ts'
|
|
15
17
|
import { PLUGIN_VERSION } from '../logic/constants.ts'
|
|
@@ -23,7 +25,7 @@ import { AboutModal } from './modals/AboutModal.tsx'
|
|
|
23
25
|
import { HubUpdateModal } from './modals/HubUpdateModal.tsx'
|
|
24
26
|
import { InstalledDetailModal } from './modals/InstalledDetailModal.tsx'
|
|
25
27
|
import { NotificationsModal } from './modals/NotificationsModal.tsx'
|
|
26
|
-
import { addFailure, addSuccess, addUpdateNotice, clearNotifications, loadNotifications, removeNotification } from '../logic/failures.ts'
|
|
28
|
+
import { addFailure, addSuccess, addUpdateNotice, clearNotifications, ignoreUpdate, loadIgnoredUpdates, loadNotifications, removeNotification, removeUpdateNotice } from '../logic/failures.ts'
|
|
27
29
|
import type { NotificationRecord } from '../logic/failures.ts'
|
|
28
30
|
import { CatalogHeader } from './layout/CatalogHeader.tsx'
|
|
29
31
|
import { MarketView } from './views/MarketView.tsx'
|
|
@@ -32,6 +34,7 @@ import type { SectionView } from './layout/SectionTabs.tsx'
|
|
|
32
34
|
import { InstalledView } from './views/InstalledView.tsx'
|
|
33
35
|
import { CustomInstallView } from './views/CustomInstallView.tsx'
|
|
34
36
|
import { SettingsView } from './views/SettingsView.tsx'
|
|
37
|
+
import { CloseIcon } from './ui/icons.tsx'
|
|
35
38
|
import type { InstalledItem } from '../logic/installed.ts'
|
|
36
39
|
import { pluginOfItem } from '../logic/installed.ts'
|
|
37
40
|
|
|
@@ -86,6 +89,8 @@ export function PluginHubSection({ t: _hostT, locale }: SectionProps) {
|
|
|
86
89
|
const [notifications, setNotifications] = useState<NotificationRecord[]>(() => loadNotifications())
|
|
87
90
|
/** 通知中心弹窗:一级导航「设置」tab 后边的铃铛入口按钮打开 */
|
|
88
91
|
const [showNotifications, setShowNotifications] = useState(false)
|
|
92
|
+
/** 「忽略本次更新」确认弹窗:{ repo, version };null = 关闭 */
|
|
93
|
+
const [ignoreTarget, setIgnoreTarget] = useState<{ repo: string; version?: string } | null>(null)
|
|
89
94
|
/** 宿主机器环境快照:提交 bug 的 issue 正文附带;取不到为 null(链接少环境段,不阻塞) */
|
|
90
95
|
const [env, setEnv] = useState<EnvInfo | null>(null)
|
|
91
96
|
useEffect(() => {
|
|
@@ -128,6 +133,28 @@ export function PluginHubSection({ t: _hostT, locale }: SectionProps) {
|
|
|
128
133
|
},
|
|
129
134
|
})
|
|
130
135
|
|
|
136
|
+
/** 补齐更新提醒:把「当前有更新但通知中心还没有」的插件补写进通知(repo+version 去重)。
|
|
137
|
+
* 返回本次实际新增的条数(0 = 无可更新或全部已提示过)。启动检查与打开通知中心共用,
|
|
138
|
+
* 保证通知中心始终反映可更新状态:同一版本只一条、清空/删除后重开恢复、更新完成后消失。 */
|
|
139
|
+
const syncUpdateNotices = (): number => {
|
|
140
|
+
const updatable = catalog.installedItems.filter((i) => i.hasUpdate && i.plugin)
|
|
141
|
+
if (updatable.length === 0) return 0
|
|
142
|
+
const known = new Set(loadIgnoredUpdates())
|
|
143
|
+
// 已忽略(repo+version 持久化)与通知中心已有的更新提醒都不再补写:
|
|
144
|
+
// 忽略本次版本后不再提醒,直到下一个新版本(version 变化)才重新提示
|
|
145
|
+
for (const r of loadNotifications()) {
|
|
146
|
+
if (r.kind === 'update') known.add(`${r.repo}@${r.version ?? ''}`)
|
|
147
|
+
}
|
|
148
|
+
const fresh = updatable.filter((item) =>
|
|
149
|
+
item.repo !== null && !known.has(`${item.repo}@${item.catalogVersion ?? ''}`))
|
|
150
|
+
if (fresh.length === 0) return 0
|
|
151
|
+
for (const item of fresh) {
|
|
152
|
+
if (item.repo) addUpdateNotice({ kind: 'update', repo: item.repo, version: item.catalogVersion ?? undefined })
|
|
153
|
+
}
|
|
154
|
+
setNotifications(loadNotifications())
|
|
155
|
+
return fresh.length
|
|
156
|
+
}
|
|
157
|
+
|
|
131
158
|
// 启动更新策略:目录数据与设置都就绪后触发一次 —— 开启「启动时检查更新」→ 发现可更新插件
|
|
132
159
|
// 即写入通知中心(每条一条,点击可去更新),toast 同步提示已入通知。
|
|
133
160
|
// 绝不自动安装:是否更新完全由用户决定。
|
|
@@ -138,18 +165,13 @@ export function PluginHubSection({ t: _hostT, locale }: SectionProps) {
|
|
|
138
165
|
if (!settingsReady || catalog.plugins === null || catalog.failed) return
|
|
139
166
|
startupCheckedRef.current = true
|
|
140
167
|
if (!hubSettings.checkUpdatesOnStart) return
|
|
141
|
-
const
|
|
142
|
-
if (
|
|
143
|
-
for (const item of updatable) {
|
|
144
|
-
if (item.repo) addUpdateNotice({ kind: 'update', repo: item.repo, version: item.catalogVersion ?? undefined })
|
|
145
|
-
}
|
|
146
|
-
setNotifications(loadNotifications())
|
|
147
|
-
setToast({ id: Date.now(), kind: 'updates', n: updatable.length })
|
|
168
|
+
const added = syncUpdateNotices()
|
|
169
|
+
if (added > 0) setToast({ id: Date.now(), kind: 'updates', n: added })
|
|
148
170
|
}, [settingsReady, hubSettings, catalog.plugins, catalog.failed, catalog.installedItems, queue])
|
|
149
171
|
|
|
150
172
|
// 信任/卸载/更新说明弹窗打开时按 Esc 关闭;任务进入后台队列后可随时关闭(任务继续)
|
|
151
173
|
useEffect(() => {
|
|
152
|
-
if (!confirmPlugin && !confirmCustomTarget && !confirmGlobalNpm && !uninstallPlugin && !uninstallItem && !detailItem && !showHubUpdate && !showAbout && !showNotifications) return
|
|
174
|
+
if (!confirmPlugin && !confirmCustomTarget && !confirmGlobalNpm && !uninstallPlugin && !uninstallItem && !detailItem && !showHubUpdate && !showAbout && !showNotifications && !ignoreTarget) return
|
|
153
175
|
const onKey = (e: KeyboardEvent) => {
|
|
154
176
|
if (e.key === 'Escape') {
|
|
155
177
|
setConfirmPlugin(null)
|
|
@@ -162,11 +184,12 @@ export function PluginHubSection({ t: _hostT, locale }: SectionProps) {
|
|
|
162
184
|
setShowHubUpdate(false)
|
|
163
185
|
setShowAbout(false)
|
|
164
186
|
setShowNotifications(false)
|
|
187
|
+
setIgnoreTarget(null)
|
|
165
188
|
}
|
|
166
189
|
}
|
|
167
190
|
window.addEventListener('keydown', onKey)
|
|
168
191
|
return () => window.removeEventListener('keydown', onKey)
|
|
169
|
-
}, [confirmPlugin, confirmCustomTarget, uninstallPlugin, uninstallItem, detailItem, showHubUpdate, showAbout, showNotifications])
|
|
192
|
+
}, [confirmPlugin, confirmCustomTarget, uninstallPlugin, uninstallItem, detailItem, showHubUpdate, showAbout, showNotifications, ignoreTarget])
|
|
170
193
|
|
|
171
194
|
// Toast 统一自动消失
|
|
172
195
|
useEffect(() => {
|
|
@@ -299,7 +322,10 @@ export function PluginHubSection({ t: _hostT, locale }: SectionProps) {
|
|
|
299
322
|
// 有可用更新时版本号后紧跟红色「可更新」徽标(同一入口),点击都打开同一个更新说明弹窗。
|
|
300
323
|
// 有更新弹窗显示新版本 + Markdown 变更记录,点「直接更新」进安装弹窗覆盖重装。
|
|
301
324
|
hubUpdate: catalog.hubHasUpdate,
|
|
302
|
-
|
|
325
|
+
// 点击版本号/徽标:先强制刷新拉取最新版本信息(发版后点击即见新版本),再打开弹窗
|
|
326
|
+
onVersionClick: () => {
|
|
327
|
+
void catalog.refreshHub().then(() => setShowHubUpdate(true))
|
|
328
|
+
},
|
|
303
329
|
onAboutClick: () => setShowAbout(true),
|
|
304
330
|
}),
|
|
305
331
|
// 一级导航行:tab 占满左侧,通知入口靠右(「设置」tab 后边,右对齐)
|
|
@@ -312,7 +338,12 @@ export function PluginHubSection({ t: _hostT, locale }: SectionProps) {
|
|
|
312
338
|
t,
|
|
313
339
|
// 通知中心入口:红圈徽标 = 记录数 + 进行中任务 + 待重启(有动静才醒目)
|
|
314
340
|
noticeCount: notifications.length + queue.queue.length + queue.pendingRestarts.length,
|
|
315
|
-
onOpenNotifications: () =>
|
|
341
|
+
onOpenNotifications: () => {
|
|
342
|
+
// 打开弹窗时补齐更新提醒(受「启动时检查更新」开关约束):同一版本只一条,
|
|
343
|
+
// 清空/删除后重开仍恢复,直到插件更新完成才消失
|
|
344
|
+
if (hubSettings.checkUpdatesOnStart) syncUpdateNotices()
|
|
345
|
+
setShowNotifications(true)
|
|
346
|
+
},
|
|
316
347
|
}),
|
|
317
348
|
),
|
|
318
349
|
view === 'market'
|
|
@@ -466,10 +497,49 @@ export function PluginHubSection({ t: _hostT, locale }: SectionProps) {
|
|
|
466
497
|
queue.clearModalTask()
|
|
467
498
|
setConfirmPlugin(plugin)
|
|
468
499
|
},
|
|
500
|
+
// 更新提醒「忽略本次更新」:先弹确认弹窗,确认后该版本不再提醒(持久化),直到下一个新版本
|
|
501
|
+
onIgnoreUpdate: (repo: string, version?: string) => setIgnoreTarget({ repo, version }),
|
|
469
502
|
cancelTask: queue.cancelTask,
|
|
470
503
|
restarting,
|
|
471
504
|
onRestart: () => { void requestRestart() },
|
|
472
505
|
}),
|
|
506
|
+
// 「忽略本次更新」确认弹窗:确认后该版本写入忽略集(持久化)并从通知中心移除,
|
|
507
|
+
// 本次版本不再提醒,直到插件发布下一个新版本
|
|
508
|
+
ignoreTarget && h('div', {
|
|
509
|
+
className: modalStyles.overlay,
|
|
510
|
+
onClick: (e: MouseEvent<HTMLDivElement>) => { if (e.target === e.currentTarget) setIgnoreTarget(null) },
|
|
511
|
+
},
|
|
512
|
+
h('div', { className: modalStyles.modal, role: 'dialog', 'aria-modal': 'true' },
|
|
513
|
+
h('div', { className: modalStyles.modalHead },
|
|
514
|
+
h('div', { className: modalStyles.modalTitle }, t('ignoreUpdateConfirmTitle')),
|
|
515
|
+
h('button', {
|
|
516
|
+
className: modalStyles.modalClose,
|
|
517
|
+
type: 'button',
|
|
518
|
+
'aria-label': t('errorClose'),
|
|
519
|
+
onClick: () => setIgnoreTarget(null),
|
|
520
|
+
}, h(CloseIcon)),
|
|
521
|
+
),
|
|
522
|
+
h('div', { className: modalStyles.modalBody },
|
|
523
|
+
h('div', { className: modalStyles.failPrepareHint }, t('ignoreUpdateConfirmDetail')),
|
|
524
|
+
h('div', { className: modalStyles.modalActions },
|
|
525
|
+
h('button', {
|
|
526
|
+
className: modalStyles.restartLater,
|
|
527
|
+
type: 'button',
|
|
528
|
+
onClick: () => setIgnoreTarget(null),
|
|
529
|
+
}, t('confirmCancel')),
|
|
530
|
+
h('button', {
|
|
531
|
+
className: modalStyles.restartNow,
|
|
532
|
+
type: 'button',
|
|
533
|
+
onClick: () => {
|
|
534
|
+
ignoreUpdate(ignoreTarget.repo, ignoreTarget.version)
|
|
535
|
+
setNotifications(removeUpdateNotice(ignoreTarget.repo, ignoreTarget.version))
|
|
536
|
+
setIgnoreTarget(null)
|
|
537
|
+
},
|
|
538
|
+
}, t('ignoreUpdateConfirm')),
|
|
539
|
+
),
|
|
540
|
+
),
|
|
541
|
+
),
|
|
542
|
+
),
|
|
473
543
|
// 安装确认弹窗:锁定/进度/结果视图逻辑收敛在 modals.tsx;更新与安装同入口,仅标记 update
|
|
474
544
|
confirmPlugin && h(InstallModal, {
|
|
475
545
|
plugin: confirmPlugin,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* DSH
|
|
2
|
+
* DSH Plugin Hub — the community plugin marketplace for DeepSeek Harness.
|
|
3
3
|
* Website: https://dsh-plugin.org
|
|
4
4
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
5
|
*
|
|
@@ -40,7 +40,7 @@ export function CatalogHeader({ t, langPath, statsTotal, statsVerified, onToggle
|
|
|
40
40
|
h(LogoIcon),
|
|
41
41
|
h('h1', { className: styles.title }, t('title')),
|
|
42
42
|
),
|
|
43
|
-
// 版本号:紧跟标题「DSH
|
|
43
|
+
// 版本号:紧跟标题「DSH Plugin Hub」之后,常驻可点入口(<a> 内不能嵌套按钮,故从官网链接里移出放在旁边)。
|
|
44
44
|
// 点击打开更新记录弹窗 —— 有更新看新版本内容,无更新看当前版本内容。
|
|
45
45
|
// 有更新时版本号后直接跟一个红色「可更新」徽标(同一按钮内,不分开成两个元素),静态显示不闪烁。
|
|
46
46
|
h('button', {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* DSH
|
|
2
|
+
* DSH Plugin Hub — the community plugin marketplace for DeepSeek Harness.
|
|
3
3
|
* Website: https://dsh-plugin.org
|
|
4
4
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
5
|
*
|
|
6
|
-
* 头部「关注我们」弹窗:介绍 DSH
|
|
6
|
+
* 头部「关注我们」弹窗:介绍 DSH Plugin Hub 的功能定位 + 用户反馈群二维码。
|
|
7
7
|
* 内容以 Markdown 形式由 dsh-update Worker 的 /about 接口下发(key = hub:about),
|
|
8
8
|
* 客户端按接口返回渲染,不内置固定文案。
|
|
9
9
|
* 图片(反馈群二维码)经 renderMarkdown 的  语法嵌入,居中、最大高度受控。
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* DSH
|
|
2
|
+
* DSH Plugin Hub — the community plugin marketplace for DeepSeek Harness.
|
|
3
3
|
* Website: https://dsh-plugin.org
|
|
4
4
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
5
|
*
|
|
@@ -55,7 +55,7 @@ function BadgeGlyph({ ok }: { ok: boolean }) {
|
|
|
55
55
|
}))
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
export function NotificationsModal({ records, tasks, pendingRestarts, t, env, onClose, onCopy, onClear, onRemove, onUpdate, cancelTask, restarting, onRestart }: {
|
|
58
|
+
export function NotificationsModal({ records, tasks, pendingRestarts, t, env, onClose, onCopy, onClear, onRemove, onUpdate, onIgnoreUpdate, cancelTask, restarting, onRestart }: {
|
|
59
59
|
records: NotificationRecord[]
|
|
60
60
|
/** 进行中的安装/卸载任务(实时进度,与队列弹窗同一数据源) */
|
|
61
61
|
tasks: QueueTask[]
|
|
@@ -70,6 +70,8 @@ export function NotificationsModal({ records, tasks, pendingRestarts, t, env, on
|
|
|
70
70
|
onRemove: (id: number) => void
|
|
71
71
|
/** 更新提醒通知点击:跳去该插件的更新确认弹窗 */
|
|
72
72
|
onUpdate: (repo: string) => void
|
|
73
|
+
/** 更新提醒「忽略本次更新」:先弹确认,确认后本次版本不再提醒,下一个新版本再通知 */
|
|
74
|
+
onIgnoreUpdate: (repo: string, version?: string) => void
|
|
73
75
|
cancelTask: (id: number) => void
|
|
74
76
|
restarting: boolean
|
|
75
77
|
onRestart: () => void
|
|
@@ -217,6 +219,13 @@ export function NotificationsModal({ records, tasks, pendingRestarts, t, env, on
|
|
|
217
219
|
onClick: (e: MouseEvent<HTMLButtonElement>) => { e.stopPropagation(); onUpdate(r.repo) },
|
|
218
220
|
}, t('updateNoticeGo'))
|
|
219
221
|
: null,
|
|
222
|
+
// 更新提醒:「忽略本次更新」按钮 —— 先弹确认,确认后本次版本不再提醒
|
|
223
|
+
isUpdate
|
|
224
|
+
? h('button', {
|
|
225
|
+
className: styles.noticeIgnore,
|
|
226
|
+
onClick: (e: MouseEvent<HTMLButtonElement>) => { e.stopPropagation(); onIgnoreUpdate(r.repo, r.version) },
|
|
227
|
+
}, t('ignoreUpdateRun'))
|
|
228
|
+
: null,
|
|
220
229
|
// 每条通知右侧的删除按钮:单独移除这一条
|
|
221
230
|
h('button', {
|
|
222
231
|
className: styles.noticeRemove,
|