dsh-plugin 1.6.11 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +1 -1
- package/README.md +1 -1
- package/client/client.js +705 -803
- 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/package-entry.js +6 -11
- package/lib/services/install/registry-check.js +5 -16
- package/lib/services/install/release-target.js +8 -9
- package/lib/services/install/task-queue.js +74 -57
- 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/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 +32 -31
- 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 +23 -27
- 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 +52 -24
- package/src/client/components/modals/ProgressView.tsx +3 -4
- package/src/client/components/modals/modals.tsx +58 -30
- 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 +96 -123
- package/src/client/logic/constants.ts +1 -2
- package/src/client/logic/ensure-plugin-css.ts +8 -12
- package/src/client/logic/failures.ts +41 -28
- 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 +55 -50
- 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 +4 -5
- 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/package-entry.ts +6 -11
- package/src/server/services/install/registry-check.ts +5 -16
- package/src/server/services/install/release-target.ts +8 -9
- package/src/server/services/install/task-queue.ts +79 -63
- 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
|
@@ -1,15 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 反查 repo(`owner/repo`)对应的官方 npm 包名;未命中、网络异常或超时返回 null。
|
|
3
|
-
* 返回 null 不代表仓库一定没有 npm 包,只代表本次未能确认 —— 调用方应保留
|
|
4
|
-
* 原有错误路径,反查只是额外的一次尝试。只有「已确认」的结果(包名,或 200
|
|
5
|
-
* 响应下确认无匹配)会进缓存;网络异常/超时/解析失败不缓存,同一会话内下次
|
|
6
|
-
* 安装仍会重试反查,避免一次瞬时故障把整个会话锁死在 git 通道。
|
|
7
|
-
* registry 参数:npm 镜像源地址,空串 = 官方源;与安装通道吃同一 registry,
|
|
8
|
-
* 保证「配置了镜像」时反查和安装走同一个源(镜像节点同步完整时结果一致)。
|
|
9
|
-
* proxy 参数:GitHub 改名跳转探测走的代理(与安装/诊断同一口径)。
|
|
10
|
-
* 慢网络下失败不阻塞安装。
|
|
11
|
-
*/
|
|
12
|
-
export declare function resolveNpmPackage(repo: string, registry?: string, proxy?: string): Promise<string | null>;
|
|
13
1
|
type NpmSearchPackage = {
|
|
14
2
|
name?: unknown;
|
|
15
3
|
keywords?: unknown;
|
|
@@ -19,10 +7,10 @@ type NpmSearchPackage = {
|
|
|
19
7
|
repository?: unknown;
|
|
20
8
|
dsh?: unknown;
|
|
21
9
|
};
|
|
22
|
-
type NpmSearchObject = {
|
|
23
|
-
package?: NpmSearchPackage;
|
|
24
|
-
};
|
|
25
10
|
export declare function isDshPackageMetadataForRepo(pkg: NpmSearchPackage, repo: string): boolean;
|
|
11
|
+
/**
|
|
12
|
+
* 校验包名对应的 npm 包确实属于该仓库且声明为 DSH 插件:取 registry 上的 `latest`
|
|
13
|
+
* 元数据比对 repository 与插件标记。网络异常、超时、非 200 一律返回 false,不阻塞调用方。
|
|
14
|
+
*/
|
|
26
15
|
export declare function isDshNpmPackageForRepo(packageName: string, repo: string, registry?: string, proxy?: string): Promise<boolean>;
|
|
27
|
-
export declare function selectNpmPackageForRepo(objects: NpmSearchObject[], repo: string): string | null;
|
|
28
16
|
export {};
|
|
@@ -4,17 +4,12 @@
|
|
|
4
4
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
5
|
*
|
|
6
6
|
* 分发文件解析:从 package.json 推出装完后必须真实存在的文件 —— 作者声明的入口
|
|
7
|
-
* (exports["."] / main)与 bundle 声明的补丁(dsh.bundle.patch
|
|
8
|
-
* 与 npm 通道(装后 statSync 校验)共用,保证两条通道对同一个包判定一致。
|
|
7
|
+
* (exports["."] / main)与 bundle 声明的补丁(dsh.bundle.patch),供装后校验使用。
|
|
9
8
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* 例外是补丁自己 mount 了本包:dsh 的《打包与安装插件》里组合包的常规写法就是
|
|
16
|
-
* `- insert: - name: <本包名>`,宿主随后按包名 import 本包,入口那时必须存在。
|
|
17
|
-
* 所以只有补丁真的写了自己的包名,才把这个入口算进分发要求。
|
|
9
|
+
* 只在包自己声明了入口时才要求它存在:宿主加载 bundle 读的是它的 package.json 与声明的补丁,
|
|
10
|
+
* 从不 import 包本身。硬要一个作者没声明过的 index.js,会把完整的包判成分发残缺、把一条假缺陷
|
|
11
|
+
* 推到作者仓库。例外是补丁自己 mount 了本包(组合包的常规写法)—— 那时宿主按包名 import 本包,
|
|
12
|
+
* 入口必须存在,所以只有补丁真的写了自己的包名才把这个入口算进分发要求。
|
|
18
13
|
*/
|
|
19
14
|
/** 解析所需的 manifest 字段;其余字段不读。 */
|
|
20
15
|
export interface PackageManifest {
|
|
@@ -36,7 +31,7 @@ export declare function resolvePackageEntry(meta: PackageManifest): string | nul
|
|
|
36
31
|
/**
|
|
37
32
|
* 解析 `dsh.bundle.patch` 声明的补丁文件(相对包根,不带 `./` 前缀)。
|
|
38
33
|
* 形状不合规范(既不是字符串也不是字符串数组)在这里不判定:那是 dsh 启动时
|
|
39
|
-
* bundlePatchFiles
|
|
34
|
+
* bundlePatchFiles 的报错职责,装后校验只负责「声明了就要在包里存在」。
|
|
40
35
|
*/
|
|
41
36
|
export declare function resolveBundlePatchFiles(meta: PackageManifest): string[];
|
|
42
37
|
/** 分发里必须存在的全部文件:作者声明的入口,加上 bundle 声明的补丁,入口在前。
|
|
@@ -3,14 +3,13 @@
|
|
|
3
3
|
* Website: https://dsh-plugin.org
|
|
4
4
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
5
|
*
|
|
6
|
-
* 预构建 GitHub release
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* 校验共用(单一事实来源,避免两端口径漂移)。
|
|
6
|
+
* 预构建 release 直链的仓库身份解析。部分插件(如 loopx)当初以 GitHub release 的 .tgz
|
|
7
|
+
* 安装过,仓库 HEAD 反而是 monorepo 根、走 git 直装会因缺入口文件失败;老安装记录里留的
|
|
8
|
+
* 就是这种 release 直链。这里把「历史安装目标」还原成所属仓库身份,供已装态读回、队列
|
|
9
|
+
* 去重与展示复用(单一事实来源,避免两端口径漂移)。
|
|
11
10
|
*
|
|
12
11
|
* 只接受固定形态的 HTTPS `github.com/<owner>/<repo>/releases/download/<tag>/<asset>.tgz`:
|
|
13
|
-
*
|
|
12
|
+
* 输入是记录/展示数据、绝非 shell 代码,绝不执行其文本;跨仓跳转、外部主机、凭据、
|
|
14
13
|
* 移动的 latest 链接、query/fragment、多余 CLI 参数与 shell 片段一律拒绝。
|
|
15
14
|
*/
|
|
16
15
|
/** release 包目标及其所属仓库身份(owner/repo);非法返回 null。 */
|
|
@@ -21,8 +20,8 @@ export interface GithubReleaseTarget {
|
|
|
21
20
|
repo: string;
|
|
22
21
|
}
|
|
23
22
|
/**
|
|
24
|
-
*
|
|
25
|
-
* @param value -
|
|
23
|
+
* 解析一个历史安装目标里的 release 直链。
|
|
24
|
+
* @param value - 完整命令(`dsh plugin --profile web add <url>`)或裸直链。
|
|
26
25
|
* @returns 命中返回 `{ target, repo }`,否则 null。
|
|
27
26
|
*/
|
|
28
27
|
export declare function githubReleaseTarget(value: string): GithubReleaseTarget | null;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { InstallResult, InstallTask } from './install-types.ts';
|
|
1
|
+
import type { InstallResult, InstallTask, Invocation } from './install-types.ts';
|
|
2
2
|
import type { LoaderHandle } from '../loader.ts';
|
|
3
|
-
/**
|
|
3
|
+
/** 任务快照(避免消费方改动实时对象)。 */
|
|
4
4
|
export declare function getTask(id: number): InstallTask | undefined;
|
|
5
5
|
export interface ActiveTaskInfo {
|
|
6
6
|
id: number;
|
|
@@ -9,28 +9,26 @@ export interface ActiveTaskInfo {
|
|
|
9
9
|
status: 'pending' | 'running';
|
|
10
10
|
progress: number;
|
|
11
11
|
lines: string[];
|
|
12
|
-
/**
|
|
12
|
+
/** 尝试过的安装方式(实际执行过的命令):失败时前端 issue 预填需要 */
|
|
13
13
|
attempts: string[];
|
|
14
|
-
/** 自定义安装入口渠道(NPM
|
|
15
|
-
installChannel?: 'npm'
|
|
14
|
+
/** 自定义安装入口渠道(NPM 包卡片):前端刷新恢复后仍能精准溯源 */
|
|
15
|
+
installChannel?: 'npm';
|
|
16
16
|
}
|
|
17
|
-
/**
|
|
17
|
+
/** 全部未终态任务,按队列顺序(running 在前、pending 在后)。供客户端刷新页面后恢复队列。 */
|
|
18
18
|
export declare function activeTask(): ActiveTaskInfo[];
|
|
19
19
|
/**
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* (the close event lands later) — the slot must stay held until then, otherwise
|
|
24
|
-
* a new task would start while the old pnpm still runs in the same profile dir.
|
|
20
|
+
* 变更子进程仍存活时为 true。并发占位以存活子进程集合为准,而非 task.status:
|
|
21
|
+
* 取消运行中任务会立即翻转其状态,但子进程可能仍在退出中(close 事件稍后才到)——
|
|
22
|
+
* 占位必须维持到那时,否则旧 pnpm 还在同一 profile 目录跑,新任务就开始了。
|
|
25
23
|
*/
|
|
26
24
|
export declare function hasRunningTask(): boolean;
|
|
27
|
-
/**
|
|
25
|
+
/** 目标在队列里是否已有未终态任务(安装/卸载去重)。 */
|
|
28
26
|
export declare function hasQueuedTarget(target: string): boolean;
|
|
29
27
|
/**
|
|
30
|
-
*
|
|
31
|
-
* - pending
|
|
32
|
-
* - running
|
|
33
|
-
*
|
|
28
|
+
* 取消排队中或运行中的任务。
|
|
29
|
+
* - pending:立即移出队列(从未 spawn)。
|
|
30
|
+
* - running:杀掉子进程;队列 worker 随后取下一个。
|
|
31
|
+
* 任务不存在或已结束时返回 false。
|
|
34
32
|
*/
|
|
35
33
|
export declare function cancelTask(id: number): boolean;
|
|
36
34
|
/**
|
|
@@ -41,9 +39,8 @@ export declare function cancelTask(id: number): boolean;
|
|
|
41
39
|
*/
|
|
42
40
|
export declare function desktopCliBootstrap(entry: string): string | null;
|
|
43
41
|
/**
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
* Progress is visible through `getTask(id)` / `activeTask()` until done.
|
|
42
|
+
* 插件变更入队并返回其任务。任务严格串行:队列 worker 只在上一个结束后才启动下一个。
|
|
43
|
+
* 完成前可通过 `getTask(id)` / `activeTask()` 查看进度。
|
|
47
44
|
*/
|
|
48
45
|
export declare function startPluginMutation(options: {
|
|
49
46
|
action: 'add' | 'remove' | 'update';
|
|
@@ -58,17 +55,17 @@ export declare function startPluginMutation(options: {
|
|
|
58
55
|
/** 运行中 loader:安装成功后热挂新条目、卸载成功后停用旧条目,两类操作都尽量立即生效
|
|
59
56
|
* (缺失时安装/卸载仍需重启) */
|
|
60
57
|
loader?: LoaderHandle;
|
|
61
|
-
/**
|
|
58
|
+
/** 入队前已尝试的安装方式:失败提 Issue 时如实展示;实际执行的命令由 spawn 时追加 */
|
|
62
59
|
attempts?: string[];
|
|
63
60
|
/** 更新已安装的 npm 包(mode=update):命令显式带 @latest,让 pnpm 真正解析最新版本 */
|
|
64
61
|
updateNpm?: boolean;
|
|
65
|
-
/**
|
|
66
|
-
*
|
|
67
|
-
installChannel?: 'npm'
|
|
62
|
+
/** 自定义安装入口渠道(NPM 包卡片):任务输出行与系统日志据此溯源「从哪个入口发起」;
|
|
63
|
+
* 目录插件安装不传 */
|
|
64
|
+
installChannel?: 'npm';
|
|
68
65
|
}): InstallTask;
|
|
69
66
|
/**
|
|
70
67
|
* 安装后校验已装包里 package.json 声明的文件(main / exports["."].default 与
|
|
71
|
-
* dsh.bundle.patch)是否真实存在。
|
|
68
|
+
* dsh.bundle.patch)是否真实存在。npm 包也可能漏发构建产物(lib/ 等),pnpm 装完没有
|
|
72
69
|
* 报错,但宿主重启加载插件树时会 ERR_MODULE_NOT_FOUND 直接崩溃、网页打不开 —— 这里在
|
|
73
70
|
* 登记「待重启」前就把这类残缺包拦下。没有声明过的文件不在这里追究:没声明入口的
|
|
74
71
|
* bundle 包(只挂技能)本来就不需要入口,宿主读的是它的 package.json 与声明的补丁。
|
|
@@ -79,13 +76,17 @@ export declare function verifyInstalledEntry(profile: string, target: string): {
|
|
|
79
76
|
missing: string | null;
|
|
80
77
|
};
|
|
81
78
|
/**
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
*
|
|
79
|
+
* 宿主自身 CLI 调不起来时的 ASCII 标记行:spawn 我们解析出的宿主入口报 ENOENT 时返回
|
|
80
|
+
* `[host-spawn]`,供前端单独归因、不引导提 Issue。PATH 上的 dsh/npm 各有自己的标记,返回 null。
|
|
81
|
+
* 导出供单测覆盖判据。
|
|
82
|
+
*/
|
|
83
|
+
export declare function hostSpawnMarker(invocation: Invocation, error: unknown): string | null;
|
|
84
|
+
/**
|
|
85
|
+
* 执行插件变更,带一条恢复路径:当 `add` 因 pnpm 的 allowBuilds 门禁拦下构建脚本而失败 ——
|
|
86
|
+
* 或是 git 源包自己的 `prepare`(`ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED`,key 取自打印的
|
|
87
|
+
* key@url 提示),或是某依赖的原生构建(`ERR_PNPM_IGNORED_BUILDS`,key = 列出的 name@version)——
|
|
88
|
+
* 就把确切的 key 写进 profile 的 pnpm-workspace.yaml 并重试一次。
|
|
89
|
+
* 其它失败一律落到单次 spawn 的结果。
|
|
89
90
|
*/
|
|
90
91
|
export declare function runPluginMutation(options: {
|
|
91
92
|
action: 'add' | 'remove' | 'update';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface NpmRelease {
|
|
2
|
+
version: string;
|
|
3
|
+
publishedAt: string;
|
|
4
|
+
}
|
|
5
|
+
/** 请求体包名数量上限:只为已安装插件按需查询,几十个足矣,封顶防滥用。 */
|
|
6
|
+
export declare const MAX_RELEASE_AGE_PACKAGES = 50;
|
|
7
|
+
/** 单包查询超时(ms):慢网络下失败即返回 null,不拖住整个响应。 */
|
|
8
|
+
export declare const RELEASE_AGE_TIMEOUT_MS = 8000;
|
|
9
|
+
export declare const DEFAULT_NPM_REGISTRY = "https://registry.npmjs.org";
|
|
10
|
+
/** 从完整 packument 取 dist-tags.latest 对应版本及其发布时间;缺任一字段返回 null。 */
|
|
11
|
+
export declare function latestReleaseOf(packument: unknown): NpmRelease | null;
|
|
12
|
+
/** 请求体 → 去重后的合法包名列表;非数组 / 超量 / 含非法包名一律 null(拒绝非法输入)。 */
|
|
13
|
+
export declare function parseReleaseAgeRequest(body: unknown): string[] | null;
|
|
14
|
+
/** 查询一个包的最新版本与发布时间(成功结果缓存,同会话内同包不重复请求);registry 为空回退官方源,失败返回 null。 */
|
|
15
|
+
export declare function lookupReleaseAge(packageName: string, registry: string, proxy: string, timeoutMs?: number): Promise<NpmRelease | null>;
|
|
@@ -33,7 +33,7 @@ export declare function tlsRevocationBlocked(stderr: string): boolean;
|
|
|
33
33
|
* `http=host:port;https=host:port`(按协议分别配置)。
|
|
34
34
|
* 字符类必须同时排除 `/` 与 `=`:只排除 `=` 时,带 scheme 的值会让「主机名」吞掉 `//`,
|
|
35
35
|
* 拼出 `http:////127.0.0.1:10793` 这种立刻失败的非法地址 —— 代理本来是通的,却把诊断、
|
|
36
|
-
*
|
|
36
|
+
* 目录拉取、安装前网络探测全判成「不可达」。
|
|
37
37
|
*/
|
|
38
38
|
export declare function parseWinProxyServer(stdout: string): string | null;
|
|
39
39
|
/**
|
|
@@ -49,13 +49,13 @@ export declare function systemProxy(): string | null;
|
|
|
49
49
|
*/
|
|
50
50
|
export declare function probeUrl(url: string, proxy: string, timeoutMs: number): Promise<ProbeResult>;
|
|
51
51
|
/**
|
|
52
|
-
* git
|
|
52
|
+
* git 真实克隆握手探测:spawn git ls-remote(https 传输,与 pnpm 克隆前的
|
|
53
53
|
* ref 握手一致),注入代理 env,GIT_TERMINAL_PROMPT=0 防凭据提示挂起。
|
|
54
54
|
*
|
|
55
55
|
* 为什么要真实 git 而非 curl 打网页:网页「能打开」和 git「能克隆」是两码事 ——
|
|
56
56
|
* 防火墙/代理常按端口与协议区分,HTTP 页可达不代表 git 传输可达。这里测的就是
|
|
57
|
-
*
|
|
58
|
-
* 供系统诊断 GitHub
|
|
57
|
+
* 克隆握手本身,回答「能不能连上 github.com」。
|
|
58
|
+
* 供系统诊断 GitHub 可达性使用;探测目标用 dshplugin/hello-dsh 小仓库(秒级完成,
|
|
59
59
|
* 不打 17MB 的 dsh-plugin-hub 主页)。
|
|
60
60
|
*/
|
|
61
61
|
export declare function gitLsRemote(url: string, proxy: string, timeoutMs: number): Promise<ProbeResult>;
|
|
@@ -4,21 +4,19 @@ export interface InstalledVersionRecord {
|
|
|
4
4
|
/** 安装时的仓库最近更新时间(ISO,GitHub pushed_at);无 release 仓库的更新依据 */
|
|
5
5
|
updatedAt: string;
|
|
6
6
|
installedAt: string;
|
|
7
|
-
/**
|
|
7
|
+
/** 安装时记录在案的官方 npm 包名(repo → 包名映射):目录数据可能未下发
|
|
8
8
|
* npmPackage(组织 scope 与 GitHub 用户名不一致),客户端据此把依赖 key 匹配回仓库 */
|
|
9
9
|
npmPackage?: string;
|
|
10
10
|
}
|
|
11
11
|
export type InstalledVersionTable = Record<string, InstalledVersionRecord>;
|
|
12
12
|
export declare function versionsFilePath(profile: string): string;
|
|
13
13
|
export declare function readInstalledVersions(profile: string): InstalledVersionTable;
|
|
14
|
-
/**
|
|
14
|
+
/** 记录某仓库安装时的目录信号(版本 + 仓库更新时间)。 */
|
|
15
15
|
export declare function recordInstalledVersion(profile: string, repo: string, version: string, updatedAt: string): void;
|
|
16
16
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* harmless when the repo is not (yet) installed — the client only treats a repo
|
|
20
|
-
* as installed when its dependency key actually exists.
|
|
17
|
+
* 记录某仓库解析出的 npm 包名。在 `/install` 时调用,让映射在任务完成前就存在;
|
|
18
|
+
* 仓库尚未安装时也无害 —— 客户端只在依赖 key 真实存在时才把仓库当作已安装。
|
|
21
19
|
*/
|
|
22
20
|
export declare function recordResolvedNpmPackage(profile: string, repo: string, npmPackage: string): void;
|
|
23
|
-
/**
|
|
21
|
+
/** 清除某仓库记录的版本(卸载时调用)。 */
|
|
24
22
|
export declare function removeInstalledVersion(profile: string, repo: string): void;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/** 从启动参数解析当前 profile,取不到落回 `web`。 */
|
|
2
2
|
export declare function readProfileArg(fallback?: string): string;
|
|
3
3
|
/**
|
|
4
4
|
* 从启动参数解析当前 profile,两种宿主形态都认:
|
|
@@ -10,32 +10,28 @@ export declare function readProfileArg(fallback?: string): string;
|
|
|
10
10
|
* 位置参数按「父目录名为 profiles」精确识别 profile 目录,避免误取其它位置参数。
|
|
11
11
|
*/
|
|
12
12
|
export declare function profileFromArgv(argv: readonly string[], fallback?: string): string;
|
|
13
|
-
/**
|
|
13
|
+
/** 解析 profile 目录(`DSH_HOME` 或 `~/.dsh`)。 */
|
|
14
14
|
export declare function profileDirectory(profile: string): string;
|
|
15
|
-
/**
|
|
16
|
-
*
|
|
17
|
-
* explicitly pinned revision reaches the installer instead of being dropped. */
|
|
15
|
+
/** 把仓库身份转成规范的 GitHub 目标(git+https),仓库不合法时返回 null。
|
|
16
|
+
* 带 ref 时按 Git ref(`#tag` / `#commit`)拼接;安装通道已不再使用 git 目标,此函数仅做身份规范化。 */
|
|
18
17
|
export declare function githubTarget(repo: string, ref?: string | null): string | null;
|
|
19
|
-
export declare function githubRefOf(value: string): string | null;
|
|
20
18
|
export declare function installTargetOf(value: string): string;
|
|
21
19
|
export declare function globalNpmPackagesOf(value: string): string[] | null;
|
|
22
|
-
/**
|
|
20
|
+
/** 从目录值或已装 Git spec 中提取 owner/repo 身份。 */
|
|
23
21
|
export declare function githubRepoOf(value: string): string | null;
|
|
24
|
-
/**
|
|
22
|
+
/** 校验 npm 包名(卸载目标的语法)。 */
|
|
25
23
|
export declare function validPackageName(name: string): boolean;
|
|
26
24
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* the only reliable key is the one pnpm prints verbatim. Null when absent.
|
|
25
|
+
* 取 pnpm 在 `ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED` 提示里原样打印的 `allowBuilds` key。
|
|
26
|
+
* Git 安装时 pnpm 按 `name@<实际 fetch URL>`(如
|
|
27
|
+
* `https://codeload.github.com/<owner>/<repo>/tar.gz/<commit>`)匹配,而不是 `git+ssh://`
|
|
28
|
+
* 或分支 spec —— 裸包名与 `git+` spec 都匹配不上,唯一可靠的 key 就是 pnpm 原样输出的那个。
|
|
29
|
+
* 无则返回 null。
|
|
33
30
|
*/
|
|
34
31
|
export declare function parseAllowBuildsKey(output: string): string | null;
|
|
35
32
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* `set this to true or false` value) are dropped on rewrite.
|
|
33
|
+
* 向 profile 的 pnpm-workspace.yaml 追加一条 `allowBuilds` 记录:与已有条目合并,
|
|
34
|
+
* 其余 yaml 保持不变;重写时丢弃损坏的占位条目(pnpm 安装失败的 bug 会写入
|
|
35
|
+
* 字面量 `set this to true or false`)。
|
|
40
36
|
*/
|
|
41
37
|
export declare function addAllowBuildsKey(profile: string, key: string): void;
|
|
@@ -3,19 +3,16 @@
|
|
|
3
3
|
* Website: https://dsh-plugin.org
|
|
4
4
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
5
|
*
|
|
6
|
-
*
|
|
6
|
+
* 安装执行器共用的纯进度估算工具。
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* line and map it onto a 0-100 estimate; keeping them free of I/O makes the
|
|
12
|
-
* estimation logic directly unit-testable (see tests/progress.test.ts).
|
|
8
|
+
* dsh CLI 包装了 pnpm,pnpm 用回车原地刷新 `Progress: …` 行,因此原始输出是一串碎片。
|
|
9
|
+
* 这些工具把单个碎片归一化成干净的终端行,并映射到 0-100 估算值;不碰 I/O
|
|
10
|
+
* 让估算逻辑可直接单测(见 tests/progress.test.ts)。
|
|
13
11
|
*/
|
|
14
|
-
/**
|
|
12
|
+
/** 剥掉 `[out]` / `[err]` 来源前缀与 ANSI 转义序列。 */
|
|
15
13
|
export declare function cleanLine(raw: string): string;
|
|
16
14
|
/**
|
|
17
|
-
*
|
|
18
|
-
* `Progress: resolved N, reused X, downloaded Y, added Z
|
|
19
|
-
* phase; resolution/install phase lines bump the estimate towards done.
|
|
15
|
+
* 从单行 CLI 输出估算 0-100 进度。pnpm 在 fetch 阶段输出
|
|
16
|
+
* `Progress: resolved N, reused X, downloaded Y, added Z`;解析/安装阶段的行把估算值推向完成。
|
|
20
17
|
*/
|
|
21
18
|
export declare function estimateProgress(line: string): number;
|
|
@@ -7,10 +7,6 @@ export interface HubSettings {
|
|
|
7
7
|
npmRegistry: string;
|
|
8
8
|
/** 启用命令行安装 NPM 包(默认开;关掉后输入 npm 包名安装被拦截) */
|
|
9
9
|
enableNpmInstall: boolean;
|
|
10
|
-
/** 启用命令行安装 GitHub 源码(默认开;关掉后输入 GitHub 地址安装被拦截) */
|
|
11
|
-
enableGitInstall: boolean;
|
|
12
|
-
/** 启用命令行粘贴 dsh plugin 命令安装(默认开;关掉后命令输入被禁用) */
|
|
13
|
-
enableDshInstall: boolean;
|
|
14
10
|
/** 日志存放位置覆盖(空串 = 默认 ~/.dsh/profiles/<profile>/hub.log;可填目录或 .log 文件路径) */
|
|
15
11
|
logPath: string;
|
|
16
12
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-plugin",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "A community plugin marketplace for DeepSeek Harness, built to the official plugin spec — browse, search and install 10000+ human-curated community plugins without leaving the app. · DeepSeek Harness 社区插件市场(遵循官方开发规范):10000+ 人工精选社区插件,每日更新。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -3,10 +3,8 @@
|
|
|
3
3
|
* Website: https://dsh-plugin.org
|
|
4
4
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* rendering is delegated to the small presentational components in this
|
|
9
|
-
* folder; it hosts the dialogs/toast and the section-level copy actions.
|
|
6
|
+
* 插件中心板块:把目录数据管道、后台任务队列与反馈状态接在一起。数据/队列逻辑在 hooks/,
|
|
7
|
+
* 渲染交给本目录下的小型展示组件;本文件托管各弹窗/Toast 与板块级复制动作。
|
|
10
8
|
*/
|
|
11
9
|
import { createElement as h, useEffect, useRef, useState } from 'react'
|
|
12
10
|
import type { MouseEvent } from 'react'
|
|
@@ -27,7 +25,7 @@ import { HubUpdateModal } from './modals/HubUpdateModal.tsx'
|
|
|
27
25
|
import { InstalledDetailModal } from './modals/InstalledDetailModal.tsx'
|
|
28
26
|
import { NotificationsModal } from './modals/NotificationsModal.tsx'
|
|
29
27
|
import { addFailure, addSuccess, addUpdateNotice, clearNotifications, ignoreUpdate, loadIgnoredUpdates, loadNotifications, removeNotification, removeUpdateNotice } from '../logic/failures.ts'
|
|
30
|
-
import type { NotificationRecord } from '../logic/failures.ts'
|
|
28
|
+
import type { FailureAction, NotificationRecord } from '../logic/failures.ts'
|
|
31
29
|
import { CatalogHeader } from './layout/CatalogHeader.tsx'
|
|
32
30
|
import { MarketView } from './views/MarketView.tsx'
|
|
33
31
|
import { SectionTabs } from './layout/SectionTabs.tsx'
|
|
@@ -78,10 +76,10 @@ export function PluginHubSection({ t: _hostT, locale }: SectionProps) {
|
|
|
78
76
|
const [confirmPlugin, setConfirmPlugin] = useState<HubPlugin | null>(null)
|
|
79
77
|
/** 弹窗动作是否为「更新」:已安装插件点「更新」→ 走同一条 add 命令原位覆盖重装,文案区分安装/更新 */
|
|
80
78
|
const [confirmIsUpdate, setConfirmIsUpdate] = useState(false)
|
|
81
|
-
/** 命令行安装确认弹窗:记录待安装的裸目标(npm
|
|
79
|
+
/** 命令行安装确认弹窗:记录待安装的裸目标(npm 包名)——
|
|
82
80
|
* 与应用商店同一套确认/进度/结果弹窗,提供时 plugin 传 null 走 customTarget 模式 */
|
|
83
81
|
const [confirmCustomTarget, setConfirmCustomTarget] = useState<string | null>(null)
|
|
84
|
-
/**
|
|
82
|
+
/** 自定义安装入口渠道(NPM 包卡片):随确认弹窗记录,提交时上报服务端,
|
|
85
83
|
* 报错/日志据此精准溯源「从哪个入口发起」(npm 全局安装固定为 'npm') */
|
|
86
84
|
const [confirmCustomChannel, setConfirmCustomChannel] = useState<InstallChannel | null>(null)
|
|
87
85
|
/** 全局 npm 安装确认弹窗(官方 `npm install -g <pkgs>`):记录原始命令 + 解析出的包列表,
|
|
@@ -114,8 +112,8 @@ export function PluginHubSection({ t: _hostT, locale }: SectionProps) {
|
|
|
114
112
|
const [uninstallNeedsReload, setUninstallNeedsReload] = useState(false)
|
|
115
113
|
/** 结果视图「立即重启」:请求宿主重启后进入等待,服务回来后整页刷新 */
|
|
116
114
|
const [restarting, setRestarting] = useState(false)
|
|
117
|
-
/**
|
|
118
|
-
const [errorMsg, setErrorMsg] = useState<{ message: string; repo: string | null;
|
|
115
|
+
/** 操作失败信息 + 归属仓库 + 动作(决定弹窗标题与上报措辞)+ 命令与尝试记录(issue 预填用) */
|
|
116
|
+
const [errorMsg, setErrorMsg] = useState<{ message: string; repo: string | null; action: FailureAction; command?: string; attempts?: string[] } | null>(null)
|
|
119
117
|
/** 通知中心记录:localStorage 持久化,每次安装/卸载任务成败都留痕(启动时读回) */
|
|
120
118
|
const [notifications, setNotifications] = useState<NotificationRecord[]>(() => loadNotifications())
|
|
121
119
|
/** 通知中心弹窗:一级导航「设置」tab 后边的铃铛入口按钮打开 */
|
|
@@ -142,7 +140,7 @@ export function PluginHubSection({ t: _hostT, locale }: SectionProps) {
|
|
|
142
140
|
|
|
143
141
|
const queue = useTaskQueue({
|
|
144
142
|
t,
|
|
145
|
-
//
|
|
143
|
+
// 界面语言带给服务端:安装前网络探测等错误消息按用户语言提示(中文界面给中文,英文界面给英文)
|
|
146
144
|
langKey,
|
|
147
145
|
refreshInstalled: catalog.refreshInstalled,
|
|
148
146
|
onInstallDone: (viaModal, repo, needsRestart, update, needsReload) => {
|
|
@@ -173,7 +171,8 @@ export function PluginHubSection({ t: _hostT, locale }: SectionProps) {
|
|
|
173
171
|
// 提 Issue 目标先反查仓库身份:npm 包名能在已安装表/目录里反查到 owner/repo 才保留,
|
|
174
172
|
// 反查不到置 null —— 错误弹窗据此不显示「一键提 Issue」按钮(防提到错误仓库)
|
|
175
173
|
const issueRepo = issueRepoOf(repo ?? '', catalog.installedItems, catalog.plugins)
|
|
176
|
-
|
|
174
|
+
// 动作:更新失败要显示「更新失败」,其余按安装/卸载
|
|
175
|
+
setErrorMsg({ message, repo: issueRepo, action: update ? 'update' : kind, command, attempts })
|
|
177
176
|
// 失败自动写入通知记录:任务在后台结束时没人盯着也能留痕。
|
|
178
177
|
// 存原始 repo(可能只是裸包名)而非反查结果 —— 反查只服务于提 Issue,
|
|
179
178
|
// 通知中心的展示反查由 NotificationsModal 做、查不到会回退显示原始值,
|
|
@@ -223,10 +222,12 @@ export function PluginHubSection({ t: _hostT, locale }: SectionProps) {
|
|
|
223
222
|
useEffect(() => {
|
|
224
223
|
if (startupCheckedRef.current) return
|
|
225
224
|
if (!settingsReady || catalog.plugins === null || catalog.failed) return
|
|
225
|
+
// 供应链门禁结果未就绪时先等:否则刚发布的插件会先写进通知中心,冒出装不上的更新提醒
|
|
226
|
+
if (!catalog.releaseAgeReady) return
|
|
226
227
|
startupCheckedRef.current = true
|
|
227
228
|
if (!hubSettings.checkUpdatesOnStart) return
|
|
228
229
|
syncUpdateNotices()
|
|
229
|
-
}, [settingsReady, hubSettings, catalog.plugins, catalog.failed, catalog.installedItems, queue])
|
|
230
|
+
}, [settingsReady, hubSettings, catalog.plugins, catalog.failed, catalog.installedItems, catalog.releaseAgeReady, queue])
|
|
230
231
|
|
|
231
232
|
// 信任/卸载/更新说明弹窗打开时按 Esc 关闭;任务进入后台队列后可随时关闭(任务继续)
|
|
232
233
|
useEffect(() => {
|
|
@@ -264,8 +265,7 @@ export function PluginHubSection({ t: _hostT, locale }: SectionProps) {
|
|
|
264
265
|
await navigator.clipboard.writeText(text)
|
|
265
266
|
return true
|
|
266
267
|
} catch {
|
|
267
|
-
// Clipboard API
|
|
268
|
-
// legacy hidden-textarea trick, which works in any context.
|
|
268
|
+
// Clipboard API 不可用(权限/iframe)—— 退回旧的隐藏 textarea 方案,任何环境都可用。
|
|
269
269
|
const ta = document.createElement('textarea')
|
|
270
270
|
ta.value = text
|
|
271
271
|
ta.style.position = 'fixed'
|
|
@@ -305,7 +305,7 @@ export function PluginHubSection({ t: _hostT, locale }: SectionProps) {
|
|
|
305
305
|
Object.keys(catalog.installed).some((k) => k.toLowerCase() === pkg)
|
|
306
306
|
}
|
|
307
307
|
|
|
308
|
-
/** 弹窗动作一:复制安装命令到剪贴板,引导去终端粘贴执行(npm
|
|
308
|
+
/** 弹窗动作一:复制安装命令到剪贴板,引导去终端粘贴执行(npm 安装显示包名命令)。 */
|
|
309
309
|
const copyCommand = async (p: HubPlugin) => {
|
|
310
310
|
const repo = p.source?.repo ?? ''
|
|
311
311
|
const ok = await doCopy(installCommandOf(p))
|
|
@@ -416,8 +416,8 @@ export function PluginHubSection({ t: _hostT, locale }: SectionProps) {
|
|
|
416
416
|
noticeCount: notifications.length + queue.queue.length + queue.pendingRestarts.length,
|
|
417
417
|
onOpenNotifications: () => {
|
|
418
418
|
// 打开弹窗时补齐更新提醒(受「启动时检查更新」开关约束):同一版本只一条,
|
|
419
|
-
//
|
|
420
|
-
if (hubSettings.checkUpdatesOnStart) syncUpdateNotices()
|
|
419
|
+
// 清空/删除后重开仍恢复,直到插件更新完成才消失;门禁结果未就绪时先不写,避免提示装不上的更新
|
|
420
|
+
if (hubSettings.checkUpdatesOnStart && catalog.releaseAgeReady) syncUpdateNotices()
|
|
421
421
|
setShowNotifications(true)
|
|
422
422
|
},
|
|
423
423
|
}),
|
|
@@ -488,17 +488,13 @@ export function PluginHubSection({ t: _hostT, locale }: SectionProps) {
|
|
|
488
488
|
: view === 'custom'
|
|
489
489
|
? h(CustomInstallView, {
|
|
490
490
|
t,
|
|
491
|
-
// 命令行安装:与应用商店同一套确认/进度/结果弹窗 —— 输入 npm 包名 /
|
|
492
|
-
//
|
|
491
|
+
// 命令行安装:与应用商店同一套确认/进度/结果弹窗 —— 输入 npm 包名 / npm、pnpm
|
|
492
|
+
// 安装命令即装(custom 源,受安全设置开关控制)。点安装先弹确认窗,
|
|
493
493
|
// 确认后走队列安装(实时进度 + 成功结果视图);目标已安装 → 弹窗转「更新」覆盖重装,
|
|
494
494
|
// 不会撞「already installed」报错。
|
|
495
|
-
//
|
|
495
|
+
// 安全信任开关关掉 → 卡片禁用并提示去设置打开(onOpenSettings 跳到设置页)。
|
|
496
496
|
enableNpm: hubSettings.enableNpmInstall,
|
|
497
|
-
enableGit: hubSettings.enableGitInstall,
|
|
498
|
-
enableDsh: hubSettings.enableDshInstall,
|
|
499
497
|
onOpenSettings: () => setView('settings'),
|
|
500
|
-
// 当前 profile:一键插入的 Hub 更新命令用它拼 --profile
|
|
501
|
-
profile: env?.profile ?? 'web',
|
|
502
498
|
onInstallCustom: (raw, opts) => {
|
|
503
499
|
const target = raw.trim()
|
|
504
500
|
if (!target) return
|
|
@@ -515,7 +511,7 @@ export function PluginHubSection({ t: _hostT, locale }: SectionProps) {
|
|
|
515
511
|
return
|
|
516
512
|
}
|
|
517
513
|
setConfirmGlobalNpm(null)
|
|
518
|
-
//
|
|
514
|
+
// 记录入口渠道:报错/日志据此精准提示「从哪张卡片发起的安装」
|
|
519
515
|
setConfirmCustomChannel(opts.installChannel)
|
|
520
516
|
setConfirmIsUpdate(customTargetInstalled(target))
|
|
521
517
|
setConfirmCustomTarget(target)
|
|
@@ -695,7 +691,7 @@ export function PluginHubSection({ t: _hostT, locale }: SectionProps) {
|
|
|
695
691
|
},
|
|
696
692
|
onInstall: () => void queue.installCustom(confirmCustomTarget, {
|
|
697
693
|
update: confirmIsUpdate,
|
|
698
|
-
// 入口渠道(NPM
|
|
694
|
+
// 入口渠道(NPM 包卡片):上报服务端,报错/日志据此溯源
|
|
699
695
|
channel: confirmCustomChannel ?? 'npm',
|
|
700
696
|
}),
|
|
701
697
|
onRestart: () => setShowRestartConfirm(true),
|
|
@@ -793,7 +789,7 @@ export function PluginHubSection({ t: _hostT, locale }: SectionProps) {
|
|
|
793
789
|
errorMsg && h(ErrorModal, {
|
|
794
790
|
message: errorMsg.message,
|
|
795
791
|
repo: errorMsg.repo,
|
|
796
|
-
|
|
792
|
+
action: errorMsg.action,
|
|
797
793
|
command: errorMsg.command,
|
|
798
794
|
attempts: errorMsg.attempts,
|
|
799
795
|
t,
|
|
@@ -3,10 +3,8 @@
|
|
|
3
3
|
* Website: https://dsh-plugin.org
|
|
4
4
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* the category or install-status filter changes so the replaced content is
|
|
9
|
-
* not mistaken for a no-op update.
|
|
6
|
+
* 目录列表:承载加载/失败/空三态、插件卡片与计数页脚的滚动容器。分类或安装状态
|
|
7
|
+
* 筛选变化时重置滚动位置,避免替换后的内容被误当成「没变化」。
|
|
10
8
|
*/
|
|
11
9
|
import { createElement as h, useEffect, useRef } from 'react'
|
|
12
10
|
import styles from '../../styles/List.module.css'
|
|
@@ -3,9 +3,8 @@
|
|
|
3
3
|
* Website: https://dsh-plugin.org
|
|
4
4
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* uninstall actions.
|
|
6
|
+
* 目录列表里的单张插件卡片:名称/分类/verified 徽标、描述 + 标签、Star/Fork/日期
|
|
7
|
+
* 统计,以及详情/安装/卸载动作。
|
|
9
8
|
*/
|
|
10
9
|
import { createElement as h } from 'react'
|
|
11
10
|
import styles from '../../styles/List.module.css'
|
|
@@ -3,12 +3,11 @@
|
|
|
3
3
|
* Website: https://dsh-plugin.org
|
|
4
4
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* button shows an up/down arrow for the current direction).
|
|
6
|
+
* 市场列表上方的工具栏:
|
|
7
|
+
* 第一行 —— 整行宽的搜索框;
|
|
8
|
+
* 第二行 —— 左侧结果计数,右侧排序分段控件
|
|
9
|
+
* (与已安装视图一致:点同一排序切换正/倒序,点新排序用其默认方向;
|
|
10
|
+
* 激活按钮显示当前方向的上/下箭头)。
|
|
12
11
|
*/
|
|
13
12
|
import { createElement as h, Fragment } from 'react'
|
|
14
13
|
import type { FormEvent, ReactNode } from 'react'
|
|
@@ -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
|
-
* by the purple ad banner that promotes the catalog stats.
|
|
6
|
+
* 板块头部:品牌标题行(H1 + 官网入口按钮)+ 副标题,其后是宣传目录统计的紫色广告条。
|
|
8
7
|
*/
|
|
9
8
|
import { createElement as h, Fragment } from 'react'
|
|
10
9
|
import styles from '../../styles/Header.module.css'
|
|
@@ -3,9 +3,8 @@
|
|
|
3
3
|
* Website: https://dsh-plugin.org
|
|
4
4
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* carry no counts so their widths stay uniform.
|
|
6
|
+
* 分类 Tab:先是「全部」chip,其后每个目录分类一个 chip。「全部」chip 带插件总数,
|
|
7
|
+
* 各分类 chip 不带计数以保持宽度一致。
|
|
9
8
|
*/
|
|
10
9
|
import { createElement as h } from 'react'
|
|
11
10
|
import styles from '../../styles/Header.module.css'
|