dsh-deepseek-style-theme 2.0.81 → 2.0.82
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 +20 -9
- package/lib/client.js +81 -37
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
- **流体跟随笔刷**(`fluidBrush`,默认关):开启后光标才把速度写进流场、拖出尾迹;**改完刷新页面生效**。
|
|
51
51
|
- **玻璃风格**(`glassStyle`,默认 `液态`):
|
|
52
52
|
- **液态**(1.43.0+):Apple Liquid Glass 方向——填充降到 `rgb(255 255 255 / .14)`、顶部光泽降到 `.07`,背板链改为靠 `saturate(1.75)` 让身后颜色发亮,共用的 `feDisplacementMap` 走更大的 scale(104,白磨砂仍是 60),边框上再跑一圈自走的 `conic-gradient` 高光(`@property` 注册的 `<angle>`,与鼠标无关)。上玻璃的只有四处:**侧边栏外壳 / 对话框 / 标题栏(悬停)/ 用户消息气泡**;
|
|
53
|
-
- **白磨砂**:1.42.x 那套白磨砂 +
|
|
53
|
+
- **白磨砂**:1.42.x 那套白磨砂 + 蓝变光边框,**样式逐像素不变**(新配方整块挂在 `html[data-dshome-glass="liquid"]` 下,已用 1991 个元素的计算样式签名验证过:切到白磨砂时新样式表贡献为零)。**唯一的例外是悬停倾斜**:它自 2.0.82 起两套配方都生效(原先只在液态下挂),运动与配方无关——见「已知产品侧缺陷」一节;
|
|
54
54
|
|
|
55
55
|
### 高峰判定(1.37.0+)
|
|
56
56
|
|
|
@@ -88,7 +88,7 @@
|
|
|
88
88
|
|
|
89
89
|
- **探测**:只读 DOM 标记 `[data-dsh-better-sidebar]`(它的面板宿主)与 `[data-dsh-center-col]`(它给中心列打的标记);宿主侧无需任何配合。
|
|
90
90
|
- **布局**:它用 `#root [data-dsh-center-col]{margin-bottom:var(--dsh-sidebar-height)}` 把会话列顶起来;本主题**不写中心列的尺寸、不动它的 tab 栏 / 面板内部**,只对产品自己的表面(侧栏外壳、对话框、标题栏)做玻璃与配色。
|
|
91
|
-
- **已知交互(重要)**:它的面板宿主几何同步会在检测到**页面级 transform** 时降级(它自己会打 `data-dsh-panel-host-degraded`)。本主题的「对话框悬停倾斜」会给对话框卡片写 `transform: perspective(800px) … scale(1.01)` —— 这是**卡片级**而非页面级,正常情况无碍;但若你在同装后看到悬停抖动 /
|
|
91
|
+
- **已知交互(重要)**:它的面板宿主几何同步会在检测到**页面级 transform** 时降级(它自己会打 `data-dsh-panel-host-degraded`)。本主题的「对话框悬停倾斜」会给对话框卡片写 `transform: perspective(800px) … scale(1.01)` —— 这是**卡片级**而非页面级,正常情况无碍;但若你在同装后看到悬停抖动 / 底部滑块闪动,先按上一节的顺序二分:`__dshomeTilt()` 看倾斜此刻是否在动、`tilt(false)` 一行冻结它、再 `ambient(false)` 关掉动态背景画布(2.0.82 起两套玻璃配方都会倾斜,所以"切白磨砂"不再是有效的二分手段)。
|
|
92
92
|
- **右侧栏配色**:它面板内部的配色由它自己管;本主题的深色规则只覆盖产品原生的右栏表面(`.VOzbGW_panel` 等),不改它的 class 命名空间。
|
|
93
93
|
|
|
94
94
|
### 适配清单(写给以后接手的人)
|
|
@@ -117,7 +117,7 @@
|
|
|
117
117
|
|
|
118
118
|
**主题的处置**(2.0.78):给会话滚动区加 `overflow-x: clip`(守护块,锚点 `.wSkVaW_scrollBody`,规则按 `[class*="scrollBody"]` 子串匹配,散列类改名也不会静默失效)。横向不再产生滚动条,那 8px 就不存在,闭环断开;每个代码块自己的横向滚动不受影响。代价:超出列宽的部分会被裁掉而不是可滚动 —— 在聊天列里这是更小的恶。**溢出的根因在产品侧,值得上报。**
|
|
119
119
|
|
|
120
|
-
**第二半:我们自己的倾斜**(2.0.79
|
|
120
|
+
**第二半:我们自己的倾斜**(2.0.79 想修,2.0.82 才算修对)。滚动条链条断掉后,真机逐帧采样显示剩下的抖动来自悬停倾斜本身:
|
|
121
121
|
|
|
122
122
|
```
|
|
123
123
|
card=700/757 … under=div.uV2eYG_row hovCard=1
|
|
@@ -126,12 +126,19 @@ card=699/761 … under=div.wSkVaW_widthHandle hovCard=0 ← 边缘越过了
|
|
|
126
126
|
card=700/757 … under=div.wSkVaW_widthHandle hovCard=0 ← 倾斜解除、卡片缩回
|
|
127
127
|
```
|
|
128
128
|
|
|
129
|
-
指针**静止**时,1% 放大把卡片边缘推出 8px,于是指针底下的元素在"对话框控件"与"列的宽度把手"之间翻转,而每次翻转都会重新武装或解除倾斜 → 因为过渡只有 0.1–0.24s
|
|
129
|
+
指针**静止**时,1% 放大把卡片边缘推出 8px,于是指针底下的元素在"对话框控件"与"列的宽度把手"之间翻转,而每次翻转都会重新武装或解除倾斜 → 因为过渡只有 0.1–0.24s,比被滚动条中介的那条**更快**。
|
|
130
130
|
|
|
131
|
-
|
|
132
|
-
2. 指针在**交互控件或拖拽把手**上时不倾斜(`TILT_INTERACTIVE`,`pointerover` 与 `pointermove` 双入口判定)—— 倾斜只作用在对话框的空白处。
|
|
131
|
+
**2.0.79 修错了**:它把抬升去掉(`TILT_SCALE: 1.01 → 1`),并把 `input / textarea / [contenteditable]` 也算成"不能压在动面上的控件"。两条都是矫枉过正,而且同因:**输入框几乎盖满整张卡片**,把它排除等于让倾斜在"鼠标放到对话框上"这个它唯一存在的姿势里永不触发,抬升也跟着一起没了。用户报回来的是「鼠标放在对话框,放大、倾斜没了」——修法本身成了新的缺陷。
|
|
133
132
|
|
|
134
|
-
|
|
133
|
+
**2.0.82 的修法(保留效果,切断回路)**:
|
|
134
|
+
|
|
135
|
+
1. **抬起回来**:`TILT_SCALE` 恢复 `1.01`;
|
|
136
|
+
2. **输入框上可以倾斜**:`TILT_CONTROLS` 只留 `button / [role=button] / a[href] / [class*="andle" i]`,文字录入不再挡。倾斜是**带着输入框一起动**的,指针不会因此掉出输入框;
|
|
137
|
+
3. **滞回取代"越界即释放"**(关键):指针进入控件时**冻结当前角度**——不重画、不回弹、不重新武装;回到卡片空白处再续;只有指针**离开卡片自己的矩形**才释放,释放后还有 `TILT_COOLDOWN_MS = 200` 的冷却期才允许下一次进入。那条自激回路需要"释放"来喂它,而现在指针静止时倾角是**死的**,没有任何反馈通路。
|
|
138
|
+
4. **两套玻璃配方都倾斜**:原先 `paint` / `onOver` 各有一道"不是液态就早退"的闸门(理由是白磨砂要保持逐像素不变),结果是**用白磨砂的人永远看不到这个效果**——用户点名的第二条要求就是「白磨砂玻璃也要有」。现在闸门拆掉,而且运动**自带过渡**(`TILT_TRANSITION`,值与原液态样式表里那条相同),这样任何配方都不会因为少了一条 `transition: transform` 而变成硬跳;
|
|
139
|
+
5. 状态可从页面直接读:`window.__dshomeTilt()` → `{ recipe, leaning, frozen, engagements, sinceRelease }`。"效果不见了"因此不再需要猜是哪道闸门吃掉的。
|
|
140
|
+
|
|
141
|
+
> 排查方法记在这里,方便复现:`ResizeObserver` 盯住卡片到 body 的祖先链(谁改高度谁自己报),再对滚动区打 `scrollWidth/clientWidth` 与"右边缘越界元素";最后用 rAF 逐帧记录"指针底下是谁 + 候选元素的矩形",翻转对就能一眼看出。跨机器复现用 `tools/symptom-probe.js`(贴进控制台,`run(秒)` 同时采样 GPU 状态与光标下的逐字段 diff,`tilt(false)` 一行就能把倾斜冻结做对照)。
|
|
135
142
|
|
|
136
143
|
## 健壮性设计(1.33.1+)
|
|
137
144
|
|
|
@@ -303,13 +310,15 @@ dsh plugin --profile web remove dsh-deepseek-style-theme
|
|
|
303
310
|
|
|
304
311
|
## 自测工具(开发用,不随包发布)
|
|
305
312
|
|
|
306
|
-
`tools/` 里的脚本都在本机直接跑,**不进发布产物**(`package.json` 的 `files` 只含 `lib/`、`cordis.patch.yml`、`README.md`、`LICENSE`)。除 `gui-probe.mjs` 与 `--open` 的 `bridge-smoke.mjs` 外,都不需要浏览器、不需要网络。
|
|
313
|
+
`tools/` 里的脚本都在本机直接跑,**不进发布产物**(`package.json` 的 `files` 只含 `lib/`、`cordis.patch.yml`、`README.md`、`LICENSE`)。除 `gui-probe.mjs`、`symptom-probe-selftest.mjs` 与 `--open` 的 `bridge-smoke.mjs` 外,都不需要浏览器、不需要网络。
|
|
307
314
|
|
|
308
315
|
| 工具 | 作用 | 运行 |
|
|
309
316
|
|---|---|---|
|
|
317
|
+
| `symptom-probe.js` | **贴进浏览器控制台的现场探针**(2.0.81+):专门给「症状机器不是开发机」的情况用。先打印一份环境 JSON——每个补丁块是否落到 DOM 上(`style[data-plugin-css]` 清单 + 13 个锚点是否存在:锚点按产品构建哈希命名,换构建就会静默跳过,2.0.78 的 `overflow-x: clip` 就是靠它守住的)、`data-dshome-*` 标记、每块 canvas 的 z-index/opacity/所在 GPU/是否 context lost、消息列的 `scrollWidth - clientWidth` 与超宽/自溢出的子元素、发送按钮等控件的盒模型与 transition;再给 `run(秒)`:**同时**采样 GPU 状态(fps、context lost、`gl.getError()`、canvas 尺寸抖动)与光标下的逐帧变化(命中元素链、盒、transform、padding、margin 的逐字段 diff,带 240 条截断与变化率),最后是 `tilt(false)` / `clip(true)` / `ambient(false)` / `wallpaper(false)` / `glass(false)` 五个开关,用来把「关掉 X 之后症状还在不在」变成一句话答案。不写任何设置、不弹权限框 | 控制台粘贴整份文件 |
|
|
318
|
+
| `symptom-probe-selftest.mjs` | 上面那份探针的自测(19 项):在一个合成页面上跑(我们自己的 canvas + 带超宽子元素的消息列 + 每 60 ms 改一次 transform 的卡片 + CDP 派发真实鼠标移动),断言环境报告的字段形状、`run()` 的采样与逐字段 diff、五个开关的实际效果(含 `clip` 在 `overflow-y:auto` 旁会计算成 `hidden` 这条 CSS Overflow 3 规则) | `chrome --headless=new --remote-debugging-port=9333 --user-data-dir=%TEMP%\dsh-probe-chrome about:blank`,然后 `node tools/symptom-probe-selftest.mjs` |
|
|
310
319
|
| `bridge-smoke.mjs` | 宿主端私有 RPC 通道:协议、错误路径,以及完整围栏(非回环对端 / 缺 `Host` / DNS rebinding / 跨站来源 / `Origin` 不匹配 / 非 JSON 类型 / UNC 路径 / `file.*` 端点识别与校验,24 项检查;加 `--open` 为 25 项,会真的用系统默认应用打开一个临时文件,**会在桌面弹出窗口**,默认不启用) | `node tools/bridge-smoke.mjs [已安装的 lib/index.js] [--open]` |
|
|
311
320
|
| `dstt-schema-smoke.mjs` | DSTT 设置 schema 与写入路径的一致性:**每个能写进 `settings.yaml` 的值都必须过 `register()` 的校验**(1.43.2 那次 schema 缺 `wide` 的发布阻塞就靠它守)。含背景方式四档的往返、未知 id 拒写、自定义背景的归一化(控制字符→空格、去首尾、长度上限)、动态背景开关的读写与布尔回读,以及「客户端枚举 = 宿主枚举」的跨文件检查(29 项) | `node tools/dstt-schema-smoke.mjs [lib/index.js]` |
|
|
312
|
-
| `bg-recipes-smoke.mjs` | 客户端源码层的数据与契约断言(2.0.73+,随每版增补):① 必须含纯白 + 近白、② 只有两个颜色参与、`bold` 浅色无 `#FFFFFF`、③ 的基底/让位规则与选择器守卫、壁纸引擎标记与「提示而非自动切换」、桌面壁纸端点与 `WallpaperStyle` 映射、`darkSync`
|
|
321
|
+
| `bg-recipes-smoke.mjs` | 客户端源码层的数据与契约断言(2.0.73+,随每版增补):① 必须含纯白 + 近白、② 只有两个颜色参与、`bold` 浅色无 `#FFFFFF`、③ 的基底/让位规则与选择器守卫、壁纸引擎标记与「提示而非自动切换」、桌面壁纸端点与 `WallpaperStyle` 映射、`darkSync` 作用域、两条反引号守卫(整表扫描)、发送按钮 hover 不再位移、流场 16F 探测与回退、**悬停倾斜的抬升 1.01 与 `TILT_CONTROLS` 不含文字录入、进入控件时冻结而非释放、释放只在离开卡片矩形、两套玻璃配方都生效(2.0.82)**、横向裁切覆盖消息列/对话框/转写区(64 项) | `node tools/bg-recipes-smoke.mjs [lib/client.js]` |
|
|
313
322
|
| `fluid-precision-test.mjs` | **流场存储精度对照实验**(2.0.81+):把 `lib/client.js` 里的真实 `VERTEX_SHADER` / `FLOW_SHADER` 抠出来,在无头 Chrome 里对同一场景各跑 240 步,分别存 RGBA8 与 RGBA16F,读回统计"取值分布 / 落在 1/255 网格上的层级 / max / mean",并判定 16F 是否真的摆脱了量化(5 项)。**需要 Chromium 系浏览器**,且 Chrome 在受限进程里起不来,请从普通终端跑 | `node tools/fluid-precision-test.mjs [--chrome <路径>] [--keep]` |
|
|
314
323
|
| `catalog-sync-smoke.mjs` | 模型目录同步的全部分支:一致 / 可描述漂移 / 聚合网关目录 / 只追加 / `off` / baseURL 解析顺序 / 无密钥 / 端点故障 / 命名空间未就绪 / 版本冲突(27 项检查,全用替身,无需凭据与网络) | `node tools/catalog-sync-smoke.mjs [已安装的 lib/index.js]` |
|
|
315
324
|
| `catalog-sync-live.mjs` | 用**真实端点 + 真实密钥**跑一遍同步:A 场景(目录已一致)应 0 写入,B 场景(人为制造漂移)应恰好 1 次写入并复原条目;写入被拦下,不落盘 | `DEEPSEEK_API_KEY=... node tools/catalog-sync-live.mjs [--profile web] [--drift-id deepseek-v4-pro]` |
|
|
@@ -337,6 +346,8 @@ node tools/gui-probe.mjs --models --file-card
|
|
|
337
346
|
│ ├── bridge-smoke.mjs # 宿主端私有 RPC 通道冒烟测试
|
|
338
347
|
│ ├── catalog-sync-smoke.mjs # 模型目录同步冒烟测试(六种分支)
|
|
339
348
|
│ ├── catalog-sync-live.mjs # 同步的真端点/真密钥校验(写入不落盘)
|
|
349
|
+
│ ├── symptom-probe.js # 贴进控制台的现场探针(环境报告 + 采样 + 逐帧 diff + 开关)
|
|
350
|
+
│ ├── symptom-probe-selftest.mjs # 上面那份探针的自测(合成页面 + CDP 真实鼠标)
|
|
340
351
|
│ └── gui-probe.mjs # 实时页面的无头浏览器探测与像素扫描
|
|
341
352
|
└── lib/
|
|
342
353
|
├── index.js # host 端:打开工作区 / 交付文件 RPC、DSTT 设置、模型目录同步
|
package/lib/client.js
CHANGED
|
@@ -1374,8 +1374,17 @@ body[data-dshome-dark] .uV2eYG_card::before{background:conic-gradient(from var(-
|
|
|
1374
1374
|
// whatever sticks out past the column edge is clipped instead of
|
|
1375
1375
|
// scrollable, which in a chat column is the lesser evil -- and this is
|
|
1376
1376
|
// the guarded-block pattern, so it disappears with the anchor.
|
|
1377
|
+
//
|
|
1378
|
+
// 2.0.82 widens the net to the column's other scrollers. The mechanism
|
|
1379
|
+
// needs only ONE `overflow-y: auto` container (the other axis computes
|
|
1380
|
+
// to `auto`, so it can grow a horizontal scrollbar) whose child grows on
|
|
1381
|
+
// hover; the first report named the message list, but the same 8px step
|
|
1382
|
+
// under the send button can come from the composer's own seat or the
|
|
1383
|
+
// transcript's view area, which that first fix did not cover. Popups
|
|
1384
|
+
// portalled to <body> are unaffected; anything rendered inline inside
|
|
1385
|
+
// these containers is clipped, exactly as it already was for the list.
|
|
1377
1386
|
{ anchor: ".wSkVaW_scrollBody", css: `
|
|
1378
|
-
[class*="scrollBody"]{overflow-x:clip}
|
|
1387
|
+
[class*="scrollBody"],[class*="composerSeat"],[class*="viewArea"]{overflow-x:clip}
|
|
1379
1388
|
` },
|
|
1380
1389
|
// Empty-state headline + preview badge.
|
|
1381
1390
|
{ anchor: ".pXSMma_headlineText", css: `
|
|
@@ -2236,92 +2245,125 @@ html[data-dshome-blur="off"] body{
|
|
|
2236
2245
|
// cursor is on, about its own centre, under an 800px perspective. The
|
|
2237
2246
|
// cursor-relative offsets are the same half-range clamp, and the release
|
|
2238
2247
|
// writes the neutral transform first and only drops the inline properties
|
|
2239
|
-
// SETTLE_MS later, so the
|
|
2240
|
-
//
|
|
2248
|
+
// SETTLE_MS later, so the transition animates the return instead of
|
|
2249
|
+
// snapping.
|
|
2241
2250
|
//
|
|
2242
2251
|
// Only the composer is tiltable here. mineradio tilts every spot it owns;
|
|
2243
2252
|
// in this theme the other spots are the sidebar shell and the transcript's
|
|
2244
2253
|
// message bubbles, where a whole-column lean reads as a bug rather than as
|
|
2245
|
-
// depth.
|
|
2246
|
-
//
|
|
2254
|
+
// depth. "The hover effect is missing" is a question about a guard or the
|
|
2255
|
+
// recipe, so the state is readable from the page (`__dshomeTilt()`).
|
|
2256
|
+
//
|
|
2257
|
+
// 2.0.79 -> 2.0.82, the whole story of one hover effect:
|
|
2258
|
+
// * 2.0.79 chased an ~8 Hz hover loop by dropping the lift (`TILT_SCALE`
|
|
2259
|
+
// 1.01 -> 1, measured as an 8px card growth under a stationary cursor)
|
|
2260
|
+
// AND by listing text entry as an interactive control. Both were
|
|
2261
|
+
// over-corrections with one cause: the input covers the card, so the
|
|
2262
|
+
// tilt stopped engaging for the very gesture it exists for, and the lift
|
|
2263
|
+
// went with it. Reported as "鼠标放在对话框,放大和倾斜没了".
|
|
2264
|
+
// * 2.0.82 restores the lift, lets the tilt engage over text entry (the
|
|
2265
|
+
// lean moves the input together with the card, so the pointer cannot
|
|
2266
|
+
// fall off it), and replaces "release on control crossing" with
|
|
2267
|
+
// HYSTERESIS: entering a control FREEZES the current lean -- no repaint,
|
|
2268
|
+
// no snap back, no re-arm -- it resumes on the card body, and it is
|
|
2269
|
+
// released only when the pointer leaves the card's own box, followed by
|
|
2270
|
+
// a cooldown before the next engagement. The loop 2.0.79 was chasing
|
|
2271
|
+
// needed that release to feed it; a lean held still under a stationary
|
|
2272
|
+
// pointer has no feedback path left.
|
|
2273
|
+
// * 2.0.82 also DROPS THE RECIPE GATE. The tilt used to return early
|
|
2274
|
+
// unless the glass was `liquid`, on the theory that the frosted pane
|
|
2275
|
+
// should stay observably identical to what shipped before -- which made
|
|
2276
|
+
// the effect invisible for anyone on the default-looking white frosted
|
|
2277
|
+
// recipe. Both recipes tilt now, and because the liquid sheet used to be
|
|
2278
|
+
// the only place carrying `transition: transform`, the motion carries
|
|
2279
|
+
// its own transition (TILT_TRANSITION) so a recipe cannot lose it.
|
|
2247
2280
|
const TILT_SELECTOR = '[data-composer-card],.uV2eYG_card';
|
|
2248
2281
|
const TILT_MAX = 0.0175;
|
|
2249
2282
|
const TILT_PERSPECTIVE = 800;
|
|
2250
|
-
|
|
2251
|
-
// 757 to 765 px under a stationary cursor (8 px, the same amount the
|
|
2252
|
-
// horizontal scrollbar used to steal), and that was enough to flip the
|
|
2253
|
-
// element under the pointer between the composer's own controls and the
|
|
2254
|
-
// column's width handle; each flip re-armed or released the tilt, giving an
|
|
2255
|
-
// ~8 Hz hover loop the user reported as "the buttons flicker". The lean
|
|
2256
|
-
// itself (the two rotations) stays: it moves things by ~1 px instead of 8.
|
|
2257
|
-
const TILT_SCALE = 1;
|
|
2283
|
+
const TILT_SCALE = 1.01;
|
|
2258
2284
|
const TILT_SETTLE_MS = 240;
|
|
2285
|
+
const TILT_COOLDOWN_MS = 200;
|
|
2286
|
+
/** The lean's own easing. Mirrors the liquid sheet's values, and is removed
|
|
2287
|
+
* together with the inline transform so nothing keeps animating afterwards. */
|
|
2288
|
+
const TILT_TRANSITION = 'transform .24s cubic-bezier(.2,.8,.2,1),background-color .18s ease,box-shadow .18s ease';
|
|
2259
2289
|
/**
|
|
2260
|
-
*
|
|
2261
|
-
*
|
|
2262
|
-
*
|
|
2263
|
-
*
|
|
2264
|
-
* hashed class name; the attribute
|
|
2290
|
+
* Controls the pointer can land on *inside* the card, where aiming is the
|
|
2291
|
+
* user's whole intent. Text entry is deliberately NOT in this list: the input
|
|
2292
|
+
* is the surface the tilt exists for, and the lean carries it along with the
|
|
2293
|
+
* card. `andle` covers the column's width handle (`wSkVaW_widthHandle`) and
|
|
2294
|
+
* any future drag handle without pinning a hashed class name; the attribute
|
|
2295
|
+
* match is case-insensitive.
|
|
2265
2296
|
*/
|
|
2266
|
-
const
|
|
2297
|
+
const TILT_CONTROLS = 'button,[role="button"],a[href],[class*="andle" i]';
|
|
2267
2298
|
function startComposerTilt() {
|
|
2268
2299
|
let current = null;
|
|
2269
2300
|
let raf = 0;
|
|
2301
|
+
/** A control is under the pointer: hold the lean, paint nothing, release nothing. */
|
|
2302
|
+
let frozen = false;
|
|
2303
|
+
/** performance.now() of the last release; the next engagement waits out the cooldown. */
|
|
2304
|
+
let releasedAt = -Infinity;
|
|
2305
|
+
let engagements = 0;
|
|
2270
2306
|
const tilted = new WeakSet();
|
|
2271
2307
|
const settle = new Map();
|
|
2272
2308
|
const reduced = typeof window.matchMedia === 'function' && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
2273
2309
|
if (reduced) return () => {};
|
|
2310
|
+
// Readable from the page: the effect is guarded twice, so "it is missing"
|
|
2311
|
+
// is otherwise a guess about which guard ate it. `__dshomeTilt()` answers
|
|
2312
|
+
// that in one call (and reports the recipe it is running under).
|
|
2313
|
+
try {
|
|
2314
|
+
window.__dshomeTilt = () => ({ recipe: dsttGetGlass(), leaning: current !== null, frozen, engagements, sinceRelease: releasedAt === -Infinity ? null : Math.round(performance.now() - releasedAt) });
|
|
2315
|
+
} catch (error) {}
|
|
2274
2316
|
const release = (spot) => {
|
|
2275
2317
|
const pending = settle.get(spot);
|
|
2276
2318
|
if (pending !== undefined) {
|
|
2277
2319
|
clearTimeout(pending);
|
|
2278
2320
|
settle.delete(spot);
|
|
2279
2321
|
}
|
|
2322
|
+
frozen = false;
|
|
2280
2323
|
if (!tilted.has(spot)) return;
|
|
2324
|
+
releasedAt = performance.now();
|
|
2281
2325
|
tilted.delete(spot);
|
|
2282
2326
|
spot.style.transform = 'perspective(' + TILT_PERSPECTIVE + 'px) rotateX(0rad) rotateY(0rad) scale(1)';
|
|
2283
2327
|
const handle = window.setTimeout(() => {
|
|
2284
2328
|
settle.delete(spot);
|
|
2285
2329
|
spot.style.removeProperty('transform');
|
|
2286
2330
|
spot.style.removeProperty('transform-origin');
|
|
2331
|
+
spot.style.removeProperty('transition');
|
|
2287
2332
|
}, TILT_SETTLE_MS);
|
|
2288
2333
|
settle.set(spot, handle);
|
|
2289
2334
|
};
|
|
2290
2335
|
const paint = (spot, rect, clientX, clientY) => {
|
|
2336
|
+
if (frozen) return;
|
|
2291
2337
|
if (raf !== 0) return;
|
|
2292
2338
|
raf = requestAnimationFrame(() => {
|
|
2293
2339
|
raf = 0;
|
|
2294
|
-
if (current !== spot) return;
|
|
2295
|
-
if (dsttGetGlass() !== 'liquid') {
|
|
2296
|
-
release(spot);
|
|
2297
|
-
return;
|
|
2298
|
-
}
|
|
2340
|
+
if (current !== spot || frozen) return;
|
|
2299
2341
|
if (clientX < rect.left || clientX > rect.right || clientY < rect.top || clientY > rect.bottom) return;
|
|
2300
2342
|
const dx = Math.min(0.5, Math.max(-0.5, (clientX - rect.left) / rect.width - 0.5));
|
|
2301
2343
|
const dy = Math.min(0.5, Math.max(-0.5, (clientY - rect.top) / rect.height - 0.5));
|
|
2302
2344
|
spot.style.transformOrigin = (rect.width / 2) + 'px ' + (rect.height / 2) + 'px';
|
|
2345
|
+
spot.style.transition = TILT_TRANSITION;
|
|
2303
2346
|
spot.style.transform = 'perspective(' + TILT_PERSPECTIVE + 'px)'
|
|
2304
2347
|
+ ' rotateX(' + (TILT_MAX * -2 * dy).toFixed(5) + 'rad)'
|
|
2305
2348
|
+ ' rotateY(' + (TILT_MAX * 2 * dx).toFixed(5) + 'rad)'
|
|
2306
2349
|
+ ' scale(' + TILT_SCALE + ')';
|
|
2350
|
+
if (!tilted.has(spot)) engagements += 1;
|
|
2307
2351
|
tilted.add(spot);
|
|
2308
2352
|
});
|
|
2309
2353
|
};
|
|
2310
2354
|
const onOver = (event) => {
|
|
2311
|
-
if (dsttGetGlass() !== 'liquid') return;
|
|
2312
2355
|
const target = event.target;
|
|
2313
2356
|
if (target === null || typeof target.closest !== 'function') return;
|
|
2314
2357
|
const spot = target.closest(TILT_SELECTOR);
|
|
2315
2358
|
if (spot === null) return;
|
|
2316
|
-
// A control
|
|
2317
|
-
//
|
|
2318
|
-
if (target.closest(
|
|
2319
|
-
if (current === spot)
|
|
2320
|
-
release(spot);
|
|
2321
|
-
current = null;
|
|
2322
|
-
}
|
|
2359
|
+
// A control is under the pointer: hold the lean exactly where it is.
|
|
2360
|
+
// Releasing here is what used to feed the ~8 Hz hover loop.
|
|
2361
|
+
if (target.closest(TILT_CONTROLS) !== null) {
|
|
2362
|
+
if (current === spot) frozen = true;
|
|
2323
2363
|
return;
|
|
2324
2364
|
}
|
|
2365
|
+
// The cooldown gates a fresh engagement only, never an ongoing lean.
|
|
2366
|
+
if (!tilted.has(spot) && performance.now() - releasedAt < TILT_COOLDOWN_MS) return;
|
|
2325
2367
|
const rect = spot.getBoundingClientRect();
|
|
2326
2368
|
if (event.clientX < rect.left || event.clientX > rect.right || event.clientY < rect.top || event.clientY > rect.bottom) return;
|
|
2327
2369
|
const pending = settle.get(spot);
|
|
@@ -2329,17 +2371,18 @@ html[data-dshome-blur="off"] body{
|
|
|
2329
2371
|
clearTimeout(pending);
|
|
2330
2372
|
settle.delete(spot);
|
|
2331
2373
|
}
|
|
2374
|
+
frozen = false;
|
|
2332
2375
|
current = spot;
|
|
2333
2376
|
paint(spot, rect, event.clientX, event.clientY);
|
|
2334
2377
|
};
|
|
2335
2378
|
const onMove = (event) => {
|
|
2336
2379
|
if (current === null) return;
|
|
2337
2380
|
const target = event.target;
|
|
2338
|
-
if (target !== null && typeof target.closest === 'function' && target.closest(
|
|
2339
|
-
|
|
2340
|
-
current = null;
|
|
2381
|
+
if (target !== null && typeof target.closest === 'function' && target.closest(TILT_CONTROLS) !== null) {
|
|
2382
|
+
frozen = true;
|
|
2341
2383
|
return;
|
|
2342
2384
|
}
|
|
2385
|
+
frozen = false;
|
|
2343
2386
|
paint(current, current.getBoundingClientRect(), event.clientX, event.clientY);
|
|
2344
2387
|
};
|
|
2345
2388
|
const onOut = (event) => {
|
|
@@ -2367,6 +2410,7 @@ html[data-dshome-blur="off"] body{
|
|
|
2367
2410
|
for (const spot of document.querySelectorAll(TILT_SELECTOR)) {
|
|
2368
2411
|
spot.style.removeProperty('transform');
|
|
2369
2412
|
spot.style.removeProperty('transform-origin');
|
|
2413
|
+
spot.style.removeProperty('transition');
|
|
2370
2414
|
}
|
|
2371
2415
|
current = null;
|
|
2372
2416
|
};
|
|
@@ -2386,7 +2430,7 @@ html[data-dshome-blur="off"] body{
|
|
|
2386
2430
|
if (!supportsWebgl2()) return startParticles();
|
|
2387
2431
|
// Which build is running, readable from the page (see README). One line in
|
|
2388
2432
|
// the console answers "is the bundle I just installed actually live?".
|
|
2389
|
-
try { window.__dshomeBuild = '2.0.
|
|
2433
|
+
try { window.__dshomeBuild = '2.0.82 hover-lift'; } catch (error) {}
|
|
2390
2434
|
const canvas = document.createElement('canvas');
|
|
2391
2435
|
canvas.setAttribute('data-dsh-deepseek-canvas', '');
|
|
2392
2436
|
const style = canvas.style;
|
|
@@ -2491,7 +2535,7 @@ html[data-dshome-blur="off"] body{
|
|
|
2491
2535
|
}
|
|
2492
2536
|
|
|
2493
2537
|
function startParticles() {
|
|
2494
|
-
try { window.__dshomeBuild = '2.0.
|
|
2538
|
+
try { window.__dshomeBuild = '2.0.82 hover-lift'; } catch (error) {}
|
|
2495
2539
|
const canvas = document.createElement('canvas');
|
|
2496
2540
|
canvas.setAttribute('data-dsh-deepseek-canvas', '');
|
|
2497
2541
|
const style = canvas.style;
|
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": "2.0.
|
|
4
|
+
"version": "2.0.82",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"deepseek",
|