dsh-plugin 1.6.12 → 2.0.1
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 +13 -13
- package/README.md +13 -13
- package/client/client.js +791 -761
- package/client/client.js.map +1 -1
- package/lib/http/catalog.js +2 -3
- package/lib/http/diagnostics.js +6 -7
- package/lib/http/install.js +47 -213
- package/lib/http/logs.js +1 -2
- package/lib/http/npm-release-age.js +42 -0
- package/lib/http/routes.js +8 -7
- package/lib/http/settings.js +2 -2
- package/lib/http/shared.js +2 -2
- package/lib/http/task.js +1 -2
- package/lib/index.js +5 -6
- package/lib/services/install/install-types.js +4 -4
- package/lib/services/install/install.js +1 -1
- package/lib/services/install/npm-check.js +3 -2
- package/lib/services/install/npm-resolve.js +9 -96
- package/lib/services/install/operation-log.js +53 -1
- package/lib/services/install/package-entry.js +6 -11
- package/lib/services/install/registry-check.js +2 -2
- package/lib/services/install/release-target.js +8 -9
- package/lib/services/install/task-queue.js +44 -48
- package/lib/services/npm-release-age.js +119 -0
- package/lib/services/probe.js +4 -4
- package/lib/services/profile/installed-versions.js +15 -21
- package/lib/services/profile/profile.js +16 -44
- package/lib/services/profile/progress.js +8 -11
- package/lib/services/settings.js +2 -5
- package/lib/types/http/catalog.d.ts +1 -1
- package/lib/types/http/install.d.ts +2 -2
- package/lib/types/http/npm-release-age.d.ts +2 -0
- package/lib/types/http/routes.d.ts +10 -12
- package/lib/types/http/shared.d.ts +5 -6
- package/lib/types/index.d.ts +7 -8
- package/lib/types/services/install/install-types.d.ts +12 -13
- package/lib/types/services/install/install.d.ts +1 -1
- package/lib/types/services/install/npm-resolve.d.ts +4 -16
- package/lib/types/services/install/operation-log.d.ts +9 -0
- package/lib/types/services/install/package-entry.d.ts +6 -11
- package/lib/types/services/install/release-target.d.ts +7 -8
- package/lib/types/services/install/task-queue.d.ts +25 -30
- package/lib/types/services/npm-release-age.d.ts +15 -0
- package/lib/types/services/probe.d.ts +4 -4
- package/lib/types/services/profile/installed-versions.d.ts +5 -7
- package/lib/types/services/profile/profile.d.ts +14 -18
- package/lib/types/services/profile/progress.d.ts +7 -10
- package/lib/types/services/settings.d.ts +0 -4
- package/package.json +1 -1
- package/src/client/components/PluginHubSection.tsx +26 -22
- package/src/client/components/catalog/CatalogList.tsx +2 -4
- package/src/client/components/catalog/PluginCard.tsx +2 -3
- package/src/client/components/layout/CatalogControls.tsx +5 -6
- package/src/client/components/layout/CatalogHeader.tsx +1 -2
- package/src/client/components/layout/CategoryTabs.tsx +2 -3
- package/src/client/components/layout/SectionTabs.tsx +3 -5
- package/src/client/components/modals/ConfirmDialog.tsx +4 -6
- package/src/client/components/modals/InstalledDetailModal.tsx +3 -6
- package/src/client/components/modals/LogsModal.tsx +7 -12
- package/src/client/components/modals/NotificationsModal.tsx +74 -24
- package/src/client/components/modals/ProgressView.tsx +3 -4
- package/src/client/components/modals/modals.tsx +75 -26
- package/src/client/components/ui/Dropdown.tsx +4 -6
- package/src/client/components/ui/Toggle.tsx +2 -3
- package/src/client/components/ui/icons.tsx +1 -2
- package/src/client/components/views/CustomInstallView.tsx +20 -178
- package/src/client/components/views/DiagnosticsView.tsx +3 -6
- package/src/client/components/views/InstalledView.tsx +6 -11
- package/src/client/components/views/MarketView.tsx +2 -4
- package/src/client/components/views/SettingsView.tsx +9 -32
- package/src/client/css-modules.d.ts +1 -1
- package/src/client/data/catalog.ts +2 -3
- package/src/client/data/host.ts +26 -3
- package/src/client/data/hub.ts +2 -3
- package/src/client/hooks/useCatalog.ts +79 -24
- package/src/client/hooks/useIncrementalList.ts +3 -5
- package/src/client/hooks/useLanguage.ts +4 -6
- package/src/client/hooks/useSettings.ts +4 -11
- package/src/client/hooks/useTaskQueue.ts +36 -42
- package/src/client/index.tsx +2 -3
- package/src/client/locales.ts +187 -114
- package/src/client/logic/constants.ts +1 -2
- package/src/client/logic/ensure-plugin-css.ts +8 -12
- package/src/client/logic/failures.ts +20 -19
- package/src/client/logic/format.ts +1 -1
- package/src/client/logic/install-command.ts +14 -35
- package/src/client/logic/installed.ts +15 -10
- package/src/client/logic/normalize.ts +3 -3
- package/src/client/logic/release-age.ts +19 -0
- package/src/client/logic/urls.ts +24 -21
- package/src/client/styles/CustomInstallView.module.css +3 -30
- package/src/client/styles/DiagnosticsView.module.css +3 -5
- package/src/client/styles/Dropdown.module.css +2 -4
- package/src/client/styles/Header.module.css +6 -7
- package/src/client/styles/InstalledView.module.css +4 -7
- package/src/client/styles/List.module.css +4 -5
- package/src/client/styles/LogsView.module.css +2 -3
- package/src/client/styles/Modal.module.css +46 -6
- package/src/client/styles/SectionTabs.module.css +2 -4
- package/src/client/styles/SettingsView.module.css +3 -5
- package/src/client/styles/Toggle.module.css +2 -4
- package/src/client/styles/tokens.module.css +14 -18
- package/src/client/types.ts +11 -11
- package/src/server/http/catalog.ts +2 -3
- package/src/server/http/diagnostics.ts +6 -7
- package/src/server/http/install.ts +47 -208
- package/src/server/http/logs.ts +1 -2
- package/src/server/http/npm-release-age.ts +42 -0
- package/src/server/http/routes.ts +12 -12
- package/src/server/http/settings.ts +2 -2
- package/src/server/http/shared.ts +5 -6
- package/src/server/http/task.ts +1 -2
- package/src/server/index.ts +7 -8
- package/src/server/services/install/install-types.ts +12 -13
- package/src/server/services/install/install.ts +0 -1
- package/src/server/services/install/npm-check.ts +3 -2
- package/src/server/services/install/npm-resolve.ts +9 -99
- package/src/server/services/install/operation-log.ts +47 -1
- package/src/server/services/install/package-entry.ts +6 -11
- package/src/server/services/install/registry-check.ts +2 -2
- package/src/server/services/install/release-target.ts +8 -9
- package/src/server/services/install/task-queue.ts +51 -54
- package/src/server/services/npm-release-age.ts +107 -0
- package/src/server/services/probe.ts +4 -4
- package/src/server/services/profile/installed-versions.ts +16 -22
- package/src/server/services/profile/profile.ts +16 -42
- package/src/server/services/profile/progress.ts +8 -11
- package/src/server/services/settings.ts +2 -9
- package/lib/services/install/preflight.js +0 -178
- package/lib/types/services/install/preflight.d.ts +0 -23
- package/src/server/services/install/preflight.ts +0 -184
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
* Website: https://dsh-plugin.org
|
|
4
4
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
* side effects, no network — so every consumer shares one definition.
|
|
6
|
+
* 常量:站点地址、分类元数据与排序 key。纯数据、无副作用、无网络,供所有使用方共享同一份定义。
|
|
8
7
|
*/
|
|
9
8
|
import type { LocaleId } from '../types.ts'
|
|
10
9
|
|
|
@@ -1,17 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* 自愈缺失的插件 CSS 标签。
|
|
3
3
|
*
|
|
4
|
-
* CSS
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* module cache still holds the old factory — so the factory never re-runs and
|
|
8
|
-
* the styles never come back until some interaction triggers a reload.
|
|
4
|
+
* CSS 注入是 bundle 工厂的一次性副作用(以「标签已存在」为守卫)。宿主重启后页面仍在,
|
|
5
|
+
* 但宿主 HMR 链可能移除 <style data-plugin> 标签,而内存模块缓存仍持有旧工厂,工厂不再
|
|
6
|
+
* 重跑,样式就一直缺失,直到某次交互触发刷新才恢复。
|
|
9
7
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* missing tags, turning the "click around to fix it" self-heal into an
|
|
14
|
-
* automatic one on mount / view switch.
|
|
8
|
+
* tsdown 的 `dsh-css-modules-inline` 构建插件会在工厂执行时把每个模块的 { tagId, css }
|
|
9
|
+
* 登记进全局列表 `__DSH_PLUGIN_CSS__`。此工具随后比对列表与实时 DOM,补注入缺失的标签,
|
|
10
|
+
* 把「点一圈才自愈」变成挂载 / 切换视图时自动完成。
|
|
15
11
|
*/
|
|
16
12
|
|
|
17
13
|
interface PluginCssEntry {
|
|
@@ -25,7 +21,7 @@ declare global {
|
|
|
25
21
|
}
|
|
26
22
|
}
|
|
27
23
|
|
|
28
|
-
/**
|
|
24
|
+
/** 补注入 DOM 中缺失的已登记样式表,幂等。 */
|
|
29
25
|
export function ensurePluginCss(): void {
|
|
30
26
|
if (typeof document === 'undefined') return
|
|
31
27
|
const registry = window.__DSH_PLUGIN_CSS__
|
|
@@ -3,13 +3,10 @@
|
|
|
3
3
|
* Website: https://dsh-plugin.org
|
|
4
4
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
* install/remove notifications.
|
|
6
|
+
* 失败归类、Issue 正文摘要与持久化的安装/卸载通知。
|
|
8
7
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* notification center — so a result is never lost even when the dialog
|
|
12
|
-
* was dismissed or nobody was watching the progress strip.
|
|
8
|
+
* 每个结算后的任务都会双重留痕:服务端系统日志(hub.log)与馈送通知中心的客户端
|
|
9
|
+
* localStorage 记录 —— 即使弹窗被关闭、无人盯着进度条,结果也不会丢失。
|
|
13
10
|
*/
|
|
14
11
|
export interface NotificationRecord {
|
|
15
12
|
id: number
|
|
@@ -27,7 +24,7 @@ export interface NotificationRecord {
|
|
|
27
24
|
version?: string
|
|
28
25
|
/** 实际执行的安装/卸载命令(issue 预填时如实展示);历史记录可能缺失 */
|
|
29
26
|
command?: string
|
|
30
|
-
/**
|
|
27
|
+
/** 尝试过的安装方式(实际执行过的命令,按先后顺序):失败提 Issue 时贴给作者,便于反推正确的 npm 包名 */
|
|
31
28
|
attempts?: string[]
|
|
32
29
|
/** 结束时间(epoch ms) */
|
|
33
30
|
at: number
|
|
@@ -151,7 +148,7 @@ export function removeNotification(id: number): NotificationRecord[] {
|
|
|
151
148
|
return next
|
|
152
149
|
}
|
|
153
150
|
|
|
154
|
-
export type FailureKind = 'npmTooOld' | 'dshMissing' | 'gitMissing' | 'pnpmMissing' | 'npmMissing' | 'pnpmStore' | 'pnpmWorkspace' | 'originRejected' | 'pnpmPolicy' | 'pnpmUnusedPatch' | 'pnpmMissingDep' | 'pluginMissingDep' | 'registryVersionStale' | 'pnpmLinkedDirMissing' | 'fileLocked' | 'accessDenied' | 'fsUnavailable' | 'pnpmIgnoredBuild' | 'pluginPrepare' | 'network' | 'hostCliSpawn' | 'hubState' | 'timeout' | 'profileLock' | 'hub'
|
|
151
|
+
export type FailureKind = 'npmTooOld' | 'dshMissing' | 'gitMissing' | 'pnpmMissing' | 'npmMissing' | 'pnpmStore' | 'pnpmWorkspace' | 'originRejected' | 'pnpmPolicy' | 'pnpmUnusedPatch' | 'pnpmMissingDep' | 'pluginMissingDep' | 'registryVersionStale' | 'pnpmLinkedDirMissing' | 'fileLocked' | 'accessDenied' | 'fsUnavailable' | 'pnpmIgnoredBuild' | 'pluginPrepare' | 'hostIncompatible' | 'network' | 'hostCliSpawn' | 'hubState' | 'timeout' | 'profileLock' | 'hub'
|
|
155
152
|
|
|
156
153
|
/** 失败动作口径:标题、上报正文与提示文案按安装/更新/卸载取词,不写死「安装」。 */
|
|
157
154
|
export type FailureAction = 'install' | 'update' | 'uninstall'
|
|
@@ -172,7 +169,7 @@ export function actionOfRecord(rec: { kind: NotificationRecord['kind']; action?:
|
|
|
172
169
|
return rec.kind === 'uninstall' ? 'uninstall' : rec.kind === 'update' ? 'update' : 'install'
|
|
173
170
|
}
|
|
174
171
|
|
|
175
|
-
/**
|
|
172
|
+
/** 只匹配 pnpm 的错误码,不匹配泛泛出现的 checksum / integrity 字样。 */
|
|
176
173
|
function missingTarballIntegrity(message: string): boolean {
|
|
177
174
|
return /\bERR_PNPM_MISSING_TARBALL_INTEGRITY\b/.test(message)
|
|
178
175
|
}
|
|
@@ -187,9 +184,9 @@ export function releaseAgeRetryAfter(message: string): string | null {
|
|
|
187
184
|
}
|
|
188
185
|
|
|
189
186
|
/**
|
|
190
|
-
*
|
|
191
|
-
*
|
|
192
|
-
*
|
|
187
|
+
* 同一策略子类型也用于选择实时/已保存失败的提示文案:minimumReleaseAge 只有在能算出
|
|
188
|
+
* 可重试时刻时才走专属文案(给出确切的「何时可再试」),算不出时退回通用文案,
|
|
189
|
+
* 避免出现「预计可重试时间:」后接空值的残缺句子。
|
|
193
190
|
*/
|
|
194
191
|
export function pnpmPolicyHintOf(message: string): 'failPnpmTarballIntegrityHint' | 'failPnpmReleaseAgeHint' | 'failPnpmPolicyHint' {
|
|
195
192
|
if (missingTarballIntegrity(message)) return 'failPnpmTarballIntegrityHint'
|
|
@@ -211,6 +208,9 @@ export function pnpmPolicyHintParams(message: string): Record<string, string> {
|
|
|
211
208
|
* (只影响这个插件 → 才引导提 Issue)。各分支判定顺序有依赖,见函数内注释。
|
|
212
209
|
*/
|
|
213
210
|
export function classifyFailure(message: string): FailureKind {
|
|
211
|
+
// 宿主版本门禁:插件声明的兼容范围不含当前 dsh 版本,宿主在安装前直接拒绝。
|
|
212
|
+
// 必须最先判 —— 这是宿主自己给的结论,不是 pnpm 的安装失败
|
|
213
|
+
if (/installation rejected:.*incompatible with dsh/i.test(message)) return 'hostIncompatible'
|
|
214
214
|
// npm 自身崩溃(edgesOut)或服务端标记的本机 npm 版本过低。必须最先判,否则会被外层
|
|
215
215
|
// ERR_PNPM_PREPARE_PACKAGE 吞成「插件打包问题」
|
|
216
216
|
if (/\[npm-too-low\]|edgesOut/i.test(message)) return 'npmTooOld'
|
|
@@ -245,8 +245,9 @@ export function classifyFailure(message: string): FailureKind {
|
|
|
245
245
|
// 构建脚本被 allowBuilds 拦截。必须在 network 之前判:日志尾部混重试残留的连接失败特征
|
|
246
246
|
if (/ERR_PNPM_IGNORED_BUILDS|Ignored build scripts:|ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED/i.test(message)) return 'pnpmIgnoredBuild'
|
|
247
247
|
// 网络/代理/源配置问题。必须在 prepare 之前判:git fetch 失败常被外层包成 "Command failed"
|
|
248
|
-
|
|
249
|
-
|
|
248
|
+
// tarball 下载失败的括号里可能是数字码(pnpm 自己的)或符号码(undici 的 UND_ERR_*),两种都要认
|
|
249
|
+
if (/\[network\]|ERR_PNPM_GIT_FETCH_FAILED|ETIMEDOUT|ECONNREFUSED|ECONNRESET|ENOTFOUND|EAI_AGAIN|EPIPE|EHOSTUNREACH|ENETUNREACH|UND_ERR_[A-Z0-9_]+|getaddrinfo|Could not connect|Could not resolve host|Network unreachable|Failed to connect|socket hang up|CERT_HAS_EXPIRED|SSL certificate problem|\bTLS\b|\bSSL\b|fetch failed|failed to fetch|\bGET https?:\/\/\S+\.tgz\s+error \([A-Za-z0-9_]+\)/i.test(message)) return 'network'
|
|
250
|
+
// 装后校验发现入口文件缺失([packaging])= 分发物缺构建产物
|
|
250
251
|
if (/\[packaging\]|entry file missing/i.test(message)) return 'pluginPrepare'
|
|
251
252
|
// registry 上不存在的依赖(404),profile 任何安装都会卡在这条。必须在 pluginPrepare 之前判
|
|
252
253
|
// [registry-missing](服务端官方源复核后注入)= 作者侧缺依赖,须先于 pnpmMissingDep(同样含 404 文本)
|
|
@@ -262,7 +263,7 @@ export function classifyFailure(message: string): FailureKind {
|
|
|
262
263
|
// 安装器自身产生的状态类消息(未安装 / 不在此处卸载 / 已在队列中 / 已安装):这几句由本服务的
|
|
263
264
|
// 守卫发出,不是 CLI 输出、也不是插件问题,而是插件中心的已安装列表与实读快照不同步。
|
|
264
265
|
// 单独归类,让前端给「刷新后重试」这类可行动提示,而不是当作未知失败引导去提 Issue。
|
|
265
|
-
// already installed
|
|
266
|
+
// already installed 只认「整行就是它」的短形态;带「——」说明的
|
|
266
267
|
// 包名冲突另有处置,不能按状态不同步提示。
|
|
267
268
|
if (/plugin is not installed|plugin cannot be uninstalled here|already queued:|^already installed: \S+$/im.test(message)) return 'hubState'
|
|
268
269
|
// 安装器自己的超时标记(命令跑满上限被强杀):既不是插件问题、也不是用户环境问题,而且很可能是
|
|
@@ -345,8 +346,8 @@ export function coreErrorCode(message: string): string | null {
|
|
|
345
346
|
}
|
|
346
347
|
|
|
347
348
|
/** 「包在 registry 不存在」失败的提示变体:服务端用官方源复核过(会注入 `[registry-mirror-lag]`)
|
|
348
|
-
* 时,说明包在官方源上存在、是本机镜像源未同步 →
|
|
349
|
-
*
|
|
349
|
+
* 时,说明包在官方源上存在、是本机镜像源未同步 → 提示换源;否则按「profile 残留依赖」提示
|
|
350
|
+
* (删掉 profile 里那条依赖即可)。 */
|
|
350
351
|
export function registryMissingHintOf(message: string): 'failPnpmMissingDepHint' | 'failRegistryMirrorLagHint' {
|
|
351
352
|
return /\[registry-mirror-lag\]/i.test(message) ? 'failRegistryMirrorLagHint' : 'failPnpmMissingDepHint'
|
|
352
353
|
}
|
|
@@ -365,12 +366,12 @@ export function missingRegistryPackageOf(message: string): string | null {
|
|
|
365
366
|
|
|
366
367
|
/**
|
|
367
368
|
* 从网络类失败消息里提取「具体连不上的地址」,供弹窗精准提示(你的网络无法访问什么)。
|
|
368
|
-
* 优先取服务端 [network]
|
|
369
|
+
* 优先取服务端 [network] 网络探测消息括号里的探测地址(中文「(…)」/ 英文「(…)」),
|
|
369
370
|
* 否则取消息里出现的第一个 URL(git fetch 的仓库地址、registry 域名等)。
|
|
370
371
|
* 提取不到返回 null —— 调用方据此跳过地址行。
|
|
371
372
|
*/
|
|
372
373
|
export function unreachableTargetOf(message: string): string | null {
|
|
373
|
-
// [network]
|
|
374
|
+
// [network] 网络探测消息:`无法连接到 GitHub(https://github.com/)` / `cannot reach the npm registry (https://registry.npmjs.org/)`
|
|
374
375
|
const paren = message.match(/[((](https?:\/\/[^\s'"`<>()+]+)[))]/)
|
|
375
376
|
if (paren) return paren[1]
|
|
376
377
|
// 其余网络失败(git fetch / npm 连接失败):取第一个 URL,去掉行尾标点
|
|
@@ -3,15 +3,13 @@
|
|
|
3
3
|
* Website: https://dsh-plugin.org
|
|
4
4
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
* vs prebuilt GitHub release vs explicit-HTTPS GitHub), build the display
|
|
8
|
-
* command and normalize a raw install spec back to its owner/repo identity.
|
|
6
|
+
* 安装目标/命令工具:为目录条目取 npm 包名、构造展示命令,并把原始安装 spec 归一化回 owner/repo 身份。
|
|
9
7
|
*/
|
|
10
8
|
import type { HubPlugin } from '../types.ts'
|
|
11
9
|
import { githubReleaseTarget } from '../../server/services/install/release-target.ts'
|
|
12
10
|
|
|
13
|
-
/**
|
|
14
|
-
*
|
|
11
|
+
/** 后端 attempts 按时间先后排列:取最后一次实际执行的命令,取不到再回退到展示用命令。
|
|
12
|
+
* 这里匹配的是任务队列记录的指令形态,不是要在此执行的命令。
|
|
15
13
|
*/
|
|
16
14
|
export function executedCommandOf(command?: string, attempts?: string[]): string | undefined {
|
|
17
15
|
if (attempts === undefined) return command
|
|
@@ -21,44 +19,25 @@ export function executedCommandOf(command?: string, attempts?: string[]): string
|
|
|
21
19
|
}
|
|
22
20
|
|
|
23
21
|
/**
|
|
24
|
-
*
|
|
25
|
-
* (走 npm registry tarball
|
|
26
|
-
*
|
|
27
|
-
* 都没有 → git 直装。
|
|
28
|
-
* 返回值 target 即传给后端 /install 的安装目标(npm 包名 / release URL / owner/repo)。
|
|
22
|
+
* 目录条目的安装目标(用户无感知):目录条目必带 npm 包名,直接用该包名安装
|
|
23
|
+
* (走 npm registry tarball)。
|
|
24
|
+
* 返回值即传给后端 /install 的安装目标(npm 包名)。
|
|
29
25
|
*/
|
|
30
|
-
export function installTargetOf(p: HubPlugin):
|
|
31
|
-
|
|
32
|
-
const repo = (p.source?.repo ?? '').trim()
|
|
33
|
-
if (pkg && repo) return { target: pkg, via: 'npm' }
|
|
34
|
-
// 目录下发的权威命令若是「同仓库」的固定 release 直链,改走该预构建包;
|
|
35
|
-
// 跨仓 / 非法 release 目标不采信,退回 git 直装。
|
|
36
|
-
const release = githubReleaseTarget(p.install?.githubCommand ?? '')
|
|
37
|
-
if (release !== null && repo !== '' && release.repo.toLowerCase() === repo.toLowerCase()) {
|
|
38
|
-
return { target: release.target, via: 'release' }
|
|
39
|
-
}
|
|
40
|
-
return { target: repo, via: 'github' }
|
|
26
|
+
export function installTargetOf(p: HubPlugin): string {
|
|
27
|
+
return (p.source?.npmPackage ?? '').trim()
|
|
41
28
|
}
|
|
42
29
|
|
|
43
30
|
/** 展示用安装命令(复制/弹窗):目录下发的权威命令优先(CLI-only 插件如 dsh-tui
|
|
44
|
-
* 需专属 profile(--profile dsh-tui),无法从
|
|
45
|
-
*
|
|
31
|
+
* 需专属 profile(--profile dsh-tui),无法从 npm 包名推断,必须用官方命令);
|
|
32
|
+
* 常规插件无目录命令时用 npm 包名回退生成。 */
|
|
46
33
|
export function installCommandOf(p: HubPlugin, withProfile = false): string {
|
|
47
|
-
const
|
|
48
|
-
if (
|
|
49
|
-
const cmd = p.install?.command
|
|
50
|
-
if (cmd) return cmd
|
|
51
|
-
} else {
|
|
52
|
-
// release 与 git 通道共用目录下发的 githubCommand(git+ 显式 HTTPS 或 release 直链)
|
|
53
|
-
const cmd = p.install?.githubCommand
|
|
54
|
-
if (cmd) return cmd
|
|
55
|
-
}
|
|
34
|
+
const cmd = p.install?.command
|
|
35
|
+
if (cmd) return cmd
|
|
56
36
|
const prefix = `dsh plugin${withProfile ? ' --profile web' : ''} add `
|
|
57
|
-
|
|
58
|
-
return `${prefix}git+https://github.com/${target}.git`
|
|
37
|
+
return `${prefix}${installTargetOf(p)}`
|
|
59
38
|
}
|
|
60
39
|
|
|
61
|
-
/**
|
|
40
|
+
/** 把任务/安装目标归一化为 owner/repo 展示身份。 */
|
|
62
41
|
export function repoFromInstallTarget(value: string): string {
|
|
63
42
|
const input = value.trim()
|
|
64
43
|
// release 直链(预构建 .tgz)走同一条身份归一化:解析出所属仓库,已装态/更新信号才能
|
|
@@ -3,13 +3,11 @@
|
|
|
3
3
|
* Website: https://dsh-plugin.org
|
|
4
4
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
5
|
*
|
|
6
|
-
*
|
|
6
|
+
* 已安装插件模型(纯逻辑,不依赖 React/UI)。
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* install matching / update detection used by the market view, so both
|
|
12
|
-
* views share one source of truth.
|
|
8
|
+
* 把宿主 `/installed` 路由下发的本地已装表(npm 包名 → manifest spec)与目录投影合并成
|
|
9
|
+
* 统一的 `InstalledItem` 视图模型,供「已安装」视图消费;同时承载市场视图用的安装匹配
|
|
10
|
+
* 与更新检测,让两个视图共享唯一数据源。
|
|
13
11
|
*/
|
|
14
12
|
import type { HubPlugin } from '../types.ts'
|
|
15
13
|
import { HUB_REPO } from './constants.ts'
|
|
@@ -56,7 +54,7 @@ function signalOf(repo: string | null, versions: Record<string, InstalledVersion
|
|
|
56
54
|
return versions[repo.toLowerCase()] ?? null
|
|
57
55
|
}
|
|
58
56
|
|
|
59
|
-
/** 插件是否已安装:匹配 Git spec 中的 owner/repo,或 npm
|
|
57
|
+
/** 插件是否已安装:匹配 Git spec 中的 owner/repo,或 npm 安装的依赖包名;命中返回 npm 包名。 */
|
|
60
58
|
export function installedNameOf(
|
|
61
59
|
plugin: HubPlugin,
|
|
62
60
|
installed: Record<string, string>,
|
|
@@ -68,8 +66,8 @@ export function installedNameOf(
|
|
|
68
66
|
for (const [name, spec] of Object.entries(installed)) {
|
|
69
67
|
if (repoFromInstallTarget(spec).toLowerCase() === needle) return name
|
|
70
68
|
}
|
|
71
|
-
// npm
|
|
72
|
-
//
|
|
69
|
+
// npm 安装:profile 依赖 key 直接是 npm 包名。包名来源两处——目录数据(npmPackage),
|
|
70
|
+
// 或服务端安装时持久化的映射(versions[repo].npmPackage,覆盖组织 scope 与 GitHub
|
|
73
71
|
// 用户名不一致、目录未下发包名的场景);命中任一并依赖 key 真实存在即视为已安装。
|
|
74
72
|
// 只认 npm 安装的依赖(spec 无法解析出仓库身份):git/本地安装的依赖身份由仓库匹配决定,
|
|
75
73
|
// 包名同名不代表同仓库——否则「git 装 A 仓库」会被「npm 包名同名但仓库不同」的目录条目
|
|
@@ -91,6 +89,13 @@ export function installedVersionOf(plugin: HubPlugin, versions: Record<string, I
|
|
|
91
89
|
return versions[repo.toLowerCase()]?.version ?? null
|
|
92
90
|
}
|
|
93
91
|
|
|
92
|
+
/** 该插件对应的 npm 包名(目录下发,或安装时持久化的映射);无则 null。决定更新是否走 npm registry。 */
|
|
93
|
+
export function npmPackageOf(plugin: HubPlugin, versions: Record<string, InstalledVersionSignal>): string | null {
|
|
94
|
+
const repo = plugin.source?.repo
|
|
95
|
+
const pkg = (plugin.source?.npmPackage || (repo ? versions[repo.toLowerCase()]?.npmPackage : undefined) || '').trim()
|
|
96
|
+
return pkg === '' ? null : pkg
|
|
97
|
+
}
|
|
98
|
+
|
|
94
99
|
/**
|
|
95
100
|
* 是否有更新:仅对已安装插件有意义。
|
|
96
101
|
* 双信号判定——有 release 版本的比版本;无版本(repo 不打 tag)的比仓库最近更新时间
|
|
@@ -174,7 +179,7 @@ function isHubSelf(name: string, spec: string): boolean {
|
|
|
174
179
|
if (spec.startsWith('file:') || spec.startsWith('link:')) return true
|
|
175
180
|
const repo = repoFromInstallTarget(spec)
|
|
176
181
|
if (isRepoLike(repo) && repo.toLowerCase() === HUB_REPO.toLowerCase()) return true
|
|
177
|
-
// npm
|
|
182
|
+
// npm 覆盖重装 hub 自身后,file:/link: 本地链接会被替换成 registry 版本号/范围
|
|
178
183
|
// (如 1.3.0、^1.3.0)——依赖 key `dsh-plugin` 在 npm 上唯一属于本插件(不存在第三方
|
|
179
184
|
// 同名包),版本号形态与仓库身份同样视为 hub 自身:命令窗口「自己装自己」成功后,
|
|
180
185
|
// 条目仍不进已安装列表(卸载接口对它的 400 拒绝是服务端最后一道防线)。
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* Website: https://dsh-plugin.org
|
|
4
4
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* 归一化器:把在线 API 的短 key 载荷(s/o/n/vr/c/t/f/d/r/v/u/a/sg/fk)映射为
|
|
7
|
+
* HubPlugin,让渲染层只面对一种数据形态。
|
|
8
8
|
*/
|
|
9
9
|
import type { HubPlugin } from '../types.ts'
|
|
10
10
|
|
|
@@ -30,7 +30,7 @@ export function normalize(raw: Record<string, unknown>): HubPlugin {
|
|
|
30
30
|
: undefined,
|
|
31
31
|
// 安装信息:wi 仅在 false 时下发(缺省视为 true 可一键安装);
|
|
32
32
|
// mi 仅在 true 时下发(人工干预:网页端替不了终端,只引导用户自己去终端装);
|
|
33
|
-
// ic/igc = 目录下发的权威安装命令(
|
|
33
|
+
// ic/igc = 目录下发的权威安装命令(CLI-only 插件如 dsh-tui 必须用)
|
|
34
34
|
install: raw.wi === false || raw.mi === true || typeof raw.ic === 'string' || typeof raw.igc === 'string'
|
|
35
35
|
? {
|
|
36
36
|
webInstallable: raw.wi === false ? false : undefined,
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DSH Plugin Hub — the community plugin marketplace for DeepSeek Harness.
|
|
3
|
+
* Website: https://dsh-plugin.org
|
|
4
|
+
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
|
+
*
|
|
6
|
+
* 供应链门禁的时间判定(纯函数):pnpm 会静默回退发布未满门槛的版本,
|
|
7
|
+
* 故发布太新的版本不该提示更新。Hub 自身更新与插件更新共用同一门槛。
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/** pnpm 供应链门禁:dsh profile 默认 minimumReleaseAge=1440(分钟);+30 分钟缓冲抵消网络/CDN 边缘时间偏差。 */
|
|
11
|
+
export const MIN_RELEASE_AGE_MS = 24 * 60 * 60 * 1000 + 30 * 60 * 1000
|
|
12
|
+
|
|
13
|
+
/** 发布时间是否仍在门禁窗口内(太新 → 暂缓提示);无值/解析不出返回 false(不拦真实更新)。 */
|
|
14
|
+
export function isReleaseTooFresh(publishedAt: string | null | undefined, now: number): boolean {
|
|
15
|
+
if (typeof publishedAt !== 'string' || publishedAt === '') return false
|
|
16
|
+
const ms = Date.parse(publishedAt)
|
|
17
|
+
if (Number.isNaN(ms)) return false
|
|
18
|
+
return now - ms < MIN_RELEASE_AGE_MS
|
|
19
|
+
}
|
package/src/client/logic/urls.ts
CHANGED
|
@@ -3,9 +3,8 @@
|
|
|
3
3
|
* Website: https://dsh-plugin.org
|
|
4
4
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
5
|
*
|
|
6
|
-
* URL
|
|
7
|
-
*
|
|
8
|
-
* URL stays within GitHub's 8192-byte request limit.
|
|
6
|
+
* URL 构造:目录详情页、插件官网链接,以及失败弹窗用的预填 GitHub Issue 链接。
|
|
7
|
+
* 均为纯函数,保证 issue URL 不超过 GitHub 8192 字节的请求上限。
|
|
9
8
|
*/
|
|
10
9
|
import type { EnvInfo, HubPlugin, LocaleId } from '../types.ts'
|
|
11
10
|
import { GITHUB_URL, HUB_REPO, PLUGIN_VERSION, SITE_URL } from './constants.ts'
|
|
@@ -62,6 +61,7 @@ function reasonTitleOf(kind: FailureKind, action: FailureAction): string {
|
|
|
62
61
|
case 'fsUnavailable': return 'local file system unavailable on the user machine'
|
|
63
62
|
case 'pnpmIgnoredBuild': return 'build scripts blocked by pnpm allowlist'
|
|
64
63
|
case 'pluginPrepare': return 'plugin distribution incomplete'
|
|
64
|
+
case 'hostIncompatible': return 'plugin does not support the DSH version in use'
|
|
65
65
|
case 'network': return 'network failure on the user side'
|
|
66
66
|
case 'hostCliSpawn': return 'the DSH host could not be launched on the user machine'
|
|
67
67
|
case 'hubState': return `stale ${word} state in the plugin hub`
|
|
@@ -79,21 +79,23 @@ function reasonTitleOf(kind: FailureKind, action: FailureAction): string {
|
|
|
79
79
|
* 错误核心摘要(完整日志太长,塞进 URL 会被 GitHub 以「request URL too long」拒绝,
|
|
80
80
|
* 故只收集重点)。错误弹窗、失败记录共用此逻辑。
|
|
81
81
|
* env 取不到时为 null,链接照常生成、只是少环境段。
|
|
82
|
-
* attempts
|
|
82
|
+
* attempts(尝试过的安装方式,实际执行过的命令):作者看到我们试过的命令,
|
|
83
83
|
* 组织 scope 与 GitHub 用户名不一致时也能直接指认正确的 npm 包名。
|
|
84
84
|
*/
|
|
85
85
|
export function pluginIssueUrl(repo: string, message: string, env?: EnvInfo | null, command?: string, attempts?: string[], action: FailureAction = 'install'): string {
|
|
86
86
|
// 原因判定:按失败类型归类,便于作者判断问题是否出在插件侧。
|
|
87
|
-
// [packaging]
|
|
88
|
-
//
|
|
87
|
+
// [packaging](装后校验拦截)单独列:分发物不完整(package.json 声明的文件
|
|
88
|
+
// 在发布物里缺失),作者照此补齐即可。
|
|
89
89
|
const kind = classifyFailure(message)
|
|
90
90
|
const executed = executedCommandOf(command, attempts)
|
|
91
91
|
// 动作词:标题与正文的「安装/更新/卸载」统一取自这里
|
|
92
92
|
const zhWord = actionWordZh(action)
|
|
93
93
|
// 标题 = 错误原因(非动作词),让 issue 列表一眼可分流
|
|
94
94
|
const title = `[dsh-plugin.org | dsh-plugin-hub] ${reasonTitleOf(kind, action)}: ${repo}`
|
|
95
|
-
const reason =
|
|
96
|
-
? 'plugin
|
|
95
|
+
const reason = kind === 'hostIncompatible'
|
|
96
|
+
? 'the plugin declares a dsh compatibility range that does not include the runtime in use, so the host rejects it before installing anything; please extend the supported dsh versions or publish a compatible release'
|
|
97
|
+
: /\[packaging\]/i.test(message)
|
|
98
|
+
? 'plugin distribution is incomplete — a file the package needs (its entry from main / exports, an entry a bundled patch mounts, or the patch declared by dsh.bundle.patch) is missing from the published npm package; please publish a complete package'
|
|
97
99
|
: /\[registry-missing\]/i.test(message)
|
|
98
100
|
// 服务端用官方 registry 复核过:缺的包在官方源上也不存在,且不是用户 profile 的残留依赖
|
|
99
101
|
? 'a dependency declared by the plugin is not published on the public npm registry (missing, removed, or never released); please publish or correct that dependency, or ship it with the plugin'
|
|
@@ -131,17 +133,17 @@ export function pluginIssueUrl(repo: string, message: string, env?: EnvInfo | nu
|
|
|
131
133
|
`本错误信息由 [dsh-plugin-hub](${GITHUB_URL}) 插件市场的安装程序自动生成,随本次${zhWord}失败一并提交。`,
|
|
132
134
|
`- 实际执行的${zhWord}命令:${executed ? `\`${executed}\`` : 'unknown (no executed command recorded)'}`,
|
|
133
135
|
`- 执行结果:${zhWord}失败,未能${zhWord}该插件。`,
|
|
134
|
-
//
|
|
136
|
+
// 尝试过的安装方式(实际执行过的命令,按先后顺序):作者据此指认正确的 npm 包名。
|
|
135
137
|
// 不写「安装」:卸载失败也走这条正文。
|
|
136
138
|
...(attempts && attempts.length > 0
|
|
137
139
|
? ['', '## Attempted channels(已尝试的方式)', ...attempts.map((a) => `- ${a}`)]
|
|
138
140
|
: []),
|
|
139
|
-
// [packaging]
|
|
141
|
+
// [packaging] 场景:说明当前插件的 npm 分发物不完整
|
|
140
142
|
...(/\[packaging\]/i.test(message)
|
|
141
143
|
? [
|
|
142
144
|
'',
|
|
143
145
|
'## 安装方式说明',
|
|
144
|
-
'
|
|
146
|
+
'当前插件的 npm 分发物缺少它需要的文件(`main` / `exports` 声明的入口、补丁按本包名 mount 的入口,或 `dsh.bundle.patch` 声明的补丁)。请发布包含完整构建产物的 npm 包后重试。',
|
|
145
147
|
]
|
|
146
148
|
: []),
|
|
147
149
|
'',
|
|
@@ -194,14 +196,14 @@ export function pluginIssueUrl(repo: string, message: string, env?: EnvInfo | nu
|
|
|
194
196
|
}
|
|
195
197
|
|
|
196
198
|
/**
|
|
197
|
-
* 插件中心自身仓库的预填 Issue
|
|
198
|
-
*
|
|
199
|
+
* 插件中心自身仓库的预填 Issue 链接:用于「非插件侧」失败(无法识别的未知原因,
|
|
200
|
+
* 以及判定由插件中心自己产出、可能误报时的次要反馈入口)。正文带上目标插件与完整
|
|
199
201
|
* 错误摘要,供插件中心维护者分诊 —— 不再把环境问题/插件中心自身问题误投给插件作者。
|
|
200
202
|
*/
|
|
201
203
|
export type HubIssueVariant =
|
|
202
|
-
/**
|
|
204
|
+
/** 缺省:安装器主动把失败送到插件中心分诊(未分类兜底) */
|
|
203
205
|
| 'triage'
|
|
204
|
-
/**
|
|
206
|
+
/** 从判定由插件中心自己产出的分支(插件打包问题 / allowBuilds 拦截)的次要入口提交:用户怀疑是误报 */
|
|
205
207
|
| 'misjudge'
|
|
206
208
|
|
|
207
209
|
export function hubIssueUrl(
|
|
@@ -230,16 +232,17 @@ export function hubIssueUrl(
|
|
|
230
232
|
`- Auto-classified cause: ${reasonTitleOf(kind, action)}`,
|
|
231
233
|
`- Target plugin: \`${repo}\``,
|
|
232
234
|
...(code ? [`- Key error: \`${code}\``] : []),
|
|
233
|
-
//
|
|
235
|
+
// 为什么投递到这里而非插件仓库:只有确有证据是插件自身构建/分发失败(pluginPrepare、
|
|
236
|
+
// 或自动放行 allowBuilds 重试后仍失败的构建拦截)才投插件仓库
|
|
234
237
|
...(variant === 'misjudge'
|
|
235
|
-
//
|
|
236
|
-
//
|
|
238
|
+
// 这些分支的判据由插件中心自己产出、或掺了我们的动作(自动放行 allowBuilds)——
|
|
239
|
+
// 一旦我们自己误报,用户就被送去作者仓库、我们永远收不到反馈。
|
|
237
240
|
// 正文写清它来自哪条分支、判据是谁给的,维护者才不会把它当成未分类兜底。
|
|
238
241
|
? [
|
|
239
|
-
'- Filed from:
|
|
240
|
-
'- Source of that verdict: produced by dsh-plugin-hub\'s own
|
|
242
|
+
'- Filed from: a branch that normally routes to the plugin repo — filed here because that verdict may be our own misjudgment.',
|
|
243
|
+
'- Source of that verdict: produced by dsh-plugin-hub\'s own check or its auto-allow retry, not by plain pnpm output.',
|
|
241
244
|
]
|
|
242
|
-
: ['- Why filed here: the installer only routes a failure to the plugin repo when
|
|
245
|
+
: ['- Why filed here: the installer only routes a failure to the plugin repo when the cause is demonstrably the plugin\'s own build/distribution (pluginPrepare, or build scripts pnpm blocks that even an auto-allow retry cannot get past). This failure does not match that, so it is routed here for triage.']),
|
|
243
246
|
'',
|
|
244
247
|
`## [DSH Plugin 插件市场](${SITE_URL}) · ${zhWord} Plugin 失败错误信息`,
|
|
245
248
|
`本错误信息由 [dsh-plugin-hub](${GITHUB_URL}) 插件市场的安装程序自动生成,随本次${zhWord}失败一并提交。`,
|
|
@@ -2,11 +2,9 @@
|
|
|
2
2
|
* Website: https://dsh-plugin.org
|
|
3
3
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* label + example hint above the input + input row (input + install button)
|
|
9
|
-
* + optional format-error note. Colors resolve through the --hub-* tokens.
|
|
5
|
+
* 自定义安装视图:独立一级板块,用于手动安装目录之外的 npm 包。安装卡片逐行堆叠:
|
|
6
|
+
* 渠道标签 + 输入框上方的示例提示 + 输入行(输入框 + 安装按钮)+ 可选的格式错误提示。
|
|
7
|
+
* 颜色经 --hub-* 变量解析。
|
|
10
8
|
*/
|
|
11
9
|
|
|
12
10
|
.root {
|
|
@@ -84,31 +82,6 @@
|
|
|
84
82
|
border-color: var(--hub-brand-border-soft);
|
|
85
83
|
}
|
|
86
84
|
|
|
87
|
-
/* 命令卡片标题旁的「一键插入更新命令」:右对齐小号描边按钮,弱化但可点,hover 提亮品牌色 */
|
|
88
|
-
.installInsertBtn {
|
|
89
|
-
flex-shrink: 0;
|
|
90
|
-
margin-left: auto;
|
|
91
|
-
font-family: inherit;
|
|
92
|
-
font-size: 11px;
|
|
93
|
-
line-height: 16px;
|
|
94
|
-
font-weight: 500;
|
|
95
|
-
color: var(--hub-text-secondary);
|
|
96
|
-
background: transparent;
|
|
97
|
-
border: 1px solid var(--hub-border-2);
|
|
98
|
-
border-radius: 6px;
|
|
99
|
-
padding: 2px 9px;
|
|
100
|
-
cursor: pointer;
|
|
101
|
-
white-space: nowrap;
|
|
102
|
-
user-select: none;
|
|
103
|
-
transition: color 120ms ease, border-color 120ms ease, background 120ms ease;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
.installInsertBtn:hover {
|
|
107
|
-
color: var(--hub-brand);
|
|
108
|
-
border-color: var(--hub-brand-border-soft);
|
|
109
|
-
background: var(--hub-brand-tint);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
85
|
/* 文本框上方的示例:灰字小字,告诉用户该通道的推荐写法 */
|
|
113
86
|
.installExample {
|
|
114
87
|
font-size: 11px;
|
|
@@ -2,11 +2,9 @@
|
|
|
2
2
|
* Website: https://dsh-plugin.org
|
|
3
3
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* a button — click to re-probe just that channel with a visible
|
|
9
|
-
* running→result feedback loop. Colors resolve through the --hub-* tokens. */
|
|
5
|
+
* 系统诊断列表:每个连通性探测一行 —— 名称、目标地址、延迟与实时状态徽标
|
|
6
|
+
* (200 OK / 不可达 / 检测中…)。页面打开时自动跑一轮;每行都是按钮 ——
|
|
7
|
+
* 点击只重测该通道,形成「运行→结果」的可见反馈闭环。颜色经 --hub-* 变量解析。 */
|
|
10
8
|
|
|
11
9
|
.panel {
|
|
12
10
|
display: flex;
|
|
@@ -2,10 +2,8 @@
|
|
|
2
2
|
* Website: https://dsh-plugin.org
|
|
3
3
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* a floating menu with hover fills and a brand-highlighted active option.
|
|
8
|
-
* Colors resolve through the --hub-* design tokens. */
|
|
5
|
+
* 按钮式下拉框:触发器呈现为紧凑工具栏按钮(细边框 + 淡填充 + 当前值 + 箭头),
|
|
6
|
+
* 面板是浮层菜单,选项有 hover 填充、激活项品牌高亮。颜色经 --hub-* 设计变量解析。 */
|
|
9
7
|
|
|
10
8
|
.dropdown {
|
|
11
9
|
position: relative;
|
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
* Website: https://dsh-plugin.org
|
|
3
3
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* tabs. Colors resolve through the --hub-* design tokens. */
|
|
5
|
+
* 插件中心板块头部:品牌行、副标题、广告条、搜索框、筛选工具栏
|
|
6
|
+
* (已安装/未安装、失败入口)与分类 tab。颜色经 --hub-* 设计变量解析。 */
|
|
8
7
|
|
|
9
8
|
.root {
|
|
10
9
|
height: 100%;
|
|
@@ -15,7 +14,7 @@
|
|
|
15
14
|
color: var(--hub-text-primary);
|
|
16
15
|
}
|
|
17
16
|
|
|
18
|
-
/* ——
|
|
17
|
+
/* —— 头部 —— */
|
|
19
18
|
|
|
20
19
|
.header {
|
|
21
20
|
display: flex;
|
|
@@ -241,7 +240,7 @@
|
|
|
241
240
|
text-align: center;
|
|
242
241
|
}
|
|
243
242
|
|
|
244
|
-
/* ——
|
|
243
|
+
/* —— 筛选结果数 —— */
|
|
245
244
|
|
|
246
245
|
/* 筛选结果数:弱化灰字信息「结果 N 条插件」,位于工具栏行最左侧 */
|
|
247
246
|
.resultSeg {
|
|
@@ -324,7 +323,7 @@
|
|
|
324
323
|
background: var(--hub-brand);
|
|
325
324
|
}
|
|
326
325
|
|
|
327
|
-
/* ——
|
|
326
|
+
/* —— 广告条 —— */
|
|
328
327
|
|
|
329
328
|
/* 广告条:固定蓝紫渐变 + 白字,不随主题变量漂移,两种主题下都醒目。
|
|
330
329
|
与 toast 相同思路:固定颜色保证可读性。 */
|
|
@@ -411,7 +410,7 @@
|
|
|
411
410
|
border-color: var(--hub-brand);
|
|
412
411
|
}
|
|
413
412
|
|
|
414
|
-
/* ——
|
|
413
|
+
/* —— 分类 chips —— */
|
|
415
414
|
|
|
416
415
|
.tabs {
|
|
417
416
|
display: flex;
|
|
@@ -2,13 +2,10 @@
|
|
|
2
2
|
* Website: https://dsh-plugin.org
|
|
3
3
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* lines: title line (running-status dot + name + version/update badges +
|
|
10
|
-
* fixed action buttons on the right), meta line (repo · category), and a
|
|
11
|
-
* one-line description. Colors resolve through the --hub-* tokens. */
|
|
5
|
+
* 已安装视图:可搜索、可排序的已安装插件清单,混排目录插件(完整元数据)与自定义安装
|
|
6
|
+
* (仅运行时信息,琥珀色自定义徽标)。工具栏含搜索框与两组分段单选(来源筛选 + 排序)。
|
|
7
|
+
* 每行三行整洁内容:标题行(运行状态点 + 名称 + 版本/更新徽标 + 右侧固定操作按钮)、
|
|
8
|
+
* 元信息行(仓库 · 分类)与单行描述。颜色经 --hub-* 变量解析。 */
|
|
12
9
|
|
|
13
10
|
.root {
|
|
14
11
|
display: flex;
|