dsh-deepseek-style-theme 1.43.1 → 1.43.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 +17 -2
- package/lib/client.js +261 -35
- package/lib/index.js +860 -824
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -112,13 +112,28 @@ dsh plugin --profile web add ./releases/dsh-deepseek-style-theme-1.43.1.tgz
|
|
|
112
112
|
|
|
113
113
|
### 从 npm
|
|
114
114
|
|
|
115
|
+
> ⚠️ **暂时不要用这一条。** npm 上的 `latest` 仍是 **1.43.1**,而那一版带着一个致命缺陷:客户端 bundle 的注册 id 还是旧 scope 的 `@dsh-external/dsh-deepseek-style-theme`,而宿主按包名 `dsh-deepseek-style-theme` 要求它自我注册,于是 **bundle 加载成功却什么都没注册**,主题的整个客户端半边失效。1.43.3 已修复。
|
|
116
|
+
>
|
|
117
|
+
> 在 1.43.3 发到 npm 之前,请用上面的 **tgz 别名地址** 或 `github:` 安装。
|
|
118
|
+
|
|
115
119
|
```bash
|
|
120
|
+
# 修复版发到 npm 之后可用;在那之前用 tgz 别名地址
|
|
116
121
|
dsh plugin --profile web add dsh-deepseek-style-theme
|
|
117
122
|
```
|
|
118
123
|
|
|
119
|
-
> 包名是**无 scope** 的 `dsh-deepseek-style-theme`。这是刻意的:`@dsh-external` 这个 scope 在 npm 上属于别人(`wulei1107`,下面挂着 `@dsh-external/dsh-vision-toolkit
|
|
124
|
+
> 包名是**无 scope** 的 `dsh-deepseek-style-theme`。这是刻意的:`@dsh-external` 这个 scope 在 npm 上属于别人(`wulei1107`,下面挂着 `@dsh-external/dsh-vision-toolkit`),不是本项目的命名空间,也发不进去。
|
|
125
|
+
>
|
|
126
|
+
> **改名的三个引用点必须同时对齐**,缺一个都会静默失效,且失效方式各不相同:
|
|
127
|
+
>
|
|
128
|
+
> | 位置 | 不对齐的后果 |
|
|
129
|
+
> |---|---|
|
|
130
|
+
> | `package.json` 的 `name` | 包装不进去 / 目录名不符 |
|
|
131
|
+
> | `cordis.patch.yml` 的 `name:` | Loader 解析不到模块,**插件装配失败** |
|
|
132
|
+
> | `lib/client.js` 里 `__ModuleLoader__.load` 的 `id` | bundle 加载成功却什么都没注册,**客户端半边失效**(1.43.1 的缺陷) |
|
|
133
|
+
>
|
|
134
|
+
> 自查:`grep '@dsh-external' lib/` 应当**一处都不命中**;命中即漏改(有意保留的兼容位置除外)。
|
|
120
135
|
>
|
|
121
|
-
> npm 是最省事的安装源:不走 `git clone`(绕开代理/证书问题),也不依赖 GitHub Release 资产(绕开 `releases/latest` 那类 URL
|
|
136
|
+
> npm 是最省事的安装源:不走 `git clone`(绕开代理/证书问题),也不依赖 GitHub Release 资产(绕开 `releases/latest` 那类 URL 腐烂)。
|
|
122
137
|
|
|
123
138
|
```bash
|
|
124
139
|
# 或任意 git 仓库地址(若该仓库未提交构建产物,
|
package/lib/client.js
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
window.__ModuleLoader__.load({
|
|
2
|
-
id:
|
|
2
|
+
// This id must equal the package name: the host builds the browser bundle
|
|
3
|
+
// URL as /plugins/<entry.id>/client.js (dsh-client-modules), and the loader
|
|
4
|
+
// then requires that the bundle registers itself under that same id. The
|
|
5
|
+
// package was renamed to the unscoped dsh-deepseek-style-theme in 1.43.1
|
|
6
|
+
// while this literal kept the old scope, so on any profile carrying the new
|
|
7
|
+
// name the bundle loaded and registered nothing --
|
|
8
|
+
// "loaded without registering \"dsh-deepseek-style-theme\" via __ModuleLoader__.load"
|
|
9
|
+
// -- which took the whole client half down. It stayed invisible on the
|
|
10
|
+
// development machine because that profile still installs the scoped package,
|
|
11
|
+
// whose bundle URL matches the stale literal.
|
|
12
|
+
id: "dsh-deepseek-style-theme",
|
|
3
13
|
factory: (require) => {
|
|
4
14
|
var module = { exports: {} };
|
|
5
15
|
var exports = module.exports;
|
|
@@ -26,7 +36,7 @@ window.__ModuleLoader__.load({
|
|
|
26
36
|
// page data — session titles, file paths, workspace names — must go
|
|
27
37
|
// through `textContent`/`title`/`setAttribute`, never `innerHTML`. An
|
|
28
38
|
// interpolated value there would be DOM XSS.
|
|
29
|
-
const PLUGIN_ID = "
|
|
39
|
+
const PLUGIN_ID = "dsh-deepseek-style-theme";
|
|
30
40
|
|
|
31
41
|
// ─── fluid background + liquid-glass refraction ───────────────────────
|
|
32
42
|
// Ported from dsh-theme-mineradio v2.3.8 (MIT, Copyright (c) 2026 John Wu).
|
|
@@ -632,7 +642,40 @@ void main() {
|
|
|
632
642
|
var TALL_FILTER_ID = "dshome-glass-dispersion-tall";
|
|
633
643
|
var TALL_TINT_ID = "dshome-glass-dispersion-tall-tint";
|
|
634
644
|
var TALL_REFRACT_ID = "dshome-glass-dispersion-tall-refract";
|
|
635
|
-
|
|
645
|
+
// Two composer filters: `narrow` (an 8px band) and `wide` (16px).
|
|
646
|
+
//
|
|
647
|
+
// The band is sized by what the user can actually SEE, which is the top and
|
|
648
|
+
// bottom edges: the card's top abuts the 任务 bar and its bottom abuts the
|
|
649
|
+
// status line, so displacing the backdrop across those hard edges shows,
|
|
650
|
+
// while left and right sit over a continuous background where the same
|
|
651
|
+
// displacement changes nothing. The user accepted that asymmetry ("允许左右
|
|
652
|
+
// 无反射"), so these maps are tuned for the VERTICAL band and the horizontal
|
|
653
|
+
// one is left wherever it lands.
|
|
654
|
+
//
|
|
655
|
+
// Vertical band on screen = (edge + softness) / mapHeight * 1.56 * cardHeight
|
|
656
|
+
// -- the filter region is 156% of the surface's height (see filterMarkup) --
|
|
657
|
+
// so at the upstream 400x92 aspect and a 112px-tall card that is 1.90x the
|
|
658
|
+
// map inset: 1.7 + 2.5 gives 8px, 3.4 + 5 gives 16px.
|
|
659
|
+
var COMPOSER_FILTER_ID = "dshome-glass-dispersion-composer";
|
|
660
|
+
var COMPOSER_TINT_ID = "dshome-glass-dispersion-composer-tint";
|
|
661
|
+
var COMPOSER_REFRACT_ID = "dshome-glass-dispersion-composer-refract";
|
|
662
|
+
var WIDE_FILTER_ID = "dshome-glass-dispersion-composer-wide";
|
|
663
|
+
var WIDE_TINT_ID = "dshome-glass-dispersion-composer-wide-tint";
|
|
664
|
+
var WIDE_REFRACT_ID = "dshome-glass-dispersion-composer-wide-refract";
|
|
665
|
+
var COMPOSER_MAP_W = 400;
|
|
666
|
+
var COMPOSER_MAP_H = 92;
|
|
667
|
+
var COMPOSER_MAP_RADIUS = 50;
|
|
668
|
+
var COMPOSER_MAP_EDGE = 1.7;
|
|
669
|
+
var COMPOSER_MAP_SOFTNESS = 2.5;
|
|
670
|
+
var COMPOSER_WIDE_EDGE = 3.4;
|
|
671
|
+
var COMPOSER_WIDE_SOFTNESS = 5;
|
|
672
|
+
// Chromatic aberration, composer filters only. 0.18 splits the base
|
|
673
|
+
// displacement into three: red at 118%, blue at 82%, green in the middle,
|
|
674
|
+
// which is what makes a lens edge fringe instead of merely tinting. Kept to
|
|
675
|
+
// the two composer filters because it triples the displacement passes, and
|
|
676
|
+
// because the shared filters have to keep upstream's exact shape for the
|
|
677
|
+
// frosted recipe.
|
|
678
|
+
var COMPOSER_ABERRATION = 0.18;
|
|
636
679
|
var DEFAULT_REF_SCALE = 60;
|
|
637
680
|
// The liquid recipe drives the same feDisplacementMap harder: at 60 the
|
|
638
681
|
// bend is there but only just legible once the blur sits on top of it, and
|
|
@@ -644,15 +687,20 @@ void main() {
|
|
|
644
687
|
var TINT_SAT = 0.85;
|
|
645
688
|
var TINT_LIGHT = 0.6;
|
|
646
689
|
var DEFAULT_TINT_HUE = 44;
|
|
647
|
-
|
|
690
|
+
// Upstream derives the inset from the map's SHORT side (7% of it, halved),
|
|
691
|
+
// which is only uniform if the map is square. The composer passes `edge`
|
|
692
|
+
// instead: one absolute inset for both axes, which comes out uniform as
|
|
693
|
+
// long as the map's aspect matches the filter region's (see COMPOSER_MAP_*).
|
|
694
|
+
// Defaults reproduce upstream's numbers exactly, so the frosted recipe's
|
|
695
|
+
// map stays byte for byte what it was.
|
|
696
|
+
function buildDisplacementMapDataUrl(width = 400, height = 92, radius = 50, edge = null, softness = 11) {
|
|
648
697
|
const w = Math.max(240, Math.round(width));
|
|
649
698
|
const h = Math.max(48, Math.round(height));
|
|
650
699
|
const r = Math.max(12, Math.round(radius));
|
|
651
|
-
const
|
|
652
|
-
const
|
|
653
|
-
const
|
|
654
|
-
const
|
|
655
|
-
const svg2 = `<svg viewBox="0 0 ${w} ${h}" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="glass-x" x1="0%" y1="0%" x2="100%" y2="0%"><stop offset="0%" stop-color="#000"/><stop offset="100%" stop-color="#f00"/></linearGradient><linearGradient id="glass-y" x1="0%" y1="0%" x2="0%" y2="100%"><stop offset="0%" stop-color="#000"/><stop offset="100%" stop-color="#00f"/></linearGradient></defs><rect x="0" y="0" width="${w}" height="${h}" fill="#000"/><rect x="0" y="0" width="${w}" height="${h}" rx="${r}" fill="url(#glass-x)"/><rect x="0" y="0" width="${w}" height="${h}" rx="${r}" fill="url(#glass-y)" style="mix-blend-mode:screen"/><rect x="${edge.toFixed(2)}" y="${edge.toFixed(2)}" width="${innerW.toFixed(2)}" height="${innerH.toFixed(2)}" rx="${r}" fill="hsl(0 0% 50% / 1)" style="filter:blur(11px)"/></svg>`;
|
|
700
|
+
const inset = edge === null ? Math.min(w, h) * 0.035 : Math.max(0, edge);
|
|
701
|
+
const innerW = Math.max(1, w - inset * 2);
|
|
702
|
+
const innerH = Math.max(1, h - inset * 2);
|
|
703
|
+
const svg2 = `<svg viewBox="0 0 ${w} ${h}" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="glass-x" x1="0%" y1="0%" x2="100%" y2="0%"><stop offset="0%" stop-color="#000"/><stop offset="100%" stop-color="#f00"/></linearGradient><linearGradient id="glass-y" x1="0%" y1="0%" x2="0%" y2="100%"><stop offset="0%" stop-color="#000"/><stop offset="100%" stop-color="#00f"/></linearGradient></defs><rect x="0" y="0" width="${w}" height="${h}" fill="#000"/><rect x="0" y="0" width="${w}" height="${h}" rx="${r}" fill="url(#glass-x)"/><rect x="0" y="0" width="${w}" height="${h}" rx="${r}" fill="url(#glass-y)" style="mix-blend-mode:screen"/><rect x="${inset.toFixed(2)}" y="${inset.toFixed(2)}" width="${innerW.toFixed(2)}" height="${innerH.toFixed(2)}" rx="${r}" fill="hsl(0 0% 50% / 1)" style="filter:blur(${softness})"/></svg>`;
|
|
656
704
|
return `data:image/svg+xml,${encodeURIComponent(svg2)}`;
|
|
657
705
|
}
|
|
658
706
|
// Inlined from upstream `src/client/glass-dispersion.ts` (line 2409) —
|
|
@@ -693,8 +741,29 @@ void main() {
|
|
|
693
741
|
}
|
|
694
742
|
// Inlined from upstream `src/client/glass-dispersion.ts` (line 2443) —
|
|
695
743
|
// builds the wide/tall SVG <filter> pair startGlassDispersion injects.
|
|
696
|
-
|
|
697
|
-
|
|
744
|
+
//
|
|
745
|
+
// `aberration` (0 = off, upstream's shape byte for byte) adds true
|
|
746
|
+
// chromatic aberration: the backdrop is displaced TWICE MORE at slightly
|
|
747
|
+
// different scales, and the result is assembled channel by channel -- R
|
|
748
|
+
// from the stronger displacement, B from the weaker, G from the base. That
|
|
749
|
+
// is what puts a red/blue fringe on a real lens edge; the tinted
|
|
750
|
+
// difference-blend below is only a colour wash and does not separate
|
|
751
|
+
// channels at all.
|
|
752
|
+
//
|
|
753
|
+
// The two extra maps carry a data-aberration factor so setRefraction() can
|
|
754
|
+
// rescale them in step with the base one instead of flattening all three
|
|
755
|
+
// to the same value.
|
|
756
|
+
function filterMarkup(id, refractId, tintId, map, aberration = 0) {
|
|
757
|
+
const chromatic = aberration <= 0 ? '' :
|
|
758
|
+
`<feDisplacementMap data-aberration="${(1 + aberration).toFixed(3)}" in="SourceGraphic" in2="map" scale="${DEFAULT_REF_SCALE}" xChannelSelector="R" yChannelSelector="B" result="refractR"></feDisplacementMap>` +
|
|
759
|
+
`<feDisplacementMap data-aberration="${(1 - aberration).toFixed(3)}" in="SourceGraphic" in2="map" scale="${DEFAULT_REF_SCALE}" xChannelSelector="R" yChannelSelector="B" result="refractB"></feDisplacementMap>` +
|
|
760
|
+
`<feColorMatrix in="refracted" type="matrix" values="0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0" result="onlyG"></feColorMatrix>` +
|
|
761
|
+
`<feColorMatrix in="refractR" type="matrix" values="1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0" result="onlyR"></feColorMatrix>` +
|
|
762
|
+
`<feColorMatrix in="refractB" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0" result="onlyB"></feColorMatrix>` +
|
|
763
|
+
`<feBlend in="onlyG" in2="onlyR" mode="screen" result="rgMerge"></feBlend>` +
|
|
764
|
+
`<feBlend in="rgMerge" in2="onlyB" mode="screen" result="aberrated"></feBlend>`;
|
|
765
|
+
const merged = chromatic === '' ? 'refracted' : 'aberrated';
|
|
766
|
+
return `<filter id="${id}" color-interpolation-filters="sRGB" x="-12%" y="-28%" width="124%" height="156%"><feImage href="${map}" x="0" y="0" width="100%" height="100%" preserveAspectRatio="none" result="map"></feImage><feDisplacementMap id="${refractId}" in="SourceGraphic" in2="map" scale="${DEFAULT_REF_SCALE}" xChannelSelector="R" yChannelSelector="B" result="refracted"></feDisplacementMap>${chromatic}<feOffset in="${merged}" dx="${EDGE_DX}" dy="0" result="shifted"></feOffset><feBlend in="shifted" in2="${merged}" mode="difference" result="edgeDiff"></feBlend><feColorMatrix id="${tintId}" in="edgeDiff" type="matrix" values="${tintMatrix(DEFAULT_TINT_HUE, TINT_OPACITY)}" result="edgeTint"></feColorMatrix><feBlend in="${merged}" in2="edgeTint" mode="screen" result="output"></feBlend><feGaussianBlur in="output" stdDeviation="0.5"></feGaussianBlur></filter>`;
|
|
698
767
|
}
|
|
699
768
|
/**
|
|
700
769
|
* Mount the two SVG backdrop filters and set `ATTR` on <html> so the
|
|
@@ -721,7 +790,9 @@ void main() {
|
|
|
721
790
|
}
|
|
722
791
|
const mapWide = buildDisplacementMapDataUrl(400, 92, 50);
|
|
723
792
|
const mapTall = buildDisplacementMapDataUrl(92, 400, 50);
|
|
724
|
-
const
|
|
793
|
+
const mapComposer = buildDisplacementMapDataUrl(COMPOSER_MAP_W, COMPOSER_MAP_H, COMPOSER_MAP_RADIUS, COMPOSER_MAP_EDGE, COMPOSER_MAP_SOFTNESS);
|
|
794
|
+
const mapComposerWide = buildDisplacementMapDataUrl(COMPOSER_MAP_W, COMPOSER_MAP_H, COMPOSER_MAP_RADIUS, COMPOSER_WIDE_EDGE, COMPOSER_WIDE_SOFTNESS);
|
|
795
|
+
const markup = `<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" width="0" height="0" style="position:absolute;overflow:hidden"><defs>` + filterMarkup(FILTER_ID, REFRACT_ID, TINT_ID, mapWide) + filterMarkup(TALL_FILTER_ID, TALL_REFRACT_ID, TALL_TINT_ID, mapTall) + filterMarkup(COMPOSER_FILTER_ID, COMPOSER_REFRACT_ID, COMPOSER_TINT_ID, mapComposer, COMPOSER_ABERRATION) + filterMarkup(WIDE_FILTER_ID, WIDE_REFRACT_ID, WIDE_TINT_ID, mapComposerWide, COMPOSER_ABERRATION) + `</defs></svg>`;
|
|
725
796
|
const container = document.createElement("div");
|
|
726
797
|
container.setAttribute("aria-hidden", "true");
|
|
727
798
|
container.style.cssText = "position:absolute;width:0;height:0;overflow:hidden;";
|
|
@@ -736,8 +807,15 @@ void main() {
|
|
|
736
807
|
tint.forEach((node) => node.setAttribute("values", values));
|
|
737
808
|
},
|
|
738
809
|
setRefraction(scale) {
|
|
739
|
-
|
|
740
|
-
|
|
810
|
+
// The aberration maps are the same displacement at a slightly
|
|
811
|
+
// different scale, so they carry a factor and are rescaled in step;
|
|
812
|
+
// flattening them all to one value would erase the fringing.
|
|
813
|
+
const base = Math.max(0, Math.min(140, Number(scale) || 0));
|
|
814
|
+
refract.forEach((node) => {
|
|
815
|
+
const factor = Number(node.getAttribute("data-aberration"));
|
|
816
|
+
const value = Number.isFinite(factor) && factor > 0 ? base * factor : base;
|
|
817
|
+
node.setAttribute("scale", String(Math.max(0, Math.min(140, value))));
|
|
818
|
+
});
|
|
741
819
|
},
|
|
742
820
|
dispose() {
|
|
743
821
|
root.removeAttribute(ATTR);
|
|
@@ -1343,7 +1421,15 @@ html[data-dshome-dispersion] .VOzbGW_panel{
|
|
|
1343
1421
|
// agent is already working is `"steering"`. Measured on the current build,
|
|
1344
1422
|
// a transcript can contain ONLY steering bubbles — anchoring the selector
|
|
1345
1423
|
// on "user" alone silently skipped every bubble on the page.
|
|
1346
|
-
|
|
1424
|
+
//
|
|
1425
|
+
// The second half covers the gap the user hit: a message typed while the
|
|
1426
|
+
// agent is still working is painted in the transcript BEFORE it is picked
|
|
1427
|
+
// up, and at that moment it sits outside any flow-kind container, so the
|
|
1428
|
+
// two anchored selectors miss it and it renders unglazed until the turn
|
|
1429
|
+
// consumes it. Rather than enumerate the container it lands in, this side
|
|
1430
|
+
// takes any bubble that is NOT part of an assistant step — which is the
|
|
1431
|
+
// only place a bubble must not be glassed.
|
|
1432
|
+
const LIQUID_BUBBLE = ':is([data-chat-flow-kind="user"],[data-chat-flow-kind="steering"]) [class*="bubble" i],[class*="bubble" i]:not([data-chat-flow-kind="assistant-step"] *)';
|
|
1347
1433
|
const LIQUID_CSS = `
|
|
1348
1434
|
@property --dshome-liquid-sweep{syntax:"<angle>";inherits:false;initial-value:0deg}
|
|
1349
1435
|
@keyframes dshome-liquid-sweep{from{--dshome-liquid-sweep:0deg}to{--dshome-liquid-sweep:360deg}}
|
|
@@ -1362,6 +1448,13 @@ html[data-dshome-glass="liquid"]{
|
|
|
1362
1448
|
than everything else on purpose: at the shared 11px the flow behind it stays
|
|
1363
1449
|
legible and the pane never separates from the transcript. */
|
|
1364
1450
|
--dshome-liquid-blur-sidebar:24px;
|
|
1451
|
+
/* The composer blurs less than the rest on purpose. Filters in a backdrop
|
|
1452
|
+
chain apply left to right, so url(refract) followed by blur(N) blurs the
|
|
1453
|
+
refraction AFTER it happens: a band narrower than about the blur radius is
|
|
1454
|
+
simply erased. At 11px the purpose-built ~4px band was invisible, which is
|
|
1455
|
+
exactly what was reported. 8px lets a ~10px band read while still frosting
|
|
1456
|
+
the backdrop. */
|
|
1457
|
+
--dshome-liquid-blur-composer:8px;
|
|
1365
1458
|
/* One extra veil on top of the sidebar's glass. The pane sits over the fluid
|
|
1366
1459
|
at roughly the same tone as the conversation it borders, so without this
|
|
1367
1460
|
lift the two read as one surface. It is white in light mode. */
|
|
@@ -1424,6 +1517,25 @@ html[data-dshome-glass="liquid"] [data-composer-card]::after{
|
|
|
1424
1517
|
background-image:none!important;
|
|
1425
1518
|
box-shadow:none!important;
|
|
1426
1519
|
}
|
|
1520
|
+
/* The frosted recipe gives the composer a brand-tinted conic ring, painted by
|
|
1521
|
+
the patch block at inset:-1px and spun by dsh-border-spin. Under liquid the
|
|
1522
|
+
user asked for it to go: it sits one pixel OUTSIDE the card's own 1.5px white
|
|
1523
|
+
border, so the pair reads as a coloured line with a white line outside it, and
|
|
1524
|
+
two competing edges is what liquid glass is not. content:none removes the
|
|
1525
|
+
pseudo-element outright rather than painting it transparent. */
|
|
1526
|
+
html[data-dshome-glass="liquid"] body [data-composer-card]::before{
|
|
1527
|
+
content:none!important;
|
|
1528
|
+
background:none!important;
|
|
1529
|
+
animation:none!important;
|
|
1530
|
+
}
|
|
1531
|
+
/* With the ring gone the card's own 1.5px white border is the only edge left,
|
|
1532
|
+
and on its own it reads as a flat white outline rather than as glass — which
|
|
1533
|
+
is what the user saw next. Liquid's edge is the inset hairline plus the
|
|
1534
|
+
refraction, so the border goes transparent. The WIDTH stays: removing it
|
|
1535
|
+
would resize the box and shift every control inside by 3px. */
|
|
1536
|
+
html[data-dshome-glass="liquid"] body [data-composer-card]{
|
|
1537
|
+
border-color:transparent!important;
|
|
1538
|
+
}
|
|
1427
1539
|
/* The sidebar pane carries its own fill and its own shape: rounded only on the
|
|
1428
1540
|
edge that faces the conversation, so it reads as a panel clipped to the
|
|
1429
1541
|
window rather than as a full-bleed column. The hairline ring in the shared
|
|
@@ -1462,9 +1574,14 @@ html[data-dshome-glass="liquid"] body .hHd-Xa_root::after{
|
|
|
1462
1574
|
}
|
|
1463
1575
|
/* The travelling edge highlight. mask-composite:exclude turns the filled
|
|
1464
1576
|
conic gradient into a 1px perimeter band; padding:1px on the pseudo-element
|
|
1465
|
-
sets that band's width without touching the product's box model.
|
|
1466
|
-
|
|
1467
|
-
|
|
1577
|
+
sets that band's width without touching the product's box model.
|
|
1578
|
+
It is drawn on the SIDEBAR ONLY. The user asked for it off the composer and
|
|
1579
|
+
off the message bubbles under liquid: on a small pill the band reads as a
|
|
1580
|
+
bright white outline rather than as light travelling a glass edge, and on the
|
|
1581
|
+
composer it doubled up with the card's own border. The bubbles and the
|
|
1582
|
+
composer keep liquid's other three marks — the translucent fill, the sheen
|
|
1583
|
+
and the refraction. */
|
|
1584
|
+
html[data-dshome-glass="liquid"] .hHd-Xa_root::after{
|
|
1468
1585
|
content:"";position:absolute;inset:0;z-index:2;pointer-events:none;border-radius:inherit;
|
|
1469
1586
|
padding:1px;
|
|
1470
1587
|
background:conic-gradient(from var(--dshome-liquid-sweep),
|
|
@@ -1477,12 +1594,31 @@ html[data-dshome-glass="liquid"] ${LIQUID_BUBBLE}::after{
|
|
|
1477
1594
|
mask-composite:exclude;
|
|
1478
1595
|
animation:dshome-liquid-sweep 6.5s linear infinite;
|
|
1479
1596
|
}
|
|
1480
|
-
|
|
1597
|
+
/* The composer's edge refraction. Three states, selected by the durable
|
|
1598
|
+
composerRefraction setting (see html[data-dshome-composer]):
|
|
1599
|
+
narrow (default) -- an 8px band top and bottom
|
|
1600
|
+
wide -- a 16px band top and bottom
|
|
1601
|
+
off -- no dispersion at all
|
|
1602
|
+
Left and right are left as they fall: the band is there, but over a
|
|
1603
|
+
continuous background a horizontal displacement has nothing to show, which
|
|
1604
|
+
the user accepted. */
|
|
1481
1605
|
html[data-dshome-dispersion][data-dshome-glass="liquid"] body .wSkVaW_header:hover::before,
|
|
1482
1606
|
html[data-dshome-dispersion][data-dshome-glass="liquid"] body ${LIQUID_BUBBLE}{
|
|
1483
1607
|
-webkit-backdrop-filter:url(#dshome-glass-dispersion) blur(var(--dshome-liquid-blur)) saturate(var(--dshome-liquid-saturate))!important;
|
|
1484
1608
|
backdrop-filter:url(#dshome-glass-dispersion) blur(var(--dshome-liquid-blur)) saturate(var(--dshome-liquid-saturate))!important;
|
|
1485
1609
|
}
|
|
1610
|
+
html[data-dshome-dispersion][data-dshome-glass="liquid"][data-dshome-composer="narrow"] body [data-composer-card]{
|
|
1611
|
+
-webkit-backdrop-filter:url(#dshome-glass-dispersion-composer) blur(var(--dshome-liquid-blur-composer)) saturate(var(--dshome-liquid-saturate))!important;
|
|
1612
|
+
backdrop-filter:url(#dshome-glass-dispersion-composer) blur(var(--dshome-liquid-blur-composer)) saturate(var(--dshome-liquid-saturate))!important;
|
|
1613
|
+
}
|
|
1614
|
+
html[data-dshome-dispersion][data-dshome-glass="liquid"][data-dshome-composer="wide"] body [data-composer-card]{
|
|
1615
|
+
-webkit-backdrop-filter:url(#dshome-glass-dispersion-composer-wide) blur(var(--dshome-liquid-blur-composer)) saturate(var(--dshome-liquid-saturate))!important;
|
|
1616
|
+
backdrop-filter:url(#dshome-glass-dispersion-composer-wide) blur(var(--dshome-liquid-blur-composer)) saturate(var(--dshome-liquid-saturate))!important;
|
|
1617
|
+
}
|
|
1618
|
+
html[data-dshome-dispersion][data-dshome-glass="liquid"][data-dshome-composer="off"] body [data-composer-card]{
|
|
1619
|
+
-webkit-backdrop-filter:blur(var(--dshome-liquid-blur-composer)) saturate(var(--dshome-liquid-saturate))!important;
|
|
1620
|
+
backdrop-filter:blur(var(--dshome-liquid-blur-composer)) saturate(var(--dshome-liquid-saturate))!important;
|
|
1621
|
+
}
|
|
1486
1622
|
/* The workspace list is faded out at its bottom by a gradient whose end stop is
|
|
1487
1623
|
the sidebar-fill token — an OPAQUE colour matched to the product's own sidebar
|
|
1488
1624
|
background. Over a translucent glass pane that stop no longer matches what is
|
|
@@ -1504,8 +1640,7 @@ html[data-dshome-dispersion][data-dshome-glass="liquid"] body .hHd-Xa_root::befo
|
|
|
1504
1640
|
backdrop-filter:url(#dshome-glass-dispersion) blur(var(--dshome-liquid-blur-sidebar)) saturate(var(--dshome-liquid-saturate))!important;
|
|
1505
1641
|
}
|
|
1506
1642
|
@media (prefers-reduced-motion:reduce){
|
|
1507
|
-
html[data-dshome-glass="liquid"] .hHd-Xa_root::after
|
|
1508
|
-
html[data-dshome-glass="liquid"] ${LIQUID_BUBBLE}::after{
|
|
1643
|
+
html[data-dshome-glass="liquid"] .hHd-Xa_root::after{
|
|
1509
1644
|
animation:none;
|
|
1510
1645
|
}
|
|
1511
1646
|
}
|
|
@@ -1577,12 +1712,17 @@ html[data-dshome-dispersion][data-dshome-glass="liquid"] body .hHd-Xa_root::befo
|
|
|
1577
1712
|
function createDarkSync(ctx, theme) {
|
|
1578
1713
|
const apply = () => {
|
|
1579
1714
|
let dark = false;
|
|
1580
|
-
//
|
|
1581
|
-
//
|
|
1582
|
-
//
|
|
1583
|
-
//
|
|
1584
|
-
|
|
1585
|
-
|
|
1715
|
+
// Two signals, and BOTH of them lie in some state -- measured, not
|
|
1716
|
+
// assumed:
|
|
1717
|
+
// * the theme service reported `light` while the page was in dark
|
|
1718
|
+
// mode, so treating it as the only source painted a dark page light;
|
|
1719
|
+
// * `data-ds-dark-theme` was still on <body> after a switch back to
|
|
1720
|
+
// light, so treating IT as authoritative painted a light page dark.
|
|
1721
|
+
// This is the precedence that shipped through 1.43.1 and behaved in both
|
|
1722
|
+
// modes: ask the service first, and let the product's own attribute
|
|
1723
|
+
// override a "not dark" answer. Keep it, and sanity-check the signal
|
|
1724
|
+
// before touching it again.
|
|
1725
|
+
if (theme !== undefined && theme !== null && typeof theme.getTheme === 'function') {
|
|
1586
1726
|
try {
|
|
1587
1727
|
const snap = theme.getTheme();
|
|
1588
1728
|
dark = snap !== undefined && snap !== null &&
|
|
@@ -1592,6 +1732,7 @@ html[data-dshome-dispersion][data-dshome-glass="liquid"] body .hHd-Xa_root::befo
|
|
|
1592
1732
|
dark = false;
|
|
1593
1733
|
}
|
|
1594
1734
|
}
|
|
1735
|
+
if (!dark) dark = document.body.hasAttribute('data-ds-dark-theme');
|
|
1595
1736
|
if (dark) document.body.setAttribute('data-dshome-dark', '');
|
|
1596
1737
|
else document.body.removeAttribute('data-dshome-dark');
|
|
1597
1738
|
};
|
|
@@ -1629,12 +1770,14 @@ html[data-dshome-dispersion][data-dshome-glass="liquid"] body .hHd-Xa_root::befo
|
|
|
1629
1770
|
const root = document.documentElement;
|
|
1630
1771
|
const apply = () => {
|
|
1631
1772
|
root.setAttribute('data-dshome-glass', dsttGetGlass());
|
|
1773
|
+
root.setAttribute('data-dshome-composer', dsttGetComposer());
|
|
1632
1774
|
};
|
|
1633
1775
|
apply();
|
|
1634
1776
|
const unsubscribe = dsttSubscribe(apply);
|
|
1635
1777
|
return () => {
|
|
1636
1778
|
unsubscribe();
|
|
1637
1779
|
root.removeAttribute('data-dshome-glass');
|
|
1780
|
+
root.removeAttribute('data-dshome-composer');
|
|
1638
1781
|
};
|
|
1639
1782
|
}
|
|
1640
1783
|
|
|
@@ -3258,6 +3401,18 @@ body[data-dshome-dark] .${CLS}{background:rgba(14,30,60,.94);box-shadow:0 18px 4
|
|
|
3258
3401
|
liquid: { zh: "液态", en: "Liquid" },
|
|
3259
3402
|
frosted: { zh: "白磨砂", en: "Frosted" }
|
|
3260
3403
|
};
|
|
3404
|
+
// How wide the composer's edge refraction is under liquid, measured on the
|
|
3405
|
+
// top and bottom edges -- the only two where a displacement has anything to
|
|
3406
|
+
// show. The stylesheet gates the three states on <html data-dshome-composer>.
|
|
3407
|
+
// `origin` was this option's first id and is still accepted from a settings
|
|
3408
|
+
// file written before the rename; it reads back as `wide`.
|
|
3409
|
+
const DSTT_COMPOSER = ["narrow", "wide", "off"];
|
|
3410
|
+
const DSTT_COMPOSER_DEFAULT = "narrow";
|
|
3411
|
+
const DSTT_COMPOSER_LABELS = {
|
|
3412
|
+
narrow: { zh: "窄 8px", en: "Narrow 8px" },
|
|
3413
|
+
wide: { zh: "宽 16px", en: "Wide 16px" },
|
|
3414
|
+
off: { zh: "无", en: "Off" }
|
|
3415
|
+
};
|
|
3261
3416
|
|
|
3262
3417
|
/**
|
|
3263
3418
|
* Shared in-memory mirror of the durable DSTT mode. The settings domain's
|
|
@@ -3265,21 +3420,66 @@ body[data-dshome-dark] .${CLS}{background:rgba(14,30,60,.94);box-shadow:0 18px 4
|
|
|
3265
3420
|
* plugin's own loopback channel instead; every reader (the color driver,
|
|
3266
3421
|
* the settings tab) subscribes to this mirror.
|
|
3267
3422
|
*/
|
|
3268
|
-
const dsttState = { mode: "peakvalley-redblue", fluidBrush: false, glassStyle: DSTT_GLASS_DEFAULT, listeners: new Set() };
|
|
3423
|
+
const dsttState = { mode: "peakvalley-redblue", fluidBrush: false, glassStyle: DSTT_GLASS_DEFAULT, composer: DSTT_COMPOSER_DEFAULT, listeners: new Set() };
|
|
3424
|
+
// Fields the user has chosen in this session. A host half that predates a
|
|
3425
|
+
// field ignores it and answers without it -- or with its own default -- so
|
|
3426
|
+
// without this a fresh choice gets overwritten by the next sync and the
|
|
3427
|
+
// option appears to snap straight back to its default.
|
|
3428
|
+
const dsttTouched = new Set();
|
|
3269
3429
|
const dsttGetMode = () => dsttState.mode;
|
|
3270
3430
|
const dsttGetGlass = () => (DSTT_GLASS_STYLES.includes(dsttState.glassStyle) ? dsttState.glassStyle : DSTT_GLASS_DEFAULT);
|
|
3271
|
-
|
|
3431
|
+
const dsttGetComposer = () => {
|
|
3432
|
+
if (dsttState.composer === "origin") return "wide";
|
|
3433
|
+
return DSTT_COMPOSER.includes(dsttState.composer) ? dsttState.composer : DSTT_COMPOSER_DEFAULT;
|
|
3434
|
+
};
|
|
3435
|
+
/**
|
|
3436
|
+
* Persist the composer's refraction width through the bridge.
|
|
3437
|
+
*
|
|
3438
|
+
* Applied locally FIRST, before the round trip. Both this and glassStyle
|
|
3439
|
+
* only flip a CSS attribute, so the choice is fully usable the moment it is
|
|
3440
|
+
* made — and it has to be, because a running dsh keeps the host half it
|
|
3441
|
+
* booted with: until `dsh web` is restarted, `dstt.mode.set` ignores fields
|
|
3442
|
+
* it does not know and answers without them, which used to make a brand new
|
|
3443
|
+
* setting look broken. The cost of applying first is that a choice made
|
|
3444
|
+
* against a stale host is not persisted and reverts on reload, so the
|
|
3445
|
+
* return value still reports the truth.
|
|
3446
|
+
*/
|
|
3447
|
+
async function dsttSetComposer(ctx, next) {
|
|
3448
|
+
if (!DSTT_COMPOSER.includes(next)) return false;
|
|
3449
|
+
dsttState.composer = next;
|
|
3450
|
+
dsttTouched.add("composer");
|
|
3451
|
+
dsttNotify();
|
|
3452
|
+
try {
|
|
3453
|
+
const result = await bridgeCall(DSTT_SET, { mode: dsttState.mode, composerRefraction: next });
|
|
3454
|
+
// The response is used ONLY to confirm. It must never be written back
|
|
3455
|
+
// into the mirror: a host that accepts the call but does not store the
|
|
3456
|
+
// field answers with its own value (the default, `narrow`), and copying
|
|
3457
|
+
// that in is what made the option snap straight back the moment the
|
|
3458
|
+
// round trip landed.
|
|
3459
|
+
if (result !== undefined && result !== null && result.ok === true && DSTT_COMPOSER.includes(result.value.composerRefraction)) {
|
|
3460
|
+
return result.value.composerRefraction === next;
|
|
3461
|
+
}
|
|
3462
|
+
} catch (error) {
|
|
3463
|
+
// Fall through: the local choice stands, it just was not stored.
|
|
3464
|
+
}
|
|
3465
|
+
return false;
|
|
3466
|
+
}
|
|
3467
|
+
/** Persist the glass recipe through the bridge; resolves with success.
|
|
3468
|
+
* Applied locally first for the same reason as dsttSetComposer above. */
|
|
3272
3469
|
async function dsttSetGlass(ctx, next) {
|
|
3273
3470
|
if (!DSTT_GLASS_STYLES.includes(next)) return false;
|
|
3471
|
+
dsttState.glassStyle = next;
|
|
3472
|
+
dsttTouched.add("glassStyle");
|
|
3473
|
+
dsttNotify();
|
|
3274
3474
|
try {
|
|
3275
3475
|
const result = await bridgeCall(DSTT_SET, { mode: dsttState.mode, glassStyle: next });
|
|
3476
|
+
// Confirmation only -- see dsttSetComposer for why the host's echo must
|
|
3477
|
+
// not be copied back into the mirror.
|
|
3276
3478
|
if (result !== undefined && result !== null && result.ok === true && DSTT_GLASS_STYLES.includes(result.value.glassStyle)) {
|
|
3277
|
-
|
|
3278
|
-
dsttNotify();
|
|
3279
|
-
return true;
|
|
3479
|
+
return result.value.glassStyle === next;
|
|
3280
3480
|
}
|
|
3281
3481
|
} catch (error) {
|
|
3282
|
-
// Fall through
|
|
3482
|
+
// Fall through: the local choice stands, it just was not stored.
|
|
3283
3483
|
}
|
|
3284
3484
|
return false;
|
|
3285
3485
|
}
|
|
@@ -3335,8 +3535,12 @@ body[data-dshome-dark] .${CLS}{background:rgba(14,30,60,.94);box-shadow:0 18px 4
|
|
|
3335
3535
|
dsttState.mode = result.value.mode;
|
|
3336
3536
|
dsttState.fluidBrush = result.value.fluidBrush === true;
|
|
3337
3537
|
// An older host half answers without a glass recipe: keep the
|
|
3338
|
-
// default rather than clearing the attribute to nothing.
|
|
3339
|
-
|
|
3538
|
+
// default rather than clearing the attribute to nothing. And a
|
|
3539
|
+
// field the user has already chosen this session is never taken
|
|
3540
|
+
// back from the host -- a stale host answers with its own default,
|
|
3541
|
+
// which is what made a fresh choice look like it snapped back.
|
|
3542
|
+
if (!dsttTouched.has("glassStyle") && DSTT_GLASS_STYLES.includes(result.value.glassStyle)) dsttState.glassStyle = result.value.glassStyle;
|
|
3543
|
+
if (!dsttTouched.has("composer") && DSTT_COMPOSER.includes(result.value.composerRefraction)) dsttState.composer = result.value.composerRefraction;
|
|
3340
3544
|
dsttNotify();
|
|
3341
3545
|
}
|
|
3342
3546
|
} catch (error) {}
|
|
@@ -3592,6 +3796,11 @@ body[data-dshome-dark] .${CLS}{background:rgba(14,30,60,.94);box-shadow:0 18px 4
|
|
|
3592
3796
|
if (dstt === undefined || typeof dstt.setGlass !== 'function' || next === glassNow) return;
|
|
3593
3797
|
dstt.setGlass(next);
|
|
3594
3798
|
};
|
|
3799
|
+
const composerNow = dstt !== undefined && typeof dstt.composer === 'function' ? dstt.composer() : DSTT_COMPOSER_DEFAULT;
|
|
3800
|
+
const pickComposer = (next) => {
|
|
3801
|
+
if (dstt === undefined || typeof dstt.setComposer !== 'function' || next === composerNow) return;
|
|
3802
|
+
dstt.setComposer(next);
|
|
3803
|
+
};
|
|
3595
3804
|
|
|
3596
3805
|
return React.createElement('div', { className: 'dshome-dstt' },
|
|
3597
3806
|
React.createElement('h3', null,
|
|
@@ -3643,6 +3852,21 @@ body[data-dshome-dark] .${CLS}{background:rgba(14,30,60,.94);box-shadow:0 18px 4
|
|
|
3643
3852
|
className: 'dshome-dstt-seg-btn' + (glassNow === style ? ' dshome-on' : ''),
|
|
3644
3853
|
onClick: () => pickGlass(style)
|
|
3645
3854
|
}, isZh ? DSTT_GLASS_LABELS[style].zh : DSTT_GLASS_LABELS[style].en)))),
|
|
3855
|
+
React.createElement('div', { className: 'dshome-dstt-row' },
|
|
3856
|
+
React.createElement('div', { className: 'dshome-dstt-row-body' },
|
|
3857
|
+
React.createElement('div', { className: 'dshome-dstt-row-title' }, isZh ? '对话框边缘倒影' : 'Composer edge reflection'),
|
|
3858
|
+
React.createElement('div', { className: 'dshome-dstt-row-hint' }, isZh
|
|
3859
|
+
? '液态下对话框边缘的折射带宽(按上下量——左右背后是连续背景,同样的位移看不出变化)。窄 8px(默认)/ 宽 16px / 无,三者都是专为这张卡片做的位移图。'
|
|
3860
|
+
: 'Width of the lens band at the composer edge under liquid, measured top and bottom — left and right sit over a continuous backdrop where the same displacement shows nothing. Narrow 8px (default) / Wide 16px / Off; each is a map built for this card.')),
|
|
3861
|
+
React.createElement('div', { className: 'dshome-dstt-seg', role: 'radiogroup', 'aria-label': isZh ? '对话框边缘倒影' : 'Composer edge reflection' },
|
|
3862
|
+
DSTT_COMPOSER.map((choice) => React.createElement('button', {
|
|
3863
|
+
key: choice,
|
|
3864
|
+
type: 'button',
|
|
3865
|
+
role: 'radio',
|
|
3866
|
+
'aria-checked': composerNow === choice,
|
|
3867
|
+
className: 'dshome-dstt-seg-btn' + (composerNow === choice ? ' dshome-on' : ''),
|
|
3868
|
+
onClick: () => pickComposer(choice)
|
|
3869
|
+
}, isZh ? DSTT_COMPOSER_LABELS[choice].zh : DSTT_COMPOSER_LABELS[choice].en)))),
|
|
3646
3870
|
React.createElement('div', { className: 'dshome-dstt-status', 'data-peak': timeSliced && peakNow },
|
|
3647
3871
|
'当前:' + colorName + ' · ' + status));
|
|
3648
3872
|
}
|
|
@@ -3760,6 +3984,8 @@ body[data-dshome-dark] .${CLS}{background:rgba(14,30,60,.94);box-shadow:0 18px 4
|
|
|
3760
3984
|
setBrush: (next) => dsttSetBrush(ctx, next),
|
|
3761
3985
|
glass: dsttGetGlass,
|
|
3762
3986
|
setGlass: (next) => dsttSetGlass(ctx, next),
|
|
3987
|
+
composer: dsttGetComposer,
|
|
3988
|
+
setComposer: (next) => dsttSetComposer(ctx, next),
|
|
3763
3989
|
subscribe: dsttSubscribe
|
|
3764
3990
|
},
|
|
3765
3991
|
locale: ctx.get('locale')
|