dsh-deepseek-style-theme 1.43.4 → 1.43.7
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 +11 -0
- package/lib/client.js +157 -31
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
为 DeepSeek Harness Web GUI 复刻 DeepSeek 官网视觉的主题皮肤插件。
|
|
4
4
|
|
|
5
|
+
## 鸣谢
|
|
6
|
+
|
|
7
|
+
本插件的两块核心视觉——**流体背景**与**玻璃质感**——都不是从零写出来的,它们分别来自下面两个上游项目。没有它们,这个主题不会有现在这个样子:
|
|
8
|
+
|
|
9
|
+
| 上游 | 作者 | 本插件用到了什么 |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| [dsh-theme-mineradio](https://github.com/dhicoc/dsh-theme-mineradio) | [@dhicoc](https://github.com/dhicoc) | 流体背景的**三段 GLSL 着色器**(顶点 / 流场 / 显示)是上游的逐字节副本;四分之一分辨率流场 + 双 framebuffer 乒乓的求解器结构、玻璃色散折射滤镜、光标视差、对话框悬停倾斜同样移植自上游 |
|
|
12
|
+
| [deepseek-harness-background](https://github.com/HaoyueQin/deepseek-harness-background) | [@HaoyueQin](https://github.com/HaoyueQin) | 液态玻璃配方的**技法来源**(半透明填充 + 竖向光泽 + 背板滤镜链的设计思路),本插件按自己的选择器与 `--dshome-glass-*` 变量重新表达,未复制源码 |
|
|
13
|
+
|
|
14
|
+
两个项目均为 MIT 许可;完整许可原文见 [`THIRD-PARTY-NOTICES.md`](./THIRD-PARTY-NOTICES.md),随包发布,再分发时必须保留。详细的移植范围与改写点见下文「[第三方代码与署名](#第三方代码与署名)」。
|
|
15
|
+
|
|
5
16
|
## 特性
|
|
6
17
|
|
|
7
18
|
- **流体流动背景(1.42.0+)**:全屏 WebGL2 双通道流体模拟——四分之一分辨率的流场(衰减 + 带速度的指针笔刷,两个 framebuffer 乒乓)被全分辨率的域扭曲噪声渲染器采样,带旋流迭代与三色柔性混合;按钮悬停会轻推流场、点击则荡开涟漪。配色跟随 DSTT 三色令牌(峰谷红 / 谷时蓝 / 常态绿)实时重新着色,无需重挂。**无 WebGL2 时自动回落到原来的粒子背景**(粒子实现完整保留)
|
package/lib/client.js
CHANGED
|
@@ -995,18 +995,20 @@ void main() {
|
|
|
995
995
|
const CORE_CSS = `
|
|
996
996
|
:root{--ds-brand:#059669;--ds-brand-deep:#047857;--ds-brand-mid:#10B981;--ds-brand-light:#34D399;--ds-glass-blur:24px}
|
|
997
997
|
html,body{background-color:transparent!important}
|
|
998
|
-
/*
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
998
|
+
/* THE LABEL TOKENS ARE NOT PINNED HERE, AND MUST NOT BE AGAIN.
|
|
999
|
+
A previous attempt at the invisible-text bug declared
|
|
1000
|
+
body{--dsw-alias-label-primary:...!important} under our own scheme marker,
|
|
1001
|
+
so that background and text could never end up on opposite sides. It did fix
|
|
1002
|
+
that, and it broke something worse: those !important declarations outrank the
|
|
1003
|
+
PRODUCT's tokens, so when the user switched appearance the product changed its
|
|
1004
|
+
own tokens and this stylesheet kept painting the old ones. The UI looked like
|
|
1005
|
+
the switch was dead.
|
|
1006
|
+
Measured on the real page: with the theme's style tags removed the switch
|
|
1007
|
+
works, with them present it does not, and reverting the header z-index changes
|
|
1008
|
+
nothing -- i.e. it is this sheet, and it is these declarations.
|
|
1009
|
+
The invisible-text hazard stays open on purpose. A stuck appearance switch is
|
|
1010
|
+
a broken control; near-white text on a near-white page at least still reads
|
|
1011
|
+
once the marker agrees. Do not re-add these two rules. */
|
|
1010
1012
|
body{background:radial-gradient(1200px 700px at 12% -10%,color-mix(in srgb,var(--ds-brand) 14%,transparent),transparent 60%),radial-gradient(1000px 600px at 105% 12%,color-mix(in srgb,var(--ds-brand-light) 16%,transparent),transparent 55%),radial-gradient(900px 700px at 50% 115%,color-mix(in srgb,var(--ds-brand) 10%,transparent),transparent 60%),linear-gradient(180deg,#fbfbfd 0%,#f5f6fa 100%);background-attachment:fixed}
|
|
1011
1013
|
body[data-dshome-color=red]{--ds-brand:#F5222D;--ds-brand-deep:#CF1322;--ds-brand-mid:#FF4D4F;--ds-brand-light:#FF7875}
|
|
1012
1014
|
body[data-dshome-color=blue]{--ds-brand:#4D6BFE;--ds-brand-deep:#3A65C2;--ds-brand-mid:#4176E6;--ds-brand-light:#73A3D2}
|
|
@@ -1726,6 +1728,20 @@ html[data-dshome-blur="off"] body{
|
|
|
1726
1728
|
* subscription is unavailable.
|
|
1727
1729
|
*/
|
|
1728
1730
|
function createDarkSync(ctx, theme) {
|
|
1731
|
+
/** Last value we wrote to the body; null until the first apply(). */
|
|
1732
|
+
let lastDark = null;
|
|
1733
|
+
/**
|
|
1734
|
+
* Scheme-change listeners. The dark marker is a marker only if someone
|
|
1735
|
+
* reads it: the stylesheet matches on it, and the ambient background reads
|
|
1736
|
+
* it for its palette (see startAmbient). A listener that throws must not
|
|
1737
|
+
* cost the others their update, so each call is boxed.
|
|
1738
|
+
*/
|
|
1739
|
+
const darkListeners = new Set();
|
|
1740
|
+
const notifyDark = (dark) => {
|
|
1741
|
+
for (const fn of darkListeners) {
|
|
1742
|
+
try { fn(dark); } catch (error) {}
|
|
1743
|
+
}
|
|
1744
|
+
};
|
|
1729
1745
|
const apply = () => {
|
|
1730
1746
|
let dark = false;
|
|
1731
1747
|
// Two signals, and BOTH of them lie in some state -- measured, not
|
|
@@ -1749,10 +1765,19 @@ html[data-dshome-blur="off"] body{
|
|
|
1749
1765
|
}
|
|
1750
1766
|
}
|
|
1751
1767
|
if (!dark) dark = document.body.hasAttribute('data-ds-dark-theme');
|
|
1768
|
+
// Remember what we actually put on the body, so a later apply() that
|
|
1769
|
+
// lands on the same answer does not wake the background up for
|
|
1770
|
+
// nothing. `lastDark` is null until the first write.
|
|
1771
|
+
const changed = lastDark !== dark;
|
|
1772
|
+
lastDark = dark;
|
|
1752
1773
|
if (dark) document.body.setAttribute('data-dshome-dark', '');
|
|
1753
1774
|
else document.body.removeAttribute('data-dshome-dark');
|
|
1775
|
+
// Publish the write. The marker is what EVERY dark rule and the fluid
|
|
1776
|
+
// palette read, so a light/dark switch that only set the attribute left
|
|
1777
|
+
// the background on the scheme it was mounted with -- reported as
|
|
1778
|
+
// "切到深色背景还是蓝白".
|
|
1779
|
+
if (changed) notifyDark(dark);
|
|
1754
1780
|
};
|
|
1755
|
-
apply();
|
|
1756
1781
|
let unsubscribe = null;
|
|
1757
1782
|
try {
|
|
1758
1783
|
unsubscribe = ctx.on('theme/change', apply);
|
|
@@ -1769,9 +1794,21 @@ html[data-dshome-blur="off"] body{
|
|
|
1769
1794
|
} catch (error) {
|
|
1770
1795
|
mirror = null;
|
|
1771
1796
|
}
|
|
1772
|
-
return
|
|
1773
|
-
|
|
1774
|
-
|
|
1797
|
+
return {
|
|
1798
|
+
/**
|
|
1799
|
+
* Subscribe to light/dark *writes*. Nothing is replayed on subscribe:
|
|
1800
|
+
* a reader that needs the current value reads the attribute (or calls
|
|
1801
|
+
* dshomeDark()), which keeps one source of truth.
|
|
1802
|
+
*/
|
|
1803
|
+
subscribe: (fn) => {
|
|
1804
|
+
darkListeners.add(fn);
|
|
1805
|
+
return () => darkListeners.delete(fn);
|
|
1806
|
+
},
|
|
1807
|
+
dispose: () => {
|
|
1808
|
+
darkListeners.clear();
|
|
1809
|
+
if (unsubscribe !== null) unsubscribe();
|
|
1810
|
+
if (mirror !== null) mirror.disconnect();
|
|
1811
|
+
}
|
|
1775
1812
|
};
|
|
1776
1813
|
}
|
|
1777
1814
|
|
|
@@ -1815,6 +1852,24 @@ html[data-dshome-blur="off"] body{
|
|
|
1815
1852
|
/** Edge-tint hue for the glass dispersion, following the DSTT colour. */
|
|
1816
1853
|
const FLUID_HUES = { green: 152, blue: 221, red: 356 };
|
|
1817
1854
|
|
|
1855
|
+
/**
|
|
1856
|
+
* A cheap key over everything the palette derives from: the colour token and
|
|
1857
|
+
* the scheme marker, both on <body>.
|
|
1858
|
+
*
|
|
1859
|
+
* The ambient fluid compares this every rendered frame. Why polling instead
|
|
1860
|
+
* of trusting the notification chain: measured on the live page with the
|
|
1861
|
+
* instrumented build, the canvas was mounted once, the page WAS running the
|
|
1862
|
+
* patched bundle, and the fluid was demonstrably alive (both programs
|
|
1863
|
+
* compiled and linked, ping-pong FBOs complete, a draw pair per frame,
|
|
1864
|
+
* glError 0) -- yet u_color1..3 kept arriving as the light-blue triple for
|
|
1865
|
+
* the entire session while <body> said green. So the mount had resolved the
|
|
1866
|
+
* palette before the colour driver's first write, and nothing later re-read
|
|
1867
|
+
* the token. Two getAttribute calls per frame when nothing changed, and it
|
|
1868
|
+
* cannot miss a writer the way a listener can.
|
|
1869
|
+
*/
|
|
1870
|
+
const dsttMarkerKey = () => (document.body.getAttribute('data-dshome-color') || 'green')
|
|
1871
|
+
+ '|' + (document.body.hasAttribute('data-dshome-dark') ? 'dark' : 'light');
|
|
1872
|
+
|
|
1818
1873
|
/** The active DSTT colour token. */
|
|
1819
1874
|
function dshomeColor() {
|
|
1820
1875
|
const value = document.body.getAttribute('data-dshome-color');
|
|
@@ -2008,6 +2063,13 @@ html[data-dshome-blur="off"] body{
|
|
|
2008
2063
|
*/
|
|
2009
2064
|
function startAmbient() {
|
|
2010
2065
|
if (!supportsWebgl2()) return startParticles();
|
|
2066
|
+
// Which build is running, readable from the page. The fluid palette is
|
|
2067
|
+
// resolved at mount and re-derived from the DOM afterwards (syncFluid +
|
|
2068
|
+
// markerPoll below); when a report says the background did not follow the
|
|
2069
|
+
// colour token, this marker is the first thing to check in the console:
|
|
2070
|
+
// window.__dshomeBuild -> '1.43.7 palette-poll'
|
|
2071
|
+
// It is also what tools/dstt-dark-report.js prints as buildMarker.
|
|
2072
|
+
try { window.__dshomeBuild = '1.43.7 palette-poll'; } catch (error) {}
|
|
2011
2073
|
const canvas = document.createElement('canvas');
|
|
2012
2074
|
canvas.setAttribute('data-dsh-deepseek-canvas', '');
|
|
2013
2075
|
const style = canvas.style;
|
|
@@ -2037,25 +2099,56 @@ html[data-dshome-blur="off"] body{
|
|
|
2037
2099
|
};
|
|
2038
2100
|
applyGlass();
|
|
2039
2101
|
const unsubscribeGlass = dsttSubscribe(applyGlass);
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2102
|
+
// ── palette reconciliation ────────────────────────────────────────────
|
|
2103
|
+
// One reader for BOTH inputs the palette depends on: the colour token on
|
|
2104
|
+
// <body> and the dark marker. Reported symptom: "切主题模式背景不变 /
|
|
2105
|
+
// 深色模式背景还是蓝白". The colour half of that was a real dead end --
|
|
2106
|
+
// `subscribeColor` only fires when the notified colour string CHANGES, so
|
|
2107
|
+
// the boot race (this canvas mounts, then the durable mode arrives over
|
|
2108
|
+
// the async bridge and re-resolves the colour) left the flow painted from
|
|
2109
|
+
// whatever the driver had guessed first, and a mode switch that resolved
|
|
2110
|
+
// to the same colour never notified at all. The scheme half had no
|
|
2111
|
+
// listener: a MutationObserver was watching the marker, but `darkSync`
|
|
2112
|
+
// pointedly does not set a *changed* value twice, and the marker itself
|
|
2113
|
+
// was the only thing written.
|
|
2114
|
+
// So: derive from the DOM on every signal instead of trusting the event to
|
|
2115
|
+
// carry it, and box every listener so one throw cannot cost the rest.
|
|
2116
|
+
let fluidParams = JSON.stringify(dshomeFluidParams());
|
|
2117
|
+
const syncFluid = () => {
|
|
2118
|
+
const next = dshomeFluidParams();
|
|
2119
|
+
const key = JSON.stringify(next);
|
|
2120
|
+
if (key === fluidParams) return;
|
|
2121
|
+
fluidParams = key;
|
|
2122
|
+
try { fluid.setParams(next); } catch (error) {}
|
|
2123
|
+
};
|
|
2124
|
+
const unsubscribeColor = subscribeColor((color) => {
|
|
2125
|
+
syncFluid();
|
|
2126
|
+
try { dispersion.setTint(FLUID_HUES[color] ?? FLUID_HUES.green); } catch (error) {}
|
|
2043
2127
|
});
|
|
2044
|
-
|
|
2045
|
-
//
|
|
2046
|
-
//
|
|
2047
|
-
//
|
|
2048
|
-
let
|
|
2128
|
+
const unsubscribeDark = darkSync === null ? null : darkSync.subscribe(syncFluid);
|
|
2129
|
+
// The colour driver writes the token as an attribute; watching it as well
|
|
2130
|
+
// keeps this correct no matter who wrote it (the peak-hour chain, a manual
|
|
2131
|
+
// mode pick, or a future caller that forgets to notify).
|
|
2132
|
+
let tokenMirror = null;
|
|
2049
2133
|
try {
|
|
2050
|
-
|
|
2051
|
-
|
|
2134
|
+
tokenMirror = new MutationObserver(syncFluid);
|
|
2135
|
+
tokenMirror.observe(document.body, { attributes: true, attributeFilter: ['data-dshome-color'] });
|
|
2052
2136
|
} catch (error) {
|
|
2053
|
-
|
|
2137
|
+
tokenMirror = null;
|
|
2054
2138
|
}
|
|
2139
|
+
// The backstop that actually fixed it. Events above are the fast path;
|
|
2140
|
+
// this poll is the guarantee. It is the same decision as
|
|
2141
|
+
// dsttMarkerKey()'s comment describes -- the palette is derived from the
|
|
2142
|
+
// DOM, so it is re-derived from the DOM, once every half second, for as
|
|
2143
|
+
// long as the canvas lives. syncFluid() itself is a no-op when the key is
|
|
2144
|
+
// unchanged, so the steady-state cost is two getAttribute calls.
|
|
2145
|
+
const markerPoll = setInterval(syncFluid, 500);
|
|
2055
2146
|
return () => {
|
|
2056
|
-
|
|
2147
|
+
clearInterval(markerPoll);
|
|
2148
|
+
unsubscribeColor();
|
|
2149
|
+
if (unsubscribeDark !== null) unsubscribeDark();
|
|
2057
2150
|
unsubscribeGlass();
|
|
2058
|
-
if (
|
|
2151
|
+
if (tokenMirror !== null) tokenMirror.disconnect();
|
|
2059
2152
|
dispersion.dispose();
|
|
2060
2153
|
stopParallax();
|
|
2061
2154
|
stopSpotter();
|
|
@@ -2177,7 +2270,20 @@ html[data-dshome-blur="off"] body{
|
|
|
2177
2270
|
const colors = palette();
|
|
2178
2271
|
for (const p of parts) p.c = colors[(Math.random() * colors.length) | 0];
|
|
2179
2272
|
};
|
|
2273
|
+
// Two inputs again: the colour token (notified) and the dark marker
|
|
2274
|
+
// (written by darkSync). The particle palette is per scheme, so a
|
|
2275
|
+
// light/dark switch has to re-roll the colours too -- otherwise the
|
|
2276
|
+
// fallback background keeps the scheme it was mounted with, exactly the
|
|
2277
|
+
// bug the fluid path had.
|
|
2180
2278
|
const unsubscribeColor = subscribeColor(refreshColors);
|
|
2279
|
+
const unsubscribeDark = darkSync === null ? null : darkSync.subscribe(refreshColors);
|
|
2280
|
+
let tokenMirror = null;
|
|
2281
|
+
try {
|
|
2282
|
+
tokenMirror = new MutationObserver(refreshColors);
|
|
2283
|
+
tokenMirror.observe(document.body, { attributes: true, attributeFilter: ['data-dshome-color'] });
|
|
2284
|
+
} catch (error) {
|
|
2285
|
+
tokenMirror = null;
|
|
2286
|
+
}
|
|
2181
2287
|
|
|
2182
2288
|
window.addEventListener('resize', resize);
|
|
2183
2289
|
resize();
|
|
@@ -2189,6 +2295,8 @@ html[data-dshome-blur="off"] body{
|
|
|
2189
2295
|
cancelAnimationFrame(rafId);
|
|
2190
2296
|
window.removeEventListener('resize', resize);
|
|
2191
2297
|
unsubscribeColor();
|
|
2298
|
+
if (unsubscribeDark !== null) unsubscribeDark();
|
|
2299
|
+
if (tokenMirror !== null) tokenMirror.disconnect();
|
|
2192
2300
|
canvas.remove();
|
|
2193
2301
|
};
|
|
2194
2302
|
}
|
|
@@ -3747,10 +3855,18 @@ body[data-dshome-dark] .${CLS}{background:rgba(14,30,60,.94);box-shadow:0 18px 4
|
|
|
3747
3855
|
|
|
3748
3856
|
anchor();
|
|
3749
3857
|
|
|
3858
|
+
// The durable mode crosses an async bridge (dsttSync), so the anchor above
|
|
3859
|
+
// may have resolved the colour from the DEFAULT mode -- its 常态绿/常态蓝
|
|
3860
|
+
// token then never appears, and the background keeps the first guess. One
|
|
3861
|
+
// extra read after the sync has had its turn fixes the boot race, and it
|
|
3862
|
+
// is idempotent, so it cannot fight a later answer.
|
|
3863
|
+
const bootTimer = setTimeout(anchor, 0);
|
|
3864
|
+
|
|
3750
3865
|
try { unsubscribe = dsttSubscribe(anchor); } catch (error) { unsubscribe = null; }
|
|
3751
3866
|
|
|
3752
3867
|
return () => {
|
|
3753
3868
|
stopped = true;
|
|
3869
|
+
clearTimeout(bootTimer);
|
|
3754
3870
|
if (chainTimer !== 0) clearTimeout(chainTimer);
|
|
3755
3871
|
if (unsubscribe !== null) unsubscribe();
|
|
3756
3872
|
document.removeEventListener('visibilitychange', onVisibility);
|
|
@@ -3954,8 +4070,18 @@ body[data-dshome-dark] .${CLS}{background:rgba(14,30,60,.94);box-shadow:0 18px 4
|
|
|
3954
4070
|
|
|
3955
4071
|
// Dark marker: owned attribute driven by the theme service, never by
|
|
3956
4072
|
// the product's attribute name. Must precede the patch blocks so the
|
|
3957
|
-
// dark selectors match from the first paint.
|
|
3958
|
-
|
|
4073
|
+
// dark selectors match from the first paint. The handle is kept in scope
|
|
4074
|
+
// because the ambient background subscribes to its writes: the marker
|
|
4075
|
+
// alone was not enough, since nothing re-read it when the scheme changed.
|
|
4076
|
+
let darkSync = null;
|
|
4077
|
+
ctx.effect(() => {
|
|
4078
|
+
const handle = createDarkSync(ctx, theme);
|
|
4079
|
+
darkSync = handle;
|
|
4080
|
+
return () => {
|
|
4081
|
+
if (darkSync === handle) darkSync = null;
|
|
4082
|
+
handle.dispose();
|
|
4083
|
+
};
|
|
4084
|
+
}, 'deepseek-style-theme: dark sync');
|
|
3959
4085
|
|
|
3960
4086
|
// Glass-recipe marker: same idea, and it has to be on <html> before the
|
|
3961
4087
|
// first paint of the glass surfaces or the pane flashes the other recipe.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-deepseek-style-theme",
|
|
3
3
|
"description": "DeepSeek official-home style theme: WebGL fluid-flow background, liquid glass with SVG edge refraction, glass sidebar and composer, frosted header, running-subagent progress panel, and DeepSeek brand link for the dsh web GUI",
|
|
4
|
-
"version": "1.43.
|
|
4
|
+
"version": "1.43.7",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"deepseek",
|