@huaweidevtools/aishell-dsh-brand 0.0.1-beta.0 → 0.0.1-beta.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/README.md +9 -2
- package/lib/client.js +85 -1
- package/lib/types/client/title-shadow.d.ts +42 -0
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @huaweidevtools/aishell-dsh-brand
|
|
2
2
|
|
|
3
|
-
AI Shell brand reskin for DSH web. Replaces the default DeepSeek fish mark in the sidebar and blank-session hero with the AI Shell icon and wordmark.
|
|
3
|
+
AI Shell brand reskin for DSH web. Replaces the default DeepSeek fish mark in the sidebar and blank-session hero with the AI Shell icon and wordmark, and shadows the browser/window product title at runtime.
|
|
4
4
|
|
|
5
5
|
## How it works
|
|
6
6
|
|
|
@@ -14,6 +14,12 @@ All three are `kind: 'single'` slots, so this plugin replaces whatever occupied
|
|
|
14
14
|
|
|
15
15
|
SVG assets are inlined as CSS `background-image` data URIs via lightningcss CSS Modules — no runtime fetch, no flash of missing image.
|
|
16
16
|
|
|
17
|
+
## Window title shadow
|
|
18
|
+
|
|
19
|
+
The product title "DeepSeek Harness" is baked into the dsh tree at build time (`DSH_CLIENT_TITLE` → both the served `dsh-web-frontend/dist/index.html` and the ui-layout `DocumentTitle` component), and that tree is covered by the desktop-runtime digest — the asar shell-layer swap cannot reach it. The plugin therefore shadows the title at runtime (`src/client/title-shadow.ts`): an own `title` property on the document intercepts every write and rewrites the product name to "AI Shell" in flight, covering both the initial page title and the repeated writes `DocumentTitle` performs on each session/panel change. The Electron main process never calls `setTitle` on the main window, so the window title follows `document.title` and this single seam covers both.
|
|
20
|
+
|
|
21
|
+
Shadowing is a default, not a force: the effect's disposer removes the own property and restores the official title, so disabling the plugin returns the shell to its official behavior.
|
|
22
|
+
|
|
17
23
|
## Install
|
|
18
24
|
|
|
19
25
|
```sh
|
|
@@ -26,8 +32,9 @@ dsh plugin --profile <name> add @huaweidevtools/aishell-dsh-base
|
|
|
26
32
|
|
|
27
33
|
## What it does NOT touch
|
|
28
34
|
|
|
29
|
-
- Navigation, settings, panels, conversation — only the three brand slots.
|
|
35
|
+
- Navigation, settings, panels, conversation — only the three brand slots and the product title.
|
|
30
36
|
- DSH default plugins — no rows disabled.
|
|
37
|
+
- Session titles in the browser title — the part before the product name passes through unchanged.
|
|
31
38
|
|
|
32
39
|
## License
|
|
33
40
|
|
package/lib/client.js
CHANGED
|
@@ -180,7 +180,7 @@ function AishellBrandName() {
|
|
|
180
180
|
"aria-label": "AI Shell",
|
|
181
181
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
182
182
|
className: brand_module_css_default.version,
|
|
183
|
-
children: "0.0.1-beta.
|
|
183
|
+
children: "0.0.1-beta.2"
|
|
184
184
|
})
|
|
185
185
|
});
|
|
186
186
|
}
|
|
@@ -231,6 +231,89 @@ function injectPoweredBy() {
|
|
|
231
231
|
}
|
|
232
232
|
injectPoweredBy();
|
|
233
233
|
|
|
234
|
+
//#endregion
|
|
235
|
+
//#region src/client/title-shadow.ts
|
|
236
|
+
/**
|
|
237
|
+
* Runtime window-title shadowing for the branded distribution.
|
|
238
|
+
*
|
|
239
|
+
* The official product title is baked into the dsh tree at build time
|
|
240
|
+
* (`DSH_CLIENT_TITLE` → "DeepSeek Harness" in both the served
|
|
241
|
+
* dsh-web-frontend dist index.html and the ui-layout DocumentTitle
|
|
242
|
+
* component), and that tree is covered by the desktop-runtime digest —
|
|
243
|
+
* the asar shell-layer swap cannot reach it. The plugin side therefore
|
|
244
|
+
* shadows the title at runtime: this module intercepts every
|
|
245
|
+
* `document.title` write in the renderer and rewrites the product name
|
|
246
|
+
* to "AI Shell" in flight. The Electron main process never calls
|
|
247
|
+
* setTitle on the main window, so the window title follows
|
|
248
|
+
* document.title and this single seam covers both.
|
|
249
|
+
*
|
|
250
|
+
* Shadowing is a default, not a force (plan §2.2): install() and the
|
|
251
|
+
* disposer it returns are symmetric, so disabling the plugin restores
|
|
252
|
+
* the official title behavior exactly.
|
|
253
|
+
*
|
|
254
|
+
* @module @huaweidevtools/aishell-dsh-brand/client
|
|
255
|
+
*/
|
|
256
|
+
/** The official product title, baked into the digest-protected dsh tree. */
|
|
257
|
+
const OFFICIAL_TITLE = "DeepSeek Harness";
|
|
258
|
+
/** The branded product title that shadows it. */
|
|
259
|
+
const BRANDED_TITLE = "AI Shell";
|
|
260
|
+
/**
|
|
261
|
+
* Rewrite the product name inside a title write.
|
|
262
|
+
* Anything else in the value (the session-title prefix ui-layout adds)
|
|
263
|
+
* passes through untouched, and a value without the product name is
|
|
264
|
+
* returned as-is — the shadow never invents content.
|
|
265
|
+
*/
|
|
266
|
+
function rebrandTitle(value) {
|
|
267
|
+
return value.split(OFFICIAL_TITLE).join(BRANDED_TITLE);
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Find the `title` property descriptor on the prototype chain.
|
|
271
|
+
*
|
|
272
|
+
* `document.title` is a WebIDL accessor on Document.prototype — the
|
|
273
|
+
* instance itself has no own property to describe, so the descriptor
|
|
274
|
+
* must be located by walking up the chain.
|
|
275
|
+
*/
|
|
276
|
+
function findTitleDescriptor(target) {
|
|
277
|
+
let proto = target;
|
|
278
|
+
while (proto !== null) {
|
|
279
|
+
const desc = Object.getOwnPropertyDescriptor(proto, "title");
|
|
280
|
+
if (desc !== void 0) return desc;
|
|
281
|
+
proto = Object.getPrototypeOf(proto);
|
|
282
|
+
}
|
|
283
|
+
return void 0;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Install the title shadow on the document.
|
|
287
|
+
*
|
|
288
|
+
* Defines an own `title` property on the instance (shadowing the
|
|
289
|
+
* prototype accessor) whose setter rewrites each write, then rewrites
|
|
290
|
+
* the current title once so the page's initial <title> from the served
|
|
291
|
+
* dist is covered too. Returns the disposer: it removes the own
|
|
292
|
+
* property (the prototype accessor takes over again) and rewrites the
|
|
293
|
+
* live value back to its official form, so an unload leaves the shell
|
|
294
|
+
* exactly as it was found.
|
|
295
|
+
*/
|
|
296
|
+
function installTitleShadow(target) {
|
|
297
|
+
const original = findTitleDescriptor(target);
|
|
298
|
+
if (original === void 0 || original.configurable !== true || typeof original.set !== "function") return () => {};
|
|
299
|
+
const nativeSet = original.set;
|
|
300
|
+
const shadow = {
|
|
301
|
+
configurable: true,
|
|
302
|
+
enumerable: original.enumerable ?? false,
|
|
303
|
+
...original.get !== void 0 ? { get: original.get } : {},
|
|
304
|
+
set(value) {
|
|
305
|
+
nativeSet.call(this, typeof value === "string" ? rebrandTitle(value) : value);
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
Object.defineProperty(target, "title", shadow);
|
|
309
|
+
const initial = target.title;
|
|
310
|
+
target.title = initial;
|
|
311
|
+
return () => {
|
|
312
|
+
delete target.title;
|
|
313
|
+
target.title = target.title.split(BRANDED_TITLE).join(OFFICIAL_TITLE);
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
|
|
234
317
|
//#endregion
|
|
235
318
|
//#region src/client/index.ts
|
|
236
319
|
/** Required service: the UI slot registry. */
|
|
@@ -248,6 +331,7 @@ function apply(ctx) {
|
|
|
248
331
|
name: "conversation.hero.brand.mark",
|
|
249
332
|
priority: -1
|
|
250
333
|
}, AishellHeroBrandMark));
|
|
334
|
+
if (typeof document !== "undefined") ctx.effect(() => installTitleShadow(document), "aishell-brand: window title shadow");
|
|
251
335
|
}
|
|
252
336
|
|
|
253
337
|
//#endregion
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime window-title shadowing for the branded distribution.
|
|
3
|
+
*
|
|
4
|
+
* The official product title is baked into the dsh tree at build time
|
|
5
|
+
* (`DSH_CLIENT_TITLE` → "DeepSeek Harness" in both the served
|
|
6
|
+
* dsh-web-frontend dist index.html and the ui-layout DocumentTitle
|
|
7
|
+
* component), and that tree is covered by the desktop-runtime digest —
|
|
8
|
+
* the asar shell-layer swap cannot reach it. The plugin side therefore
|
|
9
|
+
* shadows the title at runtime: this module intercepts every
|
|
10
|
+
* `document.title` write in the renderer and rewrites the product name
|
|
11
|
+
* to "AI Shell" in flight. The Electron main process never calls
|
|
12
|
+
* setTitle on the main window, so the window title follows
|
|
13
|
+
* document.title and this single seam covers both.
|
|
14
|
+
*
|
|
15
|
+
* Shadowing is a default, not a force (plan §2.2): install() and the
|
|
16
|
+
* disposer it returns are symmetric, so disabling the plugin restores
|
|
17
|
+
* the official title behavior exactly.
|
|
18
|
+
*
|
|
19
|
+
* @module @huaweidevtools/aishell-dsh-brand/client
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* Rewrite the product name inside a title write.
|
|
23
|
+
* Anything else in the value (the session-title prefix ui-layout adds)
|
|
24
|
+
* passes through untouched, and a value without the product name is
|
|
25
|
+
* returned as-is — the shadow never invents content.
|
|
26
|
+
*/
|
|
27
|
+
export declare function rebrandTitle(value: string): string;
|
|
28
|
+
/** The disposer restores the official title behavior; a no-op install returns this. */
|
|
29
|
+
export type TitleShadowDisposer = () => void;
|
|
30
|
+
/**
|
|
31
|
+
* Install the title shadow on the document.
|
|
32
|
+
*
|
|
33
|
+
* Defines an own `title` property on the instance (shadowing the
|
|
34
|
+
* prototype accessor) whose setter rewrites each write, then rewrites
|
|
35
|
+
* the current title once so the page's initial <title> from the served
|
|
36
|
+
* dist is covered too. Returns the disposer: it removes the own
|
|
37
|
+
* property (the prototype accessor takes over again) and rewrites the
|
|
38
|
+
* live value back to its official form, so an unload leaves the shell
|
|
39
|
+
* exactly as it was found.
|
|
40
|
+
*/
|
|
41
|
+
export declare function installTitleShadow(target: Document): TitleShadowDisposer;
|
|
42
|
+
//# sourceMappingURL=title-shadow.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@huaweidevtools/aishell-dsh-brand",
|
|
3
3
|
"description": "AI Shell brand reskin for DSH web — replaces sidebar and hero brand marks with AI Shell SVG",
|
|
4
|
-
"version": "0.0.1-beta.
|
|
4
|
+
"version": "0.0.1-beta.2",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -44,10 +44,10 @@
|
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@deepseek-ai/cordis": "^4.0.4",
|
|
47
|
-
"@deepseek-ai/dsh-client-ui-conversation": "0.2.0-
|
|
48
|
-
"@deepseek-ai/dsh-client-ui-renderer": "0.2.0-
|
|
49
|
-
"@deepseek-ai/dsh-client-ui-sidebar": "0.2.0-
|
|
50
|
-
"@deepseek-ai/dsh-client-ui-slots": "0.2.0-
|
|
47
|
+
"@deepseek-ai/dsh-client-ui-conversation": ">=0.2.0-0 <0.3.0-0",
|
|
48
|
+
"@deepseek-ai/dsh-client-ui-renderer": ">=0.2.0-0 <0.3.0-0",
|
|
49
|
+
"@deepseek-ai/dsh-client-ui-sidebar": ">=0.2.0-0 <0.3.0-0",
|
|
50
|
+
"@deepseek-ai/dsh-client-ui-slots": ">=0.2.0-0 <0.3.0-0",
|
|
51
51
|
"@types/react": "~18.3.1",
|
|
52
52
|
"react": "^18.2.0",
|
|
53
53
|
"tsdown": "^0.10.0",
|