@linbin-mk/dsh-brand-deepseek 0.1.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/LICENSE +21 -0
- package/NOTICE +60 -0
- package/README.en.md +114 -0
- package/README.md +114 -0
- package/cordis.patch.yml +3 -0
- package/lib/client.js +1233 -0
- package/lib/index.js +26 -0
- package/package.json +121 -0
package/lib/client.js
ADDED
|
@@ -0,0 +1,1233 @@
|
|
|
1
|
+
window.__ModuleLoader__.load({
|
|
2
|
+
id: "@linbin-mk/dsh-brand-deepseek",
|
|
3
|
+
factory: (require) => {
|
|
4
|
+
var module = { exports: {} };
|
|
5
|
+
var exports = module.exports;
|
|
6
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
8
|
+
let react = require("react");
|
|
9
|
+
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
10
|
+
//#region src/client/brand.tsx
|
|
11
|
+
/**
|
|
12
|
+
* Official DeepSeek logo, copied verbatim from https://www.deepseek.com/
|
|
13
|
+
* (header <a> mark: /html/body/div[1]/div/div[1]/div/a/svg).
|
|
14
|
+
*
|
|
15
|
+
* It is split into the two brand slots the sidebar exposes:
|
|
16
|
+
* - the whale mark (the `clip0_logo` group in the source SVG)
|
|
17
|
+
* - the `DeepSeek` wordmark (the letter paths in the source SVG)
|
|
18
|
+
* Both fill with the plugin-managed brand color (`--dsh-brand-deepseek-color`,
|
|
19
|
+
* set on the document root from the Settings → 品牌样式 page), falling back to
|
|
20
|
+
* the official DeepSeek blue so the logo renders in the official color before
|
|
21
|
+
* the persisted style loads.
|
|
22
|
+
*/
|
|
23
|
+
const WHALE_PATH = "M26.5174 3.39471C26.235 3.2567 26.1137 3.52006 25.9487 3.65346C25.8923 3.69659 25.8446 3.75294 25.7969 3.80469C25.3846 4.24516 24.9027 4.53439 24.2737 4.49989C23.3536 4.44814 22.5682 4.73737 21.8735 5.44119C21.7258 4.57349 21.2353 4.0554 20.4889 3.72304C20.0985 3.55054 19.7034 3.37746 19.4297 3.00197C19.2388 2.73459 19.1865 2.43673 19.091 2.14289C19.0301 1.96579 18.9697 1.78466 18.7656 1.75418C18.5442 1.71968 18.4574 1.90541 18.3705 2.06067C18.0232 2.69549 17.8887 3.39471 17.9019 4.10313C17.9324 5.6965 18.6051 6.96556 19.9421 7.86834C20.0939 7.97184 20.133 8.07535 20.0852 8.22658C19.9938 8.53766 19.8857 8.83955 19.7903 9.15063C19.7293 9.34901 19.6384 9.39271 19.4257 9.30588C18.692 8.9994 18.0583 8.54571 17.4982 7.99772C16.5477 7.07827 15.6881 6.06336 14.6162 5.26869C14.3644 5.08296 14.1125 4.91045 13.8521 4.746C12.7584 3.68394 13.9952 2.81164 14.2816 2.70814C14.5812 2.60003 14.3857 2.22857 13.4179 2.23317C12.4502 2.2372 11.5646 2.56151 10.4359 2.99335C10.2708 3.05832 10.0972 3.10547 9.91951 3.14457C8.8954 2.95022 7.83162 2.90709 6.72069 3.03245C4.62877 3.26533 2.95777 4.25436 1.72954 5.94261C0.254043 7.97184 -0.0932678 10.2777 0.33167 12.6824C0.778458 15.2171 2.07225 17.3153 4.06008 18.9558C6.12152 20.6567 8.49577 21.4905 11.2047 21.3306C12.8498 21.2358 14.6812 21.0155 16.7473 19.2669C17.2682 19.5262 17.8151 19.6297 18.7219 19.7074C19.4205 19.7723 20.0933 19.6729 20.6143 19.5648C21.4302 19.3923 21.3739 18.6367 21.0789 18.4981C18.6874 17.3843 19.2124 17.8374 18.7351 17.4706C19.9501 16.033 21.8063 13.4776 22.379 9.99821C22.4353 9.61409 22.5072 9.073 22.4986 8.76192C22.494 8.57216 22.5377 8.49856 22.7545 8.47671C23.3536 8.40771 23.935 8.24383 24.4692 7.94999C26.0188 7.10357 26.6439 5.71318 26.7911 4.04678C26.8129 3.79204 26.7865 3.52869 26.5174 3.39471ZM13.0143 18.3946C10.6964 16.5724 9.5722 15.9726 9.10816 15.9985C8.67402 16.0244 8.75222 16.5212 8.84768 16.8449C8.94773 17.1646 9.07768 17.3849 9.25996 17.6655C9.38589 17.8512 9.47272 18.1272 9.13404 18.3348C8.38766 18.7965 7.08985 18.1796 7.0289 18.1491C5.51833 17.2595 4.25559 16.0853 3.36546 14.4793C2.50581 12.9337 2.0067 11.2753 1.92447 9.50542C1.90262 9.07818 2.02855 8.92695 2.45406 8.84932C3.01413 8.74582 3.59144 8.72397 4.15093 8.80619C6.51656 9.15178 8.53027 10.2092 10.2185 11.8848C11.1822 12.8388 11.9114 13.979 12.6623 15.0929C13.461 16.2757 14.3201 17.4027 15.4144 18.3268C15.8008 18.6505 16.109 18.8966 16.404 19.0783C15.5144 19.1778 14.0297 19.1991 13.0143 18.3958V18.3946ZM14.1252 11.2489C14.1252 11.0591 14.277 10.9079 14.4679 10.9079C14.511 10.9079 14.5501 10.9165 14.5852 10.9292C14.6329 10.9464 14.6766 10.9723 14.7111 11.0114C14.7721 11.0718 14.8066 11.158 14.8066 11.2489C14.8066 11.4386 14.6548 11.5899 14.4639 11.5899C14.273 11.5899 14.1252 11.4386 14.1252 11.2489ZM17.5759 13.0188C17.3545 13.1096 17.1331 13.1873 16.9203 13.1959C16.5903 13.2131 16.2303 13.0791 16.0348 12.9153C15.7312 12.6605 15.5139 12.5179 15.423 12.0734C15.3839 11.8837 15.4057 11.5899 15.4402 11.4214C15.5185 11.0585 15.4316 10.8257 15.1757 10.614C14.9676 10.4415 14.7025 10.3938 14.4115 10.3938C14.3029 10.3938 14.2034 10.3461 14.1292 10.3076C14.0079 10.2472 13.9078 10.096 14.0033 9.91023C14.0338 9.84985 14.1815 9.70322 14.216 9.67734C14.6111 9.45251 15.0665 9.52612 15.488 9.6946C15.8784 9.85445 16.174 10.1477 16.5989 10.5623C17.033 11.0631 17.1112 11.2011 17.3585 11.5772C17.554 11.871 17.7317 12.1729 17.8536 12.5185C17.9272 12.7341 17.8317 12.9107 17.5759 13.0188Z";
|
|
24
|
+
const WORDMARK_PATHS = [
|
|
25
|
+
"M78.6784 18.6813H77.1077V16.2462H78.6784C79.6513 16.2462 80.6341 16.0037 81.2672 15.3298C81.9009 14.6559 82.14 13.6222 82.14 12.589C82.14 11.5559 81.9109 10.5222 81.2672 9.84884C80.6246 9.17496 79.6513 8.93245 78.6784 8.93245C77.7056 8.93245 76.7227 9.17496 76.0885 9.84884C75.4549 10.5227 75.2157 11.5559 75.2157 12.589V22.5899H72.4604V6.50684H75.2157V7.53106H75.7209C75.7756 7.46792 75.8304 7.41428 75.8857 7.36064C76.5752 6.73146 77.6307 6.50684 78.6684 6.50684C80.2944 6.50684 81.9193 6.91138 82.9849 8.03451C84.0499 9.15764 84.4265 10.8826 84.4265 12.5991C84.4265 14.3156 84.0404 16.0316 82.9849 17.1637C81.9288 18.2958 80.2944 18.6824 78.6784 18.6824V18.6813Z",
|
|
26
|
+
"M36.7486 6.93999H38.3188V9.37511H36.7486C35.7752 9.37511 34.7929 9.61762 34.1593 10.2915C33.5256 10.9654 33.287 11.9991 33.287 13.0323C33.287 14.0654 33.5167 15.0986 34.1593 15.7725C34.8019 16.4463 35.7752 16.6888 36.7486 16.6888C37.722 16.6888 38.7049 16.4463 39.3385 15.7725C39.9722 15.0986 40.2108 14.0654 40.2108 13.0323V3.02246H42.9655V19.115H40.2108V18.0908H39.7056C39.6503 18.1534 39.5955 18.2076 39.5402 18.2612C38.8513 18.8898 37.7952 19.115 36.7576 19.115C35.1321 19.115 33.5066 18.711 32.4416 17.5879C31.3766 16.4648 31 14.7393 31 13.0233C31 11.3073 31.3856 9.5908 32.4416 8.45873C33.5066 7.3356 35.1321 6.93999 36.7486 6.93999Z",
|
|
27
|
+
"M56.7855 12.8145V13.794H49.4483V11.8445H54.3151C54.2051 11.1348 53.948 10.4699 53.4887 9.98433C52.8277 9.28363 51.8079 9.03218 50.7982 9.03218C49.7886 9.03218 48.7688 9.28363 48.1078 9.98433C47.4468 10.685 47.2076 11.7545 47.2076 12.8151C47.2076 13.8756 47.4462 14.9535 48.1078 15.6452C48.7688 16.337 49.788 16.5979 50.7982 16.5979C51.8085 16.5979 52.8277 16.3465 53.4887 15.6452C53.5804 15.5463 53.6631 15.4385 53.7458 15.3306H56.4642C56.2256 16.1755 55.849 16.9393 55.2796 17.5322C54.1777 18.6911 52.479 19.1135 50.7982 19.1135C49.1175 19.1135 47.4188 18.7 46.3169 17.5322C45.215 16.3644 44.811 14.5852 44.811 12.8151C44.811 11.0449 45.2061 9.25681 46.3169 8.09792C47.4283 6.93903 49.1175 6.5166 50.7982 6.5166C52.479 6.5166 54.1777 6.93009 55.2796 8.09792C56.3904 9.26575 56.7855 11.0449 56.7855 12.8151V12.8145Z",
|
|
28
|
+
"M70.6151 12.8145V13.794H63.2779V11.8445H68.1447C68.0341 11.1348 67.7776 10.4699 67.3183 9.98433C66.6573 9.28363 65.6375 9.03218 64.6278 9.03218C63.6181 9.03218 62.5984 9.28363 61.9374 9.98433C61.2763 10.685 61.0372 11.7545 61.0372 12.8151C61.0372 13.8756 61.2758 14.9535 61.9374 15.6452C62.5984 16.337 63.6181 16.5979 64.6278 16.5979C65.6375 16.5979 66.6573 16.3465 67.3183 15.6452C67.4105 15.5463 67.4927 15.4385 67.5748 15.3306H70.2938C70.0546 16.1755 69.678 16.9393 69.1086 17.5322C68.0067 18.6911 66.3081 19.1135 64.6278 19.1135C62.9476 19.1135 61.2484 18.7 60.1465 17.5322C59.0446 16.3644 58.6406 14.5852 58.6406 12.8151C58.6406 11.0449 59.0357 9.25681 60.1465 8.09792C61.2579 6.93903 62.9471 6.5166 64.6278 6.5166C66.3086 6.5166 68.0067 6.93009 69.1086 8.09792C70.22 9.26575 70.6151 11.0449 70.6151 12.8151V12.8145Z",
|
|
29
|
+
"M92.2781 19.1146C93.9589 19.1146 95.657 18.8721 96.7589 18.1804C97.8607 17.4886 98.2653 16.437 98.2653 15.3949C98.2653 14.3528 97.8697 13.2922 96.7589 12.6094C95.657 11.9266 93.9583 11.6746 92.2781 11.6746C91.5612 11.6746 90.9002 11.5757 90.4319 11.3153C89.9637 11.0454 89.7893 10.6414 89.7893 10.2369C89.7893 9.83234 89.9547 9.41941 90.4319 9.15846C90.9002 8.88858 91.626 8.79917 92.3418 8.79917C93.0576 8.79917 93.7834 8.89808 94.2528 9.15846C94.721 9.42835 94.8954 9.83234 94.8954 10.2369H97.6959C97.6959 9.19422 97.3383 8.13424 96.3375 7.45142C95.3368 6.76861 93.803 6.5166 92.2786 6.5166C90.7543 6.5166 89.2211 6.75911 88.2197 7.45142C87.219 8.14318 86.8603 9.19422 86.8603 10.2369C86.8603 11.2796 87.2184 12.3395 88.2197 13.0224C89.2205 13.7052 90.7538 13.9572 92.2786 13.9572C93.0682 13.9572 93.941 14.0561 94.464 14.3165C94.9881 14.5774 95.1714 14.9903 95.1714 15.3949C95.1714 15.7994 94.9881 16.2124 94.464 16.4733C93.941 16.7337 93.1419 16.8326 92.3524 16.8326C91.5629 16.8326 90.7543 16.7337 90.2397 16.4733C89.7256 16.2129 89.5323 15.7994 89.5323 15.3949H86.2998C86.2998 16.4376 86.6943 17.4975 87.8063 18.1804C88.9171 18.8632 90.5979 19.1146 92.2786 19.1146H92.2781Z",
|
|
30
|
+
"M112.094 12.8145V13.794H104.757V11.8445H109.624C109.514 11.1348 109.257 10.4699 108.798 9.98433C108.136 9.28363 107.117 9.03218 106.106 9.03218C105.095 9.03218 104.077 9.28363 103.416 9.98433C102.755 10.685 102.517 11.7545 102.517 12.8151C102.517 13.8756 102.755 14.9535 103.416 15.6452C104.077 16.337 105.097 16.5979 106.106 16.5979C107.116 16.5979 108.136 16.3465 108.798 15.6452C108.889 15.5463 108.972 15.4385 109.054 15.3306H111.772C111.533 16.1755 111.157 16.9393 110.588 17.5322C109.486 18.6911 107.787 19.1135 106.106 19.1135C104.425 19.1135 102.727 18.7 101.625 17.5322C100.524 16.3644 100.12 14.5852 100.12 12.8151C100.12 11.0449 100.515 9.25681 101.625 8.09792C102.737 6.93903 104.427 6.5166 106.106 6.5166C107.786 6.5166 109.486 6.93009 110.588 8.09792C111.699 9.26575 112.093 11.0449 112.093 12.8151L112.094 12.8145Z",
|
|
31
|
+
"M125.924 12.8145V13.794H118.586V11.8445H123.453C123.344 11.1348 123.086 10.4699 122.627 9.98433C121.966 9.28363 120.947 9.03218 119.936 9.03218C118.926 9.03218 117.907 9.28363 117.246 9.98433C116.585 10.685 116.346 11.7545 116.346 12.8151C116.346 13.8756 116.585 14.9535 117.246 15.6452C117.907 16.337 118.927 16.5979 119.936 16.5979C120.946 16.5979 121.966 16.3465 122.627 15.6452C122.719 15.5463 122.801 15.4385 122.884 15.3306H125.602C125.363 16.1755 124.987 16.9393 124.418 17.5322C123.316 18.6911 121.617 19.1135 119.936 19.1135C118.256 19.1135 116.558 18.7 115.456 17.5322C114.354 16.3644 113.949 14.5852 113.949 12.8151C113.949 11.0449 114.344 9.25681 115.456 8.09792C116.566 6.93903 118.256 6.5166 119.936 6.5166C121.617 6.5166 123.315 6.93009 124.418 8.09792C125.529 9.26575 125.924 11.0449 125.924 12.8151V12.8145Z",
|
|
32
|
+
"M130.524 3.02246H127.77V19.115H130.524V3.02246Z",
|
|
33
|
+
"M135.227 12.4374L139.744 19.1136H136.337L131.819 12.4374L136.337 7.07324H139.744L135.227 12.4374Z"
|
|
34
|
+
];
|
|
35
|
+
/** Brand fill: custom color first, official DeepSeek blue as the fallback. */
|
|
36
|
+
const BRAND_FILL = "var(--dsh-brand-deepseek-color, var(--ds-color-brand-medium-reverse, #4176e6))";
|
|
37
|
+
/** Official DeepSeek whale mark, scaled to the host surface's requested size. */
|
|
38
|
+
function BrandMark({ size, className }) {
|
|
39
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
40
|
+
width: size * (28 / 20.5),
|
|
41
|
+
height: size,
|
|
42
|
+
viewBox: "-0.5 1.5 28 20.5",
|
|
43
|
+
fill: BRAND_FILL,
|
|
44
|
+
preserveAspectRatio: "xMidYMid meet",
|
|
45
|
+
className,
|
|
46
|
+
role: "img",
|
|
47
|
+
"aria-label": "DeepSeek",
|
|
48
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: WHALE_PATH })
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
/** Official DeepSeek wordmark, height-matched to the mark for alignment. */
|
|
52
|
+
function BrandName({ size = 22, className }) {
|
|
53
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
54
|
+
width: size * (109 / 20),
|
|
55
|
+
height: size,
|
|
56
|
+
viewBox: "31 3 109 20",
|
|
57
|
+
fill: BRAND_FILL,
|
|
58
|
+
preserveAspectRatio: "xMidYMid meet",
|
|
59
|
+
className,
|
|
60
|
+
role: "img",
|
|
61
|
+
"aria-label": "DeepSeek",
|
|
62
|
+
children: WORDMARK_PATHS.map((d, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d }, i))
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
//#endregion
|
|
66
|
+
//#region src/client/hero.tsx
|
|
67
|
+
/**
|
|
68
|
+
* Sheet turning the hosted hero headline row into the DeepSeek look while the
|
|
69
|
+
* feature is on: the row drops its fixed 34px/auto/auto grid for a centered
|
|
70
|
+
* flex row, the default text spans hide, and the mark slot's hitbox box
|
|
71
|
+
* dissolves so the occupant's whale + title flow directly into the row.
|
|
72
|
+
* The container rule anchors on the hitbox (`:has([class*='_fishHitbox'])`),
|
|
73
|
+
* the hero-headline row's own unique descendant, so the shared `.headline`
|
|
74
|
+
* locals of other modules (ContextMeter, ApprovalPanel — which also render
|
|
75
|
+
* inside the composer seat during a takeover) are never touched.
|
|
76
|
+
*
|
|
77
|
+
* The default title is hidden through every local it has shipped under: the
|
|
78
|
+
* dedicated `_headlineText` span of 0.1.5-rc.1 and the `_titleGroup` wrapper
|
|
79
|
+
* (title + badge) that replaced it in 0.1.5-rc.2. Each selector is a no-op on
|
|
80
|
+
* the harness versions that never had that local, so both stay listed.
|
|
81
|
+
*/
|
|
82
|
+
const HERO_CSS = `
|
|
83
|
+
[data-composer-seat] [class*='_headlineText'],
|
|
84
|
+
[data-composer-seat] [class*='_titleGroup'],
|
|
85
|
+
[data-composer-seat] [class*='_previewBadge'] {
|
|
86
|
+
display: none;
|
|
87
|
+
}
|
|
88
|
+
[data-composer-seat] [class*='_headline']:has([class*='_fishHitbox']) {
|
|
89
|
+
display: flex;
|
|
90
|
+
align-items: center;
|
|
91
|
+
justify-content: center;
|
|
92
|
+
gap: 10px;
|
|
93
|
+
}
|
|
94
|
+
[data-composer-seat] [class*='_fishHitbox'] {
|
|
95
|
+
display: contents;
|
|
96
|
+
}
|
|
97
|
+
`;
|
|
98
|
+
/** Title style mirroring the hosted hero headline (26/32 wt500 inherited). */
|
|
99
|
+
const TITLE_STYLE = {
|
|
100
|
+
fontSize: 26,
|
|
101
|
+
lineHeight: "32px",
|
|
102
|
+
fontWeight: 600,
|
|
103
|
+
whiteSpace: "nowrap",
|
|
104
|
+
color: "var(--dsw-alias-label-primary)"
|
|
105
|
+
};
|
|
106
|
+
/**
|
|
107
|
+
* The official DeepSeek hero headline occupant: the whale mark (brand color)
|
|
108
|
+
* followed by the brand title, centered by {@link HERO_CSS}.
|
|
109
|
+
* @param props - host request mark size/class plus the injected locale seat.
|
|
110
|
+
* @returns the whale + title row fragment.
|
|
111
|
+
*/
|
|
112
|
+
function HeroHeadlineMark({ size, className, t }) {
|
|
113
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(BrandMark, {
|
|
114
|
+
size,
|
|
115
|
+
className
|
|
116
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
117
|
+
style: TITLE_STYLE,
|
|
118
|
+
children: t("hero.title")
|
|
119
|
+
})] });
|
|
120
|
+
}
|
|
121
|
+
//#endregion
|
|
122
|
+
//#region src/client/stores.ts
|
|
123
|
+
/**
|
|
124
|
+
* Current (default) brand color: the official DeepSeek blue. Persisted style
|
|
125
|
+
* falls back to it, and it is the pre-selected palette option.
|
|
126
|
+
*/
|
|
127
|
+
const DEFAULT_BRAND_COLOR = "#4176e6";
|
|
128
|
+
/** The 10 selectable brand colors; the first is the current scheme (default). */
|
|
129
|
+
const BRAND_COLORS = [
|
|
130
|
+
{
|
|
131
|
+
id: "official",
|
|
132
|
+
value: "#4176e6",
|
|
133
|
+
nameKey: "color.official"
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
id: "deep",
|
|
137
|
+
value: "#1d4ed8",
|
|
138
|
+
nameKey: "color.deep"
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
id: "sky",
|
|
142
|
+
value: "#0ea5e9",
|
|
143
|
+
nameKey: "color.sky"
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
id: "emerald",
|
|
147
|
+
value: "#10b981",
|
|
148
|
+
nameKey: "color.emerald"
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
id: "teal",
|
|
152
|
+
value: "#0d9488",
|
|
153
|
+
nameKey: "color.teal"
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
id: "amber",
|
|
157
|
+
value: "#f59e0b",
|
|
158
|
+
nameKey: "color.amber"
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
id: "orange",
|
|
162
|
+
value: "#f97316",
|
|
163
|
+
nameKey: "color.orange"
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
id: "pink",
|
|
167
|
+
value: "#ec4899",
|
|
168
|
+
nameKey: "color.pink"
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
id: "violet",
|
|
172
|
+
value: "#8b5cf6",
|
|
173
|
+
nameKey: "color.violet"
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
id: "graphite",
|
|
177
|
+
value: "#1f2937",
|
|
178
|
+
nameKey: "color.graphite"
|
|
179
|
+
}
|
|
180
|
+
];
|
|
181
|
+
function messageOf(cause) {
|
|
182
|
+
return cause instanceof Error ? cause.message : String(cause);
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Module-level observable backing the Settings → 品牌样式 page, mirroring the
|
|
186
|
+
* workspace-prompt plugin's pattern: `settings.section` renders through the
|
|
187
|
+
* root-scoped slot machinery, so the apply half (which owns `ctx`) writes the
|
|
188
|
+
* read/write handlers here and the section reads state via React's
|
|
189
|
+
* {@link useSyncExternalStore}.
|
|
190
|
+
*/
|
|
191
|
+
var BrandStyleObservable = class {
|
|
192
|
+
listeners = /* @__PURE__ */ new Set();
|
|
193
|
+
state = {
|
|
194
|
+
enabled: true,
|
|
195
|
+
hero: true,
|
|
196
|
+
trajectoryTab: true,
|
|
197
|
+
sessionLogButton: true,
|
|
198
|
+
color: DEFAULT_BRAND_COLOR,
|
|
199
|
+
busy: false,
|
|
200
|
+
error: null
|
|
201
|
+
};
|
|
202
|
+
/** Set by the apply half at activation; undefined only before first activation. */
|
|
203
|
+
handlers;
|
|
204
|
+
getSnapshot = () => this.state;
|
|
205
|
+
subscribe = (listener) => {
|
|
206
|
+
this.listeners.add(listener);
|
|
207
|
+
return () => {
|
|
208
|
+
this.listeners.delete(listener);
|
|
209
|
+
};
|
|
210
|
+
};
|
|
211
|
+
/**
|
|
212
|
+
* Reload the persisted style. The load handler also applies the side
|
|
213
|
+
* effects (brand slot registration and the color variable), so both the
|
|
214
|
+
* boot refresh and every page visit converge on the persisted state.
|
|
215
|
+
*/
|
|
216
|
+
refresh = async () => {
|
|
217
|
+
const load = this.handlers?.load;
|
|
218
|
+
if (load === void 0) return;
|
|
219
|
+
this.state = {
|
|
220
|
+
...this.state,
|
|
221
|
+
busy: true,
|
|
222
|
+
error: null
|
|
223
|
+
};
|
|
224
|
+
this.emit();
|
|
225
|
+
try {
|
|
226
|
+
const { enabled, hero, trajectoryTab, sessionLogButton, color } = await load();
|
|
227
|
+
this.state = {
|
|
228
|
+
...this.state,
|
|
229
|
+
enabled,
|
|
230
|
+
hero,
|
|
231
|
+
trajectoryTab,
|
|
232
|
+
sessionLogButton,
|
|
233
|
+
color,
|
|
234
|
+
busy: false,
|
|
235
|
+
error: null
|
|
236
|
+
};
|
|
237
|
+
} catch (cause) {
|
|
238
|
+
this.state = {
|
|
239
|
+
...this.state,
|
|
240
|
+
busy: false,
|
|
241
|
+
error: messageOf(cause)
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
this.emit();
|
|
245
|
+
};
|
|
246
|
+
/** Persist and apply the custom-style toggle. */
|
|
247
|
+
setEnabled = async (enabled) => {
|
|
248
|
+
const handler = this.handlers?.setEnabled;
|
|
249
|
+
if (handler === void 0) return;
|
|
250
|
+
this.state = {
|
|
251
|
+
...this.state,
|
|
252
|
+
busy: true,
|
|
253
|
+
error: null
|
|
254
|
+
};
|
|
255
|
+
this.emit();
|
|
256
|
+
try {
|
|
257
|
+
await handler(enabled);
|
|
258
|
+
this.state = {
|
|
259
|
+
...this.state,
|
|
260
|
+
enabled,
|
|
261
|
+
busy: false,
|
|
262
|
+
error: null
|
|
263
|
+
};
|
|
264
|
+
} catch (cause) {
|
|
265
|
+
this.state = {
|
|
266
|
+
...this.state,
|
|
267
|
+
busy: false,
|
|
268
|
+
error: messageOf(cause)
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
this.emit();
|
|
272
|
+
};
|
|
273
|
+
/** Persist and apply the hero-headline toggle. */
|
|
274
|
+
setHero = async (hero) => {
|
|
275
|
+
const handler = this.handlers?.setHero;
|
|
276
|
+
if (handler === void 0) return;
|
|
277
|
+
this.state = {
|
|
278
|
+
...this.state,
|
|
279
|
+
busy: true,
|
|
280
|
+
error: null
|
|
281
|
+
};
|
|
282
|
+
this.emit();
|
|
283
|
+
try {
|
|
284
|
+
await handler(hero);
|
|
285
|
+
this.state = {
|
|
286
|
+
...this.state,
|
|
287
|
+
hero,
|
|
288
|
+
busy: false,
|
|
289
|
+
error: null
|
|
290
|
+
};
|
|
291
|
+
} catch (cause) {
|
|
292
|
+
this.state = {
|
|
293
|
+
...this.state,
|
|
294
|
+
busy: false,
|
|
295
|
+
error: messageOf(cause)
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
this.emit();
|
|
299
|
+
};
|
|
300
|
+
/** Persist and apply the trajectory-tab toggle. */
|
|
301
|
+
setTrajectoryTab = async (visible) => {
|
|
302
|
+
const handler = this.handlers?.setTrajectoryTab;
|
|
303
|
+
if (handler === void 0) return;
|
|
304
|
+
this.state = {
|
|
305
|
+
...this.state,
|
|
306
|
+
busy: true,
|
|
307
|
+
error: null
|
|
308
|
+
};
|
|
309
|
+
this.emit();
|
|
310
|
+
try {
|
|
311
|
+
await handler(visible);
|
|
312
|
+
this.state = {
|
|
313
|
+
...this.state,
|
|
314
|
+
trajectoryTab: visible,
|
|
315
|
+
busy: false,
|
|
316
|
+
error: null
|
|
317
|
+
};
|
|
318
|
+
} catch (cause) {
|
|
319
|
+
this.state = {
|
|
320
|
+
...this.state,
|
|
321
|
+
busy: false,
|
|
322
|
+
error: messageOf(cause)
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
this.emit();
|
|
326
|
+
};
|
|
327
|
+
/** Persist and apply the session-log toggle. */
|
|
328
|
+
setSessionLogButton = async (visible) => {
|
|
329
|
+
const handler = this.handlers?.setSessionLogButton;
|
|
330
|
+
if (handler === void 0) return;
|
|
331
|
+
this.state = {
|
|
332
|
+
...this.state,
|
|
333
|
+
busy: true,
|
|
334
|
+
error: null
|
|
335
|
+
};
|
|
336
|
+
this.emit();
|
|
337
|
+
try {
|
|
338
|
+
await handler(visible);
|
|
339
|
+
this.state = {
|
|
340
|
+
...this.state,
|
|
341
|
+
sessionLogButton: visible,
|
|
342
|
+
busy: false,
|
|
343
|
+
error: null
|
|
344
|
+
};
|
|
345
|
+
} catch (cause) {
|
|
346
|
+
this.state = {
|
|
347
|
+
...this.state,
|
|
348
|
+
busy: false,
|
|
349
|
+
error: messageOf(cause)
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
this.emit();
|
|
353
|
+
};
|
|
354
|
+
/** Persist and apply the selected brand color. */
|
|
355
|
+
setColor = async (color) => {
|
|
356
|
+
const handler = this.handlers?.setColor;
|
|
357
|
+
if (handler === void 0) return;
|
|
358
|
+
this.state = {
|
|
359
|
+
...this.state,
|
|
360
|
+
busy: true,
|
|
361
|
+
error: null
|
|
362
|
+
};
|
|
363
|
+
this.emit();
|
|
364
|
+
try {
|
|
365
|
+
await handler(color);
|
|
366
|
+
this.state = {
|
|
367
|
+
...this.state,
|
|
368
|
+
color,
|
|
369
|
+
busy: false,
|
|
370
|
+
error: null
|
|
371
|
+
};
|
|
372
|
+
} catch (cause) {
|
|
373
|
+
this.state = {
|
|
374
|
+
...this.state,
|
|
375
|
+
busy: false,
|
|
376
|
+
error: messageOf(cause)
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
this.emit();
|
|
380
|
+
};
|
|
381
|
+
emit() {
|
|
382
|
+
for (const listener of this.listeners) listener();
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
/** Single instance shared by the apply half (writer) and the section (reader). */
|
|
386
|
+
const brandStyle = new BrandStyleObservable();
|
|
387
|
+
//#endregion
|
|
388
|
+
//#region src/client/BrandStyleSection.tsx
|
|
389
|
+
/**
|
|
390
|
+
* Settings page for the dsh-brand-deepseek plugin: the custom brand style.
|
|
391
|
+
*
|
|
392
|
+
* Registered by the client plugin body as a `settings.section` contribution,
|
|
393
|
+
* so it appears as its own navigation row inside the Settings panel (bottom
|
|
394
|
+
* left). The page offers:
|
|
395
|
+
* - a custom-style toggle — off restores the official default brand (the
|
|
396
|
+
* plugin stops overriding the sidebar brand slots, so the shell fallback
|
|
397
|
+
* renders);
|
|
398
|
+
* - a 会话页标题 toggle — independently switches the blank-session hero
|
|
399
|
+
* headline between the DeepSeek brand title (whale + 想从哪里开始?) and
|
|
400
|
+
* the official default (探索未至之境 + 预览版 badge);
|
|
401
|
+
* - 轨迹页签 and Session 日志按钮 toggles — show/hide two pieces of harness
|
|
402
|
+
* chrome (the trajectory view tab and the session-log header button),
|
|
403
|
+
* implemented at the DOM level only, never touching harness source;
|
|
404
|
+
* - ten selectable brand colors (the current scheme — official DeepSeek
|
|
405
|
+
* blue — is the default). Choosing one applies and persists immediately.
|
|
406
|
+
*
|
|
407
|
+
* The official default brand preview mirrors the current shell fallback
|
|
408
|
+
* (ui-sidebar): fish mark + localized local-build label + the build-version
|
|
409
|
+
* badge, stacked like the sidebar. The build version is a build-time define
|
|
410
|
+
* with no client runtime API, so the preview reads the badge the sidebar
|
|
411
|
+
* itself renders (custom style off leaves the fallback in the DOM); when it
|
|
412
|
+
* is not present (collapsed sidebar, older harness), the preview degrades to
|
|
413
|
+
* the label alone. The hero headline preview mirrors both hero states below
|
|
414
|
+
* the sidebar preview.
|
|
415
|
+
*
|
|
416
|
+
* The page remounts on every visit (the settings shell renders only the
|
|
417
|
+
* active section), so it always starts from a fresh read of the persisted
|
|
418
|
+
* document. Writes go through the Host settings RPC, owned by the apply half.
|
|
419
|
+
*/
|
|
420
|
+
/**
|
|
421
|
+
* The sidebar's local-build badge format (ui-sidebar `localBuildVersion()`):
|
|
422
|
+
* `<version>-<commit7>` optionally `-dirty`, e.g. `0.1.2-alpha.1-cd5ef81`.
|
|
423
|
+
*/
|
|
424
|
+
const LOCAL_BUILD_VERSION_PATTERN = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?-[0-9a-f]{7,40}(?:-dirty)?$/;
|
|
425
|
+
/**
|
|
426
|
+
* Read the build-version badge the sidebar's official default brand renders.
|
|
427
|
+
* The string is the exact badge text (version + commit) shown when the
|
|
428
|
+
* custom style is off; undefined when the fallback is not in the document.
|
|
429
|
+
*/
|
|
430
|
+
function readDefaultBuildVersion() {
|
|
431
|
+
if (typeof document === "undefined") return void 0;
|
|
432
|
+
const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT);
|
|
433
|
+
for (let node = walker.nextNode(); node !== null; node = walker.nextNode()) {
|
|
434
|
+
const text = (node.nodeValue ?? "").trim();
|
|
435
|
+
if (LOCAL_BUILD_VERSION_PATTERN.test(text)) return text;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
/** Shared card chrome (design tokens with neutral fallbacks). */
|
|
439
|
+
const card = {
|
|
440
|
+
border: "1px solid var(--dsw-alias-border-l2, #d0d5dd)",
|
|
441
|
+
borderRadius: 8,
|
|
442
|
+
padding: 14,
|
|
443
|
+
marginTop: 14
|
|
444
|
+
};
|
|
445
|
+
const title = {
|
|
446
|
+
fontWeight: 600,
|
|
447
|
+
fontSize: 14,
|
|
448
|
+
color: "var(--dsw-alias-label-primary, #101828)"
|
|
449
|
+
};
|
|
450
|
+
const hint = {
|
|
451
|
+
fontSize: 12,
|
|
452
|
+
lineHeight: 1.5,
|
|
453
|
+
color: "var(--dsw-alias-label-secondary, #475467)",
|
|
454
|
+
marginTop: 4
|
|
455
|
+
};
|
|
456
|
+
/**
|
|
457
|
+
* The shell's official default brand (ui-sidebar `.localBuildBrand` /
|
|
458
|
+
* `.localBuildTitle` / `.buildVersion`): stacked label + version badge,
|
|
459
|
+
* height-matched to the fish mark.
|
|
460
|
+
*/
|
|
461
|
+
const defaultBrandStack = {
|
|
462
|
+
display: "inline-flex",
|
|
463
|
+
flexDirection: "column",
|
|
464
|
+
alignItems: "flex-start",
|
|
465
|
+
justifyContent: "center",
|
|
466
|
+
gap: 1,
|
|
467
|
+
height: 24,
|
|
468
|
+
whiteSpace: "nowrap"
|
|
469
|
+
};
|
|
470
|
+
const defaultBrandTitle = {
|
|
471
|
+
fontSize: 12,
|
|
472
|
+
lineHeight: "13px",
|
|
473
|
+
fontWeight: 600,
|
|
474
|
+
letterSpacing: 0,
|
|
475
|
+
color: "var(--dsw-alias-label-primary, #101828)"
|
|
476
|
+
};
|
|
477
|
+
const defaultBuildVersion = {
|
|
478
|
+
display: "inline-flex",
|
|
479
|
+
alignItems: "center",
|
|
480
|
+
height: 10,
|
|
481
|
+
padding: "0 3px",
|
|
482
|
+
borderRadius: 2,
|
|
483
|
+
color: "var(--dsw-alias-label-primary-inverted, #ffffff)",
|
|
484
|
+
background: "var(--dsw-alias-label-primary, #101828)",
|
|
485
|
+
fontFamily: "var(--ds-font-family-code, ui-monospace, SFMono-Regular, Menlo, monospace)",
|
|
486
|
+
fontSize: 6,
|
|
487
|
+
fontWeight: 500,
|
|
488
|
+
lineHeight: "10px",
|
|
489
|
+
whiteSpace: "nowrap"
|
|
490
|
+
};
|
|
491
|
+
/** The official default hero title/badge chrome (mirrored from HeroShell). */
|
|
492
|
+
const heroTitleDefault = {
|
|
493
|
+
fontSize: 26,
|
|
494
|
+
lineHeight: "32px",
|
|
495
|
+
fontWeight: 500,
|
|
496
|
+
whiteSpace: "nowrap",
|
|
497
|
+
color: "var(--dsw-alias-label-primary, #101828)"
|
|
498
|
+
};
|
|
499
|
+
/** DeepSeek brand hero title (the toggle-on look). */
|
|
500
|
+
const heroTitleBrand = {
|
|
501
|
+
...heroTitleDefault,
|
|
502
|
+
fontWeight: 600
|
|
503
|
+
};
|
|
504
|
+
/** The official 预览版 pill (mirrored from HeroShell `.previewBadge`). */
|
|
505
|
+
const heroBadge = {
|
|
506
|
+
padding: "1px 7px 0",
|
|
507
|
+
border: "1px solid var(--dsw-alias-interactive-bg-hover, #e4e7ec)",
|
|
508
|
+
borderRadius: 24,
|
|
509
|
+
background: "var(--dsw-alias-state-business-tertiary, #eef2ff)",
|
|
510
|
+
color: "var(--dsw-alias-label-primary-bluish, #2e4fd8)",
|
|
511
|
+
fontFamily: "var(--ds-font-family-code, ui-monospace, SFMono-Regular, Menlo, monospace)",
|
|
512
|
+
fontSize: 12,
|
|
513
|
+
lineHeight: "18px",
|
|
514
|
+
fontWeight: 500,
|
|
515
|
+
whiteSpace: "nowrap"
|
|
516
|
+
};
|
|
517
|
+
/** Toggle track + knob, mirroring the design language's switch look. */
|
|
518
|
+
function Switch({ checked, disabled, onToggle }) {
|
|
519
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
520
|
+
type: "button",
|
|
521
|
+
role: "switch",
|
|
522
|
+
"aria-checked": checked,
|
|
523
|
+
disabled,
|
|
524
|
+
onClick: onToggle,
|
|
525
|
+
style: {
|
|
526
|
+
position: "relative",
|
|
527
|
+
width: 40,
|
|
528
|
+
height: 22,
|
|
529
|
+
borderRadius: 11,
|
|
530
|
+
border: "none",
|
|
531
|
+
cursor: disabled ? "default" : "pointer",
|
|
532
|
+
flexShrink: 0,
|
|
533
|
+
background: checked ? "var(--dsw-alias-button-info-fill, #4176e6)" : "var(--dsw-alias-border-l4, #98a2b3)",
|
|
534
|
+
transition: "background 0.15s ease",
|
|
535
|
+
opacity: disabled ? .6 : 1
|
|
536
|
+
},
|
|
537
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { style: {
|
|
538
|
+
position: "absolute",
|
|
539
|
+
top: 2,
|
|
540
|
+
left: checked ? 20 : 2,
|
|
541
|
+
width: 18,
|
|
542
|
+
height: 18,
|
|
543
|
+
borderRadius: 9,
|
|
544
|
+
background: "#ffffff",
|
|
545
|
+
boxShadow: "0 1px 2px rgba(0, 0, 0, 0.25)",
|
|
546
|
+
transition: "left 0.15s ease"
|
|
547
|
+
} })
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
/**
|
|
551
|
+
* Render the brand-style settings page.
|
|
552
|
+
* @param props - composed slot props (client plugin registration).
|
|
553
|
+
* @returns the page element tree.
|
|
554
|
+
*/
|
|
555
|
+
function BrandStyleSection({ useBrandStyle, refresh, setEnabled, setHero, setTrajectoryTab, setSessionLogButton, setColor, t }) {
|
|
556
|
+
const state = useBrandStyle((value) => value);
|
|
557
|
+
(0, react.useEffect)(() => {
|
|
558
|
+
refresh();
|
|
559
|
+
}, [refresh]);
|
|
560
|
+
const [defaultVersion, setDefaultVersion] = (0, react.useState)(void 0);
|
|
561
|
+
(0, react.useEffect)(() => {
|
|
562
|
+
if (state.enabled) {
|
|
563
|
+
setDefaultVersion(void 0);
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
let cancelled = false;
|
|
567
|
+
const read = () => {
|
|
568
|
+
if (!cancelled) setDefaultVersion(readDefaultBuildVersion());
|
|
569
|
+
};
|
|
570
|
+
read();
|
|
571
|
+
const timer = window.setTimeout(read, 50);
|
|
572
|
+
return () => {
|
|
573
|
+
cancelled = true;
|
|
574
|
+
window.clearTimeout(timer);
|
|
575
|
+
};
|
|
576
|
+
}, [state.enabled]);
|
|
577
|
+
const disabled = state.busy;
|
|
578
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
|
|
579
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", { children: t("settings.title") }),
|
|
580
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("settings.intro") }),
|
|
581
|
+
state.error !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
582
|
+
role: "alert",
|
|
583
|
+
style: {
|
|
584
|
+
color: "var(--dsw-alias-fg-danger, #c0392b)",
|
|
585
|
+
marginTop: 12
|
|
586
|
+
},
|
|
587
|
+
children: t("settings.error").replace("{message}", state.error)
|
|
588
|
+
}),
|
|
589
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
590
|
+
style: card,
|
|
591
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
592
|
+
style: {
|
|
593
|
+
display: "flex",
|
|
594
|
+
alignItems: "flex-start",
|
|
595
|
+
gap: 12
|
|
596
|
+
},
|
|
597
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
598
|
+
style: { flex: 1 },
|
|
599
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
600
|
+
style: title,
|
|
601
|
+
children: t("settings.enabled.label")
|
|
602
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
603
|
+
style: hint,
|
|
604
|
+
children: t("settings.enabled.hint")
|
|
605
|
+
})]
|
|
606
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Switch, {
|
|
607
|
+
checked: state.enabled,
|
|
608
|
+
disabled,
|
|
609
|
+
onToggle: () => {
|
|
610
|
+
setEnabled(!state.enabled);
|
|
611
|
+
}
|
|
612
|
+
})]
|
|
613
|
+
})
|
|
614
|
+
}),
|
|
615
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
616
|
+
style: card,
|
|
617
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
618
|
+
style: {
|
|
619
|
+
display: "flex",
|
|
620
|
+
alignItems: "flex-start",
|
|
621
|
+
gap: 12
|
|
622
|
+
},
|
|
623
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
624
|
+
style: { flex: 1 },
|
|
625
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
626
|
+
style: title,
|
|
627
|
+
children: t("settings.hero.label")
|
|
628
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
629
|
+
style: hint,
|
|
630
|
+
children: t("settings.hero.hint")
|
|
631
|
+
})]
|
|
632
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Switch, {
|
|
633
|
+
checked: state.hero,
|
|
634
|
+
disabled,
|
|
635
|
+
onToggle: () => {
|
|
636
|
+
setHero(!state.hero);
|
|
637
|
+
}
|
|
638
|
+
})]
|
|
639
|
+
})
|
|
640
|
+
}),
|
|
641
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
642
|
+
style: card,
|
|
643
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
644
|
+
style: {
|
|
645
|
+
display: "flex",
|
|
646
|
+
alignItems: "flex-start",
|
|
647
|
+
gap: 12
|
|
648
|
+
},
|
|
649
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
650
|
+
style: { flex: 1 },
|
|
651
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
652
|
+
style: title,
|
|
653
|
+
children: t("settings.trajectoryTab.label")
|
|
654
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
655
|
+
style: hint,
|
|
656
|
+
children: t("settings.trajectoryTab.hint")
|
|
657
|
+
})]
|
|
658
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Switch, {
|
|
659
|
+
checked: state.trajectoryTab,
|
|
660
|
+
disabled,
|
|
661
|
+
onToggle: () => {
|
|
662
|
+
setTrajectoryTab(!state.trajectoryTab);
|
|
663
|
+
}
|
|
664
|
+
})]
|
|
665
|
+
})
|
|
666
|
+
}),
|
|
667
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
668
|
+
style: card,
|
|
669
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
670
|
+
style: {
|
|
671
|
+
display: "flex",
|
|
672
|
+
alignItems: "flex-start",
|
|
673
|
+
gap: 12
|
|
674
|
+
},
|
|
675
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
676
|
+
style: { flex: 1 },
|
|
677
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
678
|
+
style: title,
|
|
679
|
+
children: t("settings.sessionLogButton.label")
|
|
680
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
681
|
+
style: hint,
|
|
682
|
+
children: t("settings.sessionLogButton.hint")
|
|
683
|
+
})]
|
|
684
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Switch, {
|
|
685
|
+
checked: state.sessionLogButton,
|
|
686
|
+
disabled,
|
|
687
|
+
onToggle: () => {
|
|
688
|
+
setSessionLogButton(!state.sessionLogButton);
|
|
689
|
+
}
|
|
690
|
+
})]
|
|
691
|
+
})
|
|
692
|
+
}),
|
|
693
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
694
|
+
style: {
|
|
695
|
+
...card,
|
|
696
|
+
opacity: state.enabled ? 1 : .55
|
|
697
|
+
},
|
|
698
|
+
children: [
|
|
699
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
700
|
+
style: title,
|
|
701
|
+
children: t("settings.colors.label")
|
|
702
|
+
}),
|
|
703
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
704
|
+
style: hint,
|
|
705
|
+
children: t("settings.colors.hint")
|
|
706
|
+
}),
|
|
707
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
708
|
+
style: {
|
|
709
|
+
display: "flex",
|
|
710
|
+
flexWrap: "wrap",
|
|
711
|
+
gap: 10,
|
|
712
|
+
marginTop: 12
|
|
713
|
+
},
|
|
714
|
+
children: BRAND_COLORS.map((option) => {
|
|
715
|
+
const selected = state.enabled && state.color.toLowerCase() === option.value.toLowerCase();
|
|
716
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
717
|
+
type: "button",
|
|
718
|
+
"aria-pressed": selected,
|
|
719
|
+
"aria-label": t(option.nameKey),
|
|
720
|
+
disabled: disabled || !state.enabled,
|
|
721
|
+
onClick: () => {
|
|
722
|
+
setColor(option.value);
|
|
723
|
+
},
|
|
724
|
+
style: {
|
|
725
|
+
display: "flex",
|
|
726
|
+
flexDirection: "column",
|
|
727
|
+
alignItems: "center",
|
|
728
|
+
gap: 6,
|
|
729
|
+
width: 64,
|
|
730
|
+
padding: "6px 2px",
|
|
731
|
+
border: "none",
|
|
732
|
+
borderRadius: 8,
|
|
733
|
+
background: selected ? "var(--dsw-alias-interactive-bg-active, rgba(0,0,0,0.06))" : "transparent",
|
|
734
|
+
cursor: disabled || !state.enabled ? "default" : "pointer"
|
|
735
|
+
},
|
|
736
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
737
|
+
style: {
|
|
738
|
+
display: "flex",
|
|
739
|
+
alignItems: "center",
|
|
740
|
+
justifyContent: "center",
|
|
741
|
+
width: 30,
|
|
742
|
+
height: 30,
|
|
743
|
+
borderRadius: "50%",
|
|
744
|
+
background: option.value,
|
|
745
|
+
color: "#ffffff",
|
|
746
|
+
fontSize: 15,
|
|
747
|
+
boxShadow: selected ? `0 0 0 2px var(--dsw-alias-bg-overlay, #ffffff), 0 0 0 4px ${option.value}` : "0 0 0 1px rgba(0, 0, 0, 0.12)"
|
|
748
|
+
},
|
|
749
|
+
children: selected ? "✓" : ""
|
|
750
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
751
|
+
style: {
|
|
752
|
+
fontSize: 11,
|
|
753
|
+
lineHeight: 1.3,
|
|
754
|
+
textAlign: "center",
|
|
755
|
+
color: "var(--dsw-alias-label-secondary, #475467)",
|
|
756
|
+
fontWeight: selected ? 600 : 400
|
|
757
|
+
},
|
|
758
|
+
children: t(option.nameKey)
|
|
759
|
+
})]
|
|
760
|
+
}, option.id);
|
|
761
|
+
})
|
|
762
|
+
})
|
|
763
|
+
]
|
|
764
|
+
}),
|
|
765
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
766
|
+
style: card,
|
|
767
|
+
children: [
|
|
768
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
769
|
+
style: title,
|
|
770
|
+
children: t("settings.preview.label")
|
|
771
|
+
}),
|
|
772
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
773
|
+
style: hint,
|
|
774
|
+
children: state.enabled ? t("settings.preview.on") : t("settings.preview.off")
|
|
775
|
+
}),
|
|
776
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
777
|
+
"aria-hidden": "true",
|
|
778
|
+
style: {
|
|
779
|
+
display: "flex",
|
|
780
|
+
alignItems: "center",
|
|
781
|
+
gap: 10,
|
|
782
|
+
marginTop: 12,
|
|
783
|
+
minHeight: 32
|
|
784
|
+
},
|
|
785
|
+
children: state.enabled ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(BrandMark, { size: 24 }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BrandName, { size: 22 })] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.FishLogo, { size: 24 }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
786
|
+
style: defaultBrandStack,
|
|
787
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
788
|
+
style: defaultBrandTitle,
|
|
789
|
+
children: t("brand.localBuild")
|
|
790
|
+
}), defaultVersion !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
791
|
+
style: defaultBuildVersion,
|
|
792
|
+
children: defaultVersion
|
|
793
|
+
})]
|
|
794
|
+
})] })
|
|
795
|
+
}),
|
|
796
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
797
|
+
style: {
|
|
798
|
+
marginTop: 14,
|
|
799
|
+
paddingTop: 12,
|
|
800
|
+
borderTop: "1px solid var(--dsw-alias-border-l2, #d0d5dd)"
|
|
801
|
+
},
|
|
802
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
803
|
+
style: {
|
|
804
|
+
fontSize: 12,
|
|
805
|
+
lineHeight: 1.5,
|
|
806
|
+
color: "var(--dsw-alias-label-secondary, #475467)",
|
|
807
|
+
marginBottom: 8
|
|
808
|
+
},
|
|
809
|
+
children: t("settings.preview.hero.label")
|
|
810
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
811
|
+
"aria-hidden": "true",
|
|
812
|
+
style: {
|
|
813
|
+
display: "flex",
|
|
814
|
+
alignItems: "center",
|
|
815
|
+
justifyContent: "center",
|
|
816
|
+
gap: 10,
|
|
817
|
+
minHeight: 40
|
|
818
|
+
},
|
|
819
|
+
children: state.hero ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(BrandMark, { size: 34 }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
820
|
+
style: heroTitleBrand,
|
|
821
|
+
children: t("hero.title")
|
|
822
|
+
})] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
823
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.FishLogo, { size: 34 }),
|
|
824
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
825
|
+
style: heroTitleDefault,
|
|
826
|
+
children: t("brand.heroDefaultTitle")
|
|
827
|
+
}),
|
|
828
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
829
|
+
style: heroBadge,
|
|
830
|
+
children: t("brand.heroDefaultBadge")
|
|
831
|
+
})
|
|
832
|
+
] })
|
|
833
|
+
})]
|
|
834
|
+
})
|
|
835
|
+
]
|
|
836
|
+
})
|
|
837
|
+
] });
|
|
838
|
+
}
|
|
839
|
+
//#endregion
|
|
840
|
+
//#region src/client/visibility.ts
|
|
841
|
+
/**
|
|
842
|
+
* Runtime show/hide for two pieces of harness chrome the plugin controls
|
|
843
|
+
* without touching harness source:
|
|
844
|
+
* - the "轨迹 / Trajectory" conversation view tab (ui-trajectory registers it
|
|
845
|
+
* as a `conversation.view` slot occupant);
|
|
846
|
+
* - the "Session 日志 / Session log" header button (ui-session-log-export's
|
|
847
|
+
* `conversation.session.header.utilities` occupant).
|
|
848
|
+
*
|
|
849
|
+
* Both are foreign slot occupants, and the slot registry only ever adds
|
|
850
|
+
* occupants — there is no API to unregister another plugin's entry — so the
|
|
851
|
+
* controller hides the rendered elements. Hiding means an inline
|
|
852
|
+
* `display: none` (inline style wins over every class rule), and a
|
|
853
|
+
* MutationObserver watches the document because React recreates these nodes
|
|
854
|
+
* whenever the session header / view tablist remounts (session switch,
|
|
855
|
+
* locale change, view change…), which would otherwise restore the element.
|
|
856
|
+
*
|
|
857
|
+
* Locators favor the harness CSS-module local names (`<hash>_<local>` shape,
|
|
858
|
+
* stable suffix across builds) plus the localized labels as a fallback, so a
|
|
859
|
+
* future harness that renames a class degrades to "the element stays
|
|
860
|
+
* visible" instead of harming anything.
|
|
861
|
+
*/
|
|
862
|
+
/** Localized labels of the trajectory view tab (ui-trajectory `view.trajectory`). */
|
|
863
|
+
const TRAJECTORY_TAB_LABELS = /* @__PURE__ */ new Set(["轨迹", "Trajectory"]);
|
|
864
|
+
/** Localized labels of the session-log button (ui-session-log-export `header.action`). */
|
|
865
|
+
const SESSION_LOG_LABELS = /* @__PURE__ */ new Set(["Session 日志", "Session log"]);
|
|
866
|
+
/** Collapse whitespace the way rendered button text compares. */
|
|
867
|
+
function normalizedTextOf(element) {
|
|
868
|
+
return (element.textContent ?? "").replace(/\s+/g, " ").trim();
|
|
869
|
+
}
|
|
870
|
+
/** Buttons inside `scope` whose whole text is one of `labels`. */
|
|
871
|
+
function buttonsByLabel(scope, labels) {
|
|
872
|
+
const found = [];
|
|
873
|
+
for (const candidate of scope.querySelectorAll("button")) if (labels.has(normalizedTextOf(candidate))) found.push(candidate);
|
|
874
|
+
return found;
|
|
875
|
+
}
|
|
876
|
+
/**
|
|
877
|
+
* The trajectory view tab: the one tab button inside a tablist whose label is
|
|
878
|
+
* 轨迹/Trajectory. The label is the only stable hook — the tab button shares
|
|
879
|
+
* its CSS-module local (`tab`) with every other view tab, and no other
|
|
880
|
+
* tablist in the app (trajectory detail tabs, settings plugins tabs, cordis
|
|
881
|
+
* source tabs) carries this label.
|
|
882
|
+
*/
|
|
883
|
+
function trajectoryTabElements() {
|
|
884
|
+
const found = [];
|
|
885
|
+
for (const tablist of document.querySelectorAll("[role=\"tablist\"]")) found.push(...buttonsByLabel(tablist, TRAJECTORY_TAB_LABELS));
|
|
886
|
+
return found;
|
|
887
|
+
}
|
|
888
|
+
/**
|
|
889
|
+
* The session-log header button: the `_sessionLogButton` CSS-module local is
|
|
890
|
+
* unique to it (primary); the label inside the `_headerUtilities` band, then
|
|
891
|
+
* the label anywhere in the document, are the renamed-class fallbacks.
|
|
892
|
+
*/
|
|
893
|
+
function sessionLogButtonElements() {
|
|
894
|
+
const byClass = document.querySelectorAll("[class*=\"_sessionLogButton\"]");
|
|
895
|
+
if (byClass.length > 0) return [...byClass];
|
|
896
|
+
const found = [];
|
|
897
|
+
for (const band of document.querySelectorAll("[class*=\"_headerUtilities\"]")) found.push(...buttonsByLabel(band, SESSION_LOG_LABELS));
|
|
898
|
+
if (found.length === 0) found.push(...buttonsByLabel(document, SESSION_LOG_LABELS));
|
|
899
|
+
return found;
|
|
900
|
+
}
|
|
901
|
+
/**
|
|
902
|
+
* Create the controller. Before {@link VisibilityController.start} the
|
|
903
|
+
* element hooks are inert; `dispose` restores the hidden elements, so a
|
|
904
|
+
* plugin unload leaves the harness chrome exactly as it was.
|
|
905
|
+
*/
|
|
906
|
+
function createVisibilityController() {
|
|
907
|
+
let observer;
|
|
908
|
+
let scanScheduled = false;
|
|
909
|
+
let showTrajectoryTab = true;
|
|
910
|
+
let showSessionLogButton = true;
|
|
911
|
+
const hidden = /* @__PURE__ */ new Set();
|
|
912
|
+
const applyTo = (element, visible) => {
|
|
913
|
+
const style = element.style;
|
|
914
|
+
if (visible) {
|
|
915
|
+
if (hidden.delete(element)) style.removeProperty("display");
|
|
916
|
+
} else if (!hidden.has(element)) {
|
|
917
|
+
hidden.add(element);
|
|
918
|
+
style.setProperty("display", "none");
|
|
919
|
+
}
|
|
920
|
+
};
|
|
921
|
+
const scan = () => {
|
|
922
|
+
if (typeof document === "undefined") return;
|
|
923
|
+
for (const element of trajectoryTabElements()) applyTo(element, showTrajectoryTab);
|
|
924
|
+
for (const element of sessionLogButtonElements()) applyTo(element, showSessionLogButton);
|
|
925
|
+
};
|
|
926
|
+
const scheduleScan = () => {
|
|
927
|
+
if (scanScheduled) return;
|
|
928
|
+
scanScheduled = true;
|
|
929
|
+
queueMicrotask(() => {
|
|
930
|
+
scanScheduled = false;
|
|
931
|
+
scan();
|
|
932
|
+
});
|
|
933
|
+
};
|
|
934
|
+
return {
|
|
935
|
+
start() {
|
|
936
|
+
if (observer !== void 0 || typeof document === "undefined") return;
|
|
937
|
+
observer = new MutationObserver(scheduleScan);
|
|
938
|
+
observer.observe(document.documentElement, {
|
|
939
|
+
childList: true,
|
|
940
|
+
subtree: true
|
|
941
|
+
});
|
|
942
|
+
scheduleScan();
|
|
943
|
+
},
|
|
944
|
+
setTrajectoryTab(visible) {
|
|
945
|
+
showTrajectoryTab = visible;
|
|
946
|
+
scan();
|
|
947
|
+
},
|
|
948
|
+
setSessionLogButton(visible) {
|
|
949
|
+
showSessionLogButton = visible;
|
|
950
|
+
scan();
|
|
951
|
+
},
|
|
952
|
+
dispose() {
|
|
953
|
+
observer?.disconnect();
|
|
954
|
+
observer = void 0;
|
|
955
|
+
for (const element of hidden) element.style.removeProperty("display");
|
|
956
|
+
hidden.clear();
|
|
957
|
+
}
|
|
958
|
+
};
|
|
959
|
+
}
|
|
960
|
+
//#endregion
|
|
961
|
+
//#region src/client/locales.ts
|
|
962
|
+
/** Chinese (source of truth) dictionary for the dsh-brand-deepseek client plugin. */
|
|
963
|
+
const zh = {
|
|
964
|
+
"settings.nav": "品牌样式",
|
|
965
|
+
"settings.title": "品牌样式",
|
|
966
|
+
"settings.intro": "自定义 DeepSeek 品牌样式:侧边栏品牌、会话页标题(开始会话时聊天框上方)与品牌颜色均可单独控制;还可控制会话页「轨迹」页签与「Session 日志」按钮的显示。",
|
|
967
|
+
"settings.enabled.label": "自定义样式",
|
|
968
|
+
"settings.enabled.hint": "开启后侧边栏显示 DeepSeek 品牌并应用所选颜色;关闭后恢复官方默认品牌(DSH 本地构建)。",
|
|
969
|
+
"settings.hero.label": "会话页标题",
|
|
970
|
+
"settings.hero.hint": "开启后,开始新会话时聊天框上方的标题区显示 DeepSeek 品牌标题(鲸鱼图标 + 想从哪里开始?);关闭后恢复官方默认标题(探索未至之境 + 预览版徽章)。",
|
|
971
|
+
"settings.trajectoryTab.label": "轨迹页签",
|
|
972
|
+
"settings.trajectoryTab.hint": "开启后,会话页顶部的「轨迹」页签可见;关闭后隐藏该页签(轨迹数据不受影响,仍随会话记录)。",
|
|
973
|
+
"settings.sessionLogButton.label": "Session 日志按钮",
|
|
974
|
+
"settings.sessionLogButton.hint": "开启后,会话页右上角的「Session 日志」按钮可见;关闭后隐藏该按钮(导出功能不受影响)。",
|
|
975
|
+
"settings.colors.label": "品牌颜色",
|
|
976
|
+
"settings.colors.hint": "共 10 种颜色可选,当前配色为默认选项。选择后立即生效并保存。",
|
|
977
|
+
"settings.preview.label": "预览",
|
|
978
|
+
"settings.preview.on": "自定义样式已开启,侧边栏将按所选颜色显示 DeepSeek 品牌。",
|
|
979
|
+
"settings.preview.off": "自定义样式已关闭,侧边栏显示官方默认品牌(DSH 本地构建)。",
|
|
980
|
+
"settings.preview.hero.label": "会话页标题(开始会话时聊天框上方)",
|
|
981
|
+
"settings.error": "保存失败:{message}",
|
|
982
|
+
"color.official": "官方蓝(默认)",
|
|
983
|
+
"color.deep": "深蓝",
|
|
984
|
+
"color.sky": "天蓝",
|
|
985
|
+
"color.emerald": "翡翠绿",
|
|
986
|
+
"color.teal": "青绿",
|
|
987
|
+
"color.amber": "琥珀黄",
|
|
988
|
+
"color.orange": "活力橙",
|
|
989
|
+
"color.pink": "玫粉",
|
|
990
|
+
"color.violet": "紫罗兰",
|
|
991
|
+
"color.graphite": "石墨黑",
|
|
992
|
+
"hero.title": "想从哪里开始?",
|
|
993
|
+
"brand.heroDefaultTitle": "探索未至之境",
|
|
994
|
+
"brand.heroDefaultBadge": "预览版"
|
|
995
|
+
};
|
|
996
|
+
/** English dictionary, checked complete against the zh key set. */
|
|
997
|
+
const en = {
|
|
998
|
+
"settings.nav": "Brand style",
|
|
999
|
+
"settings.title": "Brand style",
|
|
1000
|
+
"settings.intro": "Customize the DeepSeek brand (sidebar brand, hero headline above the chat box, brand color — each independent) and control whether the conversation header shows the \"Trajectory\" view tab and the \"Session log\" button.",
|
|
1001
|
+
"settings.enabled.label": "Custom style",
|
|
1002
|
+
"settings.enabled.hint": "When on, the sidebar shows the DeepSeek brand in the chosen color; when off, the official default brand (DSH Local Build) is shown.",
|
|
1003
|
+
"settings.hero.label": "Hero headline",
|
|
1004
|
+
"settings.hero.hint": "When on, a new session shows the DeepSeek brand headline above the chat box (whale mark + \"Where should we start?\"); when off, the official default headline (Into the Unknown + Preview badge) is restored.",
|
|
1005
|
+
"settings.trajectoryTab.label": "Trajectory tab",
|
|
1006
|
+
"settings.trajectoryTab.hint": "When on, the \"Trajectory\" tab shows in the conversation header; when off, the tab is hidden (trajectory data is unaffected and still recorded with the session).",
|
|
1007
|
+
"settings.sessionLogButton.label": "Session log button",
|
|
1008
|
+
"settings.sessionLogButton.hint": "When on, the \"Session log\" button shows in the session header; when off, the button is hidden (the export feature still works).",
|
|
1009
|
+
"settings.colors.label": "Brand color",
|
|
1010
|
+
"settings.colors.hint": "10 colors to choose from; the current color scheme is the default. Changes apply and save immediately.",
|
|
1011
|
+
"settings.preview.label": "Preview",
|
|
1012
|
+
"settings.preview.on": "Custom style is on: the sidebar shows the DeepSeek brand in the selected color.",
|
|
1013
|
+
"settings.preview.off": "Custom style is off: the sidebar shows the official default brand (DSH Local Build).",
|
|
1014
|
+
"settings.preview.hero.label": "Hero headline (above the chat box when starting a session)",
|
|
1015
|
+
"settings.error": "Save failed: {message}",
|
|
1016
|
+
"color.official": "DeepSeek Blue (default)",
|
|
1017
|
+
"color.deep": "Deep Blue",
|
|
1018
|
+
"color.sky": "Sky Blue",
|
|
1019
|
+
"color.emerald": "Emerald",
|
|
1020
|
+
"color.teal": "Teal",
|
|
1021
|
+
"color.amber": "Amber",
|
|
1022
|
+
"color.orange": "Orange",
|
|
1023
|
+
"color.pink": "Pink",
|
|
1024
|
+
"color.violet": "Violet",
|
|
1025
|
+
"color.graphite": "Graphite",
|
|
1026
|
+
"hero.title": "Where should we start?",
|
|
1027
|
+
"brand.heroDefaultTitle": "Into the Unknown",
|
|
1028
|
+
"brand.heroDefaultBadge": "Preview"
|
|
1029
|
+
};
|
|
1030
|
+
//#endregion
|
|
1031
|
+
//#region src/client/index.ts
|
|
1032
|
+
const inject = [
|
|
1033
|
+
"slots",
|
|
1034
|
+
"remote",
|
|
1035
|
+
"remote.settings",
|
|
1036
|
+
"locale"
|
|
1037
|
+
];
|
|
1038
|
+
/** Settings namespace holding the persisted brand style. */
|
|
1039
|
+
const NAMESPACE = "dsh-brand-deepseek";
|
|
1040
|
+
/** Document-root color variable the brand SVGs fill from. */
|
|
1041
|
+
const COLOR_VAR = "--dsh-brand-deepseek-color";
|
|
1042
|
+
/** DOM id of the injected hero-headline sheet. */
|
|
1043
|
+
const HERO_STYLE_ID = "dsh-brand-deepseek-hero-style";
|
|
1044
|
+
/** Read the persisted brand style; the defaults keep today's look. */
|
|
1045
|
+
function defaultsOf(stored) {
|
|
1046
|
+
const value = stored ?? {};
|
|
1047
|
+
return {
|
|
1048
|
+
enabled: typeof value.enabled === "boolean" ? value.enabled : true,
|
|
1049
|
+
hero: typeof value.hero === "boolean" ? value.hero : true,
|
|
1050
|
+
trajectoryTab: typeof value.trajectoryTab === "boolean" ? value.trajectoryTab : true,
|
|
1051
|
+
sessionLogButton: typeof value.sessionLogButton === "boolean" ? value.sessionLogButton : true,
|
|
1052
|
+
color: typeof value.color === "string" && value.color.length > 0 ? value.color : DEFAULT_BRAND_COLOR
|
|
1053
|
+
};
|
|
1054
|
+
}
|
|
1055
|
+
/**
|
|
1056
|
+
* Client plugin body: locale dictionaries, the persisted brand-style wiring
|
|
1057
|
+
* (brand slot registration + color variable), and the settings page entry.
|
|
1058
|
+
* @param ctx - client root context.
|
|
1059
|
+
*/
|
|
1060
|
+
function apply(ctx) {
|
|
1061
|
+
ctx.effect(() => ctx.locale.register("dsh-brand-deepseek", {
|
|
1062
|
+
zh,
|
|
1063
|
+
en
|
|
1064
|
+
}), "dsh-brand-deepseek: dictionaries");
|
|
1065
|
+
const api = ctx.remote.settings;
|
|
1066
|
+
const t = ctx.locale.bind("dsh-brand-deepseek");
|
|
1067
|
+
let brandDispose;
|
|
1068
|
+
const installBrand = () => {
|
|
1069
|
+
if (brandDispose !== void 0) return;
|
|
1070
|
+
brandDispose = ctx.slots.inject("sidebar.brand.mark", () => ctx.slots.inject("sidebar.brand.name", function* () {
|
|
1071
|
+
yield ctx.slots.register({ name: "sidebar.brand.mark" }, BrandMark);
|
|
1072
|
+
yield ctx.slots.register({ name: "sidebar.brand.name" }, BrandName);
|
|
1073
|
+
}));
|
|
1074
|
+
};
|
|
1075
|
+
const uninstallBrand = () => {
|
|
1076
|
+
brandDispose?.();
|
|
1077
|
+
brandDispose = void 0;
|
|
1078
|
+
};
|
|
1079
|
+
let heroDispose;
|
|
1080
|
+
const installHero = () => {
|
|
1081
|
+
if (heroDispose !== void 0) return;
|
|
1082
|
+
heroDispose = ctx.slots.inject("conversation.hero.brand.mark", () => ctx.slots.register({
|
|
1083
|
+
name: "conversation.hero.brand.mark",
|
|
1084
|
+
locale: "dsh-brand-deepseek"
|
|
1085
|
+
}, HeroHeadlineMark));
|
|
1086
|
+
};
|
|
1087
|
+
const uninstallHero = () => {
|
|
1088
|
+
heroDispose?.();
|
|
1089
|
+
heroDispose = void 0;
|
|
1090
|
+
};
|
|
1091
|
+
/** Mount/remove the sheet rewriting the hero headline row (feature on/off). */
|
|
1092
|
+
const applyHeroStyle = (on) => {
|
|
1093
|
+
if (typeof document === "undefined") return;
|
|
1094
|
+
const existing = document.getElementById(HERO_STYLE_ID);
|
|
1095
|
+
if (on) {
|
|
1096
|
+
if (existing === null) {
|
|
1097
|
+
const tag = document.createElement("style");
|
|
1098
|
+
tag.id = HERO_STYLE_ID;
|
|
1099
|
+
tag.textContent = HERO_CSS;
|
|
1100
|
+
document.head.appendChild(tag);
|
|
1101
|
+
}
|
|
1102
|
+
} else existing?.remove();
|
|
1103
|
+
};
|
|
1104
|
+
/** Point the brand SVGs at the selected color. */
|
|
1105
|
+
const applyBrandColor = (color) => {
|
|
1106
|
+
if (typeof document === "undefined") return;
|
|
1107
|
+
document.documentElement.style.setProperty(COLOR_VAR, color);
|
|
1108
|
+
};
|
|
1109
|
+
const visibility = createVisibilityController();
|
|
1110
|
+
visibility.start();
|
|
1111
|
+
/** Reconcile the runtime with one persisted style (idempotent). */
|
|
1112
|
+
const applyState = (state) => {
|
|
1113
|
+
if (state.enabled) installBrand();
|
|
1114
|
+
else uninstallBrand();
|
|
1115
|
+
if (state.hero) installHero();
|
|
1116
|
+
else uninstallHero();
|
|
1117
|
+
applyHeroStyle(state.hero);
|
|
1118
|
+
applyBrandColor(state.color);
|
|
1119
|
+
visibility.setTrajectoryTab(state.trajectoryTab);
|
|
1120
|
+
visibility.setSessionLogButton(state.sessionLogButton);
|
|
1121
|
+
};
|
|
1122
|
+
const readPersisted = async () => {
|
|
1123
|
+
const response = await api.describe();
|
|
1124
|
+
if (!response.ok) throw new Error(response.error.message);
|
|
1125
|
+
return defaultsOf(response.value.namespaces.find((view) => view.ns === NAMESPACE)?.value);
|
|
1126
|
+
};
|
|
1127
|
+
const mutate = async (ops) => {
|
|
1128
|
+
const response = await api.mutate(NAMESPACE, ops, void 0);
|
|
1129
|
+
if (!response.ok) throw new Error(response.error.message);
|
|
1130
|
+
};
|
|
1131
|
+
const currentOf = () => {
|
|
1132
|
+
const current = brandStyle.getSnapshot();
|
|
1133
|
+
return {
|
|
1134
|
+
enabled: current.enabled,
|
|
1135
|
+
hero: current.hero,
|
|
1136
|
+
trajectoryTab: current.trajectoryTab,
|
|
1137
|
+
sessionLogButton: current.sessionLogButton,
|
|
1138
|
+
color: current.color
|
|
1139
|
+
};
|
|
1140
|
+
};
|
|
1141
|
+
brandStyle.handlers = {
|
|
1142
|
+
load: async () => {
|
|
1143
|
+
const state = await readPersisted();
|
|
1144
|
+
applyState(state);
|
|
1145
|
+
return state;
|
|
1146
|
+
},
|
|
1147
|
+
setEnabled: async (enabled) => {
|
|
1148
|
+
await mutate([{
|
|
1149
|
+
op: "set",
|
|
1150
|
+
path: ["enabled"],
|
|
1151
|
+
value: enabled
|
|
1152
|
+
}]);
|
|
1153
|
+
applyState({
|
|
1154
|
+
...currentOf(),
|
|
1155
|
+
enabled
|
|
1156
|
+
});
|
|
1157
|
+
},
|
|
1158
|
+
setHero: async (hero) => {
|
|
1159
|
+
await mutate([{
|
|
1160
|
+
op: "set",
|
|
1161
|
+
path: ["hero"],
|
|
1162
|
+
value: hero
|
|
1163
|
+
}]);
|
|
1164
|
+
applyState({
|
|
1165
|
+
...currentOf(),
|
|
1166
|
+
hero
|
|
1167
|
+
});
|
|
1168
|
+
},
|
|
1169
|
+
setTrajectoryTab: async (trajectoryTab) => {
|
|
1170
|
+
await mutate([{
|
|
1171
|
+
op: "set",
|
|
1172
|
+
path: ["trajectoryTab"],
|
|
1173
|
+
value: trajectoryTab
|
|
1174
|
+
}]);
|
|
1175
|
+
applyState({
|
|
1176
|
+
...currentOf(),
|
|
1177
|
+
trajectoryTab
|
|
1178
|
+
});
|
|
1179
|
+
},
|
|
1180
|
+
setSessionLogButton: async (sessionLogButton) => {
|
|
1181
|
+
await mutate([{
|
|
1182
|
+
op: "set",
|
|
1183
|
+
path: ["sessionLogButton"],
|
|
1184
|
+
value: sessionLogButton
|
|
1185
|
+
}]);
|
|
1186
|
+
applyState({
|
|
1187
|
+
...currentOf(),
|
|
1188
|
+
sessionLogButton
|
|
1189
|
+
});
|
|
1190
|
+
},
|
|
1191
|
+
setColor: async (color) => {
|
|
1192
|
+
await mutate([{
|
|
1193
|
+
op: "set",
|
|
1194
|
+
path: ["color"],
|
|
1195
|
+
value: color
|
|
1196
|
+
}]);
|
|
1197
|
+
applyState({
|
|
1198
|
+
...currentOf(),
|
|
1199
|
+
color
|
|
1200
|
+
});
|
|
1201
|
+
}
|
|
1202
|
+
};
|
|
1203
|
+
brandStyle.refresh();
|
|
1204
|
+
ctx.effect(() => () => {
|
|
1205
|
+
visibility.dispose();
|
|
1206
|
+
if (typeof document === "undefined") return;
|
|
1207
|
+
document.documentElement.style.removeProperty(COLOR_VAR);
|
|
1208
|
+
document.getElementById(HERO_STYLE_ID)?.remove();
|
|
1209
|
+
}, "dsh-brand-deepseek: style cleanup");
|
|
1210
|
+
const sectionInjected = {
|
|
1211
|
+
hooks: { brandStyle },
|
|
1212
|
+
refresh: () => brandStyle.refresh(),
|
|
1213
|
+
setEnabled: (enabled) => brandStyle.setEnabled(enabled),
|
|
1214
|
+
setHero: (hero) => brandStyle.setHero(hero),
|
|
1215
|
+
setTrajectoryTab: (visible) => brandStyle.setTrajectoryTab(visible),
|
|
1216
|
+
setSessionLogButton: (visible) => brandStyle.setSessionLogButton(visible),
|
|
1217
|
+
setColor: (color) => brandStyle.setColor(color)
|
|
1218
|
+
};
|
|
1219
|
+
ctx.slots.inject("settings.section", () => ctx.slots.register({
|
|
1220
|
+
name: "settings.section",
|
|
1221
|
+
id: "brand-deepseek",
|
|
1222
|
+
order: 17,
|
|
1223
|
+
label: () => t("settings.nav"),
|
|
1224
|
+
locale: "dsh-brand-deepseek",
|
|
1225
|
+
inject: () => sectionInjected
|
|
1226
|
+
}, BrandStyleSection));
|
|
1227
|
+
}
|
|
1228
|
+
//#endregion
|
|
1229
|
+
exports.apply = apply;
|
|
1230
|
+
exports.inject = inject;
|
|
1231
|
+
return module.exports;
|
|
1232
|
+
}
|
|
1233
|
+
});
|