@huaqiu/dsh-huaqiu-about 0.4.10

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 深圳华秋智联股份有限公司
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,64 @@
1
+ # @huaqiu/dsh-huaqiu-about
2
+
3
+ An installable DSH Web profile plugin that contributes an **About** row to the
4
+ DSH settings page, following the reference prototype
5
+ (`dsh-pcb-eda/reference.html`): the HuaQiu brand mark, the platform slogan, a
6
+ `DSH 运行时 0.1.0` runtime badge, a metadata box (Distribution version, plugin
7
+ type, copy-metadata action, `deepseek-harness` link), and the copyright /
8
+ attribution lines. All text lines are localised (`zh` / `en`).
9
+
10
+ ## Install
11
+
12
+ ```powershell
13
+ dsh plugin --profile web add file:C:/Downloads/huaqiu-dsh-huaqiu-about-<version>.tgz
14
+ ```
15
+
16
+ Restart the Web profile after installing. Remove it with:
17
+
18
+ ```powershell
19
+ dsh plugin --profile web remove @huaqiu/dsh-huaqiu-about
20
+ ```
21
+
22
+ ## What it renders
23
+
24
+ The row registers into the `settings.section` slot (the same mechanism the
25
+ community `dsh-market` plugin uses for its settings page) and shows, per the
26
+ prototype:
27
+
28
+ ```
29
+ [HuaQiu brand mark]
30
+ {电子设计智能体平台 / Electronic Design Agent Platform}
31
+ [DSH 运行时 0.1.0] ← runtime label + this plugin's version
32
+ ┌─────────────────────────────────────┐
33
+ │ 发行版本 0.1.5-rc.2 │ ← Distribution label + bundled DSH version
34
+ │ 插件类型 DSH Standalone Plugin │
35
+ │ 复制元数据 deepseek-harness ↗ │
36
+ └─────────────────────────────────────┘
37
+ © 2026 深圳华秋智联股份有限公司
38
+ 由 DSH 提供支持
39
+ ```
40
+
41
+ The runtime version is this plugin's declared version; the Distribution
42
+ version is the DSH version bundled with the running harness.
43
+
44
+ ## Implementation
45
+
46
+ The package is a dual-half plugin:
47
+
48
+ - **Node half** (`src/index.ts`) mounts a same-origin version route on
49
+ `ctx.webServer` (`GET /api/v1/huaqiu/about` → `{ version, dshVersion }`) —
50
+ the same browser→node channel `@huaqiu/dsh-auth` uses for its credential
51
+ routes. `version` is this plugin's own declared version, read from the
52
+ package manifest next to the entry (`../package.json` relative to
53
+ `import.meta.url`); `dshVersion` is resolved the same way the community
54
+ `dsh-market` plugin resolves the host version (walking up from the CLI entry
55
+ for `node_modules/@deepseek-ai/dsh/package.json`, also recognising a
56
+ workspace checkout whose own `package.json` carries the `@deepseek-ai/dsh`
57
+ name, then a `require.resolve` probe, then environment fallbacks).
58
+ - **Browser half** (`src/client/index.ts`) registers the settings section and
59
+ fetches the two versions from the node route when the About row renders. It
60
+ does not depend on cross-end `ctx.provide`/`ctx.get` service sync (which is
61
+ unavailable within a single plugin's two halves).
62
+
63
+ When a version is unreachable, its slot is simply left empty; everything else
64
+ always renders.
@@ -0,0 +1,4 @@
1
+ # dsh bundle patch: inserts this plugin into a profile's layer stack.
2
+ - insert:
3
+ - id: dsh-huaqiu-about
4
+ name: '@huaqiu/dsh-huaqiu-about'
package/lib/client.js ADDED
@@ -0,0 +1,303 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "@huaqiu/dsh-huaqiu-about",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ let react = require("react");
8
+ //#region src/client/HuaqiuBrand.tsx
9
+ /**
10
+ * HuaQiu brand mark — self-contained copy (SVG) so the About plugin does not
11
+ * depend on another plugin's client bundle at runtime.
12
+ */
13
+ function HuaqiuBrandMark({ size, className }) {
14
+ return (0, react.createElement)("svg", {
15
+ "aria-hidden": true,
16
+ className,
17
+ height: size,
18
+ style: {
19
+ display: "block",
20
+ flex: "none"
21
+ },
22
+ viewBox: "0 0 974.25 974.25",
23
+ width: size,
24
+ xmlns: "http://www.w3.org/2000/svg"
25
+ }, (0, react.createElement)("path", {
26
+ d: "M865.67,758.67a107,107,0,1,0,107,107A107,107,0,0,0,865.67,758.67Z",
27
+ fill: "#c7000b",
28
+ fillRule: "evenodd"
29
+ }), (0, react.createElement)("path", {
30
+ d: "M744.11,0h-514C103.56,0,0,103.56,0,230.14v514C0,870.69,103.56,974.25,230.14,974.25h514c3.34,0,6.66-.06,10-.21a155.66,155.66,0,0,1-40-143.56H230.14a86.63,86.63,0,0,1-86.37-86.37v-514a86.63,86.63,0,0,1,86.37-86.37h514a86.61,86.61,0,0,1,86.37,86.37V714.06a156.93,156.93,0,0,1,35.19-4A155.1,155.1,0,0,1,974,754c.15-3.28.21-6.6.21-9.92v-514C974.25,103.56,870.69,0,744.11,0Z",
31
+ fill: "#c7000b",
32
+ fillRule: "evenodd"
33
+ }), (0, react.createElement)("polygon", {
34
+ points: "735.67 735.65 735.67 238.59 591.9 238.59 591.9 415.24 382.39 415.24 382.39 238.59 238.62 238.59 238.62 735.65 382.39 735.65 382.39 559.01 591.9 559.01 591.9 735.65 735.67 735.65",
35
+ fill: "#c7000b",
36
+ fillRule: "evenodd"
37
+ }));
38
+ }
39
+ //#endregion
40
+ //#region src/client/AboutSection.tsx
41
+ /**
42
+ * About row content, following the reference prototype:
43
+ *
44
+ * [HuaQiu brand mark]
45
+ * {slogan}
46
+ * [DSH 运行时 0.1.0] (runtime label + this plugin's version)
47
+ * ┌───────────────────────────┐
48
+ * │ 发行版本 0.1.5-rc.2 │ (Distribution label + bundled DSH version)
49
+ * │ 内置插件版本 0.1.0 │ (Builtin plugin version = plugin version)
50
+ * │ 复制元数据 deepseek-harness ↗ │
51
+ * └───────────────────────────┘
52
+ * © 2026 深圳华秋智联股份有限公司
53
+ * 由 DSH 提供支持
54
+ *
55
+ * The two versions are fetched from the node half's same-origin route
56
+ * (`GET /api/v1/huaqiu/about` → `{ version, dshVersion }`): the DSH Runtime
57
+ * badge and the Builtin plugin version row both show this plugin's declared
58
+ * version, the Distribution version is the DSH version bundled with the
59
+ * running harness. The block is vertically compact like the prototype — it
60
+ * centers as one dense unit instead of stretching across the panel. All text
61
+ * lines are localised.
62
+ */
63
+ const ROOT = {
64
+ display: "flex",
65
+ flexDirection: "column",
66
+ alignItems: "center",
67
+ justifyContent: "center",
68
+ height: "100%",
69
+ boxSizing: "border-box",
70
+ gap: 14,
71
+ padding: "16px 24px",
72
+ textAlign: "center"
73
+ };
74
+ const TOP = {
75
+ display: "flex",
76
+ flexDirection: "column",
77
+ alignItems: "center",
78
+ gap: 8
79
+ };
80
+ const TITLE = {
81
+ color: "var(--dsw-alias-label-primary)",
82
+ fontSize: 20,
83
+ fontWeight: 700,
84
+ letterSpacing: "0.01em"
85
+ };
86
+ const BADGE = {
87
+ display: "inline-flex",
88
+ alignItems: "center",
89
+ gap: 6,
90
+ padding: "2px 12px",
91
+ borderRadius: 999,
92
+ fontSize: 12,
93
+ fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
94
+ color: "var(--dsw-alias-label-secondary)",
95
+ background: "color-mix(in srgb, var(--dsw-alias-label-primary) 5%, transparent)",
96
+ border: "1px solid color-mix(in srgb, var(--dsw-alias-label-primary) 14%, transparent)"
97
+ };
98
+ const BADGE_VALUE = {
99
+ color: "var(--dsw-alias-label-primary)",
100
+ fontWeight: 600
101
+ };
102
+ const META_BOX = {
103
+ width: "100%",
104
+ maxWidth: 360,
105
+ display: "flex",
106
+ flexDirection: "column",
107
+ gap: 8,
108
+ padding: "12px 14px",
109
+ borderRadius: 12,
110
+ fontSize: 12,
111
+ textAlign: "left",
112
+ color: "var(--dsw-alias-label-primary)",
113
+ background: "color-mix(in srgb, var(--dsw-alias-label-primary) 3%, transparent)",
114
+ border: "1px solid color-mix(in srgb, var(--dsw-alias-label-primary) 10%, transparent)",
115
+ boxSizing: "border-box"
116
+ };
117
+ const ROW = {
118
+ display: "flex",
119
+ alignItems: "center",
120
+ justifyContent: "space-between",
121
+ gap: 12
122
+ };
123
+ const ROW_LABEL = { color: "var(--dsw-alias-label-tertiary)" };
124
+ const VERSION_PILL = {
125
+ color: "var(--dsw-alias-label-primary)",
126
+ fontWeight: 500,
127
+ fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
128
+ fontSize: 11,
129
+ padding: "1px 8px",
130
+ borderRadius: 6,
131
+ background: "color-mix(in srgb, var(--dsw-alias-label-primary) 6%, transparent)",
132
+ border: "1px solid color-mix(in srgb, var(--dsw-alias-label-primary) 14%, transparent)"
133
+ };
134
+ const ROW_BOTTOM = {
135
+ display: "flex",
136
+ alignItems: "center",
137
+ justifyContent: "space-between",
138
+ gap: 12,
139
+ paddingTop: 8,
140
+ borderTop: "1px solid color-mix(in srgb, var(--dsw-alias-label-primary) 12%, transparent)",
141
+ fontSize: 11
142
+ };
143
+ const ACTION = {
144
+ display: "inline-flex",
145
+ alignItems: "center",
146
+ gap: 6,
147
+ border: "none",
148
+ background: "none",
149
+ padding: 0,
150
+ cursor: "pointer",
151
+ font: "inherit",
152
+ color: "var(--dsw-alias-label-tertiary)"
153
+ };
154
+ const ACTION_COPIED = {
155
+ ...ACTION,
156
+ color: "var(--dsw-alias-state-success, #34c759)",
157
+ fontWeight: 500
158
+ };
159
+ const LINK = {
160
+ display: "inline-flex",
161
+ alignItems: "center",
162
+ gap: 6,
163
+ textDecoration: "none",
164
+ color: "var(--dsw-alias-label-tertiary)"
165
+ };
166
+ const FOOTER = {
167
+ display: "flex",
168
+ flexDirection: "column",
169
+ gap: 2
170
+ };
171
+ const COPYRIGHT = {
172
+ fontSize: 12,
173
+ fontWeight: 500,
174
+ color: "var(--dsw-alias-label-secondary)",
175
+ margin: 0
176
+ };
177
+ const POWERED = {
178
+ fontSize: 11,
179
+ color: "var(--dsw-alias-label-tertiary)",
180
+ margin: 0
181
+ };
182
+ /** Same-origin route mounted by the node half (see `src/index.ts`). */
183
+ const ABOUT_ROUTE = "/api/v1/huaqiu/about";
184
+ const GITHUB_URL = "https://github.com/deepseek-ai/deepseek-harness";
185
+ function AboutSection({ t }) {
186
+ const [version, setVersion] = (0, react.useState)(null);
187
+ const [dshVersion, setDshVersion] = (0, react.useState)(null);
188
+ const [copied, setCopied] = (0, react.useState)(false);
189
+ (0, react.useEffect)(() => {
190
+ let alive = true;
191
+ fetch(ABOUT_ROUTE).then((res) => res.ok ? res.json() : null).then((body) => {
192
+ if (!alive) return;
193
+ setVersion(body && typeof body.version === "string" ? body.version : null);
194
+ setDshVersion(body && typeof body.dshVersion === "string" ? body.dshVersion : null);
195
+ }).catch(() => {
196
+ if (alive) {
197
+ setVersion(null);
198
+ setDshVersion(null);
199
+ }
200
+ });
201
+ return () => {
202
+ alive = false;
203
+ };
204
+ }, []);
205
+ (0, react.useEffect)(() => {
206
+ if (!copied) return;
207
+ const timer = window.setTimeout(() => setCopied(false), 2e3);
208
+ return () => window.clearTimeout(timer);
209
+ }, [copied]);
210
+ const copyMeta = () => {
211
+ const runtime = version ?? "-";
212
+ const dist = dshVersion ?? "-";
213
+ const text = [
214
+ `App: ${t("slogan")}`,
215
+ `DSH Runtime: ${runtime}`,
216
+ `Release: ${dist}`,
217
+ `Copyright: ${t("copyright")}`
218
+ ].join("\n");
219
+ navigator.clipboard.writeText(text).catch(() => {});
220
+ setCopied(true);
221
+ };
222
+ return (0, react.createElement)("div", { style: ROOT }, (0, react.createElement)("div", { style: TOP }, (0, react.createElement)(HuaqiuBrandMark, { size: 64 }), (0, react.createElement)("div", { style: TITLE }, t("slogan")), (0, react.createElement)("div", { style: BADGE }, (0, react.createElement)("span", null, t("runtimeLabel")), (0, react.createElement)("span", { style: BADGE_VALUE }, version ?? ""))), (0, react.createElement)("div", { style: META_BOX }, (0, react.createElement)("div", { style: ROW }, (0, react.createElement)("span", { style: ROW_LABEL }, t("distributionLabel")), (0, react.createElement)("span", { style: VERSION_PILL }, dshVersion ?? "")), (0, react.createElement)("div", { style: ROW }, (0, react.createElement)("span", { style: ROW_LABEL }, t("builtinPluginLabel")), (0, react.createElement)("span", { style: VERSION_PILL }, version ?? "")), (0, react.createElement)("div", { style: ROW_BOTTOM }, (0, react.createElement)("button", {
223
+ style: copied ? ACTION_COPIED : ACTION,
224
+ onClick: copyMeta,
225
+ type: "button"
226
+ }, (0, react.createElement)("span", null, copied ? t("copied") : t("copyMetadata"))), (0, react.createElement)("a", {
227
+ style: LINK,
228
+ href: GITHUB_URL,
229
+ target: "_blank",
230
+ rel: "noreferrer"
231
+ }, (0, react.createElement)("span", null, "deepseek-harness"), (0, react.createElement)("span", { style: {
232
+ fontSize: 9,
233
+ opacity: .7
234
+ } }, "↗")))), (0, react.createElement)("div", { style: FOOTER }, (0, react.createElement)("p", { style: COPYRIGHT }, t("copyright")), (0, react.createElement)("p", { style: POWERED }, t("poweredBy"))));
235
+ }
236
+ //#endregion
237
+ //#region src/client/locales.ts
238
+ /** About-section dictionaries. */
239
+ const zh = {
240
+ nav: "关于",
241
+ slogan: "电子设计智能体平台",
242
+ runtimeLabel: "DSH 运行时",
243
+ distributionLabel: "发行版本",
244
+ builtinPluginLabel: "内置插件版本",
245
+ copyMetadata: "复制元数据 (Metadata)",
246
+ copied: "版本信息已复制",
247
+ copyright: "© 2026 深圳华秋智联股份有限公司",
248
+ poweredBy: "由 DSH 提供支持"
249
+ };
250
+ const en = {
251
+ nav: "About",
252
+ slogan: "Electronic Design Agent Platform",
253
+ runtimeLabel: "DSH Runtime",
254
+ distributionLabel: "Distribution",
255
+ builtinPluginLabel: "Builtin plugin version",
256
+ copyMetadata: "Copy metadata",
257
+ copied: "Metadata copied",
258
+ copyright: "© 2026 Shenzhen Huaqiu Zhilian Co., Ltd.",
259
+ poweredBy: "Powered by DSH"
260
+ };
261
+ //#endregion
262
+ //#region src/client/index.ts
263
+ /**
264
+ * `@huaqiu/dsh-huaqiu-about` — browser half.
265
+ *
266
+ * Contributes an "About" row to the DSH settings page through the
267
+ * `settings.section` slot (the same mechanism the community `dsh-market`
268
+ * plugin uses). The row shows the original DSH fish mark, the DSH version
269
+ * bundled with the running harness (fetched from the node half's
270
+ * same-origin route), and the "Powered by DSH" claim.
271
+ */
272
+ const inject = ["slots", "locale"];
273
+ const ABOUT_NS = "about.huaqiu";
274
+ /**
275
+ * Install the About row on the running client context.
276
+ */
277
+ function apply(ctx) {
278
+ ctx.effect?.(() => {
279
+ ctx.locale?.register(ABOUT_NS, {
280
+ zh,
281
+ en
282
+ });
283
+ }, "huaqiu about: dictionaries");
284
+ const t = ctx.locale?.bind("about.huaqiu") ?? (() => "About");
285
+ const slots = ctx.slots;
286
+ if (!slots) return;
287
+ slots.inject("settings.section", () => slots.register({
288
+ name: "settings.section",
289
+ id: "huaqiu-about",
290
+ order: 90,
291
+ label: () => t("nav"),
292
+ locale: ABOUT_NS
293
+ }, () => (0, react.createElement)(AboutSection, { t })));
294
+ }
295
+ //#endregion
296
+ exports.ABOUT_NS = ABOUT_NS;
297
+ exports.apply = apply;
298
+ exports.inject = inject;
299
+ return module.exports;
300
+ }
301
+ });
302
+
303
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","names":["createElement","useState","createElement","createElement"],"sources":["../src/client/HuaqiuBrand.tsx","../src/client/AboutSection.tsx","../src/client/locales.ts","../src/client/index.ts"],"sourcesContent":["/**\n * HuaQiu brand mark — self-contained copy (SVG) so the About plugin does not\n * depend on another plugin's client bundle at runtime.\n */\nimport { createElement } from 'react'\n\nexport function HuaqiuBrandMark({ size, className }: { size: number; className?: string }): React.ReactElement {\n return createElement(\n 'svg',\n {\n 'aria-hidden': true,\n className,\n height: size,\n style: { display: 'block', flex: 'none' },\n viewBox: '0 0 974.25 974.25',\n width: size,\n xmlns: 'http://www.w3.org/2000/svg',\n },\n createElement('path', {\n d: 'M865.67,758.67a107,107,0,1,0,107,107A107,107,0,0,0,865.67,758.67Z',\n fill: '#c7000b',\n fillRule: 'evenodd',\n }),\n createElement('path', {\n d: 'M744.11,0h-514C103.56,0,0,103.56,0,230.14v514C0,870.69,103.56,974.25,230.14,974.25h514c3.34,0,6.66-.06,10-.21a155.66,155.66,0,0,1-40-143.56H230.14a86.63,86.63,0,0,1-86.37-86.37v-514a86.63,86.63,0,0,1,86.37-86.37h514a86.61,86.61,0,0,1,86.37,86.37V714.06a156.93,156.93,0,0,1,35.19-4A155.1,155.1,0,0,1,974,754c.15-3.28.21-6.6.21-9.92v-514C974.25,103.56,870.69,0,744.11,0Z',\n fill: '#c7000b',\n fillRule: 'evenodd',\n }),\n createElement('polygon', {\n points: '735.67 735.65 735.67 238.59 591.9 238.59 591.9 415.24 382.39 415.24 382.39 238.59 238.62 238.59 238.62 735.65 382.39 735.65 382.39 559.01 591.9 559.01 591.9 735.65 735.67 735.65',\n fill: '#c7000b',\n fillRule: 'evenodd',\n }),\n )\n}\n","/**\n * About row content, following the reference prototype:\n *\n * [HuaQiu brand mark]\n * {slogan}\n * [DSH 运行时 0.1.0] (runtime label + this plugin's version)\n * ┌───────────────────────────┐\n * │ 发行版本 0.1.5-rc.2 │ (Distribution label + bundled DSH version)\n * │ 内置插件版本 0.1.0 │ (Builtin plugin version = plugin version)\n * │ 复制元数据 deepseek-harness ↗ │\n * └───────────────────────────┘\n * © 2026 深圳华秋智联股份有限公司\n * 由 DSH 提供支持\n *\n * The two versions are fetched from the node half's same-origin route\n * (`GET /api/v1/huaqiu/about` → `{ version, dshVersion }`): the DSH Runtime\n * badge and the Builtin plugin version row both show this plugin's declared\n * version, the Distribution version is the DSH version bundled with the\n * running harness. The block is vertically compact like the prototype — it\n * centers as one dense unit instead of stretching across the panel. All text\n * lines are localised.\n */\nimport { createElement, useEffect, useState, type CSSProperties, type ReactElement } from 'react'\nimport { HuaqiuBrandMark } from './HuaqiuBrand'\n\nconst ROOT: CSSProperties = {\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n height: '100%',\n boxSizing: 'border-box',\n gap: 14,\n padding: '16px 24px',\n textAlign: 'center',\n}\n\nconst TOP: CSSProperties = {\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n gap: 8,\n}\n\nconst TITLE: CSSProperties = {\n color: 'var(--dsw-alias-label-primary)',\n fontSize: 20,\n fontWeight: 700,\n letterSpacing: '0.01em',\n}\n\nconst BADGE: CSSProperties = {\n display: 'inline-flex',\n alignItems: 'center',\n gap: 6,\n padding: '2px 12px',\n borderRadius: 999,\n fontSize: 12,\n fontFamily: 'ui-monospace, SFMono-Regular, Menlo, monospace',\n color: 'var(--dsw-alias-label-secondary)',\n background: 'color-mix(in srgb, var(--dsw-alias-label-primary) 5%, transparent)',\n border: '1px solid color-mix(in srgb, var(--dsw-alias-label-primary) 14%, transparent)',\n}\n\nconst BADGE_VALUE: CSSProperties = {\n color: 'var(--dsw-alias-label-primary)',\n fontWeight: 600,\n}\n\nconst META_BOX: CSSProperties = {\n width: '100%',\n maxWidth: 360,\n display: 'flex',\n flexDirection: 'column',\n gap: 8,\n padding: '12px 14px',\n borderRadius: 12,\n fontSize: 12,\n textAlign: 'left',\n color: 'var(--dsw-alias-label-primary)',\n background: 'color-mix(in srgb, var(--dsw-alias-label-primary) 3%, transparent)',\n border: '1px solid color-mix(in srgb, var(--dsw-alias-label-primary) 10%, transparent)',\n boxSizing: 'border-box',\n}\n\nconst ROW: CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: 12,\n}\n\nconst ROW_LABEL: CSSProperties = {\n color: 'var(--dsw-alias-label-tertiary)',\n}\n\nconst VERSION_PILL: CSSProperties = {\n color: 'var(--dsw-alias-label-primary)',\n fontWeight: 500,\n fontFamily: 'ui-monospace, SFMono-Regular, Menlo, monospace',\n fontSize: 11,\n padding: '1px 8px',\n borderRadius: 6,\n background: 'color-mix(in srgb, var(--dsw-alias-label-primary) 6%, transparent)',\n border: '1px solid color-mix(in srgb, var(--dsw-alias-label-primary) 14%, transparent)',\n}\n\nconst ROW_BOTTOM: CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: 12,\n paddingTop: 8,\n borderTop: '1px solid color-mix(in srgb, var(--dsw-alias-label-primary) 12%, transparent)',\n fontSize: 11,\n}\n\nconst ACTION: CSSProperties = {\n display: 'inline-flex',\n alignItems: 'center',\n gap: 6,\n border: 'none',\n background: 'none',\n padding: 0,\n cursor: 'pointer',\n font: 'inherit',\n color: 'var(--dsw-alias-label-tertiary)',\n}\n\nconst ACTION_COPIED: CSSProperties = {\n ...ACTION,\n color: 'var(--dsw-alias-state-success, #34c759)',\n fontWeight: 500,\n}\n\nconst LINK: CSSProperties = {\n display: 'inline-flex',\n alignItems: 'center',\n gap: 6,\n textDecoration: 'none',\n color: 'var(--dsw-alias-label-tertiary)',\n}\n\nconst FOOTER: CSSProperties = {\n display: 'flex',\n flexDirection: 'column',\n gap: 2,\n}\n\nconst COPYRIGHT: CSSProperties = {\n fontSize: 12,\n fontWeight: 500,\n color: 'var(--dsw-alias-label-secondary)',\n margin: 0,\n}\n\nconst POWERED: CSSProperties = {\n fontSize: 11,\n color: 'var(--dsw-alias-label-tertiary)',\n margin: 0,\n}\n\n/** Same-origin route mounted by the node half (see `src/index.ts`). */\nconst ABOUT_ROUTE = '/api/v1/huaqiu/about'\n\nconst GITHUB_URL = 'https://github.com/deepseek-ai/deepseek-harness'\n\ninterface AboutPayload {\n version?: unknown\n dshVersion?: unknown\n}\n\nexport interface AboutSectionProps {\n /** Bound locale dictionary (injected by the client entry). */\n t: (key: string) => string\n}\n\nexport function AboutSection({ t }: AboutSectionProps): ReactElement {\n const [version, setVersion] = useState<string | null>(null)\n const [dshVersion, setDshVersion] = useState<string | null>(null)\n const [copied, setCopied] = useState(false)\n\n useEffect(() => {\n let alive = true\n void fetch(ABOUT_ROUTE)\n .then((res) => (res.ok ? res.json() as Promise<AboutPayload> : null))\n .then((body) => {\n if (!alive) return\n setVersion(body && typeof body.version === 'string' ? body.version : null)\n setDshVersion(\n body && typeof body.dshVersion === 'string' ? body.dshVersion : null,\n )\n })\n .catch(() => { if (alive) { setVersion(null); setDshVersion(null) } })\n return () => { alive = false }\n }, [])\n\n useEffect(() => {\n if (!copied) return\n const timer = window.setTimeout(() => setCopied(false), 2000)\n return () => window.clearTimeout(timer)\n }, [copied])\n\n const copyMeta = (): void => {\n const runtime = version ?? '-'\n const dist = dshVersion ?? '-'\n const text = [\n `App: ${t('slogan')}`,\n `DSH Runtime: ${runtime}`,\n `Release: ${dist}`,\n `Copyright: ${t('copyright')}`,\n ].join('\\n')\n void navigator.clipboard.writeText(text).catch(() => {})\n setCopied(true)\n }\n\n return createElement(\n 'div',\n { style: ROOT },\n createElement(\n 'div',\n { style: TOP },\n createElement(HuaqiuBrandMark, { size: 64 }),\n createElement('div', { style: TITLE }, t('slogan')),\n createElement(\n 'div',\n { style: BADGE },\n createElement('span', null, t('runtimeLabel')),\n createElement('span', { style: BADGE_VALUE }, version ?? ''),\n ),\n ),\n createElement(\n 'div',\n { style: META_BOX },\n createElement(\n 'div',\n { style: ROW },\n createElement('span', { style: ROW_LABEL }, t('distributionLabel')),\n createElement('span', { style: VERSION_PILL }, dshVersion ?? ''),\n ),\n createElement(\n 'div',\n { style: ROW },\n createElement('span', { style: ROW_LABEL }, t('builtinPluginLabel')),\n createElement('span', { style: VERSION_PILL }, version ?? ''),\n ),\n createElement(\n 'div',\n { style: ROW_BOTTOM },\n createElement(\n 'button',\n { style: copied ? ACTION_COPIED : ACTION, onClick: copyMeta, type: 'button' },\n createElement('span', null, copied ? t('copied') : t('copyMetadata')),\n ),\n createElement(\n 'a',\n { style: LINK, href: GITHUB_URL, target: '_blank', rel: 'noreferrer' },\n createElement('span', null, 'deepseek-harness'),\n createElement('span', { style: { fontSize: 9, opacity: 0.7 } }, '↗'),\n ),\n ),\n ),\n createElement(\n 'div',\n { style: FOOTER },\n createElement('p', { style: COPYRIGHT }, t('copyright')),\n createElement('p', { style: POWERED }, t('poweredBy')),\n ),\n )\n}\n","/** About-section dictionaries. */\n\nexport const zh = {\n nav: '关于',\n slogan: '电子设计智能体平台',\n runtimeLabel: 'DSH 运行时',\n distributionLabel: '发行版本',\n builtinPluginLabel: '内置插件版本',\n copyMetadata: '复制元数据 (Metadata)',\n copied: '版本信息已复制',\n copyright: '© 2026 深圳华秋智联股份有限公司',\n poweredBy: '由 DSH 提供支持',\n} as const\n\nexport type AboutKey = keyof typeof zh\n\nexport const en: Record<AboutKey, string> = {\n nav: 'About',\n slogan: 'Electronic Design Agent Platform',\n runtimeLabel: 'DSH Runtime',\n distributionLabel: 'Distribution',\n builtinPluginLabel: 'Builtin plugin version',\n copyMetadata: 'Copy metadata',\n copied: 'Metadata copied',\n copyright: '© 2026 Shenzhen Huaqiu Zhilian Co., Ltd.',\n poweredBy: 'Powered by DSH',\n}\n","/**\n * `@huaqiu/dsh-huaqiu-about` — browser half.\n *\n * Contributes an \"About\" row to the DSH settings page through the\n * `settings.section` slot (the same mechanism the community `dsh-market`\n * plugin uses). The row shows the original DSH fish mark, the DSH version\n * bundled with the running harness (fetched from the node half's\n * same-origin route), and the \"Powered by DSH\" claim.\n */\nimport { createElement } from 'react'\nimport type {} from '@deepseek-ai/dsh-client-ui-slots'\nimport { AboutSection } from './AboutSection'\nimport { en, zh, type AboutKey } from './locales'\n\nexport const inject = ['slots', 'locale'] as const\n\nexport const ABOUT_NS = 'about.huaqiu'\n\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n 'about.huaqiu': AboutKey\n }\n}\n\n/**\n * Minimal structural client context (the DSH client runtime provides these\n * faces). Declared structurally, never imported, so the package stays\n * independently installable — the same pattern as\n * `@huaqiu/dsh-tool-schematic-gen`.\n */\nexport interface ClientContext {\n slots?: {\n inject(key: string, callback: () => () => void): () => void\n register(spec: {\n name: string\n id?: string\n order?: number\n label?: () => string\n locale?: string\n }, component: unknown): () => void\n }\n locale?: {\n register(namespace: string, dictionaries: unknown): unknown\n bind(namespace: string): (key: string) => string\n }\n effect?(fn: () => (() => void) | void, label?: string): void\n}\n\n/**\n * Install the About row on the running client context.\n */\nexport function apply(ctx: ClientContext): void {\n ctx.effect?.(() => { ctx.locale?.register(ABOUT_NS, { zh, en }) }, 'huaqiu about: dictionaries')\n const t = ctx.locale?.bind(ABOUT_NS) ?? (() => 'About')\n const slots = ctx.slots\n if (!slots) return\n slots.inject('settings.section', () => slots.register({\n name: 'settings.section',\n id: 'huaqiu-about',\n order: 90,\n label: () => t('nav'),\n locale: ABOUT_NS,\n }, () => createElement(AboutSection, { t })))\n}\n"],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import { Context } from "@deepseek-ai/cordis";
2
+ //#region src/index.d.ts
3
+ declare const inject: readonly ["webServer"];
4
+ declare const ABOUT_ROUTE_PREFIX = "/api/v1/huaqiu/about";
5
+ /** This plugin's declared version from `package.json`, or null when unreadable. */
6
+ declare function pluginVersion(): string | null;
7
+ /**
8
+ * Install the About version route on the running context.
9
+ */
10
+ declare function apply(ctx: Context): void;
11
+ //#endregion
12
+ export { ABOUT_ROUTE_PREFIX, apply, inject, pluginVersion };
package/lib/index.mjs ADDED
@@ -0,0 +1,110 @@
1
+ import { createRequire } from "node:module";
2
+ import { existsSync, readFileSync } from "node:fs";
3
+ import { fileURLToPath } from "node:url";
4
+ import { dirname, join, resolve } from "node:path";
5
+ //#region src/dsh-version.ts
6
+ /**
7
+ * Resolve the DSH version bundled with the running harness.
8
+ *
9
+ * The community `dsh-market` plugin resolves the host version by walking up
10
+ * from the CLI entry for `node_modules/@deepseek-ai/dsh/package.json`. We do
11
+ * the same, plus:
12
+ * - a workspace checkout whose own `package.json` carries the
13
+ * `@deepseek-ai/dsh` name (local harness development);
14
+ * - a `require.resolve` probe;
15
+ * - environment fallbacks (`DSH_CLIENT_VERSION` / `DSH_VERSION`).
16
+ */
17
+ const DSH_PACKAGE_REL = join("node_modules", "@deepseek-ai", "dsh", "package.json");
18
+ function readVersionAt(packageJsonPath) {
19
+ try {
20
+ const manifest = JSON.parse(readFileSync(packageJsonPath, "utf8"));
21
+ return typeof manifest.version === "string" && manifest.version.length > 0 ? manifest.version : null;
22
+ } catch {
23
+ return null;
24
+ }
25
+ }
26
+ /** Read `package.json` and return its version when it IS the DSH package. */
27
+ function readDshManifestVersionAt(packageJsonPath) {
28
+ try {
29
+ const manifest = JSON.parse(readFileSync(packageJsonPath, "utf8"));
30
+ if (manifest.name !== "@deepseek-ai/dsh") return null;
31
+ return typeof manifest.version === "string" && manifest.version.length > 0 ? manifest.version : null;
32
+ } catch {
33
+ return null;
34
+ }
35
+ }
36
+ /**
37
+ * Walk up from `start` (a directory) looking for the DSH package manifest —
38
+ * either an installed `node_modules/@deepseek-ai/dsh` or a workspace checkout
39
+ * whose own `package.json` carries the `@deepseek-ai/dsh` name. Returns the
40
+ * declared version, or null when no manifest is found.
41
+ */
42
+ function findDshVersionFrom(start) {
43
+ let directory = start;
44
+ for (let depth = 0; depth < 12; depth += 1) {
45
+ const installed = readVersionAt(join(directory, DSH_PACKAGE_REL));
46
+ if (installed !== null) return installed;
47
+ const workspace = readDshManifestVersionAt(join(directory, "package.json"));
48
+ if (workspace !== null) return workspace;
49
+ const parent = dirname(directory);
50
+ if (parent === directory) break;
51
+ directory = parent;
52
+ }
53
+ return null;
54
+ }
55
+ /**
56
+ * Resolve the DSH version for the current process: walk up from the CLI entry
57
+ * (`process.argv[1]`), then probe `require.resolve('@deepseek-ai/dsh/package.json')`,
58
+ * then environment fallbacks. Returns null when nothing is resolvable.
59
+ */
60
+ function resolveDshVersion() {
61
+ const entry = typeof process.argv[1] === "string" ? process.argv[1] : null;
62
+ if (entry) {
63
+ const fromEntry = findDshVersionFrom(dirname(resolve(entry)));
64
+ if (fromEntry !== null) return fromEntry;
65
+ }
66
+ try {
67
+ const manifestPath = createRequire(import.meta.url).resolve("@deepseek-ai/dsh/package.json");
68
+ if (existsSync(manifestPath)) {
69
+ const version = readVersionAt(manifestPath);
70
+ if (version !== null) return version;
71
+ }
72
+ } catch {}
73
+ const fromEnv = process.env.DSH_CLIENT_VERSION ?? process.env.DSH_VERSION;
74
+ return fromEnv && fromEnv.length > 0 ? fromEnv : null;
75
+ }
76
+ //#endregion
77
+ //#region src/index.ts
78
+ const inject = ["webServer"];
79
+ const ABOUT_ROUTE_PREFIX = "/api/v1/huaqiu/about";
80
+ function sendJson(res, status, body) {
81
+ res.writeHead(status, { "content-type": "application/json; charset=utf-8" });
82
+ res.end(JSON.stringify(body));
83
+ }
84
+ /** This plugin's declared version from `package.json`, or null when unreadable. */
85
+ function pluginVersion() {
86
+ try {
87
+ const manifestUrl = new URL("../package.json", import.meta.url);
88
+ const manifest = JSON.parse(readFileSync(fileURLToPath(manifestUrl), "utf8"));
89
+ return typeof manifest.version === "string" && manifest.version.length > 0 ? manifest.version : null;
90
+ } catch {
91
+ return null;
92
+ }
93
+ }
94
+ /**
95
+ * Install the About version route on the running context.
96
+ */
97
+ function apply(ctx) {
98
+ ctx.effect(() => ctx.webServer.register({
99
+ kind: "prefix",
100
+ path: ABOUT_ROUTE_PREFIX,
101
+ handler: (_req, res) => {
102
+ sendJson(res, 200, {
103
+ version: pluginVersion(),
104
+ dshVersion: resolveDshVersion()
105
+ });
106
+ }
107
+ }), "huaqiu about: plugin + DSH version route");
108
+ }
109
+ //#endregion
110
+ export { ABOUT_ROUTE_PREFIX, apply, inject, pluginVersion };
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@huaqiu/dsh-huaqiu-about",
3
+ "version": "0.4.10",
4
+ "description": "HuaQiu About section in the DSH settings page: original DSH mark, bundled DSH version and the Powered-by-DSH claim.",
5
+ "type": "module",
6
+ "main": "./lib/index.mjs",
7
+ "types": "./lib/index.d.mts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./lib/index.d.mts",
11
+ "default": "./lib/index.mjs"
12
+ },
13
+ "./client": {
14
+ "default": "./lib/client.js"
15
+ },
16
+ "./cordis.patch.yml": "./cordis.patch.yml",
17
+ "./package.json": "./package.json"
18
+ },
19
+ "dsh": {
20
+ "bundle": {
21
+ "patch": "./cordis.patch.yml"
22
+ },
23
+ "client": {
24
+ "platform": "web",
25
+ "inject": [
26
+ "@deepseek-ai/dsh-client-locale",
27
+ "@deepseek-ai/dsh-client-ui-primitives",
28
+ "@deepseek-ai/dsh-client-ui-slots"
29
+ ]
30
+ }
31
+ },
32
+ "peerDependencies": {
33
+ "@deepseek-ai/cordis": "^4.0.2",
34
+ "@deepseek-ai/dsh-host-webserver": "^0.1.0-rc.0"
35
+ },
36
+ "files": [
37
+ "lib",
38
+ "src",
39
+ "cordis.patch.yml",
40
+ "README.md"
41
+ ],
42
+ "publishConfig": {
43
+ "access": "public"
44
+ },
45
+ "devDependencies": {
46
+ "@deepseek-ai/cordis": "^4.0.2",
47
+ "@deepseek-ai/dsh-client-locale": "^0.1.5-rc.2",
48
+ "@deepseek-ai/dsh-client-ui-primitives": "^0.1.5-rc.2",
49
+ "@deepseek-ai/dsh-client-ui-settings": "^0.1.5-rc.2",
50
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.5-rc.2",
51
+ "@deepseek-ai/dsh-host-webserver": "^0.1.0-rc.0",
52
+ "@types/react": "~18.3.1",
53
+ "react": "^18.3.1",
54
+ "react-dom": "^18.3.1"
55
+ },
56
+ "scripts": {
57
+ "typecheck": "tsc --noEmit",
58
+ "build": "tsdown",
59
+ "test": "vitest run"
60
+ }
61
+ }
@@ -0,0 +1,270 @@
1
+ /**
2
+ * About row content, following the reference prototype:
3
+ *
4
+ * [HuaQiu brand mark]
5
+ * {slogan}
6
+ * [DSH 运行时 0.1.0] (runtime label + this plugin's version)
7
+ * ┌───────────────────────────┐
8
+ * │ 发行版本 0.1.5-rc.2 │ (Distribution label + bundled DSH version)
9
+ * │ 内置插件版本 0.1.0 │ (Builtin plugin version = plugin version)
10
+ * │ 复制元数据 deepseek-harness ↗ │
11
+ * └───────────────────────────┘
12
+ * © 2026 深圳华秋智联股份有限公司
13
+ * 由 DSH 提供支持
14
+ *
15
+ * The two versions are fetched from the node half's same-origin route
16
+ * (`GET /api/v1/huaqiu/about` → `{ version, dshVersion }`): the DSH Runtime
17
+ * badge and the Builtin plugin version row both show this plugin's declared
18
+ * version, the Distribution version is the DSH version bundled with the
19
+ * running harness. The block is vertically compact like the prototype — it
20
+ * centers as one dense unit instead of stretching across the panel. All text
21
+ * lines are localised.
22
+ */
23
+ import { createElement, useEffect, useState, type CSSProperties, type ReactElement } from 'react'
24
+ import { HuaqiuBrandMark } from './HuaqiuBrand'
25
+
26
+ const ROOT: CSSProperties = {
27
+ display: 'flex',
28
+ flexDirection: 'column',
29
+ alignItems: 'center',
30
+ justifyContent: 'center',
31
+ height: '100%',
32
+ boxSizing: 'border-box',
33
+ gap: 14,
34
+ padding: '16px 24px',
35
+ textAlign: 'center',
36
+ }
37
+
38
+ const TOP: CSSProperties = {
39
+ display: 'flex',
40
+ flexDirection: 'column',
41
+ alignItems: 'center',
42
+ gap: 8,
43
+ }
44
+
45
+ const TITLE: CSSProperties = {
46
+ color: 'var(--dsw-alias-label-primary)',
47
+ fontSize: 20,
48
+ fontWeight: 700,
49
+ letterSpacing: '0.01em',
50
+ }
51
+
52
+ const BADGE: CSSProperties = {
53
+ display: 'inline-flex',
54
+ alignItems: 'center',
55
+ gap: 6,
56
+ padding: '2px 12px',
57
+ borderRadius: 999,
58
+ fontSize: 12,
59
+ fontFamily: 'ui-monospace, SFMono-Regular, Menlo, monospace',
60
+ color: 'var(--dsw-alias-label-secondary)',
61
+ background: 'color-mix(in srgb, var(--dsw-alias-label-primary) 5%, transparent)',
62
+ border: '1px solid color-mix(in srgb, var(--dsw-alias-label-primary) 14%, transparent)',
63
+ }
64
+
65
+ const BADGE_VALUE: CSSProperties = {
66
+ color: 'var(--dsw-alias-label-primary)',
67
+ fontWeight: 600,
68
+ }
69
+
70
+ const META_BOX: CSSProperties = {
71
+ width: '100%',
72
+ maxWidth: 360,
73
+ display: 'flex',
74
+ flexDirection: 'column',
75
+ gap: 8,
76
+ padding: '12px 14px',
77
+ borderRadius: 12,
78
+ fontSize: 12,
79
+ textAlign: 'left',
80
+ color: 'var(--dsw-alias-label-primary)',
81
+ background: 'color-mix(in srgb, var(--dsw-alias-label-primary) 3%, transparent)',
82
+ border: '1px solid color-mix(in srgb, var(--dsw-alias-label-primary) 10%, transparent)',
83
+ boxSizing: 'border-box',
84
+ }
85
+
86
+ const ROW: CSSProperties = {
87
+ display: 'flex',
88
+ alignItems: 'center',
89
+ justifyContent: 'space-between',
90
+ gap: 12,
91
+ }
92
+
93
+ const ROW_LABEL: CSSProperties = {
94
+ color: 'var(--dsw-alias-label-tertiary)',
95
+ }
96
+
97
+ const VERSION_PILL: CSSProperties = {
98
+ color: 'var(--dsw-alias-label-primary)',
99
+ fontWeight: 500,
100
+ fontFamily: 'ui-monospace, SFMono-Regular, Menlo, monospace',
101
+ fontSize: 11,
102
+ padding: '1px 8px',
103
+ borderRadius: 6,
104
+ background: 'color-mix(in srgb, var(--dsw-alias-label-primary) 6%, transparent)',
105
+ border: '1px solid color-mix(in srgb, var(--dsw-alias-label-primary) 14%, transparent)',
106
+ }
107
+
108
+ const ROW_BOTTOM: CSSProperties = {
109
+ display: 'flex',
110
+ alignItems: 'center',
111
+ justifyContent: 'space-between',
112
+ gap: 12,
113
+ paddingTop: 8,
114
+ borderTop: '1px solid color-mix(in srgb, var(--dsw-alias-label-primary) 12%, transparent)',
115
+ fontSize: 11,
116
+ }
117
+
118
+ const ACTION: CSSProperties = {
119
+ display: 'inline-flex',
120
+ alignItems: 'center',
121
+ gap: 6,
122
+ border: 'none',
123
+ background: 'none',
124
+ padding: 0,
125
+ cursor: 'pointer',
126
+ font: 'inherit',
127
+ color: 'var(--dsw-alias-label-tertiary)',
128
+ }
129
+
130
+ const ACTION_COPIED: CSSProperties = {
131
+ ...ACTION,
132
+ color: 'var(--dsw-alias-state-success, #34c759)',
133
+ fontWeight: 500,
134
+ }
135
+
136
+ const LINK: CSSProperties = {
137
+ display: 'inline-flex',
138
+ alignItems: 'center',
139
+ gap: 6,
140
+ textDecoration: 'none',
141
+ color: 'var(--dsw-alias-label-tertiary)',
142
+ }
143
+
144
+ const FOOTER: CSSProperties = {
145
+ display: 'flex',
146
+ flexDirection: 'column',
147
+ gap: 2,
148
+ }
149
+
150
+ const COPYRIGHT: CSSProperties = {
151
+ fontSize: 12,
152
+ fontWeight: 500,
153
+ color: 'var(--dsw-alias-label-secondary)',
154
+ margin: 0,
155
+ }
156
+
157
+ const POWERED: CSSProperties = {
158
+ fontSize: 11,
159
+ color: 'var(--dsw-alias-label-tertiary)',
160
+ margin: 0,
161
+ }
162
+
163
+ /** Same-origin route mounted by the node half (see `src/index.ts`). */
164
+ const ABOUT_ROUTE = '/api/v1/huaqiu/about'
165
+
166
+ const GITHUB_URL = 'https://github.com/deepseek-ai/deepseek-harness'
167
+
168
+ interface AboutPayload {
169
+ version?: unknown
170
+ dshVersion?: unknown
171
+ }
172
+
173
+ export interface AboutSectionProps {
174
+ /** Bound locale dictionary (injected by the client entry). */
175
+ t: (key: string) => string
176
+ }
177
+
178
+ export function AboutSection({ t }: AboutSectionProps): ReactElement {
179
+ const [version, setVersion] = useState<string | null>(null)
180
+ const [dshVersion, setDshVersion] = useState<string | null>(null)
181
+ const [copied, setCopied] = useState(false)
182
+
183
+ useEffect(() => {
184
+ let alive = true
185
+ void fetch(ABOUT_ROUTE)
186
+ .then((res) => (res.ok ? res.json() as Promise<AboutPayload> : null))
187
+ .then((body) => {
188
+ if (!alive) return
189
+ setVersion(body && typeof body.version === 'string' ? body.version : null)
190
+ setDshVersion(
191
+ body && typeof body.dshVersion === 'string' ? body.dshVersion : null,
192
+ )
193
+ })
194
+ .catch(() => { if (alive) { setVersion(null); setDshVersion(null) } })
195
+ return () => { alive = false }
196
+ }, [])
197
+
198
+ useEffect(() => {
199
+ if (!copied) return
200
+ const timer = window.setTimeout(() => setCopied(false), 2000)
201
+ return () => window.clearTimeout(timer)
202
+ }, [copied])
203
+
204
+ const copyMeta = (): void => {
205
+ const runtime = version ?? '-'
206
+ const dist = dshVersion ?? '-'
207
+ const text = [
208
+ `App: ${t('slogan')}`,
209
+ `DSH Runtime: ${runtime}`,
210
+ `Release: ${dist}`,
211
+ `Copyright: ${t('copyright')}`,
212
+ ].join('\n')
213
+ void navigator.clipboard.writeText(text).catch(() => {})
214
+ setCopied(true)
215
+ }
216
+
217
+ return createElement(
218
+ 'div',
219
+ { style: ROOT },
220
+ createElement(
221
+ 'div',
222
+ { style: TOP },
223
+ createElement(HuaqiuBrandMark, { size: 64 }),
224
+ createElement('div', { style: TITLE }, t('slogan')),
225
+ createElement(
226
+ 'div',
227
+ { style: BADGE },
228
+ createElement('span', null, t('runtimeLabel')),
229
+ createElement('span', { style: BADGE_VALUE }, version ?? ''),
230
+ ),
231
+ ),
232
+ createElement(
233
+ 'div',
234
+ { style: META_BOX },
235
+ createElement(
236
+ 'div',
237
+ { style: ROW },
238
+ createElement('span', { style: ROW_LABEL }, t('distributionLabel')),
239
+ createElement('span', { style: VERSION_PILL }, dshVersion ?? ''),
240
+ ),
241
+ createElement(
242
+ 'div',
243
+ { style: ROW },
244
+ createElement('span', { style: ROW_LABEL }, t('builtinPluginLabel')),
245
+ createElement('span', { style: VERSION_PILL }, version ?? ''),
246
+ ),
247
+ createElement(
248
+ 'div',
249
+ { style: ROW_BOTTOM },
250
+ createElement(
251
+ 'button',
252
+ { style: copied ? ACTION_COPIED : ACTION, onClick: copyMeta, type: 'button' },
253
+ createElement('span', null, copied ? t('copied') : t('copyMetadata')),
254
+ ),
255
+ createElement(
256
+ 'a',
257
+ { style: LINK, href: GITHUB_URL, target: '_blank', rel: 'noreferrer' },
258
+ createElement('span', null, 'deepseek-harness'),
259
+ createElement('span', { style: { fontSize: 9, opacity: 0.7 } }, '↗'),
260
+ ),
261
+ ),
262
+ ),
263
+ createElement(
264
+ 'div',
265
+ { style: FOOTER },
266
+ createElement('p', { style: COPYRIGHT }, t('copyright')),
267
+ createElement('p', { style: POWERED }, t('poweredBy')),
268
+ ),
269
+ )
270
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * HuaQiu brand mark — self-contained copy (SVG) so the About plugin does not
3
+ * depend on another plugin's client bundle at runtime.
4
+ */
5
+ import { createElement } from 'react'
6
+
7
+ export function HuaqiuBrandMark({ size, className }: { size: number; className?: string }): React.ReactElement {
8
+ return createElement(
9
+ 'svg',
10
+ {
11
+ 'aria-hidden': true,
12
+ className,
13
+ height: size,
14
+ style: { display: 'block', flex: 'none' },
15
+ viewBox: '0 0 974.25 974.25',
16
+ width: size,
17
+ xmlns: 'http://www.w3.org/2000/svg',
18
+ },
19
+ createElement('path', {
20
+ d: 'M865.67,758.67a107,107,0,1,0,107,107A107,107,0,0,0,865.67,758.67Z',
21
+ fill: '#c7000b',
22
+ fillRule: 'evenodd',
23
+ }),
24
+ createElement('path', {
25
+ d: 'M744.11,0h-514C103.56,0,0,103.56,0,230.14v514C0,870.69,103.56,974.25,230.14,974.25h514c3.34,0,6.66-.06,10-.21a155.66,155.66,0,0,1-40-143.56H230.14a86.63,86.63,0,0,1-86.37-86.37v-514a86.63,86.63,0,0,1,86.37-86.37h514a86.61,86.61,0,0,1,86.37,86.37V714.06a156.93,156.93,0,0,1,35.19-4A155.1,155.1,0,0,1,974,754c.15-3.28.21-6.6.21-9.92v-514C974.25,103.56,870.69,0,744.11,0Z',
26
+ fill: '#c7000b',
27
+ fillRule: 'evenodd',
28
+ }),
29
+ createElement('polygon', {
30
+ points: '735.67 735.65 735.67 238.59 591.9 238.59 591.9 415.24 382.39 415.24 382.39 238.59 238.62 238.59 238.62 735.65 382.39 735.65 382.39 559.01 591.9 559.01 591.9 735.65 735.67 735.65',
31
+ fill: '#c7000b',
32
+ fillRule: 'evenodd',
33
+ }),
34
+ )
35
+ }
@@ -0,0 +1,64 @@
1
+ /**
2
+ * `@huaqiu/dsh-huaqiu-about` — browser half.
3
+ *
4
+ * Contributes an "About" row to the DSH settings page through the
5
+ * `settings.section` slot (the same mechanism the community `dsh-market`
6
+ * plugin uses). The row shows the original DSH fish mark, the DSH version
7
+ * bundled with the running harness (fetched from the node half's
8
+ * same-origin route), and the "Powered by DSH" claim.
9
+ */
10
+ import { createElement } from 'react'
11
+ import type {} from '@deepseek-ai/dsh-client-ui-slots'
12
+ import { AboutSection } from './AboutSection'
13
+ import { en, zh, type AboutKey } from './locales'
14
+
15
+ export const inject = ['slots', 'locale'] as const
16
+
17
+ export const ABOUT_NS = 'about.huaqiu'
18
+
19
+ declare module '@deepseek-ai/dsh-client-ui-slots' {
20
+ interface LocaleNamespaceMap {
21
+ 'about.huaqiu': AboutKey
22
+ }
23
+ }
24
+
25
+ /**
26
+ * Minimal structural client context (the DSH client runtime provides these
27
+ * faces). Declared structurally, never imported, so the package stays
28
+ * independently installable — the same pattern as
29
+ * `@huaqiu/dsh-tool-schematic-gen`.
30
+ */
31
+ export interface ClientContext {
32
+ slots?: {
33
+ inject(key: string, callback: () => () => void): () => void
34
+ register(spec: {
35
+ name: string
36
+ id?: string
37
+ order?: number
38
+ label?: () => string
39
+ locale?: string
40
+ }, component: unknown): () => void
41
+ }
42
+ locale?: {
43
+ register(namespace: string, dictionaries: unknown): unknown
44
+ bind(namespace: string): (key: string) => string
45
+ }
46
+ effect?(fn: () => (() => void) | void, label?: string): void
47
+ }
48
+
49
+ /**
50
+ * Install the About row on the running client context.
51
+ */
52
+ export function apply(ctx: ClientContext): void {
53
+ ctx.effect?.(() => { ctx.locale?.register(ABOUT_NS, { zh, en }) }, 'huaqiu about: dictionaries')
54
+ const t = ctx.locale?.bind(ABOUT_NS) ?? (() => 'About')
55
+ const slots = ctx.slots
56
+ if (!slots) return
57
+ slots.inject('settings.section', () => slots.register({
58
+ name: 'settings.section',
59
+ id: 'huaqiu-about',
60
+ order: 90,
61
+ label: () => t('nav'),
62
+ locale: ABOUT_NS,
63
+ }, () => createElement(AboutSection, { t })))
64
+ }
@@ -0,0 +1,27 @@
1
+ /** About-section dictionaries. */
2
+
3
+ export const zh = {
4
+ nav: '关于',
5
+ slogan: '电子设计智能体平台',
6
+ runtimeLabel: 'DSH 运行时',
7
+ distributionLabel: '发行版本',
8
+ builtinPluginLabel: '内置插件版本',
9
+ copyMetadata: '复制元数据 (Metadata)',
10
+ copied: '版本信息已复制',
11
+ copyright: '© 2026 深圳华秋智联股份有限公司',
12
+ poweredBy: '由 DSH 提供支持',
13
+ } as const
14
+
15
+ export type AboutKey = keyof typeof zh
16
+
17
+ export const en: Record<AboutKey, string> = {
18
+ nav: 'About',
19
+ slogan: 'Electronic Design Agent Platform',
20
+ runtimeLabel: 'DSH Runtime',
21
+ distributionLabel: 'Distribution',
22
+ builtinPluginLabel: 'Builtin plugin version',
23
+ copyMetadata: 'Copy metadata',
24
+ copied: 'Metadata copied',
25
+ copyright: '© 2026 Shenzhen Huaqiu Zhilian Co., Ltd.',
26
+ poweredBy: 'Powered by DSH',
27
+ }
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Resolve the DSH version bundled with the running harness.
3
+ *
4
+ * The community `dsh-market` plugin resolves the host version by walking up
5
+ * from the CLI entry for `node_modules/@deepseek-ai/dsh/package.json`. We do
6
+ * the same, plus:
7
+ * - a workspace checkout whose own `package.json` carries the
8
+ * `@deepseek-ai/dsh` name (local harness development);
9
+ * - a `require.resolve` probe;
10
+ * - environment fallbacks (`DSH_CLIENT_VERSION` / `DSH_VERSION`).
11
+ */
12
+ import { existsSync, readFileSync } from 'node:fs'
13
+ import { createRequire } from 'node:module'
14
+ import { dirname, join, resolve } from 'node:path'
15
+
16
+ const DSH_PACKAGE_REL = join('node_modules', '@deepseek-ai', 'dsh', 'package.json')
17
+
18
+ function readVersionAt(packageJsonPath: string): string | null {
19
+ try {
20
+ const manifest = JSON.parse(readFileSync(packageJsonPath, 'utf8')) as { version?: unknown }
21
+ return typeof manifest.version === 'string' && manifest.version.length > 0
22
+ ? manifest.version
23
+ : null
24
+ } catch {
25
+ return null
26
+ }
27
+ }
28
+
29
+ /** Read `package.json` and return its version when it IS the DSH package. */
30
+ function readDshManifestVersionAt(packageJsonPath: string): string | null {
31
+ try {
32
+ const manifest = JSON.parse(readFileSync(packageJsonPath, 'utf8')) as {
33
+ name?: unknown
34
+ version?: unknown
35
+ }
36
+ if (manifest.name !== '@deepseek-ai/dsh') return null
37
+ return typeof manifest.version === 'string' && manifest.version.length > 0
38
+ ? manifest.version
39
+ : null
40
+ } catch {
41
+ return null
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Walk up from `start` (a directory) looking for the DSH package manifest —
47
+ * either an installed `node_modules/@deepseek-ai/dsh` or a workspace checkout
48
+ * whose own `package.json` carries the `@deepseek-ai/dsh` name. Returns the
49
+ * declared version, or null when no manifest is found.
50
+ */
51
+ export function findDshVersionFrom(start: string): string | null {
52
+ let directory = start
53
+ for (let depth = 0; depth < 12; depth += 1) {
54
+ const installed = readVersionAt(join(directory, DSH_PACKAGE_REL))
55
+ if (installed !== null) return installed
56
+ const workspace = readDshManifestVersionAt(join(directory, 'package.json'))
57
+ if (workspace !== null) return workspace
58
+ const parent = dirname(directory)
59
+ if (parent === directory) break
60
+ directory = parent
61
+ }
62
+ return null
63
+ }
64
+
65
+ /**
66
+ * Resolve the DSH version for the current process: walk up from the CLI entry
67
+ * (`process.argv[1]`), then probe `require.resolve('@deepseek-ai/dsh/package.json')`,
68
+ * then environment fallbacks. Returns null when nothing is resolvable.
69
+ */
70
+ export function resolveDshVersion(): string | null {
71
+ const entry = typeof process.argv[1] === 'string' ? process.argv[1] : null
72
+ if (entry) {
73
+ const fromEntry = findDshVersionFrom(dirname(resolve(entry)))
74
+ if (fromEntry !== null) return fromEntry
75
+ }
76
+ try {
77
+ const manifestPath = createRequire(import.meta.url).resolve(
78
+ '@deepseek-ai/dsh/package.json',
79
+ )
80
+ if (existsSync(manifestPath)) {
81
+ const version = readVersionAt(manifestPath)
82
+ if (version !== null) return version
83
+ }
84
+ } catch {
85
+ // fall through to environment fallbacks
86
+ }
87
+ const fromEnv = process.env.DSH_CLIENT_VERSION ?? process.env.DSH_VERSION
88
+ return fromEnv && fromEnv.length > 0 ? fromEnv : null
89
+ }
package/src/index.ts ADDED
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Node-side Loader seat for the browser-only About plugin.
3
+ *
4
+ * Mounts a same-origin version route on `ctx.webServer` (the same channel
5
+ * `@huaqiu/dsh-auth` uses for its credential routes): `GET
6
+ * /api/v1/huaqiu/about` returns `{ version, dshVersion }` — this plugin's own
7
+ * declared version (read from the package manifest next to this entry) and the
8
+ * DSH version bundled with the running harness. The browser half fetches them
9
+ * when the About row renders.
10
+ */
11
+ import type { Context } from '@deepseek-ai/cordis'
12
+ import type {} from '@deepseek-ai/dsh-host-webserver'
13
+ import { readFileSync } from 'node:fs'
14
+ import type { IncomingMessage, ServerResponse } from 'node:http'
15
+ import { fileURLToPath } from 'node:url'
16
+ import { resolveDshVersion } from './dsh-version'
17
+
18
+ export const inject = ['webServer'] as const
19
+
20
+ export const ABOUT_ROUTE_PREFIX = '/api/v1/huaqiu/about'
21
+
22
+ function sendJson(res: ServerResponse, status: number, body: unknown): void {
23
+ res.writeHead(status, { 'content-type': 'application/json; charset=utf-8' })
24
+ res.end(JSON.stringify(body))
25
+ }
26
+
27
+ /** This plugin's declared version from `package.json`, or null when unreadable. */
28
+ export function pluginVersion(): string | null {
29
+ try {
30
+ const manifestUrl = new URL('../package.json', import.meta.url)
31
+ const manifest = JSON.parse(readFileSync(fileURLToPath(manifestUrl), 'utf8')) as {
32
+ version?: unknown
33
+ }
34
+ return typeof manifest.version === 'string' && manifest.version.length > 0
35
+ ? manifest.version
36
+ : null
37
+ } catch {
38
+ return null
39
+ }
40
+ }
41
+
42
+ /**
43
+ * Install the About version route on the running context.
44
+ */
45
+ export function apply(ctx: Context): void {
46
+ ctx.effect(() => ctx.webServer.register({
47
+ kind: 'prefix',
48
+ path: ABOUT_ROUTE_PREFIX,
49
+ handler: (_req: IncomingMessage, res: ServerResponse) => {
50
+ sendJson(res, 200, {
51
+ version: pluginVersion(),
52
+ dshVersion: resolveDshVersion(),
53
+ })
54
+ },
55
+ }), 'huaqiu about: plugin + DSH version route')
56
+ }