@eya46/dsh-plugin-info 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -2
- package/lib/client.js +67 -5
- package/lib/index.js +1 -1
- package/lib/types/client.d.ts +24 -0
- package/lib/types/types.d.ts +5 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
面向 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 的插件信息页。包名 `@eya46/dsh-plugin-info`,挂到当前 Profile 后,设置里会出现「插件信息」分区。
|
|
4
4
|
|
|
5
|
-
它只看 **Profile 用户自己加的插件**(`profiles/<name>/package.json` 的 `dependencies`),不把 `@deepseek-ai/dsh-base` 这类随发行版带的 bundle
|
|
5
|
+
它只看 **Profile 用户自己加的插件**(`profiles/<name>/package.json` 的 `dependencies`),不把 `@deepseek-ai/dsh-base` 这类随发行版带的 bundle 算进去。默认展示全部用户插件;如需只看特定命名空间,可配置 `allowScopes`。
|
|
6
6
|
|
|
7
7
|
- 列出已安装插件、当前版本、来源(npm / 本地 / git)
|
|
8
8
|
- 对 registry 安装的包对照 `dist-tags.latest`,标出可更新
|
|
@@ -11,6 +11,12 @@
|
|
|
11
11
|
|
|
12
12
|
实现方式对齐 [dsh-model-meta-autofill](https://github.com/QJAG1024/dsh-model-meta-autofill):Host 注册本机 HTTP API,Client half 通过 `dsh.client` 挂到 `settings.section`。
|
|
13
13
|
|
|
14
|
+
### 可选:dsh-better-sidebar 标签页
|
|
15
|
+
|
|
16
|
+
若 Profile 同时安装了 [`dsh-better-sidebar`](https://github.com/omdsh-dev/DSH-better-sidebar) (≥0.4.0),本插件会自动在侧边栏 `+` 菜单里多注册一个**插件信息**标签页(id `@eya46/dsh-plugin-info`,order 90,单实例),内容与设置页完全一致——侧边栏较窄,内部已做滚动/宽度适配。
|
|
17
|
+
|
|
18
|
+
实现是**软依赖**:不写 `peerDependencies`,不 `import 'dsh-better-sidebar'`,只用 Cordis 的 `ctx.inject(['betterSidebar'], ...)` 开一个子 fiber——未安装时回调永不触发,其他功能不受影响;禁用 / HMR 任一插件时 disposer 随各自 fiber 清理,不会残留 `"already registered"`。
|
|
19
|
+
|
|
14
20
|
## 安装
|
|
15
21
|
|
|
16
22
|
在仓库根目录构建后,把它加进当前 Profile:
|
|
@@ -41,7 +47,7 @@ dsh plugin --profile web add "file:./packages/dsh-plugin-info"
|
|
|
41
47
|
| `defaultVersionLimit` | `10` | 默认版本条数 |
|
|
42
48
|
| `timeoutMs` | `15000` | 注册表 / GitHub 超时 |
|
|
43
49
|
| `cacheTtlMs` | `300000` | packument 与说明缓存 |
|
|
44
|
-
| `allowScopes` |
|
|
50
|
+
| `allowScopes` | 无(展示全部) | 名称前缀白名单;省略或为空数组时展示全部用户插件 |
|
|
45
51
|
|
|
46
52
|
覆盖写在 Profile 的 `cordis.patch.yml`:
|
|
47
53
|
|
package/lib/client.js
CHANGED
|
@@ -46,13 +46,26 @@
|
|
|
46
46
|
'.dpi-version:hover { background: var(--dsw-alias-interactive-bg-hover-solid); }',
|
|
47
47
|
'.dpi-version[data-open="1"] { background: var(--dsw-alias-interactive-bg-hover-solid); }',
|
|
48
48
|
'.dpi-notes { display: flex; flex-direction: column; gap: 6px; padding: 8px 2px 2px; }',
|
|
49
|
-
'.dpi-notesBody { margin: 0; white-space: pre-wrap; font-size: 13px; line-height: 20px; color: var(--dsw-alias-label-secondary); }',
|
|
50
49
|
'.dpi-commits { margin: 0; padding-left: 18px; font-size: 13px; line-height: 20px; }',
|
|
51
50
|
'.dpi-link { color: inherit; text-underline-offset: 3px; }',
|
|
52
51
|
'.dpi-copyTag { box-sizing: border-box; border: 1px solid var(--dsw-alias-border-l3); color: var(--dsw-alias-label-secondary); background: transparent; border-radius: 4px; padding: 1px 6px; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 11px; line-height: 16px; cursor: pointer; }',
|
|
53
52
|
'.dpi-copyTag:hover { color: var(--dsw-alias-label-primary); border-color: var(--dsw-alias-border-l2); }',
|
|
53
|
+
// Sidebar tab wrapper: the panel sits in a narrow right-side column
|
|
54
|
+
// (default 420px), where PluginInfoPanel's 720px max-width and
|
|
55
|
+
// non-scrolling body would overflow. Force full width + own scroll.
|
|
56
|
+
'.dpi-sidebar { box-sizing: border-box; width: 100%; height: 100%; overflow-y: auto; overflow-x: hidden; padding: 12px 14px 16px; background: var(--dsw-alias-bg-base); }',
|
|
57
|
+
'.dpi-sidebar .dpi-section { max-width: none; gap: 10px; }',
|
|
58
|
+
'.dpi-sidebar .dpi-card .dpi-head { padding: 10px 12px; }',
|
|
59
|
+
'.dpi-sidebar .dpi-card .dpi-body { padding: 0 12px 12px; }',
|
|
60
|
+
'.dpi-sidebar .dpi-actions { gap: 6px; }',
|
|
61
|
+
'.dpi-sidebar .dpi-rowButton { height: 26px; padding: 0 8px; }',
|
|
54
62
|
].join('\n');
|
|
55
63
|
const React = require('react');
|
|
64
|
+
// Reuse DSH's shared untrusted-Markdown renderer instead of rolling our
|
|
65
|
+
// own — it ships in the web shell's static module table and handles GFM,
|
|
66
|
+
// math, code fences, and sanitization (raw HTML / unsafe URLs stripped).
|
|
67
|
+
const uiPrimitives = require('@deepseek-ai/dsh-client-ui-primitives');
|
|
68
|
+
const MarkdownText = uiPrimitives.MarkdownText;
|
|
56
69
|
const h = React.createElement.bind(React);
|
|
57
70
|
const ensureStyles = () => {
|
|
58
71
|
if (document.querySelector('style[data-plugin="@eya46/dsh-plugin-info"]') !== null)
|
|
@@ -212,7 +225,7 @@
|
|
|
212
225
|
const updateCount = rows.filter((row) => row.hasUpdate).length;
|
|
213
226
|
const children = [
|
|
214
227
|
h('h2', { key: 'title', className: 'dpi-title' }, '插件信息'),
|
|
215
|
-
h('p', { key: 'intro', className: 'dpi-intro' }, '当前 Profile
|
|
228
|
+
h('p', { key: 'intro', className: 'dpi-intro' }, '当前 Profile 里用户添加的插件(不含随发行版内置的 bundle)。展开后默认看最近 10 个版本,点版本可看更新说明。'),
|
|
216
229
|
h('div', { key: 'toolbar', className: 'dpi-toolbar' }, h('button', { className: 'dpi-secondaryButton', disabled: busy, onClick: () => { load(); } }, busy ? '刷新中…' : '刷新'), h('span', { className: 'dpi-status' }, '共 ' + rows.length + ' 个插件' + (updateCount > 0 ? ' · ' + updateCount + ' 个可更新' : ''))),
|
|
217
230
|
];
|
|
218
231
|
if (error !== '')
|
|
@@ -296,8 +309,9 @@
|
|
|
296
309
|
else if (note.source === 'github-release') {
|
|
297
310
|
if (note.title !== undefined)
|
|
298
311
|
noteChildren.push(h('p', { key: 'nt', className: 'dpi-meta' }, note.title));
|
|
299
|
-
if (note.body !== undefined)
|
|
300
|
-
noteChildren.push(h(
|
|
312
|
+
if (note.body !== undefined && note.body !== '') {
|
|
313
|
+
noteChildren.push(h(MarkdownText, { key: 'nb', text: note.body }));
|
|
314
|
+
}
|
|
301
315
|
if (note.url !== undefined) {
|
|
302
316
|
noteChildren.push(h('a', { key: 'nu', className: 'dpi-link', href: note.url, target: '_blank', rel: 'noreferrer' }, '在 GitHub 查看 Release'));
|
|
303
317
|
}
|
|
@@ -309,7 +323,10 @@
|
|
|
309
323
|
}
|
|
310
324
|
}
|
|
311
325
|
else {
|
|
312
|
-
|
|
326
|
+
const fallback = note.body ?? '这个版本没有找到 GitHub Release 或提交说明。';
|
|
327
|
+
// Error payloads and the "none" source are plain text; keep
|
|
328
|
+
// them literal rather than feeding them through Markdown.
|
|
329
|
+
noteChildren.push(h('p', { key: 'nnone', className: 'dpi-meta' }, fallback));
|
|
313
330
|
}
|
|
314
331
|
}
|
|
315
332
|
return h('div', { key: row.version }, h('button', {
|
|
@@ -330,6 +347,48 @@
|
|
|
330
347
|
children.push(h('ul', { key: 'cards', className: 'dpi-cards' }, ...cards));
|
|
331
348
|
return h('div', { className: 'dpi-section' }, ...children);
|
|
332
349
|
};
|
|
350
|
+
/**
|
|
351
|
+
* Sidebar-tab wrapper: same content as the settings panel, but hosted in
|
|
352
|
+
* the narrow right-side better-sidebar pane where it must manage its own
|
|
353
|
+
* vertical scroll and drop the 720px max-width.
|
|
354
|
+
*/
|
|
355
|
+
const PluginInfoSidebarPanel = () => {
|
|
356
|
+
return h('div', { className: 'dpi-sidebar' }, h(PluginInfoPanel, null));
|
|
357
|
+
};
|
|
358
|
+
/** Small Cordis-plugin outline glyph (same geometry as the settings
|
|
359
|
+
* nav uses, suitable for the sidebar's 16px tab icon slot). */
|
|
360
|
+
const PluginIcon = (size) => {
|
|
361
|
+
return h('svg', {
|
|
362
|
+
width: size,
|
|
363
|
+
height: size,
|
|
364
|
+
viewBox: '0 0 14 14',
|
|
365
|
+
fill: 'none',
|
|
366
|
+
'aria-hidden': true,
|
|
367
|
+
}, h('path', {
|
|
368
|
+
fill: 'currentColor',
|
|
369
|
+
d: 'M3.03426 5.66661L1.70084 7.00003L3.0315 8.33069L2.14762 9.21457L-0.0669245 7.00003L2.15038 4.78273L3.03426 5.66661ZM7 14.067L4.77924 11.8462L5.66313 10.9623L7 12.2992L8.33342 10.9658L9.2173 11.8496L7 14.067ZM11.8489 9.21803L10.965 8.33414L12.2992 7.00003L10.9623 5.66316L11.8462 4.77927L14.0669 7.00003L11.8489 9.21803ZM8.33066 3.03153L7 1.70087L5.66589 3.03498L4.782 2.1511L7 -0.0668945L9.21454 2.14765L8.33066 3.03153Z',
|
|
370
|
+
}), h('rect', { x: 5.98535, y: 5.98535, width: 2.02942, height: 2.02942, fill: 'currentColor' }));
|
|
371
|
+
};
|
|
372
|
+
/**
|
|
373
|
+
* Register the sidebar tab ONLY when dsh-better-sidebar is present. The
|
|
374
|
+
* child fiber waits for the `betterSidebar` service; if that plugin is
|
|
375
|
+
* not installed the callback never runs and the rest of this plugin is
|
|
376
|
+
* unaffected. The disposer returned by registerTab is attached to the
|
|
377
|
+
* child fiber, so disabling/HMR of either side cleans up cleanly.
|
|
378
|
+
*/
|
|
379
|
+
const registerSidebarTab = (ctx) => {
|
|
380
|
+
ctx.inject(['betterSidebar'], ((sctx) => {
|
|
381
|
+
const service = sctx.betterSidebar;
|
|
382
|
+
sctx.effect(() => service.registerTab({
|
|
383
|
+
id: '@eya46/dsh-plugin-info',
|
|
384
|
+
title: '插件信息',
|
|
385
|
+
icon: (size) => PluginIcon(size),
|
|
386
|
+
order: 90,
|
|
387
|
+
single: true,
|
|
388
|
+
component: PluginInfoSidebarPanel,
|
|
389
|
+
}), '@eya46/dsh-plugin-info: better-sidebar tab');
|
|
390
|
+
}));
|
|
391
|
+
};
|
|
333
392
|
return {
|
|
334
393
|
name: 'dsh-plugin-info',
|
|
335
394
|
inject: ['slots'],
|
|
@@ -338,6 +397,9 @@
|
|
|
338
397
|
ctx.slots.inject('settings.section', () => {
|
|
339
398
|
ctx.slots.register({ name: 'settings.section', id: 'plugin-info', order: 16, label: '插件信息' }, PluginInfoPanel);
|
|
340
399
|
});
|
|
400
|
+
// Optional injection into dsh-better-sidebar: no-op when that
|
|
401
|
+
// plugin is absent from the profile.
|
|
402
|
+
registerSidebarTab(ctx);
|
|
341
403
|
},
|
|
342
404
|
};
|
|
343
405
|
},
|
package/lib/index.js
CHANGED
|
@@ -460,7 +460,7 @@ const Config = z.object({
|
|
|
460
460
|
defaultVersionLimit: z.number().step(1).min(1).max(50).default(10),
|
|
461
461
|
timeoutMs: z.number().step(1).min(1e3).default(15e3),
|
|
462
462
|
cacheTtlMs: z.number().step(1).min(0).default(3e5),
|
|
463
|
-
allowScopes: z.array(z.string())
|
|
463
|
+
allowScopes: z.array(z.string())
|
|
464
464
|
});
|
|
465
465
|
/**
|
|
466
466
|
* Register the plugin-info HTTP API when a web server is present.
|
package/lib/types/client.d.ts
CHANGED
|
@@ -21,14 +21,38 @@ interface MinimalReact {
|
|
|
21
21
|
useEffect(effect: () => void | (() => void), deps?: unknown[]): void;
|
|
22
22
|
useCallback<T>(fn: T, deps: unknown[]): T;
|
|
23
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* Subset of DSH's `MarkdownText` shared primitive. The real component is
|
|
26
|
+
* GFM + KaTeX with unsafe HTML/protocols stripped; we only need the `text`
|
|
27
|
+
* prop for rendering release-note bodies.
|
|
28
|
+
*/
|
|
29
|
+
type MarkdownTextComponent = (props: {
|
|
30
|
+
text: string;
|
|
31
|
+
}) => unknown;
|
|
24
32
|
interface SlotsService {
|
|
25
33
|
inject(key: string, register: () => void): void;
|
|
26
34
|
register(options: Record<string, unknown>, component: unknown): void;
|
|
27
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* Minimal structural mirror of dsh-better-sidebar's client service — the
|
|
38
|
+
* only method we call is `registerTab`. The plugin is an OPTIONAL soft
|
|
39
|
+
* dependency: this plugin does not value-import it, does not list it in
|
|
40
|
+
* peerDependencies, and reaches the service through `ctx.inject(...)` so
|
|
41
|
+
* the whole registration block is inert when better-sidebar is absent.
|
|
42
|
+
*/
|
|
43
|
+
interface BetterSidebarServiceLike {
|
|
44
|
+
registerTab(descriptor: Record<string, unknown>): () => void;
|
|
45
|
+
}
|
|
28
46
|
interface ClientContext {
|
|
29
47
|
slots: SlotsService;
|
|
30
48
|
/** Cordis fiber-lifetime effect: runs now, disposes on fiber unload. */
|
|
31
49
|
effect(callback: () => (() => void) | void, label?: string): () => void;
|
|
50
|
+
/**
|
|
51
|
+
* Cordis child-fiber spawn: waits until all named services are available
|
|
52
|
+
* before running the callback. Used to register with better-sidebar only
|
|
53
|
+
* when that plugin is loaded (optional soft dependency).
|
|
54
|
+
*/
|
|
55
|
+
inject(deps: string[], apply: (ctx: ClientContext) => void): unknown;
|
|
32
56
|
}
|
|
33
57
|
interface PluginRow {
|
|
34
58
|
name: string;
|
package/lib/types/types.d.ts
CHANGED
|
@@ -102,10 +102,11 @@ export interface PluginConfig {
|
|
|
102
102
|
/** How long packument and notes stay cached. */
|
|
103
103
|
cacheTtlMs: number;
|
|
104
104
|
/**
|
|
105
|
-
* Optional allow-list: when
|
|
106
|
-
*
|
|
107
|
-
*
|
|
105
|
+
* Optional allow-list: when omitted or empty, all profile-added plugins
|
|
106
|
+
* appear in the settings page and the version/notes APIs. When non-empty,
|
|
107
|
+
* only plugins whose npm name starts with one of these prefixes appear
|
|
108
|
+
* (case-insensitive, e.g. `@eya46/`).
|
|
108
109
|
*/
|
|
109
|
-
allowScopes
|
|
110
|
+
allowScopes?: string[];
|
|
110
111
|
}
|
|
111
112
|
//# sourceMappingURL=types.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eya46/dsh-plugin-info",
|
|
3
3
|
"description": "List profile-added DSH plugins, mark available updates, and show recent version notes",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.3",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"types": "lib/types/index.d.ts",
|