dsh-preview-market 0.1.0 → 0.1.2
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/CHANGELOG.md +11 -0
- package/README.md +40 -5
- package/README.zh.md +13 -4
- package/VERIFY.md +70 -6
- package/catalog.json +123 -0
- package/lib/client.js +155 -14
- package/package.json +8 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.2
|
|
4
|
+
|
|
5
|
+
- Add a top-level **Settings → Preview market** section, next to dsh-market, with Discover / Installed tabs.
|
|
6
|
+
- Keep the Settings → Plugins tab for discoverability; both render the same panel.
|
|
7
|
+
|
|
8
|
+
## 0.1.1
|
|
9
|
+
|
|
10
|
+
- Enable/disable installed preview plugins from the market when dsh-market is present (`/dsh-market/toggle`), with the current state read from `/dsh-market/installed`.
|
|
11
|
+
- Add a "Configure" entry per installed card that points to the child plugin's Settings → Plugins page.
|
|
12
|
+
- Without dsh-market, the toggle is disabled and the card explains where to manage it.
|
|
13
|
+
|
|
3
14
|
## 0.1.0
|
|
4
15
|
|
|
5
16
|
- New vertical preview market plugin: catalog, installed inventory, Settings → Plugins tab.
|
package/README.md
CHANGED
|
@@ -1,25 +1,29 @@
|
|
|
1
1
|
# dsh-preview-market
|
|
2
2
|
|
|
3
|
-
A **vertical preview market** for DeepSeek Harness.
|
|
3
|
+
A **vertical preview market** for DeepSeek Harness. A top-level settings section (and a Settings → Plugins tab) lists the preview plugins that exist for DSH, shows which ones the active profile already has, and hands installs/updates to [dsh-market](https://github.com/dsh-market/dsh-market) or the official `dsh plugin` command.
|
|
4
4
|
|
|
5
5
|
It is not a general plugin market, and it does not implement previews itself: every preview plugin stays an independent Loader entry with its own repository, version and settings. The market only provides the catalog, the installed view and the entry points.
|
|
6
6
|
|
|
7
|
+

|
|
8
|
+
|
|
7
9
|
## Install
|
|
8
10
|
|
|
9
11
|
```sh
|
|
10
12
|
dsh plugin --profile <profile> add dsh-preview-market
|
|
11
13
|
```
|
|
12
14
|
|
|
13
|
-
Open **Settings →
|
|
15
|
+
Open **Settings → Preview market** (the same panel is also under **Settings → Plugins**).
|
|
16
|
+
|
|
17
|
+
Upgrading from `dsh-media-preview` / `dsh-blend-preview` / `dsh-stl-preview`? See [MIGRATION.md](MIGRATION.md).
|
|
14
18
|
|
|
15
19
|
## What it does
|
|
16
20
|
|
|
17
21
|
- **Catalog** — first-party preview plugins with their formats, host requirements and versions.
|
|
18
|
-
- **Installed view** — reads the active profile's `package.json
|
|
22
|
+
- **Installed view** — reads the active profile's `package.json`, matching catalog names by default and honouring a package's `dsh.preview` manifest when present; never imports child plugin code.
|
|
19
23
|
- **Install / update** — delegates to `dsh-market` when it is present. Without it, the card shows the exact `dsh plugin --profile <profile> add <package>@<version>` command to copy.
|
|
20
24
|
- **Independent plugins** — media, Blender and STL remain separate packages; this page has no renderer and does not merge their code.
|
|
21
25
|
|
|
22
|
-
## Catalog (0.1.
|
|
26
|
+
## Catalog (0.1.2)
|
|
23
27
|
|
|
24
28
|
| Package | Formats | Host requirement |
|
|
25
29
|
|---|---|---|
|
|
@@ -38,14 +42,45 @@ Settings → Plugins → Preview market (namespace `preview-market`):
|
|
|
38
42
|
| `profileDir` | `""` | Profile to inspect. Empty uses the Host `profileContext`, then `$DSH_HOME/profiles/desktop`. |
|
|
39
43
|
| `debug` | `false` | Reserved for request diagnostics. |
|
|
40
44
|
|
|
45
|
+
## Examples
|
|
46
|
+
|
|
47
|
+
- [`examples/profile-overrides.yml`](examples/profile-overrides.yml) — a safe starting point for per-profile overrides (media chunk size, Blender path, STL unit scale).
|
|
48
|
+
- [`catalog.json`](catalog.json) — machine-readable snapshot of the shipped catalog, enforced by a unit test.
|
|
49
|
+
|
|
41
50
|
## Development
|
|
42
51
|
|
|
43
52
|
```sh
|
|
44
53
|
node --test test/*.test.js
|
|
45
54
|
```
|
|
46
55
|
|
|
56
|
+
`catalog.json` is a machine-readable snapshot of the shipped catalog; regenerate it with `npm run catalog:write` and keep it in sync (a test enforces this).
|
|
57
|
+
|
|
58
|
+
Settings-section end-to-end (requires a running host with the plugin installed):
|
|
59
|
+
|
|
60
|
+
```sh
|
|
61
|
+
node scripts/e2e-section.mjs --base http://127.0.0.1:<port> --token <token>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Settings-tab end-to-end (requires a running host with the plugin installed):
|
|
65
|
+
|
|
66
|
+
```sh
|
|
67
|
+
node scripts/e2e-settings.mjs --base http://127.0.0.1:<port> --token <token>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Fallback install/copy check (requires a profile where one catalog entry is not installed):
|
|
71
|
+
|
|
72
|
+
```sh
|
|
73
|
+
node scripts/e2e-fallback.mjs --base http://127.0.0.1:<port> --token <token>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Toggle/configure check (requires dsh-market and an installed STL preview):
|
|
77
|
+
|
|
78
|
+
```sh
|
|
79
|
+
node scripts/e2e-toggle.mjs --base http://127.0.0.1:<port> --token <token>
|
|
80
|
+
```
|
|
81
|
+
|
|
47
82
|
The client bundle is `lib/client.js` (classic `__ModuleLoader__.load` factory, no build step).
|
|
48
83
|
|
|
49
84
|
## Status
|
|
50
85
|
|
|
51
|
-
`0.1.
|
|
86
|
+
`0.1.2` — top-level Settings → Preview market section with Discover / Installed tabs, plus the Settings → Plugins tab. Enable/disable goes through dsh-market. One-click install requires the package in the connected dsh-market registry; otherwise use the copyable command. Configuration stays in each child plugin's own Settings → Plugins page.
|
package/README.zh.md
CHANGED
|
@@ -4,22 +4,26 @@
|
|
|
4
4
|
|
|
5
5
|
它不是通用插件市场,也不自己实现预览:每个预览插件仍是独立的 Loader 条目,有自己的仓库、版本和设置;市场只提供目录、已安装视图和入口。
|
|
6
6
|
|
|
7
|
+

|
|
8
|
+
|
|
7
9
|
## 安装
|
|
8
10
|
|
|
9
11
|
```sh
|
|
10
12
|
dsh plugin --profile <profile> add dsh-preview-market
|
|
11
13
|
```
|
|
12
14
|
|
|
13
|
-
打开 **设置 →
|
|
15
|
+
打开 **设置 → 预览市场**(同一个面板也在 **设置 → 插件** 下)。
|
|
16
|
+
|
|
17
|
+
从 `dsh-media-preview` / `dsh-blend-preview` / `dsh-stl-preview` 升级?见 [MIGRATION.md](MIGRATION.md)。
|
|
14
18
|
|
|
15
19
|
## 功能
|
|
16
20
|
|
|
17
21
|
- **目录** — 第一方预览插件及其格式、宿主依赖、版本。
|
|
18
|
-
- **已安装** — 读当前 profile 的 `package.json
|
|
22
|
+
- **已安装** — 读当前 profile 的 `package.json`,默认按目录名匹配;包内声明 `dsh.preview` 清单时也认。不 import 子插件代码。
|
|
19
23
|
- **安装/更新** — 检测到 dsh-market 时委托给它;没有时显示可复制的 `dsh plugin --profile <profile> add <包>@<版本>` 命令。
|
|
20
24
|
- **独立插件** — media、Blender、STL 仍是独立包;本页不注册渲染器,也不合并它们的代码。
|
|
21
25
|
|
|
22
|
-
## 目录(0.1.
|
|
26
|
+
## 目录(0.1.2)
|
|
23
27
|
|
|
24
28
|
| 包 | 格式 | 宿主依赖 |
|
|
25
29
|
|---|---|---|
|
|
@@ -38,6 +42,11 @@ dsh plugin --profile <profile> add dsh-preview-market
|
|
|
38
42
|
| `profileDir` | `""` | 要检查的 profile;空则用 Host `profileContext`,再退到 `$DSH_HOME/profiles/desktop`。 |
|
|
39
43
|
| `debug` | `false` | 预留的请求诊断。 |
|
|
40
44
|
|
|
45
|
+
## 示例
|
|
46
|
+
|
|
47
|
+
- [`examples/profile-overrides.yml`](examples/profile-overrides.yml):按 profile 覆盖媒体分块、Blender 路径、STL 单位。
|
|
48
|
+
- [`catalog.json`](catalog.json):目录的机器可读快照,单测会检查它与代码一致。
|
|
49
|
+
|
|
41
50
|
## 开发
|
|
42
51
|
|
|
43
52
|
```sh
|
|
@@ -48,4 +57,4 @@ node --test test/*.test.js
|
|
|
48
57
|
|
|
49
58
|
## 状态
|
|
50
59
|
|
|
51
|
-
`0.1.
|
|
60
|
+
`0.1.2` — 顶层「设置 → 预览市场」+ 发现/已安装两个 tab,同时保留「设置 → 插件」里的入口。启用/停用走 dsh-market;一键安装要求对应包已进入所连接 dsh-market 的 registry,否则用可复制命令。配置仍在各子插件自己的 设置 → 插件 页面。
|
package/VERIFY.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Verification record — dsh-preview-market 0.1.
|
|
1
|
+
# Verification record — dsh-preview-market 0.1.2
|
|
2
2
|
|
|
3
3
|
## Unit tests (2026-10-23)
|
|
4
4
|
|
|
@@ -11,10 +11,74 @@
|
|
|
11
11
|
| `test/installed.test.js` | profile `package.json` + `node_modules` inventory, missing profile, `profileContext` resolution |
|
|
12
12
|
| `test/client.test.js` | bundle registers exactly one `settings.plugins.tab` entry with id `dsh-preview-market` |
|
|
13
13
|
|
|
14
|
-
##
|
|
14
|
+
## Host / bundle smoke (2026-10-23, preview-test profile)
|
|
15
|
+
|
|
16
|
+
- Installed the packed tarball into a throwaway `preview-test` profile with `dsh plugin --profile preview-test add …`.
|
|
17
|
+
- `dsh --profile preview-test --dump-config` contains one `id: preview-market` row with `routeRoot: /api/preview-market`.
|
|
18
|
+
- The Web boot graph (`__DSH_BOOT__`) contains `dsh-preview-market` and injects `__DSH_PREVIEW_MARKET_CONFIG__`.
|
|
19
|
+
- The combo bundle URL returned **200** (16,213 B) and contains `settings.plugins.tab` + `dsh-preview-market`.
|
|
20
|
+
|
|
21
|
+
## Top-level section e2e (2026-10-23, preview-test)
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
node scripts/e2e-section.mjs --base http://127.0.0.1:59010 --token <token>
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Result: **PASS** — the Settings nav contains `预览市场` immediately after `插件市场`; opening it shows the `发现` / `已安装` tabs and the three catalog cards (`dsh-preview-media`, `dsh-preview-blender`, `dsh-preview-stl`). No page errors. Screenshot: `docs/preview-market-section.png`.
|
|
28
|
+
|
|
29
|
+
## Settings tab e2e (2026-10-23, preview-test)
|
|
30
|
+
|
|
31
|
+
Installed `dsh-preview-market@0.1.0` plus `dsh-preview-media@0.1.4`, `dsh-preview-blender@0.1.0`, `dsh-preview-stl@0.1.0` into `preview-test`, started the Web host, and ran:
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
node scripts/e2e-settings.mjs --base http://127.0.0.1:59003 --token <token>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Result: **PASS** — opened Settings → Built-in plugins → Preview market and found the tab plus the three installed cards (`dsh-preview-media`, `dsh-preview-blender`, `dsh-preview-stl`). No page errors; the only console 404 is the expected `/dsh-market/status` probe when dsh-market is absent. Screenshot: `%TEMP%\dsh-preview-market-settings.png`.
|
|
38
|
+
|
|
39
|
+
## Fallback install/copy e2e (2026-10-23, preview-test)
|
|
40
|
+
|
|
41
|
+
Removed `dsh-preview-stl` from `preview-test`, restarted the host, and ran:
|
|
42
|
+
|
|
43
|
+
```sh
|
|
44
|
+
node scripts/e2e-fallback.mjs --base http://127.0.0.1:59004 --token <token>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Result: **PASS** — the STL card showed `安装`; clicking it displayed `dsh plugin --profile preview-test add dsh-preview-stl@0.1.0` (dsh-market absent); the media card's copy button reported `命令已复制` and the clipboard held `dsh plugin --profile preview-test add dsh-preview-media@0.1.4`. No page errors. STL was reinstalled afterwards.
|
|
48
|
+
|
|
49
|
+
## Toggle / configure e2e (2026-10-23, preview-test + dsh-market 1.67.0)
|
|
15
50
|
|
|
16
|
-
-
|
|
17
|
-
|
|
18
|
-
|
|
51
|
+
With dsh-market installed, ran:
|
|
52
|
+
|
|
53
|
+
```sh
|
|
54
|
+
node scripts/e2e-toggle.mjs --base http://127.0.0.1:59006 --token <token>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Result: **PASS** — the STL card showed `停用`; clicking it wrote `dsh-preview-stl` into `/dsh-market/installed`'s `disabled` list and showed `已切换,刷新页面后生效`; clicking `启用` removed it again. The media card's `配置` showed `在 设置 → 插件 → dsh-preview-media 配置。` No page errors; screenshot: `%TEMP%\dsh-preview-market-toggle.png`.
|
|
58
|
+
|
|
59
|
+
## npm artifact install (2026-10-23, preview-test)
|
|
60
|
+
|
|
61
|
+
Replaced the local tarball with the published `dsh-preview-market@0.1.1` from npm (`dsh plugin --profile preview-test add dsh-preview-market@0.1.1`), then ran the settings e2e against that install:
|
|
62
|
+
|
|
63
|
+
```sh
|
|
64
|
+
node scripts/e2e-settings.mjs --base http://127.0.0.1:59007 --token <token>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Result: **PASS** — tab and all three installed cards rendered from the npm artifact. `package.json` dependency is `0.1.1`, `node_modules/dsh-preview-market/package.json` reports `0.1.1`.
|
|
68
|
+
|
|
69
|
+
## One-click install e2e (fixture registry, 2026-10-23)
|
|
70
|
+
|
|
71
|
+
To exercise the real install route without waiting for the production catalog, a local registry fixture was served on `127.0.0.1:59008`:
|
|
72
|
+
|
|
73
|
+
```json
|
|
74
|
+
{"plugins":[{"name":"dsh-preview-stl","url":"https://github.com/439436269-ctrl/dsh-preview-stl","npm":"dsh-preview-stl","category":["ui"]}]}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The host was started with `DSHM_REGISTRY_URL=http://127.0.0.1:59008/plugins.json`, `dsh-preview-stl` was removed from `preview-test`, and the market's STL card was clicked. Result: **PASS** — dshmarket installed `dsh-preview-stl@0.1.0`; `/dsh-market/installed` contained it, the notice said `已安装: dsh-preview-stl`, and the card switched to `已是最新` with `停用`/`配置`. No page errors. Screenshot: `%TEMP%\market-oneclick.png`.
|
|
78
|
+
|
|
79
|
+
This proves the install route itself; the production catalog still needs the entries scheduled for 2026-10-24.
|
|
80
|
+
|
|
81
|
+
## Not yet verified live
|
|
19
82
|
|
|
20
|
-
|
|
83
|
+
- Production one-click install through the real dsh-market catalog (the new entries are scheduled for 2026-10-24).
|
|
84
|
+
- Desktop installation (the desktop profile is Electron-managed).
|
package/catalog.json
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": 1,
|
|
3
|
+
"plugins": [
|
|
4
|
+
{
|
|
5
|
+
"name": "dsh-preview-media",
|
|
6
|
+
"npm": "dsh-preview-media",
|
|
7
|
+
"capability": "media",
|
|
8
|
+
"kind": "media",
|
|
9
|
+
"repo": "https://github.com/439436269-ctrl/dsh-preview-media",
|
|
10
|
+
"version": "0.1.4",
|
|
11
|
+
"minDsh": "0.2.0-rc.2",
|
|
12
|
+
"requires": [],
|
|
13
|
+
"extensions": [
|
|
14
|
+
"mp4",
|
|
15
|
+
"m4v",
|
|
16
|
+
"mov",
|
|
17
|
+
"webm",
|
|
18
|
+
"ogv",
|
|
19
|
+
"ogg",
|
|
20
|
+
"mkv",
|
|
21
|
+
"avi",
|
|
22
|
+
"wmv",
|
|
23
|
+
"flv",
|
|
24
|
+
"3gp",
|
|
25
|
+
"ts",
|
|
26
|
+
"m2ts",
|
|
27
|
+
"mts",
|
|
28
|
+
"mp3",
|
|
29
|
+
"wav",
|
|
30
|
+
"flac",
|
|
31
|
+
"m4a",
|
|
32
|
+
"aac",
|
|
33
|
+
"oga",
|
|
34
|
+
"opus",
|
|
35
|
+
"weba",
|
|
36
|
+
"wma",
|
|
37
|
+
"aiff",
|
|
38
|
+
"aif"
|
|
39
|
+
],
|
|
40
|
+
"formats": {
|
|
41
|
+
"video": [
|
|
42
|
+
"mp4",
|
|
43
|
+
"m4v",
|
|
44
|
+
"mov",
|
|
45
|
+
"webm",
|
|
46
|
+
"ogv",
|
|
47
|
+
"ogg",
|
|
48
|
+
"mkv",
|
|
49
|
+
"avi",
|
|
50
|
+
"wmv",
|
|
51
|
+
"flv",
|
|
52
|
+
"3gp",
|
|
53
|
+
"ts",
|
|
54
|
+
"m2ts",
|
|
55
|
+
"mts"
|
|
56
|
+
],
|
|
57
|
+
"audio": [
|
|
58
|
+
"mp3",
|
|
59
|
+
"wav",
|
|
60
|
+
"flac",
|
|
61
|
+
"m4a",
|
|
62
|
+
"aac",
|
|
63
|
+
"oga",
|
|
64
|
+
"opus",
|
|
65
|
+
"weba",
|
|
66
|
+
"wma",
|
|
67
|
+
"aiff",
|
|
68
|
+
"aif"
|
|
69
|
+
]
|
|
70
|
+
},
|
|
71
|
+
"description": {
|
|
72
|
+
"zh": "视频与音频:右侧栏原生播放器,HTTP Range 流式读取,桌面端可拖动进度。",
|
|
73
|
+
"en": "Video and audio in the right-Sidebar document preview, streamed with HTTP Range and seekable in the desktop app."
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"name": "dsh-preview-blender",
|
|
78
|
+
"npm": "dsh-preview-blender",
|
|
79
|
+
"capability": "blender",
|
|
80
|
+
"kind": "three",
|
|
81
|
+
"repo": "https://github.com/439436269-ctrl/dsh-preview-blender",
|
|
82
|
+
"version": "0.1.0",
|
|
83
|
+
"minDsh": "0.2.0-rc.2",
|
|
84
|
+
"requires": [
|
|
85
|
+
"blender"
|
|
86
|
+
],
|
|
87
|
+
"extensions": [
|
|
88
|
+
"blend"
|
|
89
|
+
],
|
|
90
|
+
"formats": {
|
|
91
|
+
"model": [
|
|
92
|
+
"blend"
|
|
93
|
+
]
|
|
94
|
+
},
|
|
95
|
+
"description": {
|
|
96
|
+
"zh": "Blender 场景:headless 导出缓存 GLB,右侧栏 WebGL 视口 + 对象/三角面/mm 包围盒/材质统计。",
|
|
97
|
+
"en": "Blender scenes: a cached headless GLB export rendered in a WebGL viewport, with object, triangle, mm-bounds and material statistics."
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
"name": "dsh-preview-stl",
|
|
102
|
+
"npm": "dsh-preview-stl",
|
|
103
|
+
"capability": "stl",
|
|
104
|
+
"kind": "three",
|
|
105
|
+
"repo": "https://github.com/439436269-ctrl/dsh-preview-stl",
|
|
106
|
+
"version": "0.1.0",
|
|
107
|
+
"minDsh": "0.2.0-rc.2",
|
|
108
|
+
"requires": [],
|
|
109
|
+
"extensions": [
|
|
110
|
+
"stl"
|
|
111
|
+
],
|
|
112
|
+
"formats": {
|
|
113
|
+
"model": [
|
|
114
|
+
"stl"
|
|
115
|
+
]
|
|
116
|
+
},
|
|
117
|
+
"description": {
|
|
118
|
+
"zh": "STL 网格:浏览器解析二进制/ASCII,右侧栏 WebGL 视口 + 尺寸/体积/表面积/水密统计。",
|
|
119
|
+
"en": "STL meshes: binary/ASCII parsed in the browser, with a WebGL viewport and size, volume, surface-area and watertightness statistics."
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
]
|
|
123
|
+
}
|
package/lib/client.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* dsh-preview-market browser half.
|
|
3
3
|
*
|
|
4
|
-
* Registers
|
|
4
|
+
* Registers a top-level Settings → Preview market section and keeps the
|
|
5
|
+
* Settings → Plugins tab. Both render the same panel: the Host catalog plus the
|
|
5
6
|
* active profile inventory, then either delegates installs/updates to
|
|
6
7
|
* dsh-market's same-origin API or shows the official `dsh plugin add` command.
|
|
7
8
|
* It never imports a preview plugin's client bundle; those stay independent
|
|
@@ -14,8 +15,9 @@ window.__ModuleLoader__.load({
|
|
|
14
15
|
var exports = module.exports;
|
|
15
16
|
const React = require("react");
|
|
16
17
|
|
|
17
|
-
/** Locale namespace and
|
|
18
|
+
/** Locale namespace, section id and Settings → Plugins tab id. */
|
|
18
19
|
const NS = "previewMarket";
|
|
20
|
+
const SECTION_ID = "preview-market";
|
|
19
21
|
const TAB_ID = "dsh-preview-market";
|
|
20
22
|
const DEFAULT_ROUTE_ROOT = "/api/preview-market";
|
|
21
23
|
|
|
@@ -45,7 +47,18 @@ window.__ModuleLoader__.load({
|
|
|
45
47
|
note: "安装/更新由 dsh-market 或 dsh CLI 完成;本页只做目录、状态与入口,不复制子插件代码。",
|
|
46
48
|
version: "版本",
|
|
47
49
|
notInstalled: "未安装",
|
|
48
|
-
disabledHint: "启用/停用请到 设置 → 插件 列表。"
|
|
50
|
+
disabledHint: "启用/停用请到 设置 → 插件 列表。",
|
|
51
|
+
enable: "启用",
|
|
52
|
+
disable: "停用",
|
|
53
|
+
config: "配置",
|
|
54
|
+
configHint: "在 设置 → 插件 → {name} 配置。",
|
|
55
|
+
manageHint: "到 设置 → 插件 启用/停用。",
|
|
56
|
+
toggled: "已切换。",
|
|
57
|
+
toggleRefresh: "已切换,刷新页面后生效。",
|
|
58
|
+
toggleFailed: "切换失败",
|
|
59
|
+
tabDiscover: "发现",
|
|
60
|
+
tabInstalled: "已安装",
|
|
61
|
+
installedEmpty: "还没有安装本市场的预览插件。"
|
|
49
62
|
};
|
|
50
63
|
|
|
51
64
|
const en = {
|
|
@@ -74,7 +87,18 @@ window.__ModuleLoader__.load({
|
|
|
74
87
|
note: "Installs and updates go through dsh-market or the dsh CLI; this page only provides the catalog, status and entry points. Child plugin code is never copied.",
|
|
75
88
|
version: "Version",
|
|
76
89
|
notInstalled: "Not installed",
|
|
77
|
-
disabledHint: "Enable or disable it in Settings → Plugins."
|
|
90
|
+
disabledHint: "Enable or disable it in Settings → Plugins.",
|
|
91
|
+
enable: "Enable",
|
|
92
|
+
disable: "Disable",
|
|
93
|
+
config: "Configure",
|
|
94
|
+
configHint: "Configure it in Settings → Plugins → {name}.",
|
|
95
|
+
manageHint: "Use Settings → Plugins to enable or disable it.",
|
|
96
|
+
toggled: "Toggled.",
|
|
97
|
+
toggleRefresh: "Toggled; refresh the page to apply.",
|
|
98
|
+
toggleFailed: "Toggle failed",
|
|
99
|
+
tabDiscover: "Discover",
|
|
100
|
+
tabInstalled: "Installed",
|
|
101
|
+
installedEmpty: "No preview plugins from this market are installed yet."
|
|
78
102
|
};
|
|
79
103
|
|
|
80
104
|
/** Route root published by the Host at boot. */
|
|
@@ -102,7 +126,7 @@ window.__ModuleLoader__.load({
|
|
|
102
126
|
}
|
|
103
127
|
|
|
104
128
|
/** A single catalog card. */
|
|
105
|
-
function Card({ item, installed, profile, dshmarket, busy, onInstall, onCopy, t }) {
|
|
129
|
+
function Card({ item, installed, profile, dshmarket, enabled, busy, onInstall, onCopy, onToggle, onConfig, t }) {
|
|
106
130
|
const current = installed !== undefined ? installed.version : "";
|
|
107
131
|
const newer = current !== "" && current !== item.version;
|
|
108
132
|
const formats = item.formats !== undefined
|
|
@@ -149,7 +173,27 @@ window.__ModuleLoader__.load({
|
|
|
149
173
|
t("copy")
|
|
150
174
|
),
|
|
151
175
|
current !== ""
|
|
152
|
-
? React.createElement(
|
|
176
|
+
? React.createElement(
|
|
177
|
+
"button",
|
|
178
|
+
{
|
|
179
|
+
type: "button",
|
|
180
|
+
className: "dsh-pm-button",
|
|
181
|
+
disabled: busy === item.name || !dshmarket,
|
|
182
|
+
title: dshmarket ? "" : t("manageHint"),
|
|
183
|
+
onClick: () => onToggle(item)
|
|
184
|
+
},
|
|
185
|
+
enabled ? t("disable") : t("enable")
|
|
186
|
+
)
|
|
187
|
+
: null,
|
|
188
|
+
current !== ""
|
|
189
|
+
? React.createElement(
|
|
190
|
+
"button",
|
|
191
|
+
{ type: "button", className: "dsh-pm-button", onClick: () => onConfig(item) },
|
|
192
|
+
t("config")
|
|
193
|
+
)
|
|
194
|
+
: null,
|
|
195
|
+
current !== "" && !dshmarket
|
|
196
|
+
? React.createElement("span", { className: "dsh-pm-hint" }, t("manageHint"))
|
|
153
197
|
: null
|
|
154
198
|
)
|
|
155
199
|
);
|
|
@@ -163,27 +207,43 @@ window.__ModuleLoader__.load({
|
|
|
163
207
|
error: "",
|
|
164
208
|
catalog: [],
|
|
165
209
|
installed: [],
|
|
210
|
+
disabled: [],
|
|
166
211
|
profile: "desktop",
|
|
167
212
|
dshmarket: false
|
|
168
213
|
});
|
|
169
214
|
const [notice, setNotice] = React.useState("");
|
|
170
215
|
const [busy, setBusy] = React.useState("");
|
|
216
|
+
const [tab, setTab] = React.useState("discover");
|
|
171
217
|
|
|
172
218
|
const load = React.useCallback(async () => {
|
|
173
219
|
setState((previous) => ({ ...previous, loading: true, error: "" }));
|
|
174
220
|
try {
|
|
175
|
-
const
|
|
221
|
+
const status = await fetch("/dsh-market/status", { cache: "no-store" })
|
|
222
|
+
.then((response) => response.ok)
|
|
223
|
+
.catch(() => false);
|
|
224
|
+
const [catalog, inventory] = await Promise.all([
|
|
176
225
|
getJson(`${marketRoute}/catalog`),
|
|
177
|
-
getJson(`${marketRoute}/installed`)
|
|
178
|
-
fetch("/dsh-market/status", { cache: "no-store" })
|
|
179
|
-
.then((response) => response.ok)
|
|
180
|
-
.catch(() => false)
|
|
226
|
+
getJson(`${marketRoute}/installed`)
|
|
181
227
|
]);
|
|
228
|
+
let disabled = [];
|
|
229
|
+
if (status === true) {
|
|
230
|
+
try {
|
|
231
|
+
const detailResponse = await fetch("/dsh-market/installed", { cache: "no-store" });
|
|
232
|
+
if (detailResponse.ok) {
|
|
233
|
+
const detail = await detailResponse.json();
|
|
234
|
+
if (Array.isArray(detail.disabled)) disabled = detail.disabled;
|
|
235
|
+
else if (Array.isArray(detail.patch?.disables)) disabled = detail.patch.disables;
|
|
236
|
+
}
|
|
237
|
+
} catch {
|
|
238
|
+
disabled = [];
|
|
239
|
+
}
|
|
240
|
+
}
|
|
182
241
|
setState({
|
|
183
242
|
loading: false,
|
|
184
243
|
error: "",
|
|
185
244
|
catalog: Array.isArray(catalog.plugins) ? catalog.plugins : [],
|
|
186
245
|
installed: Array.isArray(inventory.installed) ? inventory.installed : [],
|
|
246
|
+
disabled: Array.isArray(disabled) ? disabled : [],
|
|
187
247
|
profile: typeof inventory.profile === "string" && inventory.profile !== "" ? inventory.profile : "desktop",
|
|
188
248
|
dshmarket: status === true
|
|
189
249
|
});
|
|
@@ -201,6 +261,8 @@ window.__ModuleLoader__.load({
|
|
|
201
261
|
}, [load]);
|
|
202
262
|
|
|
203
263
|
const installedByName = new Map(state.installed.map((entry) => [entry.name, entry]));
|
|
264
|
+
const installedCards = state.catalog.filter((item) => installedByName.has(item.name));
|
|
265
|
+
const visible = tab === "installed" ? installedCards : state.catalog;
|
|
204
266
|
|
|
205
267
|
const install = React.useCallback(async (item) => {
|
|
206
268
|
setBusy(item.name);
|
|
@@ -235,6 +297,33 @@ window.__ModuleLoader__.load({
|
|
|
235
297
|
}
|
|
236
298
|
}, [state.profile, t]);
|
|
237
299
|
|
|
300
|
+
const toggle = React.useCallback(async (item) => {
|
|
301
|
+
setBusy(item.name);
|
|
302
|
+
setNotice("");
|
|
303
|
+
try {
|
|
304
|
+
const enabled = state.disabled.includes(item.name);
|
|
305
|
+
const response = await fetch("/dsh-market/toggle", {
|
|
306
|
+
method: "POST",
|
|
307
|
+
headers: { "content-type": "application/json" },
|
|
308
|
+
body: JSON.stringify({ name: item.name, enabled })
|
|
309
|
+
});
|
|
310
|
+
const body = await response.json().catch(() => null);
|
|
311
|
+
if (!response.ok || body === null || body.ok !== true) {
|
|
312
|
+
throw new Error(body !== null && typeof body.error === "string" ? body.error : `HTTP ${response.status}`);
|
|
313
|
+
}
|
|
314
|
+
setNotice(body.refresh ? t("toggleRefresh") : t("toggled"));
|
|
315
|
+
await load();
|
|
316
|
+
} catch (error) {
|
|
317
|
+
setNotice(`${t("toggleFailed")}: ${error instanceof Error ? error.message : String(error)}`);
|
|
318
|
+
} finally {
|
|
319
|
+
setBusy("");
|
|
320
|
+
}
|
|
321
|
+
}, [state.disabled, load, t]);
|
|
322
|
+
|
|
323
|
+
const config = React.useCallback((item) => {
|
|
324
|
+
setNotice(t("configHint").replace("{name}", item.name));
|
|
325
|
+
}, [t]);
|
|
326
|
+
|
|
238
327
|
installStyle();
|
|
239
328
|
|
|
240
329
|
return React.createElement(
|
|
@@ -256,6 +345,36 @@ window.__ModuleLoader__.load({
|
|
|
256
345
|
)
|
|
257
346
|
)
|
|
258
347
|
),
|
|
348
|
+
React.createElement(
|
|
349
|
+
"div",
|
|
350
|
+
{ className: "dsh-pm-tabs", role: "tablist" },
|
|
351
|
+
React.createElement(
|
|
352
|
+
"button",
|
|
353
|
+
{
|
|
354
|
+
type: "button",
|
|
355
|
+
role: "tab",
|
|
356
|
+
className: "dsh-pm-tab",
|
|
357
|
+
"data-active": tab === "discover",
|
|
358
|
+
"aria-selected": tab === "discover",
|
|
359
|
+
onClick: () => setTab("discover")
|
|
360
|
+
},
|
|
361
|
+
t("tabDiscover"),
|
|
362
|
+
React.createElement("span", { className: "dsh-pm-count" }, state.catalog.length)
|
|
363
|
+
),
|
|
364
|
+
React.createElement(
|
|
365
|
+
"button",
|
|
366
|
+
{
|
|
367
|
+
type: "button",
|
|
368
|
+
role: "tab",
|
|
369
|
+
className: "dsh-pm-tab",
|
|
370
|
+
"data-active": tab === "installed",
|
|
371
|
+
"aria-selected": tab === "installed",
|
|
372
|
+
onClick: () => setTab("installed")
|
|
373
|
+
},
|
|
374
|
+
t("tabInstalled"),
|
|
375
|
+
React.createElement("span", { className: "dsh-pm-count" }, installedCards.length)
|
|
376
|
+
)
|
|
377
|
+
),
|
|
259
378
|
state.loading
|
|
260
379
|
? React.createElement("p", { className: "dsh-pm-empty" }, t("loading"))
|
|
261
380
|
: state.error !== ""
|
|
@@ -265,21 +384,24 @@ window.__ModuleLoader__.load({
|
|
|
265
384
|
React.createElement("p", null, `${t("failed")}: ${state.error}`),
|
|
266
385
|
React.createElement("button", { type: "button", className: "dsh-pm-button", onClick: () => void load() }, t("retry"))
|
|
267
386
|
)
|
|
268
|
-
:
|
|
269
|
-
? React.createElement("p", { className: "dsh-pm-empty" }, t("empty"))
|
|
387
|
+
: visible.length === 0
|
|
388
|
+
? React.createElement("p", { className: "dsh-pm-empty" }, tab === "installed" ? t("installedEmpty") : t("empty"))
|
|
270
389
|
: React.createElement(
|
|
271
390
|
"div",
|
|
272
391
|
{ className: "dsh-pm-grid" },
|
|
273
|
-
|
|
392
|
+
visible.map((item) =>
|
|
274
393
|
React.createElement(Card, {
|
|
275
394
|
key: item.name,
|
|
276
395
|
item,
|
|
277
396
|
installed: installedByName.get(item.name),
|
|
278
397
|
profile: state.profile,
|
|
279
398
|
dshmarket: state.dshmarket,
|
|
399
|
+
enabled: !state.disabled.includes(item.name),
|
|
280
400
|
busy,
|
|
281
401
|
onInstall: (entry) => void install(entry),
|
|
282
402
|
onCopy: (entry) => void copy(entry),
|
|
403
|
+
onToggle: (entry) => void toggle(entry),
|
|
404
|
+
onConfig: (entry) => config(entry),
|
|
283
405
|
t
|
|
284
406
|
})
|
|
285
407
|
)
|
|
@@ -304,6 +426,10 @@ window.__ModuleLoader__.load({
|
|
|
304
426
|
"[data-preview-market] .dsh-pm-status{display:flex;gap:6px;flex-wrap:wrap}",
|
|
305
427
|
"[data-preview-market] .dsh-pm-pill{border-radius:999px;padding:3px 9px;font-size:11px;background:var(--dsw-alias-interactive-bg-hover,rgba(0,0,0,.06))}",
|
|
306
428
|
"[data-preview-market] .dsh-pm-pill[data-tone=ok]{color:#1f7a4d;background:rgba(46,160,92,.14)}",
|
|
429
|
+
"[data-preview-market] .dsh-pm-tabs{display:flex;gap:2px;border-bottom:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.1))}",
|
|
430
|
+
"[data-preview-market] .dsh-pm-tab{appearance:none;border:0;border-bottom:2px solid transparent;background:none;color:inherit;font:inherit;font-size:12px;padding:7px 10px;cursor:pointer;opacity:.62;display:inline-flex;align-items:center;gap:6px}",
|
|
431
|
+
"[data-preview-market] .dsh-pm-tab[data-active=true]{opacity:1;font-weight:650;border-bottom-color:var(--dsw-alias-label-accent,#2c6ef2)}",
|
|
432
|
+
"[data-preview-market] .dsh-pm-count{font-size:10.5px;border-radius:999px;padding:1px 6px;background:rgba(0,0,0,.06)}",
|
|
307
433
|
"[data-preview-market] .dsh-pm-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:12px}",
|
|
308
434
|
"[data-preview-market] .dsh-pm-card{border:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.1));border-radius:12px;padding:14px;display:flex;flex-direction:column;gap:9px;background:var(--dsw-alias-bg-layer-2,rgba(0,0,0,.02))}",
|
|
309
435
|
"[data-preview-market] .dsh-pm-card-head{display:flex;justify-content:space-between;gap:8px;align-items:center}",
|
|
@@ -337,6 +463,21 @@ window.__ModuleLoader__.load({
|
|
|
337
463
|
installStyle();
|
|
338
464
|
const t = ctx.locale.bind(NS);
|
|
339
465
|
ctx.effect(() => ctx.locale.register(NS, { zh, en }), "preview-market: dictionaries");
|
|
466
|
+
// Top-level Settings section, next to dsh-market's "Plugin market" row.
|
|
467
|
+
ctx.slots.inject("settings.section", () =>
|
|
468
|
+
ctx.slots.register(
|
|
469
|
+
{
|
|
470
|
+
name: "settings.section",
|
|
471
|
+
id: SECTION_ID,
|
|
472
|
+
order: 41,
|
|
473
|
+
label: () => t("nav"),
|
|
474
|
+
locale: NS,
|
|
475
|
+
inject: () => ({ t })
|
|
476
|
+
},
|
|
477
|
+
() => React.createElement(MarketPanel, { t })
|
|
478
|
+
)
|
|
479
|
+
);
|
|
480
|
+
// Keep the tab under Settings → Plugins for discoverability.
|
|
340
481
|
ctx.slots.inject("settings.plugins.tab", () =>
|
|
341
482
|
ctx.slots.register(
|
|
342
483
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-preview-market",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Vertical preview market for DeepSeek Harness: discover, install and configure preview plugins (media, Blender, STL, ...) from one panel.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
},
|
|
22
22
|
"files": [
|
|
23
23
|
"lib/**/*.js",
|
|
24
|
+
"catalog.json",
|
|
24
25
|
"cordis.patch.yml",
|
|
25
26
|
"README.md",
|
|
26
27
|
"README.zh.md",
|
|
@@ -65,6 +66,11 @@
|
|
|
65
66
|
"url": "https://github.com/439436269-ctrl/dsh-preview-market/issues"
|
|
66
67
|
},
|
|
67
68
|
"scripts": {
|
|
68
|
-
"test": "node --test test/*.test.js"
|
|
69
|
+
"test": "node --test test/*.test.js",
|
|
70
|
+
"catalog:write": "node scripts/write-catalog.mjs",
|
|
71
|
+
"e2e": "node scripts/e2e-settings.mjs",
|
|
72
|
+
"e2e:section": "node scripts/e2e-section.mjs",
|
|
73
|
+
"e2e:fallback": "node scripts/e2e-fallback.mjs",
|
|
74
|
+
"e2e:toggle": "node scripts/e2e-toggle.mjs"
|
|
69
75
|
}
|
|
70
76
|
}
|