@huaweidevtools/aishell-dsh-brand 0.0.1 → 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
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, and shadows the browser/window product title at runtime.
|
|
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, tab favicon, and running-status whale + copy at runtime.
|
|
4
4
|
|
|
5
5
|
## How it works
|
|
6
6
|
|
|
@@ -20,6 +20,19 @@ The product title "DeepSeek Harness" is baked into the dsh tree at build time (`
|
|
|
20
20
|
|
|
21
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
22
|
|
|
23
|
+
## Favicon and manifest shadow
|
|
24
|
+
|
|
25
|
+
The browser tab icon comes from the dist's `favicon.svg` / `favicon-dark.svg` (a DeepSeek whale, one fixed fill per theme, selected by `media="(prefers-color-scheme: …)"` links) — digest-protected like the title. `src/client/favicon-shadow.ts` repoints every `link[rel~="icon"]` at the AI Shell mark data URI (`src/client/aishell-mark.ts`, pinned byte-for-byte against `assets/aishell.svg`), dropping the media qualifier because the mark's gradient reads on both themes. A MutationObserver re-shadows links re-inserted into the head. The web manifest's display names ("DeepSeek Harness" / "DSH") are rebranded to "AI Shell" through a Blob URL when the manifest is reachable; a manifest that cannot be fetched or parsed is left exactly as found, so the install prompt never breaks over a display name. The disposer restores every touched link and revokes the Blob URL.
|
|
26
|
+
|
|
27
|
+
## Running-status shadows
|
|
28
|
+
|
|
29
|
+
The thinking line under an active turn ("深度求索中,用时 X ···" / "Deep diving for X ···") carries two DeepSeek residues, both in ui-chat with no slot to occupy, so both are shadowed at runtime:
|
|
30
|
+
|
|
31
|
+
- **The whale tail icon** (`src/client/running-mark-shadow.ts`) — the same CSS-patch approach the hero headline uses, anchored on the row's stable literal attribute `[data-chat-running]`. The patch hides the animated APNG mask and the static SVG fallback and repaints the 14px `runningIcon` box with the AI Shell mark plus a gentle bob; under `prefers-reduced-motion` the mark is static, matching the official whale's own degradation.
|
|
32
|
+
- **The copy** (`src/client/running-text-shadow.ts`) — `locale.register` throws on the claimed `chat` namespace, so the rendered text is rewritten instead. One substring replacement per language covers both keys ("深度求索中" → "思考中", "Deep diving" → "Thinking"; the duration forms share the prefix). The line re-renders every second, so a MutationObserver (characterData + childList + the `data-shimmer-text` attribute — TextShimmer paints its highlight through `content: attr()`, so a stale attribute would flash the official copy on every sweep) keeps it covered for as long as the row exists. The row's aria status span sits in the same subtree and is covered by the same sweep.
|
|
33
|
+
|
|
34
|
+
Both disposers restore the official whale and copy exactly.
|
|
35
|
+
|
|
23
36
|
## Install
|
|
24
37
|
|
|
25
38
|
```sh
|
|
@@ -32,9 +45,10 @@ dsh plugin --profile <name> add @huaweidevtools/aishell-dsh-base
|
|
|
32
45
|
|
|
33
46
|
## What it does NOT touch
|
|
34
47
|
|
|
35
|
-
- Navigation, settings, panels, conversation — only the three brand slots and the
|
|
48
|
+
- Navigation, settings, panels, conversation — only the three brand slots, the product title, the tab icon/manifest names, and the running-status icon/copy.
|
|
36
49
|
- DSH default plugins — no rows disabled.
|
|
37
50
|
- Session titles in the browser title — the part before the product name passes through unchanged.
|
|
51
|
+
- Duration units and the "···" tail in the running line — the harness's own words, not product branding.
|
|
38
52
|
|
|
39
53
|
## License
|
|
40
54
|
|
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.
|
|
183
|
+
children: "0.0.3"
|
|
184
184
|
})
|
|
185
185
|
});
|
|
186
186
|
}
|
|
@@ -314,6 +314,341 @@ function installTitleShadow(target) {
|
|
|
314
314
|
};
|
|
315
315
|
}
|
|
316
316
|
|
|
317
|
+
//#endregion
|
|
318
|
+
//#region src/client/aishell-mark.ts
|
|
319
|
+
/**
|
|
320
|
+
* The AI Shell mark as an inline source, shared by the runtime brand shadows.
|
|
321
|
+
*
|
|
322
|
+
* The slot occupants (Brand.tsx) draw the mark through a CSS module whose
|
|
323
|
+
* url() the build inlines, but the runtime shadows need the bytes as a JS
|
|
324
|
+
* string: the favicon shadow writes them into a link href, and the running
|
|
325
|
+
* status mark shadow writes them into injected patch CSS. This module is
|
|
326
|
+
* that one source, so the asset and the shadows cannot drift apart.
|
|
327
|
+
* tests/aishell-mark.spec.ts pins the constant against assets/aishell.svg.
|
|
328
|
+
*
|
|
329
|
+
* @module @huaweidevtools/aishell-dsh-brand/client/aishell-mark
|
|
330
|
+
*/
|
|
331
|
+
/** The AI Shell mark, verbatim assets/aishell.svg (the spec pins this). */
|
|
332
|
+
const AISHELL_MARK_SVG = `<svg viewBox="0 0 44 44" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="44.000000" height="44.000000" fill="none">
|
|
333
|
+
<defs>
|
|
334
|
+
<linearGradient id="paint_linear_0" x1="38.5" x2="2.19999695" y1="1.90734863e-06" y2="39.5999908" gradientUnits="userSpaceOnUse">
|
|
335
|
+
<stop stop-color="rgb(53,137,255)" offset="0" stop-opacity="1" />
|
|
336
|
+
<stop stop-color="rgb(169,38,255)" offset="1" stop-opacity="1" />
|
|
337
|
+
</linearGradient>
|
|
338
|
+
</defs>
|
|
339
|
+
<rect id="icon_AIShell" width="44.000000" height="44.000000" x="0.000000" y="0.000000" fill="rgb(255,255,255)" fill-opacity="0" />
|
|
340
|
+
<path id="合并" d="M37.4576 0.299738C37.3775 0.139877 37.2861 0.0428196 37.1832 0.00856393C37.0917 -0.0142732 37.006 0.00856394 36.9259 0.0770754C36.8459 0.145587 36.7887 0.219808 36.7545 0.299738C36.4572 1.86408 35.7369 3.21718 34.5937 4.35904C33.4618 5.48948 32.0956 6.22027 30.495 6.55141C30.3921 6.57424 30.3178 6.64275 30.272 6.75694C30.2377 6.87113 30.2435 6.97389 30.2892 7.06524C30.3464 7.15659 30.415 7.20226 30.495 7.20226C31.2078 7.35296 31.8779 7.58759 32.5055 7.90618L32.5055 7.9062L32.5056 7.90623C33.257 8.28771 33.9473 8.78955 34.5765 9.41176C35.7312 10.5422 36.4572 11.8896 36.7545 13.4539C36.8116 13.591 36.8974 13.6823 37.0117 13.728C37.1146 13.7622 37.2061 13.7565 37.2861 13.7109C37.3775 13.6538 37.4347 13.5681 37.4576 13.4539C37.7891 11.8896 38.5151 10.5422 39.6355 9.41176C40.7674 8.2699 42.1279 7.5334 43.717 7.20226C43.8199 7.17943 43.9 7.12233 43.9571 7.03099C44.0143 6.92822 44.0143 6.83116 43.9571 6.73981C43.9 6.63705 43.8199 6.57424 43.717 6.55141C42.1507 6.22027 40.7959 5.48948 39.6527 4.35904C38.5208 3.21718 37.7891 1.86408 37.4576 0.299738ZM42.625 16.4999L42.625 33C42.625 33.9493 42.4572 34.829 42.1216 35.6392C41.786 36.4495 41.2826 37.1902 40.6114 37.8614C39.9402 38.5326 39.1994 39.036 38.3892 39.3716C37.579 39.7072 36.6992 39.875 35.75 39.875L10.9999 39.875L8.24995 39.875C7.30073 39.875 6.42102 39.7072 5.61081 39.3716C4.80058 39.036 4.05985 38.5326 3.38863 37.8614C2.71742 37.1902 2.21402 36.4494 1.87841 35.6392C1.5428 34.829 1.375 33.9493 1.375 33L1.375 11C1.375 10.0508 1.5428 9.17106 1.8784 8.36083L1.87841 8.36082C2.21402 7.55059 2.71743 6.80987 3.38864 6.13866C4.05985 5.46745 4.80057 4.96404 5.61079 4.62843L5.6108 4.62843C6.42103 4.29282 7.30076 4.12502 8.25 4.12502L27.4999 4.125C28.2699 4.125 28.8749 4.73 28.8749 5.5C28.8749 6.27 28.2699 6.875 27.4999 6.875L8.25 6.87502C7.68045 6.87502 7.15261 6.9757 6.66648 7.17706C6.18034 7.37843 5.73591 7.68047 5.33318 8.0832C4.93046 8.48593 4.62841 8.93036 4.42705 9.41649C4.22568 9.90263 4.125 10.4305 4.125 11L4.125 33C4.125 33.5696 4.22568 34.0974 4.42705 34.5835C4.62841 35.0697 4.93045 35.5141 5.33317 35.9168C5.73588 36.3195 6.18028 36.6216 6.66639 36.8229C7.15256 37.0243 7.68042 37.125 8.24999 37.125L10.9999 37.125L35.75 37.125C36.3195 37.125 36.8474 37.0243 37.3335 36.823C37.8197 36.6216 38.2641 36.3196 38.6668 35.9168C39.0695 35.5141 39.3716 35.0697 39.573 34.5835C39.7743 34.0974 39.875 33.5696 39.875 33L39.875 16.4999C39.875 15.7299 40.48 15.1249 41.25 15.1249C42.02 15.1249 42.625 15.7299 42.625 16.4999ZM10.0277 12.7777C10.5722 12.2333 11.4278 12.2333 11.9723 12.7777L20.2222 21.0277C20.4907 21.2962 20.6249 21.6203 20.6249 22C20.6249 22.3797 20.4907 22.7037 20.2222 22.9722L11.9722 31.2223C11.4277 31.7668 10.5721 31.7668 10.0277 31.2223C9.48318 30.6779 9.48317 29.8223 10.0276 29.2778L17.3054 22L10.0277 14.7223C9.48325 14.1778 9.48326 13.3222 10.0277 12.7777ZM20.625 28.875C20.625 28.105 21.23 27.5 22 27.5L33 27.5C33.77 27.5 34.375 28.105 34.375 28.875C34.375 29.645 33.77 30.25 33 30.25L22 30.25C21.23 30.25 20.625 29.645 20.625 28.875Z" fill="rgb(0,0,0)" fill-opacity="0" fill-rule="evenodd" />
|
|
341
|
+
<path id="合并" d="M37.4576 0.299738C37.3775 0.139877 37.2861 0.0428196 37.1832 0.00856393C37.0917 -0.0142732 37.006 0.00856394 36.9259 0.0770754C36.8459 0.145587 36.7887 0.219808 36.7545 0.299738C36.4572 1.86408 35.7369 3.21718 34.5937 4.35904C33.4618 5.48948 32.0956 6.22027 30.495 6.55141C30.3921 6.57424 30.3178 6.64275 30.272 6.75694C30.2377 6.87113 30.2435 6.97389 30.2892 7.06524C30.3464 7.15659 30.415 7.20226 30.495 7.20226C31.2078 7.35296 31.8779 7.58759 32.5055 7.90618L32.5055 7.9062L32.5056 7.90623C33.257 8.28771 33.9473 8.78955 34.5765 9.41176C35.7312 10.5422 36.4572 11.8896 36.7545 13.4539C36.8116 13.591 36.8974 13.6823 37.0117 13.728C37.1146 13.7622 37.2061 13.7565 37.2861 13.7109C37.3775 13.6538 37.4347 13.5681 37.4576 13.4539C37.7891 11.8896 38.5151 10.5422 39.6355 9.41176C40.7674 8.2699 42.1279 7.5334 43.717 7.20226C43.8199 7.17943 43.9 7.12233 43.9571 7.03099C44.0143 6.92822 44.0143 6.83116 43.9571 6.73981C43.9 6.63705 43.8199 6.57424 43.717 6.55141C42.1507 6.22027 40.7959 5.48948 39.6527 4.35904C38.5208 3.21718 37.7891 1.86408 37.4576 0.299738ZM42.625 16.4999L42.625 33C42.625 33.9493 42.4572 34.829 42.1216 35.6392C41.786 36.4495 41.2826 37.1902 40.6114 37.8614C39.9402 38.5326 39.1994 39.036 38.3892 39.3716C37.579 39.7072 36.6992 39.875 35.75 39.875L10.9999 39.875L8.24995 39.875C7.30073 39.875 6.42102 39.7072 5.61081 39.3716C4.80058 39.036 4.05985 38.5326 3.38863 37.8614C2.71742 37.1902 2.21402 36.4494 1.87841 35.6392C1.5428 34.829 1.375 33.9493 1.375 33L1.375 11C1.375 10.0508 1.5428 9.17106 1.8784 8.36083L1.87841 8.36082C2.21402 7.55059 2.71743 6.80987 3.38864 6.13866C4.05985 5.46745 4.80057 4.96404 5.61079 4.62843L5.6108 4.62843C6.42103 4.29282 7.30076 4.12502 8.25 4.12502L27.4999 4.125C28.2699 4.125 28.8749 4.73 28.8749 5.5C28.8749 6.27 28.2699 6.875 27.4999 6.875L8.25 6.87502C7.68045 6.87502 7.15261 6.9757 6.66648 7.17706C6.18034 7.37843 5.73591 7.68047 5.33318 8.0832C4.93046 8.48593 4.62841 8.93036 4.42705 9.41649C4.22568 9.90263 4.125 10.4305 4.125 11L4.125 33C4.125 33.5696 4.22568 34.0974 4.42705 34.5835C4.62841 35.0697 4.93045 35.5141 5.33317 35.9168C5.73588 36.3195 6.18028 36.6216 6.66639 36.8229C7.15256 37.0243 7.68042 37.125 8.24999 37.125L10.9999 37.125L35.75 37.125C36.3195 37.125 36.8474 37.0243 37.3335 36.823C37.8197 36.6216 38.2641 36.3196 38.6668 35.9168C39.0695 35.5141 39.3716 35.0697 39.573 34.5835C39.7743 34.0974 39.875 33.5696 39.875 33L39.875 16.4999C39.875 15.7299 40.48 15.1249 41.25 15.1249C42.02 15.1249 42.625 15.7299 42.625 16.4999ZM10.0277 12.7777C10.5722 12.2333 11.4278 12.2333 11.9723 12.7777L20.2222 21.0277C20.4907 21.2962 20.6249 21.6203 20.6249 22C20.6249 22.3797 20.4907 22.7037 20.2222 22.9722L11.9722 31.2223C11.4277 31.7668 10.5721 31.7668 10.0277 31.2223C9.48318 30.6779 9.48317 29.8223 10.0276 29.2778L17.3054 22L10.0277 14.7223C9.48325 14.1778 9.48326 13.3222 10.0277 12.7777ZM20.625 28.875C20.625 28.105 21.23 27.5 22 27.5L33 27.5C33.77 27.5 34.375 28.105 34.375 28.875C34.375 29.645 33.77 30.25 33 30.25L22 30.25C21.23 30.25 20.625 29.645 20.625 28.875Z" fill="url(#paint_linear_0)" fill-rule="evenodd" />
|
|
342
|
+
</svg>`;
|
|
343
|
+
/**
|
|
344
|
+
* The mark as a self-contained data URI.
|
|
345
|
+
*
|
|
346
|
+
* URL-encoded rather than base64 (shorter, and the gradient reads on both
|
|
347
|
+
* light and dark themes, which is what lets one URI serve every surface).
|
|
348
|
+
*/
|
|
349
|
+
const AISHELL_MARK_URI = `data:image/svg+xml,${encodeURIComponent(AISHELL_MARK_SVG)}`;
|
|
350
|
+
|
|
351
|
+
//#endregion
|
|
352
|
+
//#region src/client/favicon-shadow.ts
|
|
353
|
+
/** The official product names the manifest shadow rewrites. */
|
|
354
|
+
const OFFICIAL_MANIFEST_NAMES = {
|
|
355
|
+
name: "DeepSeek Harness",
|
|
356
|
+
short_name: "DSH"
|
|
357
|
+
};
|
|
358
|
+
/** The branded names that shadow them. */
|
|
359
|
+
const BRANDED_MANIFEST_NAMES = {
|
|
360
|
+
name: "AI Shell",
|
|
361
|
+
short_name: "AI Shell"
|
|
362
|
+
};
|
|
363
|
+
/**
|
|
364
|
+
* Rewrite a manifest document's display names in place.
|
|
365
|
+
*
|
|
366
|
+
* Only the two fields the dist actually ships are touched, and only when
|
|
367
|
+
* they carry their official values — anything else in the document (icons,
|
|
368
|
+
* start_url, scope, display) passes through untouched, and a manifest the
|
|
369
|
+
* base customized keeps its customization.
|
|
370
|
+
*/
|
|
371
|
+
function rebrandManifest(manifest) {
|
|
372
|
+
const branded = { ...manifest };
|
|
373
|
+
for (const key of Object.keys(OFFICIAL_MANIFEST_NAMES)) if (branded[key] === OFFICIAL_MANIFEST_NAMES[key]) branded[key] = BRANDED_MANIFEST_NAMES[key];
|
|
374
|
+
return branded;
|
|
375
|
+
}
|
|
376
|
+
/** The href every icon link is shadowed with (exposed for the spec). */
|
|
377
|
+
const BRANDED_ICON_HREF = AISHELL_MARK_URI;
|
|
378
|
+
/**
|
|
379
|
+
* The rel token list of a link node, or undefined when the node is not a
|
|
380
|
+
* link (comment nodes reach the same iterator).
|
|
381
|
+
*
|
|
382
|
+
* Duck-typed on the attribute rather than `instanceof HTMLLinkElement` so
|
|
383
|
+
* the shadow works identically in the browser and under the specs' DOM
|
|
384
|
+
* stub, which provides the attribute surface without the constructor.
|
|
385
|
+
*/
|
|
386
|
+
function linkRels(node) {
|
|
387
|
+
if (node.nodeType !== Node.ELEMENT_NODE) return void 0;
|
|
388
|
+
const rel = node.getAttribute("rel");
|
|
389
|
+
if (rel === null) return void 0;
|
|
390
|
+
return rel.split(/\s+/);
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Install the favicon shadow on a document.
|
|
394
|
+
*
|
|
395
|
+
* Rewrites every current icon link's href to the AI Shell mark (dropping
|
|
396
|
+
* the media attribute so the one gradient serves both themes), shadows the
|
|
397
|
+
* manifest names when the manifest is reachable, and observes the head so
|
|
398
|
+
* re-inserted links are covered too. Returns the disposer: every touched
|
|
399
|
+
* link is restored to its original href and media, the manifest link to
|
|
400
|
+
* its original href, and the Blob URL is revoked, so an unload leaves the
|
|
401
|
+
* document exactly as it was found.
|
|
402
|
+
*/
|
|
403
|
+
function installFaviconShadow(target) {
|
|
404
|
+
if (typeof target.querySelector !== "function") return () => {};
|
|
405
|
+
const iconRestores = [];
|
|
406
|
+
let manifestLink;
|
|
407
|
+
let manifestHref;
|
|
408
|
+
let blobUrl;
|
|
409
|
+
const shadowIcon = (link) => {
|
|
410
|
+
if (iconRestores.some((restore) => restore.link === link)) return;
|
|
411
|
+
iconRestores.push({
|
|
412
|
+
link,
|
|
413
|
+
href: link.getAttribute("href") ?? "",
|
|
414
|
+
media: link.getAttribute("media") ?? ""
|
|
415
|
+
});
|
|
416
|
+
link.setAttribute("href", BRANDED_ICON_HREF);
|
|
417
|
+
link.removeAttribute("media");
|
|
418
|
+
};
|
|
419
|
+
const shadowManifest = (link) => {
|
|
420
|
+
if (manifestLink !== void 0) return;
|
|
421
|
+
const href = link.getAttribute("href");
|
|
422
|
+
if (href === null) return;
|
|
423
|
+
manifestLink = link;
|
|
424
|
+
const url = new URL(href, target.baseURI);
|
|
425
|
+
fetch(url).then((response) => response.ok ? response.text() : Promise.reject(new Error(`HTTP ${response.status}`))).then((text) => {
|
|
426
|
+
if (manifestLink !== link) return;
|
|
427
|
+
const parsed = JSON.parse(text);
|
|
428
|
+
if (typeof parsed !== "object" || parsed === null) throw new Error("manifest is not an object");
|
|
429
|
+
const branded = rebrandManifest(parsed);
|
|
430
|
+
const blob = new Blob([JSON.stringify(branded)], { type: "application/manifest+json" });
|
|
431
|
+
blobUrl = URL.createObjectURL(blob);
|
|
432
|
+
manifestHref = href;
|
|
433
|
+
link.setAttribute("href", blobUrl);
|
|
434
|
+
}).catch(() => {
|
|
435
|
+
if (manifestLink === link) manifestLink = void 0;
|
|
436
|
+
});
|
|
437
|
+
};
|
|
438
|
+
const sweep$1 = (root) => {
|
|
439
|
+
for (const link of root.querySelectorAll("link")) {
|
|
440
|
+
const rels = linkRels(link);
|
|
441
|
+
if (rels === void 0) continue;
|
|
442
|
+
if (rels.includes("icon")) shadowIcon(link);
|
|
443
|
+
else if (rels.includes("manifest")) shadowManifest(link);
|
|
444
|
+
}
|
|
445
|
+
};
|
|
446
|
+
sweep$1(target.head);
|
|
447
|
+
const observer = new MutationObserver((mutations) => {
|
|
448
|
+
for (const mutation of mutations) for (const node of mutation.addedNodes) {
|
|
449
|
+
const rels = linkRels(node);
|
|
450
|
+
if (rels === void 0) continue;
|
|
451
|
+
const element = node;
|
|
452
|
+
if (rels.includes("icon")) shadowIcon(element);
|
|
453
|
+
else if (rels.includes("manifest")) shadowManifest(element);
|
|
454
|
+
}
|
|
455
|
+
});
|
|
456
|
+
observer.observe(target.head, { childList: true });
|
|
457
|
+
return () => {
|
|
458
|
+
observer.disconnect();
|
|
459
|
+
for (const { link, href, media } of iconRestores) {
|
|
460
|
+
link.setAttribute("href", href);
|
|
461
|
+
if (media !== "") link.setAttribute("media", media);
|
|
462
|
+
else link.removeAttribute("media");
|
|
463
|
+
}
|
|
464
|
+
if (manifestLink !== void 0 && manifestHref !== void 0) manifestLink.setAttribute("href", manifestHref);
|
|
465
|
+
manifestLink = void 0;
|
|
466
|
+
if (blobUrl !== void 0) URL.revokeObjectURL(blobUrl);
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
//#endregion
|
|
471
|
+
//#region src/client/running-mark-shadow.ts
|
|
472
|
+
/** Marker attribute the patch style element carries (exposed for the spec). */
|
|
473
|
+
const RUNNING_PATCH_MARKER = "data-aishell-running-mark";
|
|
474
|
+
/**
|
|
475
|
+
* The patch CSS. The whale layers are hidden unconditionally — the static
|
|
476
|
+
* fallback too, because the mark replaces both — while the mark's own
|
|
477
|
+
* animation is guarded so reduced motion keeps it still.
|
|
478
|
+
*/
|
|
479
|
+
const RUNNING_MARK_PATCH_CSS = [
|
|
480
|
+
"[data-chat-running] [class*=\"runningWhaleAnimated\"],",
|
|
481
|
+
"[data-chat-running] [class*=\"runningWhaleStill\"] {",
|
|
482
|
+
" display: none;",
|
|
483
|
+
"}",
|
|
484
|
+
"[data-chat-running] [class*=\"runningIcon\"] {",
|
|
485
|
+
` background: url("${AISHELL_MARK_URI}") center / contain no-repeat;`,
|
|
486
|
+
"}",
|
|
487
|
+
"@keyframes aishell-running-bob {",
|
|
488
|
+
" 0%, 100% { transform: none; opacity: 1; }",
|
|
489
|
+
" 50% { transform: translateY(-0.7px); opacity: 0.82; }",
|
|
490
|
+
"}",
|
|
491
|
+
"@media (prefers-reduced-motion: no-preference) {",
|
|
492
|
+
" [data-chat-running] [class*=\"runningIcon\"] {",
|
|
493
|
+
" animation: aishell-running-bob 1.2s ease-in-out infinite;",
|
|
494
|
+
" }",
|
|
495
|
+
"}"
|
|
496
|
+
].join("\n");
|
|
497
|
+
/**
|
|
498
|
+
* Install the running-mark shadow on a document.
|
|
499
|
+
*
|
|
500
|
+
* Injects the patch CSS once (guarded by the marker attribute so a second
|
|
501
|
+
* install is a no-op) and returns the disposer that removes the element.
|
|
502
|
+
*/
|
|
503
|
+
function installRunningMarkShadow(target) {
|
|
504
|
+
if (typeof target.querySelector !== "function") return () => {};
|
|
505
|
+
const existing = target.querySelector(`style[${RUNNING_PATCH_MARKER}]`);
|
|
506
|
+
if (existing !== null) return () => {
|
|
507
|
+
existing.remove();
|
|
508
|
+
};
|
|
509
|
+
const tag = target.createElement("style");
|
|
510
|
+
tag.setAttribute(RUNNING_PATCH_MARKER, "");
|
|
511
|
+
tag.textContent = RUNNING_MARK_PATCH_CSS;
|
|
512
|
+
target.head.appendChild(tag);
|
|
513
|
+
return () => {
|
|
514
|
+
tag.remove();
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
//#endregion
|
|
519
|
+
//#region src/client/running-text-shadow.ts
|
|
520
|
+
/** Official zh phrase, the prefix of both the bare and the duration forms. */
|
|
521
|
+
const OFFICIAL_ZH = "深度求索中";
|
|
522
|
+
/** Branded zh phrase (final copy: 思考中 / 思考中,用时 {duration} ···). */
|
|
523
|
+
const BRANDED_ZH = "思考中";
|
|
524
|
+
/** Official en phrase, the prefix of both the bare and the duration forms. */
|
|
525
|
+
const OFFICIAL_EN = "Deep diving";
|
|
526
|
+
/** Branded en phrase (final copy: Thinking / Thinking for {duration} ···). */
|
|
527
|
+
const BRANDED_EN = "Thinking";
|
|
528
|
+
/** The replacements, in order. */
|
|
529
|
+
const REPLACEMENTS = [[OFFICIAL_ZH, BRANDED_ZH], [OFFICIAL_EN, BRANDED_EN]];
|
|
530
|
+
/**
|
|
531
|
+
* Rewrite the running-line copy inside a value.
|
|
532
|
+
*
|
|
533
|
+
* Every occurrence of either official phrase is replaced; a value without
|
|
534
|
+
* them is returned as-is — the shadow never invents content.
|
|
535
|
+
*/
|
|
536
|
+
function rebrandRunningText(value) {
|
|
537
|
+
let branded = value;
|
|
538
|
+
for (const [official, replacement] of REPLACEMENTS) branded = branded.split(official).join(replacement);
|
|
539
|
+
return branded;
|
|
540
|
+
}
|
|
541
|
+
/** Whether the node carries the running row's stable anchor attribute.
|
|
542
|
+
*
|
|
543
|
+
* Duck-typed on the attribute surface rather than `instanceof Element` so
|
|
544
|
+
* the shadow works identically in the browser and under the specs' DOM
|
|
545
|
+
* stub, which provides the attribute methods without the constructor. */
|
|
546
|
+
function isRunningRow(node) {
|
|
547
|
+
return node.nodeType === Node.ELEMENT_NODE && node.hasAttribute("data-chat-running");
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* Rewrite one text node and the shimmer attribute of its parent.
|
|
551
|
+
*
|
|
552
|
+
* The attribute matters as much as the node: TextShimmer's decorative
|
|
553
|
+
* layer draws `content: attr(data-shimmer-text)` rather than the node's
|
|
554
|
+
* own text, so leaving it stale resurfaces the official copy every time
|
|
555
|
+
* the highlight sweeps.
|
|
556
|
+
*/
|
|
557
|
+
function rewriteTextNode(node) {
|
|
558
|
+
const branded = rebrandRunningText(node.data);
|
|
559
|
+
if (branded === node.data) return;
|
|
560
|
+
node.data = branded;
|
|
561
|
+
const host = node.parentElement;
|
|
562
|
+
if (host !== null && host.hasAttribute("data-shimmer-text")) host.setAttribute("data-shimmer-text", rebrandRunningText(host.getAttribute("data-shimmer-text") ?? ""));
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Sweep a subtree for official copy, rewriting what it finds.
|
|
566
|
+
*
|
|
567
|
+
* The attribute-only pass matters too: TextShimmer sets the attribute to
|
|
568
|
+
* the generated string while the text node itself renders null, so some
|
|
569
|
+
* re-renders change only the attribute.
|
|
570
|
+
*/
|
|
571
|
+
function sweep(target, root) {
|
|
572
|
+
const walker = target.createTreeWalker(root, NodeFilter.SHOW_TEXT);
|
|
573
|
+
for (let node = walker.nextNode(); node !== null; node = walker.nextNode()) rewriteTextNode(node);
|
|
574
|
+
for (const host of root.querySelectorAll("[data-shimmer-text]")) {
|
|
575
|
+
const value = host.getAttribute("data-shimmer-text") ?? "";
|
|
576
|
+
const branded = rebrandRunningText(value);
|
|
577
|
+
if (branded !== value) host.setAttribute("data-shimmer-text", branded);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* Install the running-text shadow on a document.
|
|
582
|
+
*
|
|
583
|
+
* Sweeps every mounted [data-chat-running] row once, then observes the
|
|
584
|
+
* document body for new rows (childList) and their text/attribute
|
|
585
|
+
* changes (characterData, data-shimmer-text) so the per-second re-renders
|
|
586
|
+
* stay covered for as long as the row exists. Returns the disposer: the
|
|
587
|
+
* observer stops and every live row is restored to its official copy.
|
|
588
|
+
*/
|
|
589
|
+
function installRunningTextShadow(target) {
|
|
590
|
+
if (typeof target.querySelector !== "function" || typeof MutationObserver !== "function") return () => {};
|
|
591
|
+
const rewriteRow = (row) => {
|
|
592
|
+
sweep(target, row);
|
|
593
|
+
};
|
|
594
|
+
for (const row of target.querySelectorAll("[data-chat-running]")) rewriteRow(row);
|
|
595
|
+
const observer = new MutationObserver((mutations) => {
|
|
596
|
+
for (const mutation of mutations) {
|
|
597
|
+
if (mutation.type === "characterData") {
|
|
598
|
+
const node = mutation.target;
|
|
599
|
+
if (node.parentElement !== null && node.parentElement.closest("[data-chat-running]") !== null) rewriteTextNode(node);
|
|
600
|
+
continue;
|
|
601
|
+
}
|
|
602
|
+
if (mutation.type === "attributes") {
|
|
603
|
+
const host = mutation.target;
|
|
604
|
+
if (host.nodeType === Node.ELEMENT_NODE) {
|
|
605
|
+
const element = host;
|
|
606
|
+
if (element.closest("[data-chat-running]") !== null) {
|
|
607
|
+
const value = element.getAttribute("data-shimmer-text") ?? "";
|
|
608
|
+
const branded = rebrandRunningText(value);
|
|
609
|
+
if (branded !== value) element.setAttribute("data-shimmer-text", branded);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
continue;
|
|
613
|
+
}
|
|
614
|
+
for (const node of mutation.addedNodes) if (node.nodeType === Node.ELEMENT_NODE) {
|
|
615
|
+
if (isRunningRow(node)) rewriteRow(node);
|
|
616
|
+
else if (node.parentElement !== null && node.parentElement.closest("[data-chat-running]") !== null) sweep(target, node.parentElement);
|
|
617
|
+
} else if (node.nodeType === Node.TEXT_NODE && node.parentElement !== null) {
|
|
618
|
+
if (node.parentElement.closest("[data-chat-running]") !== null) rewriteTextNode(node);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
});
|
|
622
|
+
observer.observe(target.body, {
|
|
623
|
+
childList: true,
|
|
624
|
+
subtree: true,
|
|
625
|
+
characterData: true,
|
|
626
|
+
attributeFilter: ["data-shimmer-text"]
|
|
627
|
+
});
|
|
628
|
+
return () => {
|
|
629
|
+
observer.disconnect();
|
|
630
|
+
for (const row of target.querySelectorAll("[data-chat-running]")) {
|
|
631
|
+
const restore = (host) => {
|
|
632
|
+
if (host.hasAttribute("data-shimmer-text")) host.setAttribute("data-shimmer-text", unbrand(host.getAttribute("data-shimmer-text") ?? ""));
|
|
633
|
+
};
|
|
634
|
+
restore(row);
|
|
635
|
+
for (const host of row.querySelectorAll("[data-shimmer-text]")) restore(host);
|
|
636
|
+
const walker = target.createTreeWalker(row, NodeFilter.SHOW_TEXT);
|
|
637
|
+
for (let node = walker.nextNode(); node !== null; node = walker.nextNode()) {
|
|
638
|
+
const text = node;
|
|
639
|
+
const restored = unbrand(text.data);
|
|
640
|
+
if (restored !== text.data) text.data = restored;
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
/** The inverse rewrite, so the disposer restores the official copy exactly. */
|
|
646
|
+
function unbrand(value) {
|
|
647
|
+
let official = value;
|
|
648
|
+
for (const [from, to] of REPLACEMENTS) official = official.split(to).join(from);
|
|
649
|
+
return official;
|
|
650
|
+
}
|
|
651
|
+
|
|
317
652
|
//#endregion
|
|
318
653
|
//#region src/client/index.ts
|
|
319
654
|
/** Required service: the UI slot registry. */
|
|
@@ -331,7 +666,12 @@ function apply(ctx) {
|
|
|
331
666
|
name: "conversation.hero.brand.mark",
|
|
332
667
|
priority: -1
|
|
333
668
|
}, AishellHeroBrandMark));
|
|
334
|
-
if (typeof document !== "undefined")
|
|
669
|
+
if (typeof document !== "undefined") {
|
|
670
|
+
ctx.effect(() => installTitleShadow(document), "aishell-brand: window title shadow");
|
|
671
|
+
ctx.effect(() => installFaviconShadow(document), "aishell-brand: favicon shadow");
|
|
672
|
+
ctx.effect(() => installRunningMarkShadow(document), "aishell-brand: running mark shadow");
|
|
673
|
+
ctx.effect(() => installRunningTextShadow(document), "aishell-brand: running text shadow");
|
|
674
|
+
}
|
|
335
675
|
}
|
|
336
676
|
|
|
337
677
|
//#endregion
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The AI Shell mark as an inline source, shared by the runtime brand shadows.
|
|
3
|
+
*
|
|
4
|
+
* The slot occupants (Brand.tsx) draw the mark through a CSS module whose
|
|
5
|
+
* url() the build inlines, but the runtime shadows need the bytes as a JS
|
|
6
|
+
* string: the favicon shadow writes them into a link href, and the running
|
|
7
|
+
* status mark shadow writes them into injected patch CSS. This module is
|
|
8
|
+
* that one source, so the asset and the shadows cannot drift apart.
|
|
9
|
+
* tests/aishell-mark.spec.ts pins the constant against assets/aishell.svg.
|
|
10
|
+
*
|
|
11
|
+
* @module @huaweidevtools/aishell-dsh-brand/client/aishell-mark
|
|
12
|
+
*/
|
|
13
|
+
/** The AI Shell mark, verbatim assets/aishell.svg (the spec pins this). */
|
|
14
|
+
export declare const AISHELL_MARK_SVG = "<svg viewBox=\"0 0 44 44\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"44.000000\" height=\"44.000000\" fill=\"none\">\n\t<defs>\n\t\t<linearGradient id=\"paint_linear_0\" x1=\"38.5\" x2=\"2.19999695\" y1=\"1.90734863e-06\" y2=\"39.5999908\" gradientUnits=\"userSpaceOnUse\">\n\t\t\t<stop stop-color=\"rgb(53,137,255)\" offset=\"0\" stop-opacity=\"1\" />\n\t\t\t<stop stop-color=\"rgb(169,38,255)\" offset=\"1\" stop-opacity=\"1\" />\n\t\t</linearGradient>\n\t</defs>\n\t<rect id=\"icon_AIShell\" width=\"44.000000\" height=\"44.000000\" x=\"0.000000\" y=\"0.000000\" fill=\"rgb(255,255,255)\" fill-opacity=\"0\" />\n\t<path id=\"\u5408\u5E76\" d=\"M37.4576 0.299738C37.3775 0.139877 37.2861 0.0428196 37.1832 0.00856393C37.0917 -0.0142732 37.006 0.00856394 36.9259 0.0770754C36.8459 0.145587 36.7887 0.219808 36.7545 0.299738C36.4572 1.86408 35.7369 3.21718 34.5937 4.35904C33.4618 5.48948 32.0956 6.22027 30.495 6.55141C30.3921 6.57424 30.3178 6.64275 30.272 6.75694C30.2377 6.87113 30.2435 6.97389 30.2892 7.06524C30.3464 7.15659 30.415 7.20226 30.495 7.20226C31.2078 7.35296 31.8779 7.58759 32.5055 7.90618L32.5055 7.9062L32.5056 7.90623C33.257 8.28771 33.9473 8.78955 34.5765 9.41176C35.7312 10.5422 36.4572 11.8896 36.7545 13.4539C36.8116 13.591 36.8974 13.6823 37.0117 13.728C37.1146 13.7622 37.2061 13.7565 37.2861 13.7109C37.3775 13.6538 37.4347 13.5681 37.4576 13.4539C37.7891 11.8896 38.5151 10.5422 39.6355 9.41176C40.7674 8.2699 42.1279 7.5334 43.717 7.20226C43.8199 7.17943 43.9 7.12233 43.9571 7.03099C44.0143 6.92822 44.0143 6.83116 43.9571 6.73981C43.9 6.63705 43.8199 6.57424 43.717 6.55141C42.1507 6.22027 40.7959 5.48948 39.6527 4.35904C38.5208 3.21718 37.7891 1.86408 37.4576 0.299738ZM42.625 16.4999L42.625 33C42.625 33.9493 42.4572 34.829 42.1216 35.6392C41.786 36.4495 41.2826 37.1902 40.6114 37.8614C39.9402 38.5326 39.1994 39.036 38.3892 39.3716C37.579 39.7072 36.6992 39.875 35.75 39.875L10.9999 39.875L8.24995 39.875C7.30073 39.875 6.42102 39.7072 5.61081 39.3716C4.80058 39.036 4.05985 38.5326 3.38863 37.8614C2.71742 37.1902 2.21402 36.4494 1.87841 35.6392C1.5428 34.829 1.375 33.9493 1.375 33L1.375 11C1.375 10.0508 1.5428 9.17106 1.8784 8.36083L1.87841 8.36082C2.21402 7.55059 2.71743 6.80987 3.38864 6.13866C4.05985 5.46745 4.80057 4.96404 5.61079 4.62843L5.6108 4.62843C6.42103 4.29282 7.30076 4.12502 8.25 4.12502L27.4999 4.125C28.2699 4.125 28.8749 4.73 28.8749 5.5C28.8749 6.27 28.2699 6.875 27.4999 6.875L8.25 6.87502C7.68045 6.87502 7.15261 6.9757 6.66648 7.17706C6.18034 7.37843 5.73591 7.68047 5.33318 8.0832C4.93046 8.48593 4.62841 8.93036 4.42705 9.41649C4.22568 9.90263 4.125 10.4305 4.125 11L4.125 33C4.125 33.5696 4.22568 34.0974 4.42705 34.5835C4.62841 35.0697 4.93045 35.5141 5.33317 35.9168C5.73588 36.3195 6.18028 36.6216 6.66639 36.8229C7.15256 37.0243 7.68042 37.125 8.24999 37.125L10.9999 37.125L35.75 37.125C36.3195 37.125 36.8474 37.0243 37.3335 36.823C37.8197 36.6216 38.2641 36.3196 38.6668 35.9168C39.0695 35.5141 39.3716 35.0697 39.573 34.5835C39.7743 34.0974 39.875 33.5696 39.875 33L39.875 16.4999C39.875 15.7299 40.48 15.1249 41.25 15.1249C42.02 15.1249 42.625 15.7299 42.625 16.4999ZM10.0277 12.7777C10.5722 12.2333 11.4278 12.2333 11.9723 12.7777L20.2222 21.0277C20.4907 21.2962 20.6249 21.6203 20.6249 22C20.6249 22.3797 20.4907 22.7037 20.2222 22.9722L11.9722 31.2223C11.4277 31.7668 10.5721 31.7668 10.0277 31.2223C9.48318 30.6779 9.48317 29.8223 10.0276 29.2778L17.3054 22L10.0277 14.7223C9.48325 14.1778 9.48326 13.3222 10.0277 12.7777ZM20.625 28.875C20.625 28.105 21.23 27.5 22 27.5L33 27.5C33.77 27.5 34.375 28.105 34.375 28.875C34.375 29.645 33.77 30.25 33 30.25L22 30.25C21.23 30.25 20.625 29.645 20.625 28.875Z\" fill=\"rgb(0,0,0)\" fill-opacity=\"0\" fill-rule=\"evenodd\" />\n\t<path id=\"\u5408\u5E76\" d=\"M37.4576 0.299738C37.3775 0.139877 37.2861 0.0428196 37.1832 0.00856393C37.0917 -0.0142732 37.006 0.00856394 36.9259 0.0770754C36.8459 0.145587 36.7887 0.219808 36.7545 0.299738C36.4572 1.86408 35.7369 3.21718 34.5937 4.35904C33.4618 5.48948 32.0956 6.22027 30.495 6.55141C30.3921 6.57424 30.3178 6.64275 30.272 6.75694C30.2377 6.87113 30.2435 6.97389 30.2892 7.06524C30.3464 7.15659 30.415 7.20226 30.495 7.20226C31.2078 7.35296 31.8779 7.58759 32.5055 7.90618L32.5055 7.9062L32.5056 7.90623C33.257 8.28771 33.9473 8.78955 34.5765 9.41176C35.7312 10.5422 36.4572 11.8896 36.7545 13.4539C36.8116 13.591 36.8974 13.6823 37.0117 13.728C37.1146 13.7622 37.2061 13.7565 37.2861 13.7109C37.3775 13.6538 37.4347 13.5681 37.4576 13.4539C37.7891 11.8896 38.5151 10.5422 39.6355 9.41176C40.7674 8.2699 42.1279 7.5334 43.717 7.20226C43.8199 7.17943 43.9 7.12233 43.9571 7.03099C44.0143 6.92822 44.0143 6.83116 43.9571 6.73981C43.9 6.63705 43.8199 6.57424 43.717 6.55141C42.1507 6.22027 40.7959 5.48948 39.6527 4.35904C38.5208 3.21718 37.7891 1.86408 37.4576 0.299738ZM42.625 16.4999L42.625 33C42.625 33.9493 42.4572 34.829 42.1216 35.6392C41.786 36.4495 41.2826 37.1902 40.6114 37.8614C39.9402 38.5326 39.1994 39.036 38.3892 39.3716C37.579 39.7072 36.6992 39.875 35.75 39.875L10.9999 39.875L8.24995 39.875C7.30073 39.875 6.42102 39.7072 5.61081 39.3716C4.80058 39.036 4.05985 38.5326 3.38863 37.8614C2.71742 37.1902 2.21402 36.4494 1.87841 35.6392C1.5428 34.829 1.375 33.9493 1.375 33L1.375 11C1.375 10.0508 1.5428 9.17106 1.8784 8.36083L1.87841 8.36082C2.21402 7.55059 2.71743 6.80987 3.38864 6.13866C4.05985 5.46745 4.80057 4.96404 5.61079 4.62843L5.6108 4.62843C6.42103 4.29282 7.30076 4.12502 8.25 4.12502L27.4999 4.125C28.2699 4.125 28.8749 4.73 28.8749 5.5C28.8749 6.27 28.2699 6.875 27.4999 6.875L8.25 6.87502C7.68045 6.87502 7.15261 6.9757 6.66648 7.17706C6.18034 7.37843 5.73591 7.68047 5.33318 8.0832C4.93046 8.48593 4.62841 8.93036 4.42705 9.41649C4.22568 9.90263 4.125 10.4305 4.125 11L4.125 33C4.125 33.5696 4.22568 34.0974 4.42705 34.5835C4.62841 35.0697 4.93045 35.5141 5.33317 35.9168C5.73588 36.3195 6.18028 36.6216 6.66639 36.8229C7.15256 37.0243 7.68042 37.125 8.24999 37.125L10.9999 37.125L35.75 37.125C36.3195 37.125 36.8474 37.0243 37.3335 36.823C37.8197 36.6216 38.2641 36.3196 38.6668 35.9168C39.0695 35.5141 39.3716 35.0697 39.573 34.5835C39.7743 34.0974 39.875 33.5696 39.875 33L39.875 16.4999C39.875 15.7299 40.48 15.1249 41.25 15.1249C42.02 15.1249 42.625 15.7299 42.625 16.4999ZM10.0277 12.7777C10.5722 12.2333 11.4278 12.2333 11.9723 12.7777L20.2222 21.0277C20.4907 21.2962 20.6249 21.6203 20.6249 22C20.6249 22.3797 20.4907 22.7037 20.2222 22.9722L11.9722 31.2223C11.4277 31.7668 10.5721 31.7668 10.0277 31.2223C9.48318 30.6779 9.48317 29.8223 10.0276 29.2778L17.3054 22L10.0277 14.7223C9.48325 14.1778 9.48326 13.3222 10.0277 12.7777ZM20.625 28.875C20.625 28.105 21.23 27.5 22 27.5L33 27.5C33.77 27.5 34.375 28.105 34.375 28.875C34.375 29.645 33.77 30.25 33 30.25L22 30.25C21.23 30.25 20.625 29.645 20.625 28.875Z\" fill=\"url(#paint_linear_0)\" fill-rule=\"evenodd\" />\n</svg>";
|
|
15
|
+
/**
|
|
16
|
+
* The mark as a self-contained data URI.
|
|
17
|
+
*
|
|
18
|
+
* URL-encoded rather than base64 (shorter, and the gradient reads on both
|
|
19
|
+
* light and dark themes, which is what lets one URI serve every surface).
|
|
20
|
+
*/
|
|
21
|
+
export declare const AISHELL_MARK_URI: string;
|
|
22
|
+
//# sourceMappingURL=aishell-mark.d.ts.map
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime favicon and manifest shadowing for the branded distribution.
|
|
3
|
+
*
|
|
4
|
+
* The browser tab icon comes from the dsh-web-frontend dist's
|
|
5
|
+
* favicon.svg / favicon-dark.svg (a DeepSeek whale, one fixed fill per
|
|
6
|
+
* theme, selected by the index's `media="(prefers-color-scheme: …)"`
|
|
7
|
+
* links), and that dist tree is covered by the desktop-runtime digest —
|
|
8
|
+
* the file-level swap cannot reach it. The plugin side therefore shadows
|
|
9
|
+
* the links at runtime: every `link[rel~="icon"]` in the head has its
|
|
10
|
+
* href rewritten to the AI Shell mark data URI. The mark's gradient
|
|
11
|
+
* reads on both light and dark themes, so the media attribute is dropped
|
|
12
|
+
* and one icon serves both.
|
|
13
|
+
*
|
|
14
|
+
* The web manifest's display names ("DeepSeek Harness" / "DSH") are
|
|
15
|
+
* shadowed the same way when they can be: the manifest is fetched, the
|
|
16
|
+
* name/short_name fields rewritten to "AI Shell", and the link pointed
|
|
17
|
+
* at a Blob URL of the result. A manifest that cannot be fetched or
|
|
18
|
+
* parsed is left exactly as found — a broken install prompt would cost
|
|
19
|
+
* more than the name is worth.
|
|
20
|
+
*
|
|
21
|
+
* A MutationObserver watches the head, so a re-inserted icon or manifest
|
|
22
|
+
* link (HMR, later injections) is re-shadowed as it appears.
|
|
23
|
+
*
|
|
24
|
+
* Shadowing is a default, not a force (plan §2.2): install() and the
|
|
25
|
+
* disposer it returns are symmetric, so disabling the plugin restores
|
|
26
|
+
* the official links exactly.
|
|
27
|
+
*
|
|
28
|
+
* @module @huaweidevtools/aishell-dsh-brand/client/favicon-shadow
|
|
29
|
+
*/
|
|
30
|
+
/** The disposer restores the official icon and manifest links; a no-op install returns this. */
|
|
31
|
+
export type FaviconShadowDisposer = () => void;
|
|
32
|
+
/**
|
|
33
|
+
* Rewrite a manifest document's display names in place.
|
|
34
|
+
*
|
|
35
|
+
* Only the two fields the dist actually ships are touched, and only when
|
|
36
|
+
* they carry their official values — anything else in the document (icons,
|
|
37
|
+
* start_url, scope, display) passes through untouched, and a manifest the
|
|
38
|
+
* base customized keeps its customization.
|
|
39
|
+
*/
|
|
40
|
+
export declare function rebrandManifest(manifest: Record<string, unknown>): Record<string, unknown>;
|
|
41
|
+
/** The href every icon link is shadowed with (exposed for the spec). */
|
|
42
|
+
export declare const BRANDED_ICON_HREF: string;
|
|
43
|
+
/**
|
|
44
|
+
* Install the favicon shadow on a document.
|
|
45
|
+
*
|
|
46
|
+
* Rewrites every current icon link's href to the AI Shell mark (dropping
|
|
47
|
+
* the media attribute so the one gradient serves both themes), shadows the
|
|
48
|
+
* manifest names when the manifest is reachable, and observes the head so
|
|
49
|
+
* re-inserted links are covered too. Returns the disposer: every touched
|
|
50
|
+
* link is restored to its original href and media, the manifest link to
|
|
51
|
+
* its original href, and the Blob URL is revoked, so an unload leaves the
|
|
52
|
+
* document exactly as it was found.
|
|
53
|
+
*/
|
|
54
|
+
export declare function installFaviconShadow(target: Document): FaviconShadowDisposer;
|
|
55
|
+
//# sourceMappingURL=favicon-shadow.d.ts.map
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Running-status whale-tail shadow for the branded distribution.
|
|
3
|
+
*
|
|
4
|
+
* ui-chat's RunningWhaleTail renders the DeepSeek whale: an APNG
|
|
5
|
+
* (running-whale@2x.png) masked over currentColor inside a 14px
|
|
6
|
+
* runningIcon box, with a static stroke SVG fallback for reduced motion.
|
|
7
|
+
* All of it is digest-protected in the dsh tree, and there is no slot for
|
|
8
|
+
* it — the row lives inside ui-chat's ChatView, which a plugin cannot
|
|
9
|
+
* register into. The shadow therefore works the same way the hero
|
|
10
|
+
* headline patch does (Brand.tsx): a CSS patch injected into the head,
|
|
11
|
+
* anchored on the row's stable literal attribute [data-chat-running]
|
|
12
|
+
* (RunningStatus.tsx writes it directly; the class names around it are
|
|
13
|
+
* CSS-module hashes, so they are matched by the same [class*="…"]
|
|
14
|
+
* substring selectors the hero patch already relies on).
|
|
15
|
+
*
|
|
16
|
+
* The patch hides both whale layers and repaints the runningIcon box with
|
|
17
|
+
* the AI Shell mark (a data URI background), adding a gentle bob/pulse
|
|
18
|
+
* that follows the official whale's degradation: under
|
|
19
|
+
* prefers-reduced-motion the mark is static, matching the official
|
|
20
|
+
* whale's own reduced-motion fallback.
|
|
21
|
+
*
|
|
22
|
+
* Shadowing is a default, not a force (plan §2.2): install() and the
|
|
23
|
+
* disposer it returns are symmetric, so disabling the plugin restores
|
|
24
|
+
* the official whale exactly.
|
|
25
|
+
*
|
|
26
|
+
* @module @huaweidevtools/aishell-dsh-brand/client/running-mark-shadow
|
|
27
|
+
*/
|
|
28
|
+
/** The disposer removes the patch style element; a no-op install returns this. */
|
|
29
|
+
export type RunningMarkShadowDisposer = () => void;
|
|
30
|
+
/** Marker attribute the patch style element carries (exposed for the spec). */
|
|
31
|
+
export declare const RUNNING_PATCH_MARKER = "data-aishell-running-mark";
|
|
32
|
+
/**
|
|
33
|
+
* The patch CSS. The whale layers are hidden unconditionally — the static
|
|
34
|
+
* fallback too, because the mark replaces both — while the mark's own
|
|
35
|
+
* animation is guarded so reduced motion keeps it still.
|
|
36
|
+
*/
|
|
37
|
+
export declare const RUNNING_MARK_PATCH_CSS: string;
|
|
38
|
+
/**
|
|
39
|
+
* Install the running-mark shadow on a document.
|
|
40
|
+
*
|
|
41
|
+
* Injects the patch CSS once (guarded by the marker attribute so a second
|
|
42
|
+
* install is a no-op) and returns the disposer that removes the element.
|
|
43
|
+
*/
|
|
44
|
+
export declare function installRunningMarkShadow(target: Document): RunningMarkShadowDisposer;
|
|
45
|
+
//# sourceMappingURL=running-mark-shadow.d.ts.map
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Running-status copy shadow for the branded distribution.
|
|
3
|
+
*
|
|
4
|
+
* ui-chat's locale dictionary names the live running line after DeepSeek's
|
|
5
|
+
* own product language: zh "深度求索中" / "深度求索中,用时 {duration} ···",
|
|
6
|
+
* en "Deep diving" / "Deep diving for {duration} ···". locale.register
|
|
7
|
+
* throws on an already-claimed namespace, so the `chat` dictionary cannot
|
|
8
|
+
* be re-registered — the shadow rewrites the rendered text instead.
|
|
9
|
+
*
|
|
10
|
+
* The row re-renders every second while a Turn runs (LIVE_RUN_CLOCK_
|
|
11
|
+
* INTERVAL_MS = 1000), so a one-shot rewrite would flash the official
|
|
12
|
+
* copy on every tick. A MutationObserver watches [data-chat-running]
|
|
13
|
+
* subtrees — characterData for the second ticks, childList for mounts,
|
|
14
|
+
* and the data-shimmer-text attribute because TextShimmer paints its
|
|
15
|
+
* highlight layer through CSS `content: attr(data-shimmer-text)`: a text
|
|
16
|
+
* node rewritten without the attribute would keep showing the old glyphs
|
|
17
|
+
* every time the highlight sweeps across them.
|
|
18
|
+
*
|
|
19
|
+
* One substring replacement covers both keys: the duration form begins
|
|
20
|
+
* with the same prefix as the bare form, so replacing "深度求索中" →
|
|
21
|
+
* "思考中" and "Deep diving" → "Thinking" rewrites the long sentences
|
|
22
|
+
* too. The row's aria-hidden status span carries the same text and sits
|
|
23
|
+
* inside the same subtree, so it is covered by the same sweep. Duration
|
|
24
|
+
* units and the "···" tail are the harness's own words, not product
|
|
25
|
+
* branding, and pass through untouched.
|
|
26
|
+
*
|
|
27
|
+
* Shadowing is a default, not a force (plan §2.2): install() and the
|
|
28
|
+
* disposer it returns are symmetric, so disabling the plugin restores
|
|
29
|
+
* the official copy exactly.
|
|
30
|
+
*
|
|
31
|
+
* @module @huaweidevtools/aishell-dsh-brand/client/running-text-shadow
|
|
32
|
+
*/
|
|
33
|
+
/** The disposer stops the observer and restores the official copy; a no-op install returns this. */
|
|
34
|
+
export type RunningTextShadowDisposer = () => void;
|
|
35
|
+
/**
|
|
36
|
+
* Rewrite the running-line copy inside a value.
|
|
37
|
+
*
|
|
38
|
+
* Every occurrence of either official phrase is replaced; a value without
|
|
39
|
+
* them is returned as-is — the shadow never invents content.
|
|
40
|
+
*/
|
|
41
|
+
export declare function rebrandRunningText(value: string): string;
|
|
42
|
+
/**
|
|
43
|
+
* Install the running-text shadow on a document.
|
|
44
|
+
*
|
|
45
|
+
* Sweeps every mounted [data-chat-running] row once, then observes the
|
|
46
|
+
* document body for new rows (childList) and their text/attribute
|
|
47
|
+
* changes (characterData, data-shimmer-text) so the per-second re-renders
|
|
48
|
+
* stay covered for as long as the row exists. Returns the disposer: the
|
|
49
|
+
* observer stops and every live row is restored to its official copy.
|
|
50
|
+
*/
|
|
51
|
+
export declare function installRunningTextShadow(target: Document): RunningTextShadowDisposer;
|
|
52
|
+
//# sourceMappingURL=running-text-shadow.d.ts.map
|
package/package.json
CHANGED