dsh-background-by-model 0.7.10 → 0.8.0

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.zh.md CHANGED
@@ -9,15 +9,15 @@
9
9
 
10
10
  > 派生自 [`Tkingxiao/dsh-any-background`](https://github.com/Tkingxiao/dsh-any-background),并重命名为 `dsh-background-by-model`。
11
11
 
12
- 一个 **DeepSeek Harness** 外观插件,核心是一份**有序的模型规则列表**:每条规则自带壁纸(可多张)、透明度与模糊度,**每张图各有自己的布局模式、取景与主题色**,切换模型即切换背景,切换过程走你在「配置」页选定的全局[切换效果](#切换效果全局)。
12
+ 一个 **DeepSeek Harness** 外观插件,核心是一份**有序的模型规则列表**:每条规则自带壁纸(可多张)、透明度与模糊度,**每张图各有自己的布局模式、取景与主题色**。切换模型即切换背景,切换过程走你在「配置」页选定的全局[切换效果](#切换效果全局)。
13
13
 
14
- > **v0.3.0 是破坏性更新**:视频背景、生成式背景与全局主题色均已被移除。详见 [CHANGELOG.zh.md 的 v0.3.0 条目](./CHANGELOG.zh.md#v030)。
14
+ > **v0.3.0 是破坏性更新** —— 视频背景、生成式背景与全局主题色均已被移除。详见 [CHANGELOG.zh.md 的 v0.3.0 条目](./CHANGELOG.zh.md#v030)。
15
15
 
16
16
  ---
17
17
 
18
18
  ## 模型规则如何工作
19
19
 
20
- 一条规则 = 一个**匹配串** + **一整套背景外观**。切换模型时,插件读取当前会话的模型名,自上而下扫描列表,使用**第一条**“匹配串出现在模型名里”的规则(忽略大小写)。如果一条都没命中,就使用**第 1 条**——规则 1 同时兼任兜底。
20
+ 一条规则 = 一个**匹配串** + **一整套背景外观**。切换模型时,插件读取当前会话的模型名,自上而下扫描列表,使用**第一条**「匹配串出现在模型名里」的规则(忽略大小写)—— 都没命中时用**第 1 条**兜底。
21
21
 
22
22
  示例列表:
23
23
 
@@ -35,14 +35,12 @@
35
35
  | `GLM-Turbo` | 规则 3 → 图 3 | 只有 `glm` 命中 |
36
36
  | `Kimi-K3` | 兜底 → 规则 1 → 图 1 | 都没有命中 |
37
37
 
38
- 说明:
38
+ - **顺序即优先级** —— 先命中者胜,而不是最精确者胜;需要时拖动规则调整顺序。
39
+ - **匹配串留空**表示该规则不参与匹配、只作兜底,所以「单规则 + 空匹配串」就是一张图到处用。
40
+ - 匹配串会同时比对 **provider、模型 id 与模型显示名**拼接出的字符串,三者任一都能用来选中规则。
41
+ - 当前模型取自**本会话的持久化模型选择**(输入框上模型选择器读的同一个 `modelSelection` 投影),所以会话内切换模型会立即生效;页面会显示读到的是哪个模型,只读到宿主全局默认时额外标注**宿主默认**。
39
42
 
40
- - **顺序即优先级。** 先命中者胜,而不是最精确者胜——需要时拖动规则调整顺序。
41
- - **匹配串留空**表示该规则不参与匹配、只作为兜底。因此单规则 + 空匹配串就是“一张图到处用”。
42
- - 匹配串会同时比对 **provider、模型 id 与模型显示名**三者拼接出的字符串,三者任一都能用来选中规则。
43
- - 当前模型取自**本会话的持久化模型选择**(`modelSelection` 投影,和输入框上的模型选择器读的是同一份值),因此会话内切换模型会立即生效。「模型背景」页顶部会显示当前读到的是哪个模型;如果只读到宿主全局默认模型(不是本会话的选择),会额外标注 **宿主默认**。
44
-
45
- > 提醒:上面这张示例表里**没有 `kimi`**,所以切到 Kimi 会走到兜底 = 规则 1 = 图 1,看起来像“没反应”。想给 Kimi 单独配图,就加一条匹配串为 `kimi` 的规则。
43
+ > 上面的示例表里**没有 `kimi`**,所以切到 Kimi 会走到兜底 = 规则 1 = 图 1,看起来像「没反应」—— 想给 Kimi 单独配图就加一条匹配串为 `kimi` 的规则。
46
44
 
47
45
  ## 截图
48
46
 
@@ -63,7 +61,7 @@
63
61
  <p align="center">
64
62
  <img src="https://cdn.jsdelivr.net/gh/HarmlessFunny/assets@v0.1.0/dsh-background-by-model/rule-editor.webp" alt="单条规则的编辑器" width="660">
65
63
  <br/>
66
- <em>「模型背景」页的单条规则 · 壁纸、透明度与模糊度属于这条规则;布局模式、取景与主题色(色轮 / HSL / RGB / 从本图提取 / 从图片取色)属于它正在编辑的那张图</em>
64
+ <em>「模型背景」页的单条规则 · 壁纸、透明度与模糊度属于这条规则;布局模式、取景与主题色属于它正在编辑的那张图</em>
67
65
  </p>
68
66
 
69
67
  <p align="center">
@@ -74,111 +72,78 @@
74
72
 
75
73
  ### 模型规则
76
74
 
77
- - **有序规则列表** — 可新增、删除、拖动排序并命名规则;每条规则用自己的匹配串参与匹配,先命中者生效,规则 1 兼任兜底。
78
- - **实时状态提示** — 「模型背景」页顶部会显示当前状态,例如「当前模型 deepseek-flash → 命中 · 规则 1」,方便当场验证规则写对没有;检测不到当前模型时会给出提示。
79
- - **外观随规则走,逐图的三项随图走** — 壁纸(多张)、透明度与模糊度是每条规则自己的属性;**布局模式、取景和主题色则是每张图片自己的**。除「界面」页外没有任何全局外观项。
80
- - **一条规则多张图** — 每条规则持有一组有序图片,用胶片条展示:可一次加多张(多选文件、拖放,或一行一个网址批量粘贴)、排序、把某张设为第一张、**原地替换某一张**(替换会保住它在轮换里的位置)、逐张删除。不轮换时规则画的就是**第一张**。删掉最后一张只是让这条规则变空,而空不等于失效:只要它还留着自己的主题色,它就照旧参与匹配与兜底、只给界面配色而不铺壁纸;连主题色也没有(比如刚新增的规则)才会被匹配跳过。空规则是一个看得见、退得回的状态,而不是卡住出不来的死胡同。一张图都没有时胶片条会隐藏、上方那块大预览**就是**上传按钮,所以添加图片的地方只有一个、也很显眼。
81
- - **导入 / 导出** — 一键把**每一条规则连同它的全部图片**导出为 `dsh-background-by-model-theme.json`(版本 6,图片以 base64 内联),可随时导入还原。更早版本导出的文件同样能导入:规则唯一的那张图会被还原成它的图片列表,在「主题色属于图片」之前写下的颜色会被抬升到每一张还没有自己颜色的图上,在布局模式下放到规则上时写下的值会被抬升到它的每一张图上,而在[切换效果](#切换效果全局)之前写下的文件会拿到这项设置的出厂默认——所以旧主题文件还原出来的观感与当初一模一样。同一个文件也可以不从本地选、而是**从项目的素材库下载**,见[推荐配置](#推荐配置)——只有一处不同:它会先把数据目录清空再写入。
82
- - **文件持久化** — 所有设置与图片保存到文件系统 `~/.dsh/.dsh-background-by-model-data/`,不依赖 `localStorage`。
83
- - **自动迁移** — 旧版单壁纸配置在首次读取时自动升级,曾经放在规则上的布局模式也会被抬升到它的每一张图上。详见 [CHANGELOG.zh.md 的 v0.3.0 条目](./CHANGELOG.zh.md#v030)。
84
- - **中英双语** — 完整的中英文界面,自动跟随语言设置。
85
- - **主题守护** — 宿主重置主题后自动重新激活自定义主题。
75
+ 一份有序的规则列表,按当前模型名先命中者胜,第 1 条兼任兜底。
76
+
77
+ - **规则** —— 增删、排序、命名;每条自带匹配串、透明度与模糊度。
78
+ - **每条规则多张图** —— 胶片条可用文件选择、拖放或一行一个网址填入;可排序、设为第一张、或原地替换(替换会保住它在轮换里的位置)。规则也可以一张图都没有,而只要它还有自己的主题色,空规则依旧参与匹配,所以「清空」是一个看得见的状态,而不是一个陷阱。
79
+ - **实时匹配状态** —— 「模型背景」页会显示读到的是哪个模型、落在哪条规则上。
80
+ - **导入 / 导出** —— 整套规则导出为 `dsh-background-by-model-theme.json`,图片全部内联;旧版本写出的文件仍可导入。
81
+ - **存在磁盘上,不在 `localStorage`** —— 一切都在 `~/.dsh/.dsh-background-by-model-data/` 下,旧的单图配置在第一次读取时就地迁移。
82
+ - **中英双语**,自动识别语言环境;宿主重置主题时由守护逻辑重新施加。
86
83
 
87
84
  ### 多图轮换
88
85
 
89
- 按规则开关,且**默认关闭** —— 轮换是真要花流量、内存和电的,不该由默认值替你决定。
86
+ 按规则开启,**默认关闭** —— 轮换真的要花流量、内存和电,所以没有东西会替你打开它。
90
87
 
91
- - **按规则开启** — 开关就在规则卡片里,规则至少有两张图之后才能打开。
92
- - **每张停留** — 10 秒 / 30 秒 / 1 分钟 / 5 分钟 / 30 分钟,也可以自己填秒数(限制在 5 秒 – 24 小时)。
93
- - **换图顺序** — 「按顺序」自上而下走完一圈再回到第一张;「随机」会在**除当前这张之外**的所有图里等概率挑一张,因为抽到同一张看起来就跟这一次没生效一模一样。
94
- - **切换模型时也换一张** — 可选的第二种触发方式:每次切换模型落到这条规则上就往前走一张。对「每次都想看到不一样」的规则来说,这就是全部功能,而且不用挂任何定时器。
95
- - **只有正在生效的规则会轮换** — 全插件只有一个定时器,始终对准当前模型解析出的那条规则。标签页不可见时它会停下、回来时重新计时;只有一张图的规则(以及所有[节日](#节日特殊背景))从不排期。
96
- - **下一张** — 立刻换一张,方便当场检查这组图。它就在「轮换这组图」开关旁边(挨着「正在显示 N / M」),轮换开着还是关着都能用。
97
- - **跟着规则走的是外观,跟着图片走的是布局、取景与颜色** — 透明度、模糊度留在规则上、原地不动;**布局模式、取景、主题色属于图片**,所以一组图可以一张高截图用适应、旁边那张横图用填充,也可以依次是绿的、红的、以及跟随系统主题的,界面配色跟着壁纸一起换。过渡用的就是切换模型时那套交叉淡入。
98
- - **布局模式、取景、颜色都是逐图的** — 适应/填充决定的是**这一张**图怎么铺进这块区域,构图和配色也都属于某一张图:每张图各自保留自己的三项,同时共享规则的透明度与模糊度。胶片条会标出哪些图已经各自有颜色,调色区上方也会写明当前编辑的是哪一张。
99
- - **只加载用得到的图** — 启动时只读每条规则的**第一张**,其余的在展开卡片时、或者在轮换即将用到它时再读。所以「五条规则各十张图」不等于首帧之前先传五十张壁纸。
88
+ - **停留时长** —— 10 秒 / 30 秒 / 1 分钟 / 5 分钟 / 30 分钟,或任意自定义秒数(5 秒 – 24 小时)。
89
+ - **顺序** —— **按顺序**走完列表后环绕;**随机**在除当前这张之外的所有图片中等概率挑选,所以不会出现「像没触发」的一跳。
90
+ - **切换模型时也换一张** —— 第二种触发,完全不排定时器。
91
+ - **下一张** —— 随时手动换图,与定时器在不在跑无关。
92
+ - **只有生效的那条规则在轮换**,全插件只有一个定时器;标签页隐藏时轮换跟着画面一起停。
100
93
 
101
94
  ### 切换效果(全局)
102
95
 
103
- 壁纸**发生变化**时长什么样。它放在 **「配置」** 页,对所有规则是同一个(规则本身没有「切换效果」这个概念),并且覆盖壁纸变化的所有来源:切模型、[轮换](#多图轮换)换图、手动「下一张」,以及[节日](#节日特殊背景)接管。
96
+ 壁纸**切换**时的样子 —— 「配置」页上的一个设置,所有规则共用,覆盖切模型、轮换一步、手动**下一张**与节日接管。
104
97
 
105
- - **四种效果** — **淡入淡出**(此前每个版本都在用的混合方式,因此是默认)、**直接切换**(硬切)、**缩放淡入**(新图从大 6% 收拢到位)、**平移推入**(新图从侧向推入)。缩放与平移动的是 `transform`,壁纸层此前从未用过它,所以两者都不增加一个字节的传输。
106
- - **缓动** — 标准 / 匀速 / 先快后慢 / 两端慢。效果为**「直接切换」**时这一行不渲染:硬切没有曲线可调,而一个什么都不做的控件比没有这个控件更糟。
107
- - **所有切换共用一个时长** — `0`–`3000 ms`,而 `0` 就是硬切,不会被当成「没设置」。它取代了每条规则的 `rotate.fadeMs`:那个字段从 0.7.0 起就**没有任何界面能改**(只能手改配置),所以手改过的值会在旧配置第一次被读取时折进这里(等于出厂 320ms 的值会被忽略,普通配置照旧拿默认值)。
108
- - **「直接切换」和 0 秒是同一种硬切** — 同一个诉求的两种说法,所以两者都照做,而不是把其中一个读成「没设置」。
109
- - **「减少动态效果」是否决而不是缩短动画** — 系统处于 `prefers-reduced-motion: reduce` 时完全不做动画,并且卡片会直说,而不是预览一个界面根本不会执行的效果。
110
- - **有一个预览块,因为壁纸淡入到它自己身上是看不见的** — 拿已经在屏上的壁纸回放效果,什么都看不到,而这正是本插件一直在设计上规避的「我改了但什么都没发生」。预览块用**此刻真实切换会用的时长**回放所选效果,换一个效果也会立刻重放一次。
111
- - **界面配色走同一个时钟** — 一次主题色变化就是一次 token 块重写,以前它会让所有表面在一帧里跳完,而壁纸却在淡入。现在这些 alias token 被注册成颜色、按本设置的时长与缓动插值,包括那些不继承而是自己重声明这些 token 的面,以及插件内联绘制的那两处。有一个刻意的例外:**浅↔深互切立即切换**——那是换了一整套调色板而不是同一套的色调变化,在两者之间插值会让界面经过一段两个方向的字色都读不清的中间态。壁纸不受影响,照旧有动画。
112
- - **它不是滑块** — 只有调色板真的变了才武装渐变。拖动滑块会在每一帧重写同一个 token 块,那里若挂着过渡,表面就会滞后于指针。
98
+ - **四种效果** —— **淡入淡出**(默认)、**直接切换**、**缩放淡入**、**平移推入**;后两种只对进来的那一层做 `transform`,所以一个字节的额外传输都不花。
99
+ - **缓动与同一个时长** —— 标准 / 线性 / ease-out / ease-in-out,以及 `0`–`3000 ms`,其中 `0` 是硬切而不是「没设置」。它取代了 `rotate.fadeMs` 这个没有任何控件能到达的逐规则字段,手调过的值会在第一次读取时折算进来。
100
+ - **「减少动态效果」是否决**,而不是把动画缩短。
101
+ - **界面配色和壁纸走同一个时钟** —— 只有浅↔深互切立即切换,因为两套调色板之间插值会走过两个方向都读不清的中间调。
102
+ - **预览色块**会重放这个效果,因为壁纸淡入它自己是什么都看不见的。
113
103
 
114
104
  ### 节日特殊背景
115
105
 
116
- 小小一个,不是一整块设置:**「配置」页上一个开关,别的什么都没有**。它出厂是**关**的 —— 打开它,到了日子背景自己换,第二天早上自己换回。(下面的[推荐配置](#推荐配置)会替你把它打开。)
106
+ 「配置」页上一个开关,别的什么都没有:中秋与国庆当天壁纸换成该节日自己的画与固定主题色,第二天早上换回来。它出厂是**关**的,[推荐配置](#推荐配置)会替你打开。
117
107
 
118
- - **行为** — 中秋与国庆当天,壁纸换成那个节日自己的图片,并配上该节日固定的主题色(中秋 `#384A77`、国庆 `#FFF6EB`);第二天交还给模型规则。这份配色同时写在节日条目和它的图片上——主题色现在住在图片里——所以以后节日也能多图轮换、每张带自己的配色。
119
- - **中秋节** — 农历八月十五当天。它在公历上逐年移动(2024-09-17、2025-10-06、2026-09-25、2027-09-15……),所以是运行时按农历换算的,时区钉在 **Asia/Shanghai** —— 农历日期在北京时间午夜翻页,不是你那边的午夜。
120
- - **国庆节** — 公历 10 月 1–7 日。
121
- - **两者重叠时中秋优先** — 它们确实会撞(2025-10-06 就是同一天),一个精确的单日比七天窗口里的某一天更值得采信。
122
- - **没有图的节日会回落到模型规则**,而不是把背景刷空。
123
- - **画是下载来的,不是随包发的** — 每个节日一张压缩过的壁纸(原生分辨率下 228 KB 与 500 KB,原图共 8.37 MB),放在项目自己的 [assets 仓库](https://github.com/HarmlessFunny/assets)里,而不是打进包里 —— 它们占了整个 tarball 的三分之二,而这张画一年只被看一天。下载由**日历**把关:不在节日的配置一点流量都不会有;当天第一次读取会把它缓存到 `holiday-cache/<tag>/` —— 路径里带**插件所钉的那个发行版**,于是一个换成新 tag 的构建拿到的是重新下载,而不是上一代的字节 —— 此后每一次启动、往后每一年,都直接读磁盘、不再联网。引用钉死在一个**发行 tag**(`v0.1.0`)上而不是分支,所以你以后整理仓库里的截图不可能弄坏一个已经发出去的节日;先走 `jsDelivr`、失败再走 `raw.githubusercontent.com`,两个都不通时这个节日就是不接管 —— 模型规则继续画,不会白屏。
124
- - **图片和配色都不可替换,这是刻意的** — 一个要求用户去配置的彩蛋就不是彩蛋了。节日槽位是只读的:往 `modelbg-h-midautumn` / `modelbg-h-nationalday` 里放文件不会被任何一次读取采用,插件也拒绝写入、抓取和删除这两个槽位。下载落进 `holiday-cache/<tag>/`,文件名按**素材名**而不是槽位,所以数据目录里依然永远不会出现 `modelbg-h-*` 文件。主题色是节日定义里的常量、每次读取都强制覆盖——**节日条目和它的图片两处都覆盖**,所以手改 `theme-config.json` 里的 `color` 也换不掉节日配色。布局模式同理固定为定义里的**填充**(这些画本来就是满幅的,一个残留的 `fit` 会给它加黑边),而旁边的**取景**仍然是你自己的。导出的主题文件只包含你自己的规则。
125
- - **想要它** — 打开「配置」页那个开关,或者在同一个文件里写 `"holidays": { "enabled": true }`。只有真正的 `true` 算数:这个功能是「配置说了要」才开的,不是「没人说话」就默认开。它内部每个节日(中秋 / 国庆)各自那个开关是另一回事,仍然默认开着 —— 那是一个已经打开的功能内部的细节。
108
+ - **中秋是农历八月十五**,按**北京时间**从中国农历解析,所以每年都动(2024-09-17、2025-10-06、2026-09-25 …);**国庆是 10 月 1–7 日**;两者撞上时中秋优先。
109
+ - **没有图的节日会顺延**给模型规则,而不是把背景刷白。
110
+ - **画是下载的,不随包发布** —— 每个节日一张压缩后的壁纸,钉在项目 [assets 仓库](https://github.com/HarmlessFunny/assets)的一个发行 tag 上,当天第一次读取时缓存到 `holiday-cache/<tag>/`。下载由日历把关,所以平常的日子一个字节都不取,此后的每一次启动都直接读盘。
111
+ - **画和它的配色都不可以换** —— 节日槽位只读,颜色与满幅的「填充」都取自定义,主题导出也只带你的规则。
126
112
 
127
113
  ### 规则与图片各自的设置
128
114
 
129
- - **背景图片** — 一条规则可以放一张或多张图,每张图是独立文件。胶片条决定你正在编辑哪一张;不轮换时规则画的是**第一张**。规则也可以是**一张图都没有**的:这时它不再铺壁纸,但只要它有自己的主题色就照旧参与匹配与兜底(只是把界面按那个颜色配色),连主题色也没有才会被匹配跳过,直到你给它一点内容。
130
- - **主题色** — HSL 色轮 + 数值输入,外加「从本图提取」和吸管「从图片取色」。**颜色属于图片**:这些控件编辑的是胶片条里选中的那张,胶片条上会标出哪些图已经各自有颜色,而没有自己颜色的图**跟随系统主题**、不会去继承规则的颜色。**一张图都没有**的规则只剩它自己的主题色可编辑——那正是这种规则唯一能画的东西。实时生成整套 CSS 设计令牌。跟随系统主题时,界面配色交还宿主,而壁纸与「界面」页的透明度、模糊照旧生效——表面颜色是从宿主自己的令牌读出来后套上你的透明度的,所以壁纸不会被一块不透明底板盖住。
131
- - **布局模式** — 适应 / 填充 / 拉伸 / 平铺 / 居中,并且**按图分别保存**:同一组轮换里可以一张适应、一张填充。规则还没有图片时这一行是**禁用**而不是隐藏(没有图可布局),这样卡片本身就说明了加上图片后会多出什么可调项。
132
- - **取景** — 在视口比例的编辑器中拖动平移、滚轮缩放;**仅「适应」模式下可编辑**,提交的构图在窗口缩放、跨屏移动后保持一致。按图分别保存。
133
- - **背景透明度** — `0–100%`,作用于该规则的壁纸层。
134
- - **背景模糊** — `0–60 px`,作用于壁纸层。
115
+ - **壁纸** —— 每条规则一张或多张图;不轮换时规则画的是第一张。
116
+ - **主题色** —— HSL 色轮、数值输入、**从本图提取**与吸管。它属于**图片**,没有自己颜色的图跟随系统主题,而不是继承规则色。
117
+ - **布局模式** —— 适应 / 填充 / 拉伸 / 平铺 / 居中,逐图保存;规则没有图片时这排是禁用而不是隐藏。
118
+ - **取景** —— 拖动平移、滚轮缩放,只在适应模式下可编辑,逐图保存,窗口缩放后保持一致。
119
+ - **背景透明度** `0–100%` 与**背景模糊** `0–60 px`,两者都按规则。
135
120
 
136
121
  ### 全局设置(「界面」页)
137
122
 
138
- - **新装出来是什么样** — 出厂那套数字**就是作者本人的配置**,也就是[推荐配置](#推荐配置)「界面」页那套数字,所以第一次装上、什么都没下载,界面就已经是这套样子:主背景 `0%`、左侧栏 `10%`、右侧栏 `45%`、卡片面板 `100%`、代码块 `81%`、输入框与控件 `88%`(外加 `60 px` 模糊)、设置面板 `100%`、对话文本框 `35%`、轨迹页 `73%`,其余模糊都是 `0`。两边靠手工保持一致:配置文件是从真实数据目录直接发出去的,改它的数字就得同步改这些常量。(这一页不管的两项刻意**不**照抄那份配置:[节日开关](#节日特殊背景)出厂关着,[切换时长](#切换效果全局)仍保留 320 ms 的交叉淡入 —— 那份配置用的是它 3000 ms 的上限。)
139
- - **分部位界面透明度** — 主背景、左侧栏、右侧栏、卡片面板(含对话框周围的选项框/菜单)、输入框与控件(发送框、Cordis 插件面板)、设置面板与对话文本框各自独立滑块。与主题色无关:某条规则没有主题色时这些滑块同样生效。
140
- - **分部位界面模糊度** — 每个界面部位可独立调整毛玻璃 `backdrop-filter` 模糊(`0–60 px`),并通过宿主的稳定选择器为发送框与 Cordis 面板提供真实背景模糊。
141
- - **对话与轨迹页** — 消息列表自动包裹为半透明卡片,轨迹页可整页调节透明度与模糊,让壁纸从内容后方透出来。
142
- - **右侧栏** — 点开文件后从右侧滑出的那一栏有了自己的卡片:拖过一次之后这张卡就自己拥有透明度,在那之前它跟随「主背景」—— 那是「出厂值还是 `null` 的年代」写下的配置所处的状态,而新装出来就是自己拥有这一栏的,取配置里的 `45%`;模糊则在主背景模糊之上再叠加,关闭时不占位置、无副作用。
123
+ - **每个界面部件各自的透明度与模糊度** —— 主背景、左侧栏、右侧栏、卡片与菜单、输入框与控件、设置面板、对话文本框、轨迹页,各有一条滑块。
124
+ - **新装出来就已经是作者本人那套外观**,所以第一次运行不需要下载任何东西。
125
+ - **与主题色无关** —— 规则没有主题色时,这些滑块全部照常可用。
143
126
 
144
127
  ### 推荐配置
145
128
 
146
- 「配置」页的第一张卡会从项目自己的 [assets 仓库](https://github.com/HarmlessFunny/assets)下载作者本人正在用的那套配置 —— 规则、壁纸与接口设置 —— 一键装好。
147
-
148
- **它里面「界面」页的每一个数字,就是插件出厂自带的** —— `theme-config.json` 里的每一个透明度、模糊和染色 alpha,都是新装出来时的数字(见上面的「全局设置」)—— 所以在全新的机器上,这个按钮加进去的是规则和图片。它另外带来的两样东西不在这一页上:[节日开关](#节日特殊背景),出厂关着、这份配置把它打开;以及[切换时长](#切换效果全局),这份配置取它 3000 ms 的上限,而出厂默认是此前每一个版本都在用的 320 ms 交叉淡入。如果这个数据目录里的滑块已经被你动过,采用配置会把它们一并复位:它是一整份配置,不是一个补丁。
149
-
150
- **它托管在数据目录自己的形状里**,而不是打包成一个文档:
151
-
152
- ```
153
- preset/
154
- ├── preset.json 形状闸门 —— {"version": 6}
155
- ├── theme-config.json 规则与接口设置(6.5 KB)
156
- └── modelbg-m1 … modelbg-m7 每张壁纸的原始字节
157
- ```
158
-
159
- 所以它在**所在的地方就是可编辑的**:配置是几 KB 的可读 JSON,可以直接在 GitHub 上改;换一张壁纸就是替换一个文件。发布它也因此变成从 `~/.dsh/.dsh-background-by-model-data/` 里 `cp` 一下。插件先取 manifest 与 config,从 `rules[].images[].slot` 推出文件清单 —— 没有第二处清单需要同步 —— 然后**一个文件一个请求**地取壁纸(最多四路并发),过程中按钮上显示进度。
160
-
161
- 它同时也是这个面板里**唯一会销毁东西**的操作,整个设计都在围着这一点转:
129
+ 「配置」页点一下就下载作者本人的整套配置 —— 规则、壁纸与界面设置 —— 来自项目的 assets 仓库。
162
130
 
163
- - **先下载,后询问。** 确认框出现在下载成功之后,所以它能如实说出手里到底有什么 —— *7 条规则 · 7 张壁纸 · 2.9 MB* —— 而不是让你先对「一个可能根本下不来的文件」做决定。下载失败时,什么都还没有发生。
164
- - **少一张壁纸就整体失败,并且会说是哪一张。** 一份悄悄少了图的配置 —— 因为托管处的某个文件被改了名 —— 正是「节日素材钉死发行 tag」那条契约要防的静默失效;把文件名报出来,这个失败就仍然是响亮的,而这份响亮正是「配置可以从一个插件控制不了的托管处读取」可以被接受的原因。
165
- - **每张图都会自己重试,而失败的那一次会把已经下到的部分留给下一次。** 一个文件此前只有两次机会(两个镜像各一次),前面几张都下好了、下一张碰上一次 502 或一次 CDN 抽风,整份配置就失败,再按一次按钮又是从零开始。现在每个文件最多三轮(轮间 400 ms 再 800 ms),而「这个文件不在那儿」「字节本身就超限」「六十秒都没吭声」三种失败不重试 —— 前两种再问也是同一个答案,第三种是因为两个镜像在同一轮里都已经问过了,这一条也正是把最坏情况钉在三分钟而不是六分钟的东西。已经拿到的字节会留到下一次,于是下一次只补缺的那几张;前提是重新取到的配置与那些字节来自的那一份**逐值相同**(这份配置读的是 assets 仓库的一个版本,但那个版本的 tag 可以被重新指向,而没有清过缓存的边缘节点仍会回上一次的字节,所以两个不同的修订不许拼在一起)。严格性没变:第一个拿不到的文件仍然让整次失败、仍然会说是哪一个。
166
- - **写入之前先清空。** `nextSlot` 分配槽位用的是 `m1`、`m2`…… 一个计数器而不是随机串,所以下载来的配置和它落进去的那个数据目录**必然**在这些名字上撞车。「用它替换一切」只有在那个「一切」真的没了的时候才成立 —— 这也是它没有撤销的原因。
167
- - **清空发生在下载之后,绝不会在之前。** CDN 不通不该让你在等一个永远没来的推荐配置的路上,先把自己的配置弄丢 —— 现在下载是多次传输而不是一次,这一点更重要。
168
- - **不碰节日素材。** `holiday-cache/` 是下载来的、不属于任何槽位的画,不是任何人配置的一部分;重下一遍只是白白一次网络往返。而 config **确实**会列出那两个节日槽位(任何一份 config 都有),它们会被从请求清单里剔掉而不是去请求:数据目录里永远不可能存在 `modelbg-h-*` 文件。
169
- - **它和节日素材引用的是同一个发行 tag** —— 而它以前是分支,而且是刻意的:推荐配置的失败是**响亮的**(文件下不来时你正盯着那个按钮),节日的失败却是静默的,所以钉版本在这里什么都买不到,而分支能让一个不好的推荐不必等插件发版就能修正。但分支同时也买来了「插件的字节可能在装上之后、你点下去之前发生变化」;现在一个 tag 同时覆盖截图、节日素材和这份配置,assets 仓库的一个发行版就是插件所取的每一个字节的同一个版本。代价是:改一个 JSON 现在要花一个 `assets` 里的 tag、`src/index.ts` 里的一个值,以及一次 npm 发版;而且**移动**一个 tag 并不会自己抵达 jsDelivr 的边缘节点(版本引用是按不可变处理的,只有 purge 工具能重新指向它),所以修正是发一个新 tag,而不是强推旧的。真正存在的风险 —— 这份配置是写给一个本版本读不懂的配置形状的 —— 正是 `preset.json` 存在的理由:它在请求第一张壁纸之前、更在删除任何东西之前就被拒绝。
131
+ - **托管在数据目录自己的形状里**(`preset/preset.json`、`preset/theme-config.json`,每张壁纸一个 `modelbg-<slot>` 文件),所以它能在 GitHub 上直接编辑,发布它就是一次 `cp`。
132
+ - **里面每一个界面数字都是插件本来就用着的值**,所以在新装上是「加上规则和图片」;而在你已经调过的数据目录上,它把这些也一起重置 —— 它是一整份配置,不是一个补丁。
133
+ - **先下载、后提问**,所以确认框能如实说出手里有什么 —— 而清空在下载**之后**,绝不在之前。它是面板里唯一会销毁东西的操作。
134
+ - **少一张壁纸就整体失败并报出是哪个槽位**,而且每个文件各自重试,已经下到的部分留给下一次。
170
135
 
171
136
  ## 设置界面
172
137
 
173
- 设置的 **「主题」** 分类下现在有三个选项卡:
138
+ 设置的**「主题」**分类下现在有三个选项卡:
174
139
 
175
140
  | 选项卡 | 作用范围 | 内容 |
176
141
  | --- | --- | --- |
177
142
  | **界面** | 全局,所有模型共用 | 主背景、左侧栏、右侧栏、卡片面板、输入框与控件、设置面板、对话文本框、轨迹页的透明度与模糊度 |
178
- | **模型背景** | 每条规则、每张图片 | 有序规则列表、顶部实时状态,以及每条规则的图片(胶片条)、**逐图布局模式 / 取景 / 主题色**,加上规则自己的透明度、模糊度与[多图轮换](#多图轮换) |
143
+ | **模型背景** | 每条规则、每张图片 | 有序规则列表、实时匹配状态,以及每条规则的图片(胶片条)、**逐图布局模式 / 取景 / 主题色**,加上规则自己的透明度、模糊度与[多图轮换](#多图轮换) |
179
144
  | **配置** | — | 全局[切换效果](#切换效果全局)、整套规则的进出 —— [推荐配置](#推荐配置)与导入 / 导出(`dsh-background-by-model-theme.json`)—— 外加[节日特殊背景](#节日特殊背景)那一个开关 |
180
145
 
181
- 原先的 **「色彩」** 选项卡已删除——主题色改为每条规则自己的属性;原 **「背景」** 选项卡已改造成 **「模型背景」**。
146
+ 原先的**「色彩」**选项卡已删除 —— 主题色改为规则与图片各自的属性 —— 原**「背景」**选项卡改造成**「模型背景」**。
182
147
 
183
148
  ## 存储位置
184
149
 
@@ -187,8 +152,8 @@ preset/
187
152
  | 文件 | 内容 |
188
153
  | --- | --- |
189
154
  | `theme-config.json` | 规则列表(每条规则含图片列表与轮换设置)+ 全局「界面」设置 + 全局切换效果 + 节日覆盖配置 |
190
- | `modelbg-<slot>` | 一张图片,保存为原始字节,无扩展名 —— 一张图一个文件,所以一条有三张图的规则占三个槽位 |
191
- | `holiday-cache/<tag>/<asset>` | 从 CDN 下载的节日画,一个节日一个文件(`mid-autumn.webp`、`national-day.webp`),装在一个以**插件所钉的 assets 发行版**命名的目录里(`v0.1.0`)。这个目录是关键:只按素材名做键的缓存会把上一代的字节交给一个已经更新过的插件、而且什么都不说,所以发行版被放进路径里,换成新 tag 的构建从一个没人写过的目录开始。目录里的文件名仍然是**素材名**,刻意放在槽位命名空间之外 —— 节日槽位里依然永远不会有文件,这正是它只读的原因。被取代的旧代会在新图落盘之后被清掉。删掉这里的文件也没关系,下次那个节日生效时会重新下载 |
155
+ | `modelbg-<slot>` | 一张图片,保存为原始字节、无扩展名 —— 一张图一个文件,所以一条有三张图的规则占三个槽位 |
156
+ | `holiday-cache/<tag>/<asset>` | 从 CDN 下载的节日画,一个节日一个文件(`mid-autumn.webp`、`national-day.webp`),装在一个以**插件所钉的 assets 发行版**命名的目录里(`v0.1.0`)。tag 进了路径,所以钉了新 tag 的构建从一个空目录开始,而不是被告知上一代的字节就是答案;目录里的文件名仍然是**素材名**,刻意放在槽位命名空间之外,这正是节日槽位只读的原因。被取代的旧代会在新图落盘之后被清掉。删掉这里的文件也没关系,下次那个节日生效时会重新下载 |
192
157
 
193
158
  ## 安装
194
159
 
@@ -196,10 +161,10 @@ preset/
196
161
 
197
162
  ```sh
198
163
  dsh plugin --profile web add dsh-background-by-model
199
- # 或者直接从仓库安装:
200
- dsh plugin --profile web add github:HarmlessFunny/dsh-background-by-model
201
164
  ```
202
165
 
166
+ 只有这一个来源 —— `github:` 装法拉到的树里没有 `lib/`,也没有任何东西会去构建它。
167
+
203
168
  装完需要重启 `dsh web`。
204
169
 
205
170
  插件会出现在设置面板的 **“主题”** 分类中。
@@ -207,23 +172,24 @@ dsh plugin --profile web add github:HarmlessFunny/dsh-background-by-model
207
172
  ### 方式二:npx(无需全局安装)
208
173
 
209
174
  ```sh
210
- npx @deepseek-ai/dsh plugin --profile web add github:HarmlessFunny/dsh-background-by-model
175
+ npx @deepseek-ai/dsh plugin --profile web add dsh-background-by-model
211
176
  npx @deepseek-ai/dsh web
212
177
  ```
213
178
 
214
179
  ### 方式三:本地构建(开发)
215
180
 
216
- `lib/` 目录已提交,安装后无需构建。修改 `src/` 后重新构建:
181
+ `lib/` **不在仓库里** —— 它由 `src/` 构建而来:打包 tarball 时由 `prepack` 钩子构建,工作副本则由你自己构建。先 clone、安装,再构建:
217
182
 
218
183
  ```sh
219
184
  git clone https://github.com/HarmlessFunny/dsh-background-by-model.git
220
185
  cd dsh-background-by-model
221
186
  pnpm install
222
- pnpm run bundle # 产物在 lib/
223
- pnpm run typecheck
187
+ pnpm test # 构建 lib/、类型检查、跑十道检查
224
188
  ```
225
189
 
226
- 本地开发挂载方式:在 profile 的 `package.json` 里加 `link:` 依赖并把它加进 `dsh.profile.bundles`:
190
+ `pnpm run bundle` 只重建 `lib/`。两者都必须在插件能加载之前先跑一次:新 clone 出来没有 `lib/`。
191
+
192
+ 想在 profile 里挂载一份工作副本,就把它加进 profile 的 `package.json` 并注册这个 bundle:
227
193
 
228
194
  ```jsonc
229
195
  {
@@ -238,84 +204,81 @@ pnpm run typecheck
238
204
  }
239
205
  ```
240
206
 
241
- 改完 `src/` 必须重新执行 `pnpm run bundle` 才会生效——挂载的 profile 加载的是 `lib/`。
207
+ 改完 `src/` 必须重新执行 `pnpm run bundle` 才会生效 —— 挂载的 profile 加载的是 `lib/`。
242
208
 
243
- `pnpm test` 会构建、类型检查并跑十道校验(`tsdown && tsc -p tsconfig.json && node scripts/holiday-check.ts && node scripts/rotation-check.ts && node scripts/transition-check.ts && node scripts/repaint-check.ts && node scripts/code-plate-check.ts && node scripts/image-mode-check.ts && node scripts/rules-view-check.ts && node scripts/preset-check.ts && node scripts/ui-strings-check.ts && node scripts/node-half-check.mjs`)。十道都不需要依赖、不需要转译器、也不需要浏览器,也可以单独跑:`pnpm check:holiday` / `pnpm check:rotation` / `pnpm check:transition` / `pnpm check:repaint` / `pnpm check:code-plate` / `pnpm check:image-mode` / `pnpm check:rules` / `pnpm check:preset` / `pnpm check:ui` / `pnpm check:node`。
209
+ `pnpm test` 会构建、类型检查并跑十道校验,十道都不需要依赖、不需要转译器、也不需要浏览器。也可以单独跑:`pnpm check:holiday` / `pnpm check:rotation` / `pnpm check:transition` / `pnpm check:repaint` / `pnpm check:code-plate` / `pnpm check:image-mode` / `pnpm check:rules` / `pnpm check:preset` / `pnpm check:ui` / `pnpm check:node`:
244
210
 
245
- - **`check:holiday`** — 两个节日窗口对照已知日期、北京时间的跨日边界(23:59 与 00:01)、1900–2100 年间的闰八月,以及 `pickHoliday` 允许节日接管背景之前必须通过的四道闸门。
246
- - **`check:rotation`** — 轮换的判定(`nextIndex` / `isRotating`):不会走越界、随机不会重复当前这张(在整个随机取值范围内穷举)、也不会把只有一张图的规则报成「正在轮换」。
247
- - **`check:transition`** — 切换效果的判定(`resolveFadeMs` / `transitionPlan`),代码在 `src/client/transition.ts`,正是那些不报错、只是「看起来没生效」的部分:出厂默认仍然必须产出**与旧版本逐字符相同**的过渡串(`opacity 320ms ease`,且只动 opacity——没有变化的属性不能被写进过渡列表);`直接切换`、时长为 `0`、统一时长为 `0`、以及时长不是数字,四者都必须等于硬切;「减少动态效果」是否决而不是缩短;每个效果的起点都必须与终点不同(也就是「换了新效果却什么都没发生」);每个效果结束时都必须完全可见且**没有残留缩放**——一次没有复位的 zoom 会让之后每一张壁纸都被放大裁掉一圈。
248
- - **`check:repaint`** — 「这次编辑要不要重画界面」的判定(`shouldRepaint`),其中就包含实际踩过的那个坑:**这次编辑本身**制造出了生效规则(某条规则的第一张图、第一个颜色或自动提取出的颜色)时,即使被编辑的规则此刻还不是生效规则,也必须立刻重画。它同时还会在 `src/client/index.tsx` 里任何一条写入路径不再询问这个判定时报错——也就是说,把老的 `id === activeRuleId` 判断写回去,会是构建校验失败,而不是你下一次上传时界面不动;轮换步骤不再重发调色板(主题色属于图片,换图就是换配色)同样会被它拦下。
249
- - **`check:code-plate`** — 代码块底板:它读的那几个表面 token 必须在 `:root` **和** `body` 上都有声明(`var()` 是在**声明它的地方**就被替换的,只声明在 body 上的 token 被根元素消费时读到的是宿主的颜色而不是调色板的)、浅色分支也要发出 banner 而不是偷借宿主的近白色、以及两块表面各读**自己**的插件变量 —— 共用一个变量会让 banner 的写入把底板也一起画了。
250
- - **`check:image-mode`** — 0.7.5 把布局模式下放到图片上的那次抬升,以及更要紧的**接线**:某个控件编辑的是哪张图、渲染层读的又是哪张图。接错了不会报错,只会让一轮轮换里的每张图长得一样。
251
- - **`check:rules`** — 选项卡背后那两个纯判定(在 `src/client/rules-view.ts`:编辑器显示哪条规则、删除之后落在哪——落在邻居上,永远不是规则 1),以及布局所依赖的页面结构:只画一个编辑器、没有旧的堆叠容器、没有逐卡片的展开状态、胶片条既不换行也不长高,以及它的滚动条样式块仍然被限定在插件自己的类上。
252
- - **`check:preset`** — 推荐配置那几次传输背后的两个判定(`src/preset.ts`:哪些失败值得再来一轮、失败的那一次给下一次留下什么),以及两侧**真的在用**它们:六个失败原因逐个判定(三个终态正是能在本地廉价判定的那三个;超时不重试是因为同一轮里两个镜像都已经问过)、等待指数增长且有上限、同一个值的不同键顺序给出同一个「同一份配置」而图片列表的顺序**算数**(它就是轮换顺序)、续传拿到的必须是副本而不是那条记录本身 —— 再加上接线(node 半边循环轮次并按策略决定、客户端按「还缺哪些」而不是整个槽位表铺并发池、失败留字节、成功清掉、体积上限在收下文件之前判)。
253
- - **`check:ui`** — 每个 `t('…')` 键在中英两本字典里都存在、两本字典键集完全一致、每个 `dab-…` 类在样式表里都有规则、没有字典条目变成没人引用的死键,以及每一个 `var(--dsw-…)` 都必须是已安装主题确实发布过的 token —— 未定义的自定义属性会让声明被丢掉、属性悄悄回落到继承值,placeholder 因此曾经画成正文字的墨色(读不到主题时这一节跳过并说明)。
254
- - **`check:node`** — 把构建好的 node 半边通过它真实的 RPC 处理器跑一遍,`DSH_HOME` 指向临时目录,并把 `globalThis.fetch` 换成桩 —— 节日画现在住在 CDN 上,这套校验不能依赖某个 CDN 是活的,也不该去断言一个不属于自己的文件今天有多少字节。它覆盖:节日配置块穿过共享的 sanitizer;仅仅加载插件或读取配置不会发起任何下载;画是按需下载并缓存到 `holiday-cache/<所钉 tag>/` —— 按**发行版**和**素材名**而不是槽位命名,被取代的旧代会在新图落盘之后被清掉;第二次读取直接读盘、不再传输;两次并发的冷读取共用一次传输;第一个镜像不通时回落到第二个,且两个镜像读的是同一个发行 tag;所有镜像都不通时答案是「没有字节」而不是抛异常,失败的抓取什么都不缓存;镜像返回 200 但内容不是图片时被拒绝而不是被缓存;损坏的缓存条目被丢弃并重新下载;节日槽位拒绝写入 / 删除 / 抓取而丢进去的文件被忽略、且数据目录里永远不会出现 `modelbg-h-*` 文件;普通规则槽位照常写入和删除;手改过的配置无法把节日指向别的规则的图片。它还覆盖逐图主题色这套形状:读取时抬升的两个方向(缺键继承规则色、显式 `null` 不继承)、颜色经过文件往返后各自独立,以及 `null` 是写出键而不是省略。切换效果同样在覆盖范围内:旧配置拿到的出厂默认、选定的效果 / 缓动 / 统一时长能原样穿过文件往返、非法值回退而夸张时长被夹紧、时长为 `0` 被保留而不是回退成默认,以及 `transition` 内部的字段漂移会被上报而不是悄悄丢掉。推荐配置从 node 半边这一侧被整条覆盖:manifest 与 config 按序下载并当场穿过 sanitizer、请求清单由规则推导且把节日槽位剔掉、一张壁纸一个请求且类型是嗅出来的而不是信 header、jsDelivr 对单个文件死了会回落到 raw、两个镜像同时抖一下时同一个文件会被**再问一遍**并最终拿到(三个请求,日志里看得见)、托管处没有的文件仍然只花两次请求(每镜像一次、不等待)、永久 500 的镜像总共只花六次请求(三轮 × 两个镜像 —— 重试是有预算的),以及它每一种失败都是返回值而不是抛异常 —— 所有镜像都不通、镜像回 500、回 200 但内容是 HTML、manifest 的版本不是数字、写给更新形状的配置在请求第一张壁纸之前就被拒绝、config 不是 JSON、config 是 JSON 数组、config 列出的图片数超限、镜像谎报一个超大的长度;再加上严格的那一半(config 点名而托管处没有的壁纸是一个错误,而不是一份短了的配置)、两次在任何请求之前就发生的拒绝(节日槽位、以及一个根本不是槽位的名字),以及新装那条规则:空数据目录读出来正好一条空白规则(`r1`、无匹配串、无颜色、无图),而显式写空的规则列表读回仍然为空 —— 这两者是不同的答案;清空那一步:配置和每一张规则图片都没了、`holiday-cache/` 原封不动、清空后再读拿到的是同一条空白规则而不是报错,以及随后能往空目录里写入这份配置。
211
+ - **`check:holiday`** —— 两个节日窗口、北京时间的跨日边界、1900–2100 年间的闰八月,以及 `pickHoliday` 的四道闸门。
212
+ - **`check:rotation`** —— `nextIndex` / `isRotating`:不走越界、随机不重复当前这张、没有可轮换的东西时绝不报「正在轮换」。
213
+ - **`check:transition`** —— `resolveFadeMs` / `transitionPlan`:出厂默认仍然产出与旧版本逐字节相同的过渡串、每一条硬切路径都确实是硬切、「减少动态效果」是否决而非缩短,以及每种效果结束时都完全可见且没有残留缩放。
214
+ - **`check:repaint`** —— `shouldRepaint`,包括那条**制造**赢家的编辑,以及任何一条写入路径不再询问、或轮换步骤不再重发界面调色板时报错。
215
+ - **`check:code-plate`** —— 代码块底板:token 在 `:root` 与 `body` 上都有声明、浅色分支发出 banner、两块表面各读自己的插件变量。
216
+ - **`check:image-mode`** —— 布局模式下放到图片上的那次抬升,以及某个控件编辑的是哪张图、渲染层读的又是哪张图。
217
+ - **`check:rules`** —— 选项卡背后那两个住在 `src/client/rules-view.ts` 的判定,以及布局所依赖的页面结构。
218
+ - **`check:preset`** —— 推荐配置的重试策略与失败那一次给下一次留下什么,并验证两侧真的在用它们。
219
+ - **`check:ui`** —— 两本字典键集一致、每个 `dab-…` 类都有样式、没有死键,以及每个 `var(--dsw-…)` 都是已安装主题发布过的 token。
220
+ - **`check:node`** —— 把构建好的 node 半边通过真实的 RPC 处理器跑一遍(`DSH_HOME` 指向临时目录、`fetch` 换成桩):节日配置块、节日画的缓存与清理、镜像回落、逐图颜色抬升、全局切换效果,以及推荐配置的整条链路。
255
221
 
256
222
  ## 常见问题
257
223
 
258
224
  **所有模型看起来都一样,为什么?**
259
- 多半是第 1 条规则的匹配串是空的,它成了纯兜底、把整个列表兜住了。给规则 1 填一个匹配串,或把更具体的规则加到兜底规则**上面**。
225
+ 多半是第 1 条规则的匹配串是空的,它成了纯兜底、把整个列表兜住了。给它填一个匹配串,或把更具体的规则加到它**上面**。
260
226
 
261
227
  **`GLM-Flash` 命中了不是我想用的规则。**
262
- 匹配是先命中者胜,而不是最精确者胜。把更具体的规则往上拖,或调整顺序让你想要的规则排在前面。
228
+ 匹配是先命中者胜,而不是最精确者胜:把更具体的规则往上拖,或调整顺序。
263
229
 
264
230
  **图片存在哪?**
265
231
  在 `~/.dsh/.dsh-background-by-model-data/`:每张图一个 `modelbg-<slot>` 文件,外加 `theme-config.json`。
266
232
 
267
233
  **我开了轮换,背景却一直没变。**
268
- 有三件事会拦住它:规则至少要有两张图;标签页必须可见(隐藏的标签页什么都不画,轮换跟着一起停);只有当前模型解析出的那条规则在轮换——没生效的规则,「下一张」按钮就是灰的,正是这个原因。
234
+ 有三件事会拦住它:规则至少要有两张图;标签页必须可见;只有当前模型解析出的那条规则在轮换。
269
235
 
270
236
  **轮换有代价吗?**
271
- 有,所以默认关闭。每张图都按原始保真度保存(不做重编码),所以一条放了十张 8 MB 壁纸的规则就是十份 8 MB;每次换图还会重画整个界面。建议一组控制在两到五张、节奏也别太密。启动时只读每条规则的第一张,轮换也会提前预热它下一张要用的图,所以代价落在内存和磁盘上,而不是启动时间上。
237
+ 有,所以默认关闭:每张图都按原始保真度保存,每次换图还会重画整个界面。一组控制在两到五张最合适 —— 启动时只读每条规则的第一张,所以代价落在内存和磁盘上,而不是启动时间上。
272
238
 
273
239
  **删掉规则里的图片之后,主题色为什么不起作用了?**
274
- 这是已经修掉的旧行为,根因是「有没有图」被当成了「这条规则是否可用」:图片删光的规则不再参与匹配,效果自然也就无从谈起,而它原本负责的模型还会被交给下一条规则。现在只要规则**有图、或者有自己的主题色**就仍然生效:不铺壁纸,只按这个颜色给界面配色(面板上会写明当前属于哪种状态)。连主题色也没有、也就是一点内容都没有的规则才会被跳过——「这条规则还没有图片,也没有自己的主题色:没有可显示的内容,匹配和兜底都会跳过它」。
240
+ 这是已经修掉的旧行为:现在只要规则**有图、或者有自己的主题色**就仍然生效,照旧匹配、照旧当兜底,只是不铺壁纸、只给界面配色。
275
241
 
276
242
  **我更新了插件,面板说我的改动没有保存。**
277
- 浏览器半边和宿主半边是分开加载的:刷新页面后新客户端可能已经生效,而 DSH 主进程还在跑上一个版本的插件——它读不懂新的配置格式(`SCHEMA_VERSION` 比它低就知道),下一次写入就可能把规则丢掉。客户端检测到这种情况会**扣住所有配置写入**并提示你:重启 DSH 之后,多图配置、轮换设置等一切都会正常保存。
243
+ 浏览器半边和宿主半边是分开加载的,所以刷新后的新客户端可能正对着仍在跑上一个 node 半边的 DSH 进程,而它读不懂新的配置格式。在你重启 DSH 之前,客户端会扣住所有写入。
278
244
 
279
245
  **我给一张图设了颜色,其它图片却没跟着变。**
280
- 这是设计如此:从 0.7.1 起主题色属于**图片**而不是规则,所以一组图可以依次是不同调性(绿的、红的、以及跟随系统主题的)。调色控件作用于胶片条里选中的那一张,控件上方的提示会写明是哪一张。唯一例外是**一张图都没有**的规则:那时控件编辑的是规则自己的主题色——那正是这种规则能画的东西,而且它刻意不作为该规则图片的默认值。
246
+ 这是设计如此:从 0.7.1 起主题色属于**图片**,所以一组图可以依次是不同调性。唯一例外是**一张图都没有**的规则,那时控件编辑的是规则自己的主题色。
281
247
 
282
248
  **更新之前我设的主题色去哪了?**
283
- 哪儿也没去:在「逐图主题色」之前写下的图片条目**根本没有** `color` 这个键,而这正是加载时用来区分「从来没有自己的颜色」和「被主动清空过」的依据——前者会把规则的颜色抬升到每一张这样的图上。所以带主题色的规则升级后观感完全一致,此后每张图就可以各自改色了。
249
+ 哪儿也没去:在「逐图主题色」之前写下的图片条目**根本没有** `color` 键,而这正是加载时区分「从来没有自己的颜色」和「被主动清空过」的依据 —— 前者会把规则色抬升到每一张这样的图上。
284
250
 
285
251
  **能分享整套配置吗?**
286
- 可以,在 **「配置」** 页导出即可;导出的 JSON 会把每条规则的图片内联进去。
252
+ 可以,在**「配置」**页导出即可;导出的 JSON 会把每条规则的图片内联进去。
287
253
 
288
254
  **支持视频或生成式背景吗?**
289
255
  不支持。两者都在 0.3.0 中移除,每条规则使用一张静态图片。
290
256
 
291
257
  **把主题色切回「系统主题」后,界面和设置弹窗为什么不是一套配色?**
292
- 这是已经修掉的旧行为:清空主题色时插件只撤掉了 `body` 上那套令牌,设置弹窗里那三个**没有宿主回退**的重定向(`--dsw-alias-bg-layer-*` → 插件私有变量)以及轨迹页、三栏的内联背景都还留着上一次的颜色,于是出现「深色界面 + 浅色弹窗、白底白字」。现在这些表面统一由一个调色板来源驱动:有主题色时用规则自己的令牌,没有时**读取宿主当前令牌**再套上你的透明度重发,切换时一并交还宿主。
293
-
294
- **切回「系统主题」后一开始还是浅色,拖一下滑块才变深,为什么?**
295
- 同一个洞的另一半:插件在持有主题色时会**强改** `body[data-ds-dark-theme]`(深色调色板写标记、浅色调色板直接删掉),而宿主只在**主题快照变化时**才重写这个标记(它不监听属性)。于是浅色皮肤留下的「标记被删」会一直生效,插件在切回系统主题那一刻读到的就是宿主的浅色调色板,并且之后没有任何事件再去重读——直到你碰了某个滑块。现在:切换时同步交还标记(宿主深色就写成宿主的布尔形式),并把宿主解析出的配色方案(`ctx.theme` 的 `active.colorScheme`,过期则回退到 `html[data-ds-theme-source]` + `prefers-color-scheme`)推给渲染层;每秒的守护定时器在「无主题色」状态下也会重读一次宿主调色板,变了就重画。
258
+ 这是已经修掉的一个 bug:现在所有表面由同一个调色板来源驱动,清空主题色时会读回宿主自己的令牌、套上你的透明度重发。
296
259
 
297
260
  **为什么「自定义颜色」点下去就有颜色了?**
298
- 因为按下它就等于离开「跟随系统主题」,必须给出一个起始色。它先跑一次「从本图提取」(和「从本图提取」按钮同一条链路),取不到鲜明颜色时才退回默认色,所以不会再出现「点一下就从浅色跳成一块跟壁纸无关的深蓝」。
261
+ 因为按下它就等于离开「跟随系统主题」,必须给出一个起始色 —— 它先跑一次「从本图提取」,取不到鲜明颜色时才退回默认色。
262
+
263
+ **切回「系统主题」后一开始还是浅色,拖一下滑块才变深,为什么?**
264
+ 同一个 bug 的另一半,也已经修掉:插件在持有主题色时会强改 `body[data-ds-dark-theme]`,而宿主只在主题快照变化时才重写这个标记。现在切换时同步交还标记,每秒的守护定时器也会重读一次宿主调色板。
299
265
 
300
266
  **我换了新的切换效果,壁纸还是直接跳过去。**
301
- 有两件事会拦住它,而且两件都在那张卡上:效果是**「直接切换」**;或者时长是 `0`(效果和时长是同一个硬切的两种说法,而效果优先)。第三件不在插件里:系统开了**「减少动态效果」**时完全不做动画,卡片也会直说。
267
+ 效果是**「直接切换」**,或者时长是 `0` —— 效果和时长是同一个硬切的两种说法,而效果优先。不在插件里的第三种:系统开了**「减少动态效果」**时完全不做动画。
302
268
 
303
269
  ## 近期优化
304
270
 
305
- > 这里只列最近两个版本;更早的版本(v0.7.8 及以前)见 [CHANGELOG.zh.md](./CHANGELOG.zh.md)。
271
+ > 这里只列最近两个版本;更早的版本见 [CHANGELOG.zh.md](./CHANGELOG.zh.md)。
306
272
 
307
- ### v0.7.10
273
+ ### v0.8.0
308
274
 
309
- - **节日画的缓存现在按所钉的发行版做键,于是更新过的插件会重新下载,而不是被告知旧的字节就是答案** —— 缓存此前住在 `holiday-cache/<asset>`,这让「文件的身份」和「它背后的字节」脱了钩:把插件指向 assets 仓库的一个新发行版,`readHolidayAsset` 依然能在磁盘上找到 `mid-autumn.webp`,把它返回,然后什么都不说。这正是「钉死引用」本身要防的那种静默失效,只是低了一层 —— 而且它是构造上不可见的,发生在没人盯着的那一天。现在缓存是 `holiday-cache/<tag>/<asset>`(`holiday-cache/v0.1.0/mid-autumn.webp`),tag 进了路径,于是一个钉了新 tag 的构建从一个没人写过的目录开始。`ls holiday-cache/` 现在能回答「这张盘上留着哪几代」。
310
- - **被取代的那一代会被清掉,而且是在新图落盘之后、绝不在之前。** 带版本的键本来会让每一代都永远留在那里(每代约 730 KB),包括后续发行版已经不再发布的素材。清理发生在一次成功的缓存写入的末尾 —— 那时字节已经在手里、也已经 rename 到位 —— 所以一个取不到的发行版不会让用户丢掉他已经有的画。它删的是**目录**条目、而且只删名字不是本构建 tag 的那些,所以正在用的那一代无论比较结果如何都不可能被碰到。
311
- - **`check:node` 把这两半都钉住了。** 测试脚本里的 `REF` 现在是一个值,两条 URL 断言**和**缓存路径共用它;另外新增三条断言:所钉那一代里缺失的素材会被重新下载而不是读到旧的、种下的 `v0.0.9` 目录在新图落盘后消失、以及正在用的那一代原封不动。
275
+ - **构建产物在打包时生成,不再提交进仓库** —— `lib/` 是每次发版约 1 MB 的生成代码,也几乎就是这个仓库增长的全部;现在它被 gitignore 挡住、由 `prepack` 生成,所以 npm 上发出去的永远是从被打 tag 的那份源码构建出来的。
276
+ - **不能再直接从 GitHub 仓库安装了** —— 那棵树里没有 `lib/`,也没有任何东西会去构建它,所以只剩 npm 一个来源。发版也改由 GitHub Actions 走 OIDC 完成,并附带构建 provenance。
312
277
 
313
- ### v0.7.9
278
+ ### v0.7.10
314
279
 
315
- - **所有素材引用都钉到 assets 仓库的同一个发行 tag** —— `v0.1.0`。三处引用此前漂成了三种状态:README 的三张截图读的是 `main` 分支,节日素材钉的是一个原始 commit 哈希(`58a9e107…`,对人来说什么都说明不了),而推荐配置是**刻意**跟着 `main` 的 —— 于是一个插件可能在读一份它构建之后才变过的字节。现在三处都是 `…/assets@v0.1.0/…`,插件所取的每一个字节(截图、节日素材、推荐配置)都来自 assets 仓库的同一个版本,而且这个值人能读、能拿去和 GitHub 上的 tag 对照、也能在一处改完。
316
- - **节日素材的字节并没有跟着变。** `58a9e107` 是 `v0.1.0` 的祖先,两张 `holiday/*.webp` 在两者之间逐字节相同,所以下载行为不变;变的只是这个引用现在和仓库本身用同一种方式命名。raw 镜像也带 tag(`raw.githubusercontent.com/HarmlessFunny/assets/v0.1.0/…`),所以回落仍然不可能混版本。
317
- - **推荐配置是刻意放弃「不发版就能修」的** —— 它此前跟着分支,是为了让一个不好的推荐能在 assets 仓库里直接修正、不必等插件发版,理由是推荐配置的失败是**响亮的**(你正盯着那个按钮),而节日的失败是静默的,所以不可变在这里什么都买不到。它买来的代价是:插件的字节可能在装上之后、你点下去之前发生变化;现在一个 tag 同时覆盖截图、节日素材和这份配置,三者都不再有这个问题。改一个 JSON 现在要花一个 `assets` 里的 tag、`src/index.ts` 里的一个值和一次 npm 发版。也要注意 tag **不是**什么:它可以被重新指向,而 jsDelivr 把版本引用按不可变处理,所以重新指向要发一次 purge 请求而不是 push —— 这也是 `presetKey` 在这次改动后仍然保留的原因:一个没清过缓存的边缘节点仍会回同一个 URL 的上一次字节,恢复下载的那一次不能把两个修订拼在一起。
318
- - **`check:node` 在两个功能上都钉住了这个 tag。** 它的 `revision()` 辅助函数此前找的是 40 位 SHA,根本看不见 tag;现在它从仓库名旁边取出引用(jsDelivr 形状是 `HarmlessFunny/assets@<ref>`,raw 形状是 `HarmlessFunny/assets/<ref>/`),于是两个镜像仍然可以互相比较;两条断言里原来的「a commit, never a branch」和「follows the branch」现在都要求 `v0.1.0`。
280
+ - **节日画的缓存现在按所钉的发行版做键** —— `holiday-cache/<tag>/<asset>`,于是钉了新 tag 的构建从一个没人写过的目录开始,而不是被告知上一代的字节就是答案。
281
+ - **被取代的那一代会被清掉**,在一次成功的缓存写入末尾、绝不在之前,所以一个取不到的发行版不会让用户丢掉他已经有的画。
319
282
 
320
283
  ## Star History
321
284
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-background-by-model",
3
- "version": "0.7.10",
3
+ "version": "0.8.0",
4
4
  "description": "Appearance plugin for DeepSeek Harness: an ordered list of model rules, each carrying its own wallpapers — every image with its own framing and theme color — plus layout mode, opacity and blur; the background follows the model you switch to, and a rule can rotate through several images on its own rhythm.",
5
5
  "keywords": [
6
6
  "deepseek-harness",
@@ -51,6 +51,7 @@
51
51
  ],
52
52
  "scripts": {
53
53
  "bundle": "tsdown",
54
+ "prepack": "tsdown",
54
55
  "watch": "tsdown --watch",
55
56
  "typecheck": "tsc -p tsconfig.json",
56
57
  "check:holiday": "node scripts/holiday-check.ts",