dsh-background-by-model 0.7.1 → 0.7.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/CHANGELOG.md +21 -1
- package/CHANGELOG.zh.md +21 -1
- package/README.md +43 -33
- package/README.zh.md +43 -33
- package/lib/client.js +807 -146
- package/lib/client.js.map +1 -1
- package/lib/index.js +86 -8
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,26 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
> The full release history lives here; the two most recent releases (v0.7.
|
|
3
|
+
> The full release history lives here; the two most recent releases (v0.7.3 / v0.7.2) are also listed in [README.md](./README.md#recent-optimizations).
|
|
4
|
+
|
|
5
|
+
## v0.7.3
|
|
6
|
+
|
|
7
|
+
- **The switch itself became a setting: four effects, an easing and one duration, on the Config page** — every wallpaper change this plugin performs (a model switch, a rotation step, the manual **Next image**, a holiday taking over, a different rule winning) went through one hard-coded cross-fade whose length was read off a field no control could reach. The effect and its easing are global, because a rule has no notion of a transition: **cross-fade** (opacity), **instant**, **zoom** and **slide** (transform, on the incoming layer only — a property the wallpaper layer had never used, so neither new effect costs a byte of extra transfer). Instant and a `0` ms duration are the same hard cut rather than one of them being read as "unset", a `0` is kept as a real value rather than defaulted away, and `prefers-reduced-motion` is honoured as a **veto** rather than as a shorter animation. The decision is pure (`src/client/transition.ts`), which is what lets `check:transition` pin the failures that throw nothing: an effect whose start state equals its end state (visibly doing nothing), an effect that leaves the incoming layer scaled (which would crop every later wallpaper), and `fade` ceasing to be byte-for-byte the string earlier releases wrote.
|
|
8
|
+
- **The card carries a preview swatch, and that is not decoration** — a wallpaper fading into itself is invisible, so replaying the effect on the picture already on screen would show nothing at all: the one way this feature could look broken while working perfectly. The swatch replays the chosen effect at the duration a real switch would use right now, and picking an effect or an easing replays it at once.
|
|
9
|
+
- **There is exactly one duration, and the per-rule choice is gone** — the card's default was "Follow each rule", which named a value the interface cannot set: `rotate.fadeMs` had existed in the shape since 0.7.0 and no control ever reached it, so the option was a choice between a real setting and a phantom one. The slider IS the setting now, and the field it deferred to is gone with it. The removal is not a silent loss for the one kind of config that could carry it: that field could only ever be hand-edited, so `legacyFadeMs` folds the first deliberate value it finds into the global duration when a config that predates the setting is first read — values equal to the shipped 320 ms are ignored, so an ordinary config keeps the ordinary default, and an explicit `durationMs` always wins over a legacy field sitting beside it. `rules[].rotate.fadeMs` is exempt from the drift warning for the same reason: it is being migrated, not drifting.
|
|
10
|
+
- **The interface colour now changes over the same clock as the wallpaper** — a new theme colour is one rewrite of `body{--dsw-alias-…}`, which changed every surface in a single frame while the picture behind it blended: a snap in front of an animation. The fade is a **registration** problem, not a declaration one, because an unregistered custom property cannot interpolate: `src/client/palette-fade.ts` emits `@property` rules that turn the alias tokens into `<color>`s, plus a `transition` for the elements whose own declarations change with the palette — `body`, and the two surfaces that re-declare layer tokens instead of inheriting them (the settings dialog and the trajectory root). The two places the plugin paints **inline** (the app columns, and the chat/trajectory cards) do not read those tokens at all, so they carry the same duration on `background-color` directly. The duration, the easing and the vetoes come from the **same** `transitionPlan` the wallpaper uses, so "instant", a `0` ms duration and reduced motion cannot mean one thing for the picture and another for the interface. It is armed **only when the palette itself moved**: every other pass rewrites the same token block (a slider drag moves the alphas) and writes the sheet with no transition and `none` inline, because a live transition there would make the surface lag behind the pointer. A token the host does not publish is deliberately left unregistered — registration needs an `initial-value`, and a registered property that nothing else sets resolves to that value instead of to the `var(…, fallback)` chain the plugin's own stylesheet relies on, so registering it would replace a fallback with an invented colour.
|
|
11
|
+
- **A light↔dark flip switches instantly, and the wallpaper keeps animating (this one is a fix)** — interpolating between the two palettes walks the whole interface through mid-tones where the ink is wrong in both directions — dark text half-way into a dark palette, and driven by the **previous** scheme's ink — so a scheme flip is exactly the case a fade makes worse. The fade is armed only while the **scheme** stays put (light→light, dark→dark); light↔dark switches at once, which is what it did before the fade existed. The wallpaper is deliberately untouched by that rule: one image fading into another has no scheme to get wrong, so it keeps whatever the switch effect says. `paletteFadeAllowed` is the whole rule, pure and pinned in `check:transition`, and `check:repaint` asserts the call site keeps **both** halves of the question (the palette moved **and** the scheme held), because the call site is where a later edit would quietly drop one — and the surfaces the plugin paints inline take the same answer, so they cannot end up animating while the tokens do not.
|
|
12
|
+
- **The ink is written synchronously now, and that was a real bug with exactly one writer (a fix)** — switching a rule to a dark theme colour could leave a panel's text on the **previous** (light) palette while every surface followed the new one: dark ink on a dark panel, unreadable, cleared only by reloading, and with nothing in the log to explain it. Measured from the reported screenshot rather than guessed: the panel was the plugin's own dark `bg-layer-2` — `hsl(229,59%,27%)` = `rgb(28,43,109)`, which at the configured 87% panel opacity composites to the sampled `rgb(23,36,91)` — while the label ink was the **light** branch token over it: `rgba(0,0,0,0.85)` on that navy is exactly `rgb(3,6,14)`, the value sampled from the glyph cores, and `rgba(0,0,0,0.7)` is exactly the `rgb(7,10,27)` sampled from the hint line. Two exact matches, one panel: the surface and the ink came from different palettes. The asymmetry is structural — every other surface has a second, synchronous writer (the dialog's layer tokens come from `applySettingsOverrides`, the columns' backgrounds from the view cards), so only the token stylesheet could go stale, and that one writer was reachable only from a `requestAnimationFrame` callback wrapped in `catch {}`: a window that is not rendering swallows the frame while the id stays armed, parking every later update behind it, and a failure inside the write said nothing at all. Reloading was the only cure, which is exactly what the report said. A palette that moved is now written synchronously, in the same task as the surfaces it has to agree with, while the frame path is kept for what it was for (coalescing a slider drag, where the palette is already correct); a frame outstanding for more than a second is dropped instead of blocking forever, and the writer logs its failure instead of swallowing it.
|
|
13
|
+
- **The config shape moved to 5 and an export to format 6, and an older host still cannot eat a profile** — a schema-4 sanitizer REBUILDS the config from the keys it knows, so a stale host process would drop the effect, the easing and the duration on the first write after a refresh, silently and with nothing in the host log to explain it. The published `schema` therefore moves, the client's `>=` comparison puts such a host into the hold-writes path it already had, and `warnUnknownConfigKeys` compares the fixed-shape `transition` block one level down for the same reason. Theme exports move to format 6; 5 and older still import. That shape has never been released, so `5` still means what it meant when it was written — `SCHEMA_VERSION` guards a newer client against an older host process, and no host has ever announced 5 with the per-rule field.
|
|
14
|
+
- **Gates** — `check:transition` (`scripts/transition-check.ts`) joins `pnpm test`, which is now **six** checks: it pins the cross-fade string byte-for-byte, the hard-cut semantics of `instant` and a `0` duration, the reduced-motion veto, the landing state of every effect, the palette-fade CSS shape (including that `0` ms emits the registrations and no transition rule) and the scheme-flip table. `check:repaint` gained the three lines this bug was made of — the palette write must be synchronous when the palette moves, must not swallow its failure, and must ask both halves of the fade question at both the token and the inline call sites. `check:node` covers the shipped default, the round trip, hostile values, a kept `0` duration, the `transition` drift check, the announced schema of 5, and the legacy fold (a hand-tuned 1200, a deliberate `0`, the unremarkable 320, and an explicit global value winning). `check:ui` covers the new strings and the two new classes (`.dab-tr-preview`, `.dab-tr-preview-in`), and registers the effect/easing chip labels in `TABLE_KEYS` — they are reached through `Record<TransitionEffect, …>` tables, which is the documented mechanism for a label the compiler already guarantees exists.
|
|
15
|
+
|
|
16
|
+
## v0.7.2
|
|
17
|
+
|
|
18
|
+
- **The filmstrip IS the control: drag to reorder, double-click to show** — the order used to be reachable only through a pair of ↑ / ↓ arrows and a "make first" button in the row below, which is a second and worse way to say "put this one there" when the five thumbnails already show the order they are in. Grab any tile and drop it where it belongs: the drop point is a 2px bar drawn INSIDE the tile it precedes, because a bar that took up room would push around the very tiles the pointer is being measured against. Which picture is on screen is the strip's OTHER gesture, and it changes no order and writes nothing at all — the image being shown is runtime state, not a setting, so it is not saved and the next boot starts at the rule's first image again. A double-click on a tile of a rule that is not painting says why (reusing "only the active rule can step the background") instead of leaving a double-click that looks broken, and both gestures are named in the tile's tooltip.
|
|
19
|
+
- **The order and "which one is on screen" no longer interfere with each other (this one is a fix)** — both old actions (`moveRuleImage`, `setCurrentImage`) ended by moving the image index onto the image they had just moved, so tidying the list swapped the wallpaper as well: with the rotation off the index is sticky, which is exactly the state this was met in ("even with the rotation off, the background can change when I adjust the order, and it can happen by dragging or by the buttons"). `moveRuleImageTo(id, slot, to)` replaces both of them: it remembers the SLOT being painted before the splice and puts the index back on it afterwards. A reorder is not a change of subject — the three buttons left with the two uncalled actions behind them, and `check:repaint`'s list follows.
|
|
20
|
+
- **The tile that is on screen lost its ring and kept its dot** — `.is-sel` already rings the tile that is open for editing, so a second ring put two "selected" tiles in a row of near-identical thumbnails, which is the one question a filmstrip must not leave open. Being on screen is the dot, and nothing else.
|
|
21
|
+
- **Every image of a batch upload gets its own color** — a theme color belongs to an IMAGE, so an upload carrying several pictures has several palettes to fill, and the extraction was addressed by POSITION: `addRuleImages` appended the whole batch and then asked for a color for `rule.images[rule.images.length - 1]` alone. That is the right answer for the one-file upload this action grew up with, and silently the wrong one for every batch — the pictures before the last kept no color of their own and stayed on the system theme, with nothing in the interface to say why. It is now addressed by the slots the batch itself just appended, so which pictures get themed no longer depends on where they landed in the rule, nor on which one the card happens to select.
|
|
22
|
+
- **Copy trimmed, and the framing button moved** — the "Rules" heading and the priority sentence under it, and the sentence explaining the automatic extraction, are gone (the panel's title introduces the rules and the `fallback` badge on the first card states the priority; the switch and its own label stay). The labels lost their nouns: `Image 1 / 5` for `当前第 1 / 5`, and `Add` / `Replace` / `Remove` for `添加图片` / `替换这张` / `移除图片`. `Edit position` (`编辑位置`) moved out of the image-action row to the end of the layout row — what it opens edits how the picture fills the area, which is what those five modes are about — and it stays last rather than between two chips because the modes are mutually exclusive and it is not a sixth one. It still appears only in **fit**, because dragging the framing is what fit means.
|
|
23
|
+
- **Gates** — `check:repaint` gained the batch section (the batch form must walk every slot it was handed, an upload must record the slots of its own batch and theme all of them, and the positional `maybeAutoExtract(id, rule.images[…])` must not come back), and `moveRuleImageTo` joins the actions required to sample the winner and close with `repaintIfMoved` while the two deleted actions leave it. `check:ui` covers the new strings and the new class (`.dab-strip-bar`, plus the `is-dragging` state), and requires the three keys nothing renders any more to leave both dictionaries in the same commit.
|
|
4
24
|
|
|
5
25
|
## v0.7.1
|
|
6
26
|
|
package/CHANGELOG.zh.md
CHANGED
|
@@ -1,6 +1,26 @@
|
|
|
1
1
|
# 更新日志
|
|
2
2
|
|
|
3
|
-
> 这里保留完整的历史记录;最近两个版本(v0.7.
|
|
3
|
+
> 这里保留完整的历史记录;最近两个版本(v0.7.3 / v0.7.2)同时列在 [README.zh.md](./README.zh.md#近期优化) 里。
|
|
4
|
+
|
|
5
|
+
## v0.7.3
|
|
6
|
+
|
|
7
|
+
- **切换效果本身成了一个设置:四种效果、一种缓动、一个时长,都在「配置」页上** —— 这个插件做的每一次换图(切模型、轮换一步、手动「下一张」、节日接管、换成另一条规则生效)此前都走同一个写死的交叉淡入,而它的时长读的是一个没有任何控件能碰到的字段。效果和缓动是全局的,因为规则没有「过渡」这个概念:**淡入淡出**(opacity)、**直接切换**、**缩放淡入**、**平移推入**(transform,只作用在进来的那一层上 —— 壁纸层从来没用过这个属性,所以两种新效果一个字节的额外传输都不花)。「直接切换」和 `0` 毫秒是同一个硬切,而不是其中一个被读成「没设置」;`0` 是一个被保留的真实取值,不会被默认值顶掉;系统的「减少动态效果」是**否决**,而不是把动画缩短。判定是纯函数(`src/client/transition.ts`),正因如此 `check:transition` 才能钉住那些不会抛错的失败:起始状态与结束状态相同的效果(看上去什么都没做)、会把进来的那一层留在缩放状态上的效果(此后每一张壁纸都会被裁掉一块),以及 `fade` 不再逐字节等于早先版本写下的那个字符串。
|
|
8
|
+
- **卡片上带一个预览色块,而它不是装饰** —— 壁纸淡入它自己是什么都看不见的,所以「在已经在屏的那张图上重放效果」会什么都不显示:这是这个功能唯一可能「明明在工作却看起来坏了」的方式。色块按此刻一次真实切换会用的时长重放选中的效果,点效果或点缓动都会立刻重放一次。
|
|
9
|
+
- **时长只有一个,「跟随每条规则」删掉了** —— 那张卡的默认值是「跟随每条规则」,而它指向一个界面根本设不了的值:`rotate.fadeMs` 从 0.7.0 起就在格式里,却从来没有控件能到达它,所以那个选项是在一个真设置和一个幻影设置之间做选择。现在滑块本身就是那个设置,它让位的字段也一起删了。对唯一可能带上它的那种配置来说,这并不是一次无声的丢失:那个字段只可能被手工编辑,所以当一份早于这个设置的配置第一次被读取时,`legacyFadeMs` 会把找到的第一个「有意为之」的值折进全局时长 —— 等于出厂值 320 ms 的一律忽略(普通配置就保持普通默认值),而显式的 `durationMs` 永远胜过它旁边的旧字段。`rules[].rotate.fadeMs` 同样豁免漂移警告,理由相同:它是在被迁移,不是在漂移。
|
|
10
|
+
- **界面配色现在和壁纸走同一个时钟** —— 换一个主题色本质是一次 `body{--dsw-alias-…}` 的重写,它在一帧之内改掉每一个表面,而背后的图还在淡入淡出:这就是动画前面接了一下硬切。这个渐变是**注册**问题而不是声明问题 —— 未注册的自定义属性无法插值 —— 所以 `src/client/palette-fade.ts` 会发出把别名令牌变成 `<color>` 的 `@property` 规则,外加一条 `transition`,作用于「自身的声明会随调色板变化」的那些元素:`body`,以及两个**重新声明**层令牌而不是继承它们的表面(设置弹窗与轨迹页根节点)。插件**内联**绘制的两处(三栏列,以及对话/轨迹卡片)根本不读这些令牌,所以它们把同一个时长直接挂在 `background-color` 上。时长、缓动与两项否决都来自壁纸用的**同一个** `transitionPlan`,所以「直接切换」、`0` 毫秒和「减少动态效果」不可能对图是一个意思、对界面又是另一个意思。它**只在调色板确实变了**的时候武装:其它每一次 apply 都在重写同一段令牌(拖滑块改的是 alpha),那些写入不带过渡、内联写 `none`,因为那里挂一条活着的过渡会让表面跟不上指针。宿主没有公布的令牌是**刻意不注册**的 —— 注册需要 `initial-value`,而一个没有别的东西去设置它的已注册属性,会解析到那个初始值、而不是插件自己样式表依赖的 `var(…, fallback)` 链,注册它就等于用一个人为的颜色替换掉回退值。
|
|
11
|
+
- **浅↔深互切是立即切换,而壁纸照旧做动画(这一条是修复)** —— 在两套调色板之间插值会让整个界面走过中间调,而那里的墨色**两个方向都是错的**(深色文字走进一半的深色调色板,而且用的是**上一套**方案的墨色),所以浅↔深互切恰恰是渐变会让事情变糟的那一种情况。现在渐变只在**配色深浅不变**时武装(浅→浅、深→深);浅↔深立即切换,也就是渐变出现之前的行为。这条规则刻意不碰壁纸:一张图淡入另一张图没有「深浅」可以搞错,所以它照旧听切换效果的。`paletteFadeAllowed` 就是这条规则的全部,纯函数、由 `check:transition` 钉住;`check:repaint` 则要求调用点把问题的**两半**都问全(调色板变了**且**深浅没变),因为调用点正是后来某次编辑会悄悄丢掉其中一半的地方 —— 而插件内联绘制的那些表面拿到的是同一个答案,所以不可能出现「令牌不渐变、内联面板却在渐变」的半吊子状态。
|
|
12
|
+
- **墨色现在是同步写入的,而那是一个真实存在、且只有一个写入者的 bug(这一条是修复)** —— 把一条规则换成深色主题色时,面板的文字可能留在**上一套**(浅色)调色板上,而所有表面都已经跟到新的那套:深色面板上的深色墨色,读不出来,只有重新加载才能清掉,日志里什么都不会说。这是从报告里的截图**量出来**的,不是猜的:面板是插件自己的深色 `bg-layer-2` —— `hsl(229,59%,27%)` = `rgb(28,43,109)`,在配置的 87% 面板透明度下合成为采样到的 `rgb(23,36,91)` —— 而标签墨色是画在它上面的**浅色**分支令牌:`rgba(0,0,0,0.85)` 压在那块藏青上恰好就是 `rgb(3,6,14)`,也就是从字形核心采样到的值;`rgba(0,0,0,0.7)` 恰好是提示行采样到的 `rgb(7,10,27)`。两个精确吻合,一块面板:表面和墨色来自两套不同的调色板。这种不对称是结构性的 —— 其它每个表面都有第二个同步的写入者(弹窗的层令牌来自 `applySettingsOverrides`,三栏的背景来自 view cards),所以只有令牌样式表可能变旧,而这唯一的写入者只能从一个包在 `catch {}` 里的 `requestAnimationFrame` 回调到达:不在渲染的窗口会吞掉那一帧,而 id 仍然武装着,把此后每一次更新都堵在它后面;写入内部出错则什么都不说。重新加载是唯一的解药,而报告里说的正是这件事。现在调色板一旦移动,就在**与它必须保持一致的表面同一个任务里**同步写入;帧路径留给它本来要做的事(合并一次滑块拖动,那时调色板本来就是对的),积压超过一秒的帧会被丢掉而不是永久阻塞,写入者也会把失败记进日志而不是吞掉。
|
|
13
|
+
- **配置格式升到 5、导出格式升到 6,旧宿主依旧吃不掉配置** —— schema 4 的 sanitizer 会**按自己认识的键重建**配置,所以一个仍在运行的旧宿主进程会在刷新后的第一次写入时丢掉效果、缓动和时长,悄无声息,宿主日志里也什么都没有。因此公布的 `schema` 前进,客户端那句 `>=` 比较会把这样的宿主放进它本来就有的「扣住写入」分支,`warnUnknownConfigKeys` 也出于同样的理由往深一层比较固定形状的 `transition` 块。主题文件升到格式 6;5 及更早仍可导入。这个格式从未发布过,所以 `5` 仍然是它写下时的含义 —— `SCHEMA_VERSION` 防的是「新客户端配旧宿主进程」,而从来没有任何宿主用旧形状报出过 5。
|
|
14
|
+
- **门禁** —— `check:transition`(`scripts/transition-check.ts`)加入 `pnpm test`,现在是**六道**:它逐字节钉住交叉淡入的字符串、「直接切换」与 `0` 时长的硬切语义、「减少动态效果」的否决、每种效果的落点状态、配色渐变的 CSS 形状(包括 `0` 毫秒只发注册、不发过渡规则),以及浅↔深那张真值表。`check:repaint` 新增了这个 bug 由之构成的三行:调色板移动时写入必须是同步的、不许吞掉自己的失败、并且必须在令牌与内联两个调用点都把渐变问题的两半问全。`check:node` 覆盖出厂默认值、往返、恶意取值、被保留的 `0` 时长、`transition` 漂移检查、公布 schema 为 5,以及那次旧值折算(手工调过的 1200、有意的 `0`、平平无奇的 320、显式的全局值胜出)。`check:ui` 覆盖新文案和两个新类(`.dab-tr-preview`、`.dab-tr-preview-in`),并把效果/缓动胶囊的标签登记进 `TABLE_KEYS` —— 它们是通过 `Record<TransitionEffect, …>` 这类表到达的,而这正是「编译器已经保证存在的标签」的既定登记机制。
|
|
15
|
+
|
|
16
|
+
## v0.7.2
|
|
17
|
+
|
|
18
|
+
- **胶片条自己就是控件:拖拽排顺序,双击换画面** —— 顺序以前只能靠下面那排 ↑ / ↓ 和一个「设为第一张」按钮改,而五个缩略图本来就把顺序摆在那里,那对箭头是同一件事的第二种、更差的说法。现在抓住任意一张拖到新位置:落点画成一根 2px 竖条,画在目标格子**内部**,因为一块占位的竖条会把指针正在测量的那些格子推走。而「哪张在屏上」是胶片条的**另一个**手势 —— 双击它。它不改顺序,也**不写任何配置**:在屏哪张是运行时状态而不是设置,所以它不会被保存,下次启动照旧从规则的第一张开始。在非生效规则的格子上双击会说明原因(复用现成的那句「只有正在生效的规则能换画面上的图」),而不是留下一个看起来坏了的双击;两个手势都写在格子的悬停提示里。
|
|
19
|
+
- **顺序和在屏的是哪张,从此互不干涉(这一条是修复)** —— 旧的两个动作(`moveRuleImage`、`setCurrentImage`)都以「把图片下标挪到刚被移动的那张图上」结尾,于是整理列表会把壁纸一起换掉:轮换关着的时候下标是粘住的,而这正是最容易撞见它的状态(「我调整顺序时,即便关了轮换,当前背景也可能会变,拖拽或点按钮调整顺序都有可能发生」)。新的 `moveRuleImageTo(id, slot, to)` 取代了它们两个:摘出之前先记下**正在画的那个槽位**,插回去之后把下标放回它身上。重排不是换主题 —— 三个按钮连同它们背后两个不再被调用的动作一起删掉了,`check:repaint` 的清单同步。
|
|
20
|
+
- **在屏那张不再有边框,只留圆点** —— `.is-sel` 已经给「正在编辑的那张」画了圈,再给「正在显示的那张」画一个,就成了一排几乎一样的缩略图里同时有两个「选中」,而这是胶片条唯一不能留给人猜的问题。现在在屏 = 右上角那个圆点,仅此而已。
|
|
21
|
+
- **一次上传的多张图,每一张都会各自取色** —— 主题色属于**图片**,所以一次带多张的上传有好几个调色板要填,而取色是**按下标**寻址的:`addRuleImages` 先把整批追加完,然后只对 `rule.images[rule.images.length - 1]` 取色。这对它长大时面对的单张上传是对的,对批量则静默地错 —— 最后一张之前的那些图没有自己的颜色、留在系统主题上,而界面上什么都不会说。现在按**这一批自己刚追加的那些槽位**寻址,所以「哪些图会被取色」不再取决于它们落在规则的哪个位置,也不取决于卡片当时选中了哪一张。
|
|
22
|
+
- **卡片文案精简,「编辑位置」换了位置** —— 「规则列表」标题和它下面那句优先级说明、以及自动取色旁边那句解释都删了(面板标题已经介绍了规则,第一张卡上的 `兜底` 徽章说明了优先级;开关和它自己的标签留着)。标签去掉多余的名词:`当前第 1 / 5` → `第 1 / 5`,`添加图片` / `替换这张` / `移除图片` → `添加` / `替换` / `移除`,英文同步。`编辑位置` 从「图片动作」那一行移到**布局模式那一行的末尾**:它打开的东西编辑的是图片如何填满这块区域,正是那一行五个模式在说的事;它留在末尾而不是插在两个模式之间,因为那五个是互斥的,它不是第六个。它仍然只在**适应**下出现 —— 拖拽构图就是适应在做的事。
|
|
23
|
+
- **门禁** —— `check:repaint` 新增一节钉住批量取色:批量形式必须遍历它拿到的每一个槽位、上传必须记录自己这批的槽位并给它们全部取色、按下标寻址的 `maybeAutoExtract(id, rule.images[…])` 不许回来;`moveRuleImageTo` 进入「每个写入路径都必须先采样 winner、再以 `repaintIfMoved` 收尾」的清单,两个被删掉的动作则从中移出。`check:ui` 覆盖新文案与新类(`.dab-strip-bar`,以及 `is-dragging` 状态),并且要求不再有调用者的那三个字典键在同一个提交里离开两份字典。
|
|
4
24
|
|
|
5
25
|
## v0.7.1
|
|
6
26
|
|
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ English | [中文](README.zh.md)
|
|
|
9
9
|
|
|
10
10
|
> Forked from [`Tkingxiao/dsh-any-background`](https://github.com/Tkingxiao/dsh-any-background) and renamed to `dsh-background-by-model`.
|
|
11
11
|
|
|
12
|
-
A **DeepSeek Harness** appearance plugin built around an **ordered list of model rules**. Each rule carries its own wallpaper, theme color, layout mode, framing, opacity and blur — switch the model, and the background switches with it.
|
|
12
|
+
A **DeepSeek Harness** appearance plugin built around an **ordered list of model rules**. Each rule carries its own wallpaper, theme color, layout mode, framing, opacity and blur — switch the model, and the background switches with it, through the global [switch effect](#switch-effect-global) you choose on the Config page.
|
|
13
13
|
|
|
14
14
|
> **v0.3.0 is a breaking release.** Video wallpapers, generated dynamic backgrounds and the single global theme color were removed. See the [v0.3.0 entry in CHANGELOG.md](./CHANGELOG.md#v030).
|
|
15
15
|
|
|
@@ -78,7 +78,7 @@ One interface, one config — only the current model differs:
|
|
|
78
78
|
- **Live Match Readout** — The top of the Model Background tab shows the current state, e.g. `Current model deepseek-flash → matched · Rule 1`, so you can verify a rule on the spot. If the current model can't be detected, a hint is shown instead.
|
|
79
79
|
- **Per-rule Appearance** — Every rule owns its wallpapers, layout mode, opacity and blur, and **every image owns its framing and its theme color**. Nothing is shared globally except the Interface tab.
|
|
80
80
|
- **Several Images per Rule** — A rule holds an ordered set of images, shown as a filmstrip: add several at once (file picker, drag & drop, or a list of URLs, one per line), reorder them, promote one to first, **replace one in place** (which keeps its position in the rotation), or remove them one by one. The image a rule paints when nothing rotates is its **first**. Removing the last image simply leaves the rule empty — and empty is not the same as off: while the rule keeps a theme color of its own it still matches, still serves as the fallback, and paints the interface instead of a wallpaper; only a rule with neither (a freshly added one) is skipped by matching. So an emptied rule is a visible, reversible state rather than one you get stuck in. While it holds no picture the filmstrip is hidden and the big preview tile **is** the upload button, so there is exactly one obvious place to add one.
|
|
81
|
-
- **Import / Export** — Export every rule **including all of its images** to a `dsh-background-by-model-theme.json` (format version
|
|
81
|
+
- **Import / Export** — Export every rule **including all of its images** to a `dsh-background-by-model-theme.json` (format version 6, images inlined as base64) and restore it anywhere. Files written by earlier releases still import: each rule's single image is lifted into its image list, a color written before per-image colors is lifted onto every image that has none, and a file written before the [switch effect](#switch-effect-global) gets that setting's shipped default — so an older theme file restores its look unchanged.
|
|
82
82
|
- **File-based Persistence** — All settings and images are stored on the filesystem under `~/.dsh/.dsh-background-by-model-data/`, not `localStorage`.
|
|
83
83
|
- **Automatic Migration** — Old single-wallpaper configs are upgraded in place on first read. See the [v0.3.0 entry in CHANGELOG.md](./CHANGELOG.md#v030).
|
|
84
84
|
- **Bilingual** — Full Chinese / English UI with automatic locale detection.
|
|
@@ -98,6 +98,19 @@ Per rule, and **off by default** — cycling spends real bandwidth, memory and b
|
|
|
98
98
|
- **Framing is per image, and so is the color** — a crop and an accent both belong to a picture, so each image keeps its own while sharing the rule's layout, opacity and blur. The filmstrip marks the images that carry a color of their own, and the color controls always say which image they are editing.
|
|
99
99
|
- **Only the images it needs are loaded** — boot reads each rule's first image; the rest stream in when a card is expanded or when the rotation is about to need one. Ten images per rule therefore do not mean ten wallpapers transferred before the first frame.
|
|
100
100
|
|
|
101
|
+
### Switch effect (global)
|
|
102
|
+
|
|
103
|
+
What a wallpaper **change** looks like. It lives on the **Config** page, it is the same for every rule (a rule has no notion of a transition), and it covers every way the wallpaper changes: a model switch, a [rotation](#image-rotation) step, the manual **Next image**, and a [holiday](#holiday-backgrounds) taking over.
|
|
104
|
+
|
|
105
|
+
- **Four effects** — **Cross-fade** (the blend every earlier release performed, and therefore the default), **Instant** (a hard cut), **Zoom in** (the incoming image settles from 6% larger) and **Slide in** (it pushes in from the side). Zoom and slide animate `transform`, which the wallpaper layer had never used, so neither costs a byte of extra transfer.
|
|
106
|
+
- **Easing** — standard / linear / ease-out / ease-in-out. The row is not rendered while the effect is **Instant**: a hard cut has no curve to shape, and a control that does nothing is worse than no control.
|
|
107
|
+
- **One duration for every switch** — `0`–`3000 ms`, and `0` is a hard cut rather than "unset". It replaced the per-rule `rotate.fadeMs`, which from 0.7.0 on was a duration with **no control anywhere** — it could only be hand-edited — so a hand-tuned value is folded into this one when a config that predates the setting is first read (a value equal to the shipped 320 ms is ignored, so an ordinary config keeps the ordinary default).
|
|
108
|
+
- **`Instant` and `0` ms are the same hard cut** — two ways of asking for one thing, so both are honoured instead of one being read as "not set".
|
|
109
|
+
- **Reduced motion is a veto, not a shorter animation** — while `prefers-reduced-motion: reduce` is in force nothing animates at all, and the card says so rather than previewing something the interface will not do.
|
|
110
|
+
- **A preview swatch, because a wallpaper fading into itself is invisible** — replaying the effect on the wallpaper already on screen would show nothing, which is exactly the "I changed it and nothing happened" this plugin keeps designing out. The swatch replays the chosen effect with the duration a real switch would use right now, and picking a different effect replays it at once.
|
|
111
|
+
- **The interface colour changes on the same clock** — a theme colour is one rewrite of the token block, which used to change every surface in a single frame while the wallpaper faded. The alias tokens are registered as colours so they interpolate over this setting's duration and easing, including the surfaces that re-declare those tokens instead of inheriting them and the two the plugin paints inline. One deliberate exception: a **light↔dark flip switches instantly**, because that is a different palette rather than a tint of the same one, and interpolating between them walks the interface through mid-tones whose text is unreadable in both directions. The wallpaper keeps its own animation either way.
|
|
112
|
+
- **And it is not a slider** — the fade is armed only when the palette itself moved. A drag rewrites the same token block on every frame, and a live transition there would make the surface lag behind the pointer.
|
|
113
|
+
|
|
101
114
|
### Holiday backgrounds
|
|
102
115
|
|
|
103
116
|
A small one, not a settings block: **one switch on the Config page and nothing else**. It is on out of the box, and on the day the background changes by itself and changes back the next morning.
|
|
@@ -135,7 +148,7 @@ Settings → **Theme** now has three tabs:
|
|
|
135
148
|
| --- | --- | --- |
|
|
136
149
|
| **Interface** | Global, shared by every model | Opacity & blur for the main background, left panel, right panel, cards & panels, input & controls, settings panel, conversation text box and trajectory page |
|
|
137
150
|
| **Model Background** | Per rule | The ordered rule list, the live match readout, and each rule's images (filmstrip), theme color, layout mode, per-image framing, opacity, blur and [rotation](#image-rotation) |
|
|
138
|
-
| **Config** | — |
|
|
151
|
+
| **Config** | — | The global [switch effect](#switch-effect-global), import / export of the whole rule set (`dsh-background-by-model-theme.json`), plus the single [holiday background](#holiday-backgrounds) switch |
|
|
139
152
|
|
|
140
153
|
The old **Color** tab is gone — the theme color is now a property of each rule. The old **Background** tab became **Model Background**.
|
|
141
154
|
|
|
@@ -145,7 +158,7 @@ Data directory: `~/.dsh/.dsh-background-by-model-data/` (Windows: `C:\Users\<you
|
|
|
145
158
|
|
|
146
159
|
| File | Contents |
|
|
147
160
|
| --- | --- |
|
|
148
|
-
| `theme-config.json` | The rule list (each rule with its image list and rotation) plus the global Interface settings and the holiday overrides |
|
|
161
|
+
| `theme-config.json` | The rule list (each rule with its image list and rotation) plus the global Interface settings, the global switch effect and the holiday overrides |
|
|
149
162
|
| `modelbg-<slot>` | One image, stored as raw bytes without a file extension — one file per image, so a rule with three images owns three slots |
|
|
150
163
|
| `modelbg-h-midautumn` / `modelbg-h-nationalday` | Not used: both holiday wallpapers ship inside the package and are served straight out of it |
|
|
151
164
|
|
|
@@ -199,13 +212,14 @@ To mount a working copy into a profile instead, add it to the profile's `package
|
|
|
199
212
|
|
|
200
213
|
After changing anything under `src/`, run `pnpm run bundle` again — the mounted profile loads `lib/`, so edits do not take effect until the bundle is rebuilt.
|
|
201
214
|
|
|
202
|
-
`pnpm test` builds, typechecks and runs
|
|
215
|
+
`pnpm test` builds, typechecks and runs six checks (`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/ui-strings-check.ts && node scripts/node-half-check.mjs`). None of them needs a dependency, a transformer or a browser, and each can be run alone with `pnpm check:holiday` / `pnpm check:rotation` / `pnpm check:transition` / `pnpm check:repaint` / `pnpm check:ui` / `pnpm check:node`:
|
|
203
216
|
|
|
204
217
|
- **`check:holiday`** — the two holiday windows against known dates, the Beijing day boundary (23:59 vs 00:01), leap eighth months across 1900–2100, and the four gates `pickHoliday` applies before a holiday is allowed to take over the background.
|
|
205
218
|
- **`check:rotation`** — the rotation's decisions (`nextIndex` / `isRotating`): no walking off the end of a list, shuffle never repeating the image already on screen (across the whole support of the roll), and "rotating" never claimed for a rule with nothing to rotate.
|
|
219
|
+
- **`check:transition`** — the switch effect's decisions (`resolveFadeMs` / `transitionPlan`), which live in `src/client/transition.ts` and are the ones that fail without a sound: that the shipped default still produces **exactly** the transition string earlier releases wrote (`opacity 320ms ease`, opacity only — an unchanged property must not be put on the transition list), that `Instant`, a `0` duration, a `0` unified duration and a non-numeric duration each mean a hard cut, that reduced motion vetoes rather than shortens, that every effect starts somewhere other than where it ends (the "I picked the new effect and nothing happened" failure), and that every effect lands fully visible and **unscaled** — a zoom that never resets would crop every later wallpaper.
|
|
206
220
|
- **`check:repaint`** — the decision that says whether an edit has to repaint the interface (`shouldRepaint`), including the case that kept failing in the field: an edit that **creates** the winner (a rule's first picture, its first or auto-extracted color) must repaint even though the edited rule was not the active one yet. It also fails if any of the write paths in `src/client/index.tsx` stops asking — so re-introducing the old `id === activeRuleId` test breaks the build's checks instead of the user's next upload — and if a rotation step stops re-emitting the interface palette (a theme color belongs to the image, so a step changes it).
|
|
207
221
|
- **`check:ui`** — every `t('…')` key exists in both dictionaries, both dictionaries carry the same key set, every `dab-…` class has a rule in the stylesheet, and no dictionary entry has gone unreferenced.
|
|
208
|
-
- **`check:node`** — the built node half driven through its real RPC handler with `DSH_HOME` redirected to a throwaway directory: the holiday block through the shared sanitizer, the packaged wallpapers served from an empty slot (without being copied into the data directory), a holiday slot refusing writes, deletes and URL fetches while a file dropped into it is ignored, an ordinary rule slot still writing, listing and deleting, a hand-edited config unable to redirect a holiday at another rule's image,
|
|
222
|
+
- **`check:node`** — the built node half driven through its real RPC handler with `DSH_HOME` redirected to a throwaway directory: the holiday block through the shared sanitizer, the packaged wallpapers served from an empty slot (without being copied into the data directory), a holiday slot refusing writes, deletes and URL fetches while a file dropped into it is ignored, an ordinary rule slot still writing, listing and deleting, a hand-edited config unable to redirect a holiday at another rule's image, the per-image color shape: the read-time lift in both directions (an absent key inherits the rule's color, an explicit `null` does not), independent colors through the file, and a `null` written as a key rather than omitted, and the global switch effect: the shipped default for a config that predates it, a chosen effect/easing/unified duration surviving the round trip, hostile values falling back while an absurd duration clamps, a `0` duration kept rather than defaulted, and drift inside `transition` reported instead of silently dropped.
|
|
209
223
|
|
|
210
224
|
## FAQ
|
|
211
225
|
|
|
@@ -251,35 +265,31 @@ Pressing it means leaving the system-theme state, so it has to hand out a starti
|
|
|
251
265
|
**After switching back to "System theme" the interface stays light until I drag a slider — why?**
|
|
252
266
|
The other half of the same hole: while it owns a color the plugin **forces** `body[data-ds-dark-theme]` (setting its marker for a dark palette, removing the attribute for a light one), and the host only rewrites that flag when it projects a **theme snapshot** — it does not watch the attribute. A flag dropped by a light skin therefore sticks, so the readback captured the host's light palette at the moment the color was cleared and nothing ever re-read it until an unrelated apply ran. Now the switch hands the flag back synchronously in the host's own boolean form, the scheme the host resolved (`ctx.theme`'s `active.colorScheme`, falling back to `html[data-ds-theme-source]` plus `prefers-color-scheme`) is pushed into the render layer, and the one-second watchdog re-reads the host palette while a rule has no color, repainting when it moved.
|
|
253
267
|
|
|
268
|
+
**I picked a new switch effect and the wallpaper still cuts instantly.**
|
|
269
|
+
Two things can hold it, and both are on the card: the effect is **Instant**, or the duration is `0` (an effect and a duration are two ways of asking for the same hard cut, and the effect wins). A third one is outside the plugin: if your system asks for **reduced motion**, nothing animates at all and the card says so.
|
|
270
|
+
|
|
254
271
|
## Recent Optimizations
|
|
255
272
|
|
|
256
|
-
> Only the two most recent releases are listed here; older ones (v0.
|
|
257
|
-
|
|
258
|
-
### v0.7.
|
|
259
|
-
|
|
260
|
-
- **The
|
|
261
|
-
- **
|
|
262
|
-
- **
|
|
263
|
-
- **
|
|
264
|
-
- **
|
|
265
|
-
- **
|
|
266
|
-
- **The config shape moved to
|
|
267
|
-
- **
|
|
268
|
-
|
|
269
|
-
### v0.7.
|
|
270
|
-
|
|
271
|
-
- **
|
|
272
|
-
- **
|
|
273
|
-
- **
|
|
274
|
-
- **
|
|
275
|
-
- **
|
|
276
|
-
- **An empty rule is a state, not a dead end** — the first cut refused to remove a rule's last picture and cleared its bytes "instead", which left the entry itself behind: a blank tile at position 1 that could never be deleted, so the next upload landed at position 2 and the rule kept painting nothing while the preview showed an empty frame. Removing the last image now empties the list, a brand-new rule starts empty (no slot is allocated until the first picture), the picture you add becomes the one you are looking at, and a hand-edited list whose entries are all unusable lands in the same state instead of being conjured back into a broken slot.
|
|
277
|
-
- **Empty is not off: the theme color keeps working** — the first cut read "has an image" as "is usable", so a rule whose pictures had all been removed **stopped matching and stopped being the fallback**: its own theme color, the wheel, the swatches and the clear-to-system-theme button became decoration (the symptom is "the theme color does nothing") and the models it used to own were handed to the next rule. A rule now counts while it has **an image, or a theme color of its own** (`ruleCanPaint`) — no wallpaper, just the interface painted from that color. The card's copy names which of the three real states it is in (no image but a color / neither / an image whose bytes have not arrived) instead of covering all three with one sentence.
|
|
278
|
-
- **Editing a rule that is not the active one takes effect at once** — `color`, `enabled` and `match` all feed which rule wins, but the write path only re-ran the resolution when the edited rule was already the winner: picking the first color for an empty rule, or typing a match string into a rule that had not won yet, changed nothing on screen until some unrelated event re-ran it.
|
|
279
|
-
- **Only the images that are needed are loaded** — boot hydrates each rule's FIRST image (exactly what it hydrated per rule before multi-image), the rest arrive when a card is expanded or when the rotation is about to need one. Twenty pictures across five rules therefore do not mean twenty full-size data URLs before the first frame.
|
|
280
|
-
- **A stale host can no longer eat a profile** — the node half publishes the config shape it writes, and a client that finds a host announcing an older version (`SCHEMA_VERSION`, where 3 is the first that means "an empty image list is legal") **holds every config write** and says so in the panel. Without it, updating the package and merely refreshing the page would let the previous sanitizer drop every rule on the next slider drag.
|
|
281
|
-
- **Old configs still load, and old exports still import** — a rule's single `slot`/`bgState` pair is lifted into `images: [{ slot, bgState }]` on read; theme files (version 4 now, version 3 and earlier still accepted) carry, and restore, every image of every rule rather than just the first.
|
|
282
|
-
- **A fourth static gate: `pnpm check:rotation`** — the rotation's decisions are pure (`nextIndex` / `isRotating`) and are now pinned down: no walking off the end of a list, shuffle never repeating the current image (checked across the whole support of the roll, not one sample), and "rotating" never being claimed for a rule with nothing to rotate. `check:node` covers the new shape through the real RPC handler: legacy lifting, per-image framing, dedupe, clamping, nested-drift warnings and per-image slot independence.
|
|
273
|
+
> Only the two most recent releases are listed here; older ones (v0.7.1 and earlier) live in [CHANGELOG.md](./CHANGELOG.md).
|
|
274
|
+
|
|
275
|
+
### v0.7.3
|
|
276
|
+
|
|
277
|
+
- **The switch is a setting now: four effects, an easing and one duration, on the Config page** — every wallpaper change this plugin performs (a model switch, a rotation step, the manual **Next image**, a holiday taking over) went through one hard-coded cross-fade whose length was read off a field no control could reach. **Cross-fade**, **instant**, **zoom** and **slide** all ride the two layers that already existed — zoom and slide use `transform` on the incoming layer only, a property the wallpaper layer had never used, so neither costs a byte of extra transfer — instant and a `0` s duration are the same hard cut, and **reduced motion** is a veto rather than a shorter animation.
|
|
278
|
+
- **The card carries a preview swatch, and that is not decoration** — a wallpaper fading into itself is invisible, so replaying the effect on the picture already on screen would show nothing at all: the one way this feature could look broken while working. The swatch replays the chosen effect at the length a real switch would use right now.
|
|
279
|
+
- **There is exactly one duration, and "Follow each rule" is gone** — the option named a value the interface cannot set (`rotate.fadeMs` had been in the shape since 0.7.0 and no control ever reached it), so it was a choice between a real setting and a phantom one. The slider IS the setting. A hand-edited config is not dropped in silence: `legacyFadeMs` folds the first deliberate value it finds into the global duration on the first read (a value equal to the shipped 320 ms is ignored, and an explicit `durationMs` wins), and the field is exempt from the drift warning because it is being migrated, not drifting.
|
|
280
|
+
- **The interface colour now changes over the same clock as the wallpaper** — a new theme colour is one rewrite of `body{--dsw-alias-…}`, which used to change every surface in a single frame while the picture behind it blended. The fade is a **registration** problem (an unregistered custom property cannot interpolate), so the plugin emits `@property` rules that turn the alias tokens into `<color>`s, plus a `transition` for the elements whose own declarations change with the palette — `body`, the settings dialog and the trajectory root. The two surfaces it paints **inline** (the app columns, the chat/trajectory cards) do not read those tokens at all, so they carry the same duration on `background-color`. The duration, the easing and both vetoes come from the **same** `transitionPlan` the wallpaper uses, and it is armed **only when the palette itself moved** — every other pass (a slider drag moving the alphas) writes the sheet with no transition and `none` inline, because a live transition there would make the surface lag behind the pointer.
|
|
281
|
+
- **A light↔dark flip switches instantly, and the wallpaper keeps animating** — interpolating between the two palettes walks the interface through mid-tones whose ink is wrong in both directions, so a scheme flip is exactly the case a fade makes worse. The fade is armed only while the **scheme** stays put (light→light, dark→dark); the wallpaper is deliberately untouched by that rule, because one image fading into another has no scheme to get wrong.
|
|
282
|
+
- **The ink is written synchronously now, and that was a real bug with exactly one writer (a fix)** — switching a rule to a dark theme colour could leave a panel's text on the **previous** palette while every surface followed the new one: dark ink on a dark panel, unreadable, cleared only by reloading, with nothing in the log. Measured from the reported screenshot rather than guessed: the panel was the plugin's dark `bg-layer-2` (`hsl(229,59%,27%)` = `rgb(28,43,109)`, composited at 87% panel opacity to the sampled `rgb(23,36,91)`) while the ink was the **light** branch token over it (`rgba(0,0,0,0.85)` on that navy is exactly the `rgb(3,6,14)` sampled from the glyph cores, and `rgba(0,0,0,0.7)` exactly the `rgb(7,10,27)` of the hint line). Every other surface has a second, synchronous writer, so only the token stylesheet could go stale — and its single writer was reachable only from a `requestAnimationFrame` callback wrapped in `catch {}`: a window that is not rendering swallows the frame while the id stays armed and parks every later update behind it, and a failure inside the write said nothing at all. A palette that moved is now written in the same task as the surfaces it has to agree with, the frame path is kept for what it was for (coalescing a slider drag), a frame outstanding for over a second is dropped instead of blocking forever, and the writer logs its failure.
|
|
283
|
+
- **The config shape moved to 5 and an export to format 6** — a schema-4 sanitizer rebuilds the config from the keys it knows, so a stale host process would drop the effect, the easing and the duration on the first write after a refresh, silently and with nothing in the host log. The published `schema` moves with it, the hold-writes path catches such a host, and `warnUnknownConfigKeys` compares the fixed-shape `transition` block one level down.
|
|
284
|
+
- **Gates** — `check:transition` joins `pnpm test`, which is now **six** checks; `check:repaint` pins the three lines the ink bug was made of (write synchronously on a palette change, do not swallow the failure, ask both halves of the fade question at both the token and the inline call sites); `check:node` covers the shipped default, the round trip, hostile values, a kept `0` duration, the drift check and the legacy fold; `check:ui` covers the new strings and classes and registers the chip labels in `TABLE_KEYS`.
|
|
285
|
+
|
|
286
|
+
### v0.7.2
|
|
287
|
+
|
|
288
|
+
- **The filmstrip IS the control: drag to reorder, double-click to show** — the order used to be reachable only through a pair of ↑ / ↓ arrows and a "make first" button in the row below, when the five thumbnails already show the order they are in. Grab any tile and drop it where it belongs (the drop point is a 2px bar), and give "which picture is on screen" to the strip's other gesture: a double-click, which changes no order and **writes nothing at all** — the image being shown is runtime state, not a setting, so the next boot starts at the rule's first image again.
|
|
289
|
+
- **The order and "which one is on screen" no longer interfere (a fix)** — both old actions ended by moving the image index onto the image they had just moved, so tidying the list swapped the wallpaper with it (most visible with the rotation off, where the index is sticky: "even with the rotation off, the background can change when I adjust the order"). `moveRuleImageTo` replaces both: it remembers the SLOT being painted before the splice and puts the index back on it afterwards. The three buttons left with the two uncalled actions behind them.
|
|
290
|
+
- **The tile on screen keeps its dot and loses its ring** — the tile open for editing already has `.is-sel`'s ring, and a second one put two "selected" tiles in a row of near-identical thumbnails; being on screen is the dot, and nothing else.
|
|
291
|
+
- **Every image of a batch upload gets its own color** — the extraction was addressed by POSITION (it only asked about `rule.images[length - 1]`), which is right for a one-file upload and silently wrong for a batch: the pictures before the last kept no color of their own and stayed on the system theme. It is now addressed by the slots the batch itself just appended, so which pictures get themed depends on neither where they landed nor which one the card selects.
|
|
292
|
+
- **Copy trimmed** — the "Rules" heading, its priority sentence and the automatic-extraction sentence are gone (the panel's title and the `fallback` badge already say both), `Add images` / `Replace this image` / `Remove image` lose the noun they were repeating, and `Edit position` moved to the end of the layout row (it edits how the picture fills the area, and still appears in **fit** only).
|
|
283
293
|
|
|
284
294
|
## Star History
|
|
285
295
|
|
package/README.zh.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
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
14
|
> **v0.3.0 是破坏性更新**:视频背景、生成式背景与全局主题色均已被移除。详见 [CHANGELOG.zh.md 的 v0.3.0 条目](./CHANGELOG.zh.md#v030)。
|
|
15
15
|
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
- **实时状态提示** — 「模型背景」页顶部会显示当前状态,例如「当前模型 deepseek-flash → 命中 · 规则 1」,方便当场验证规则写对没有;检测不到当前模型时会给出提示。
|
|
79
79
|
- **外观随规则走,颜色随图走** — 壁纸(多张)、布局模式、透明度与模糊度是每条规则自己的属性;**取景和主题色则是每张图片自己的**。除「界面」页外没有任何全局外观项。
|
|
80
80
|
- **一条规则多张图** — 每条规则持有一组有序图片,用胶片条展示:可一次加多张(多选文件、拖放,或一行一个网址批量粘贴)、排序、把某张设为第一张、**原地替换某一张**(替换会保住它在轮换里的位置)、逐张删除。不轮换时规则画的就是**第一张**。删掉最后一张只是让这条规则变空,而空不等于失效:只要它还留着自己的主题色,它就照旧参与匹配与兜底、只给界面配色而不铺壁纸;连主题色也没有(比如刚新增的规则)才会被匹配跳过。空规则是一个看得见、退得回的状态,而不是卡住出不来的死胡同。一张图都没有时胶片条会隐藏、上方那块大预览**就是**上传按钮,所以添加图片的地方只有一个、也很显眼。
|
|
81
|
-
- **导入 / 导出** — 一键把**每一条规则连同它的全部图片**导出为 `dsh-background-by-model-theme.json`(版本
|
|
81
|
+
- **导入 / 导出** — 一键把**每一条规则连同它的全部图片**导出为 `dsh-background-by-model-theme.json`(版本 6,图片以 base64 内联),可随时导入还原。更早版本导出的文件同样能导入:规则唯一的那张图会被还原成它的图片列表,在「主题色属于图片」之前写下的颜色会被抬升到每一张还没有自己颜色的图上,而在[切换效果](#切换效果全局)之前写下的文件会拿到这项设置的出厂默认——所以旧主题文件还原出来的观感与当初一模一样。
|
|
82
82
|
- **文件持久化** — 所有设置与图片保存到文件系统 `~/.dsh/.dsh-background-by-model-data/`,不依赖 `localStorage`。
|
|
83
83
|
- **自动迁移** — 旧版单壁纸配置在首次读取时自动升级,详见 [CHANGELOG.zh.md 的 v0.3.0 条目](./CHANGELOG.zh.md#v030)。
|
|
84
84
|
- **中英双语** — 完整的中英文界面,自动跟随语言设置。
|
|
@@ -98,6 +98,19 @@
|
|
|
98
98
|
- **取景是逐图的,颜色也是** — 构图和配色都属于某一张图:每张图各自保留自己的取景与主题色,同时共享规则的布局、透明度与模糊度。胶片条会标出哪些图已经各自有颜色,调色区上方也会写明当前编辑的是哪一张。
|
|
99
99
|
- **只加载用得到的图** — 启动时只读每条规则的**第一张**,其余的在展开卡片时、或者在轮换即将用到它时再读。所以「五条规则各十张图」不等于首帧之前先传五十张壁纸。
|
|
100
100
|
|
|
101
|
+
### 切换效果(全局)
|
|
102
|
+
|
|
103
|
+
壁纸**发生变化**时长什么样。它放在 **「配置」** 页,对所有规则是同一个(规则本身没有「切换效果」这个概念),并且覆盖壁纸变化的所有来源:切模型、[轮换](#多图轮换)换图、手动「下一张」,以及[节日](#节日特殊背景)接管。
|
|
104
|
+
|
|
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 块,那里若挂着过渡,表面就会滞后于指针。
|
|
113
|
+
|
|
101
114
|
### 节日特殊背景
|
|
102
115
|
|
|
103
116
|
小小一个,不是一整块设置:**「配置」页上一个开关,别的什么都没有**。它默认就是开的;到了日子背景自己换,第二天早上自己换回。
|
|
@@ -135,7 +148,7 @@
|
|
|
135
148
|
| --- | --- | --- |
|
|
136
149
|
| **界面** | 全局,所有模型共用 | 主背景、左侧栏、右侧栏、卡片面板、输入框与控件、设置面板、对话文本框、轨迹页的透明度与模糊度 |
|
|
137
150
|
| **模型背景** | 每条规则 | 有序规则列表、顶部实时状态,以及每条规则的图片(胶片条)、**逐图主题色**、布局模式、逐图取景、透明度、模糊度与[多图轮换](#多图轮换) |
|
|
138
|
-
| **配置** | — |
|
|
151
|
+
| **配置** | — | 全局[切换效果](#切换效果全局)、导入 / 导出整套规则(`dsh-background-by-model-theme.json`),外加[节日特殊背景](#节日特殊背景)那一个开关 |
|
|
139
152
|
|
|
140
153
|
原先的 **「色彩」** 选项卡已删除——主题色改为每条规则自己的属性;原 **「背景」** 选项卡已改造成 **「模型背景」**。
|
|
141
154
|
|
|
@@ -145,7 +158,7 @@
|
|
|
145
158
|
|
|
146
159
|
| 文件 | 内容 |
|
|
147
160
|
| --- | --- |
|
|
148
|
-
| `theme-config.json` | 规则列表(每条规则含图片列表与轮换设置)+ 全局「界面」设置 + 节日覆盖配置 |
|
|
161
|
+
| `theme-config.json` | 规则列表(每条规则含图片列表与轮换设置)+ 全局「界面」设置 + 全局切换效果 + 节日覆盖配置 |
|
|
149
162
|
| `modelbg-<slot>` | 一张图片,保存为原始字节,无扩展名 —— 一张图一个文件,所以一条有三张图的规则占三个槽位 |
|
|
150
163
|
| `modelbg-h-midautumn` / `modelbg-h-nationalday` | 不使用:两张节日壁纸都随包发布,直接从包内送出 |
|
|
151
164
|
|
|
@@ -199,13 +212,14 @@ pnpm run typecheck
|
|
|
199
212
|
|
|
200
213
|
改完 `src/` 必须重新执行 `pnpm run bundle` 才会生效——挂载的 profile 加载的是 `lib/`。
|
|
201
214
|
|
|
202
|
-
`pnpm test`
|
|
215
|
+
`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/ui-strings-check.ts && node scripts/node-half-check.mjs`)。六道都不需要依赖、不需要转译器、也不需要浏览器,也可以单独跑:`pnpm check:holiday` / `pnpm check:rotation` / `pnpm check:transition` / `pnpm check:repaint` / `pnpm check:ui` / `pnpm check:node`。
|
|
203
216
|
|
|
204
217
|
- **`check:holiday`** — 两个节日窗口对照已知日期、北京时间的跨日边界(23:59 与 00:01)、1900–2100 年间的闰八月,以及 `pickHoliday` 允许节日接管背景之前必须通过的四道闸门。
|
|
205
218
|
- **`check:rotation`** — 轮换的判定(`nextIndex` / `isRotating`):不会走越界、随机不会重复当前这张(在整个随机取值范围内穷举)、也不会把只有一张图的规则报成「正在轮换」。
|
|
219
|
+
- **`check:transition`** — 切换效果的判定(`resolveFadeMs` / `transitionPlan`),代码在 `src/client/transition.ts`,正是那些不报错、只是「看起来没生效」的部分:出厂默认仍然必须产出**与旧版本逐字符相同**的过渡串(`opacity 320ms ease`,且只动 opacity——没有变化的属性不能被写进过渡列表);`直接切换`、时长为 `0`、统一时长为 `0`、以及时长不是数字,四者都必须等于硬切;「减少动态效果」是否决而不是缩短;每个效果的起点都必须与终点不同(也就是「换了新效果却什么都没发生」);每个效果结束时都必须完全可见且**没有残留缩放**——一次没有复位的 zoom 会让之后每一张壁纸都被放大裁掉一圈。
|
|
206
220
|
- **`check:repaint`** — 「这次编辑要不要重画界面」的判定(`shouldRepaint`),其中就包含实际踩过的那个坑:**这次编辑本身**制造出了生效规则(某条规则的第一张图、第一个颜色或自动提取出的颜色)时,即使被编辑的规则此刻还不是生效规则,也必须立刻重画。它同时还会在 `src/client/index.tsx` 里任何一条写入路径不再询问这个判定时报错——也就是说,把老的 `id === activeRuleId` 判断写回去,会是构建校验失败,而不是你下一次上传时界面不动;轮换步骤不再重发调色板(主题色属于图片,换图就是换配色)同样会被它拦下。
|
|
207
221
|
- **`check:ui`** — 每个 `t('…')` 键在中英两本字典里都存在、两本字典键集完全一致、每个 `dab-…` 类在样式表里都有规则,且没有字典条目变成没人引用的死键。
|
|
208
|
-
- **`check:node`** — 把构建好的 node 半边通过它真实的 RPC 处理器跑一遍,`DSH_HOME` 指向临时目录:节日配置块穿过共享的 sanitizer、内置壁纸从空槽位被送出(且不会因此写进数据目录)、节日槽位拒绝写入 / 删除 / 抓取而丢进去的文件被忽略、普通规则槽位照常写入和删除,以及手改过的配置无法把节日指向别的规则的图片。它还覆盖逐图主题色这套形状:读取时抬升的两个方向(缺键继承规则色、显式 `null` 不继承)、颜色经过文件往返后各自独立,以及 `null`
|
|
222
|
+
- **`check:node`** — 把构建好的 node 半边通过它真实的 RPC 处理器跑一遍,`DSH_HOME` 指向临时目录:节日配置块穿过共享的 sanitizer、内置壁纸从空槽位被送出(且不会因此写进数据目录)、节日槽位拒绝写入 / 删除 / 抓取而丢进去的文件被忽略、普通规则槽位照常写入和删除,以及手改过的配置无法把节日指向别的规则的图片。它还覆盖逐图主题色这套形状:读取时抬升的两个方向(缺键继承规则色、显式 `null` 不继承)、颜色经过文件往返后各自独立,以及 `null` 是写出键而不是省略。切换效果同样在覆盖范围内:旧配置拿到的出厂默认、选定的效果 / 缓动 / 统一时长能原样穿过文件往返、非法值回退而夸张时长被夹紧、时长为 `0` 被保留而不是回退成默认,以及 `transition` 内部的字段漂移会被上报而不是悄悄丢掉。
|
|
209
223
|
|
|
210
224
|
## 常见问题
|
|
211
225
|
|
|
@@ -251,35 +265,31 @@ pnpm run typecheck
|
|
|
251
265
|
**为什么「自定义颜色」点下去就有颜色了?**
|
|
252
266
|
因为按下它就等于离开「跟随系统主题」,必须给出一个起始色。它先跑一次「从本图提取」(和「从本图提取」按钮同一条链路),取不到鲜明颜色时才退回默认色,所以不会再出现「点一下就从浅色跳成一块跟壁纸无关的深蓝」。
|
|
253
267
|
|
|
268
|
+
**我换了新的切换效果,壁纸还是直接跳过去。**
|
|
269
|
+
有两件事会拦住它,而且两件都在那张卡上:效果是**「直接切换」**;或者时长是 `0`(效果和时长是同一个硬切的两种说法,而效果优先)。第三件不在插件里:系统开了**「减少动态效果」**时完全不做动画,卡片也会直说。
|
|
270
|
+
|
|
254
271
|
## 近期优化
|
|
255
272
|
|
|
256
|
-
> 这里只列最近两个版本;更早的版本(v0.
|
|
257
|
-
|
|
258
|
-
### v0.7.
|
|
259
|
-
|
|
260
|
-
-
|
|
261
|
-
-
|
|
262
|
-
-
|
|
263
|
-
-
|
|
264
|
-
-
|
|
265
|
-
-
|
|
266
|
-
- **配置格式升到
|
|
267
|
-
-
|
|
268
|
-
|
|
269
|
-
### v0.7.
|
|
270
|
-
|
|
271
|
-
-
|
|
272
|
-
-
|
|
273
|
-
-
|
|
274
|
-
-
|
|
275
|
-
-
|
|
276
|
-
- **空规则是一个状态,不是死路** — 这个功能的第一版拒绝删除最后一张,转而「清空它的字节」,结果把条目本身留了下来:胶片条第 1 位一块永远删不掉的空白图,于是下一次上传只能排到第 2 位,规则继续什么都不画、预览一直停在空帧。现在删除最后一张就是把列表清空;新建规则从空列表开始(第一张图到达时才分配槽位);你加进去的那张就是你正在看的那张;手改导致「每一项都不可用」的列表也落到这个状态,而不是被硬塞回一个坏槽位。
|
|
277
|
-
- **空规则不等于失效:主题色继续生效** — 第一版把「有没有图」当成了「这条规则是否可用」,于是把图片删光的规则**既不再匹配、也不再当兜底**:它自己那份主题色、色轮、灵感色板、「跟随系统主题」全部变成摆设(现象就是「主题色无效」),而它原本负责的模型被悄悄交给下一条规则。现在只要是**有图、或者有自己的主题色**(`ruleCanPaint`)就仍然生效 —— 不铺壁纸,只给界面配色。面板上的说明也按三种真实状态分开说(无图有颜色 / 无图无颜色 / 有图但还没取到内容),不再拿同一句话覆盖三种情况。
|
|
278
|
-
- **改一条没在生效的规则也会立刻生效** — `color` / `enabled` / `match` 都影响「哪条规则赢」,但以前只有编辑当前生效规则时才会重新解析:给空规则选上第一个颜色、或往一条还没赢的规则里输入匹配串,界面上都不会有反应,要等某个无关事件顺手重跑一次解析。
|
|
279
|
-
- **只加载用得到的图** — 启动时只灌每条规则的**第一张**(跟多图出现之前每条规则灌一张完全一致),其余的在展开卡片时、或轮换即将用到时再取。于是「五条规则二十张图」不等于首帧之前先传二十张全尺寸 data URL。
|
|
280
|
-
- **旧宿主再也不可能吃掉一份配置** — node 半边现在会公布自己写入的配置格式,客户端发现宿主报出的版本低于自己(`SCHEMA_VERSION`,2 → 3 起表示「空图片列表合法」)时会**扣住所有配置写入**并在面板里说明。没有这道闸门,更新包之后只刷新一次页面,上一个版本的 sanitizer 就会在下一次拖动滑块时把全部规则丢掉。
|
|
281
|
-
- **老配置照常读,老导出照常导入** — 规则里那对单图字段(`slot` / `bgState`)会在读取时被提升成 `images: [{ slot, bgState }]`;主题文件升到版本 4(版本 3 及更早仍可导入),导出与还原的是每条规则的全部图片,而不只是第一张。
|
|
282
|
-
- **第四道静态门禁:`pnpm check:rotation`** — 轮换的判定是纯函数(`nextIndex` / `isRotating`),现在被钉住了:不会走越界、随机不会重复当前这张(在整个随机取值范围内穷举,不是抽一次样本)、也不会把只有一张图的规则报成「正在轮换」。`check:node` 则通过真实 RPC 处理器覆盖新形状:旧配置提升、逐图取景、去重、钳位、嵌套字段漂移告警,以及各图槽位互不干扰。
|
|
273
|
+
> 这里只列最近两个版本;更早的版本(v0.7.1 及以前)见 [CHANGELOG.zh.md](./CHANGELOG.zh.md)。
|
|
274
|
+
|
|
275
|
+
### v0.7.3
|
|
276
|
+
|
|
277
|
+
- **切换效果现在是一个设置:四种效果、一种缓动、一个时长,都在「配置」页上** —— 这个插件做的每一次换图(切模型、轮换一步、手动「下一张」、节日接管)此前都走同一个写死的交叉淡入,而它的时长读的是一个没有任何控件能碰到的字段。**淡入淡出**、**直接切换**、**缩放淡入**、**平移推入**都作用在本来就有的那两层上 —— 缩放与平移只在进来的那一层上用 `transform`,而壁纸层从来没用过这个属性,所以两者都不花一个字节的额外传输 —— 「直接切换」与 `0` 秒是同一个硬切,系统的「减少动态效果」是**否决**而不是把动画缩短。
|
|
278
|
+
- **卡片上带一个预览色块,而它不是装饰** —— 壁纸淡入它自己是什么都看不见的,所以「在已经在屏的那张图上重放效果」会什么都不显示:这是这个功能唯一可能「明明在工作却看起来坏了」的方式。色块按此刻一次真实切换会用的时长重放选中的效果。
|
|
279
|
+
- **时长只有一个,「跟随每条规则」删掉了** —— 那个选项指向一个界面设不了的值(`rotate.fadeMs` 从 0.7.0 起就在格式里,却从来没有控件能到达它),所以它是在一个真设置和一个幻影设置之间做选择。滑块本身就是那个设置。手工编辑过的配置不会被无声丢掉:`legacyFadeMs` 在第一次读取时把找到的第一个「有意为之」的值折进全局时长(等于出厂值 320 ms 的忽略,显式的 `durationMs` 胜出),该字段也因「正在被迁移、而不是在漂移」而豁免漂移警告。
|
|
280
|
+
- **界面配色现在和壁纸走同一个时钟** —— 换一个主题色本质是一次 `body{--dsw-alias-…}` 的重写,而它以前在一帧之内改掉每个表面、背后的图却还在淡入淡出。这个渐变是**注册**问题(未注册的自定义属性无法插值),所以插件发出把别名令牌变成 `<color>` 的 `@property` 规则,外加一条 `transition`,作用于自身声明会随调色板变化的那些元素 —— `body`、设置弹窗与轨迹页根节点。它**内联**绘制的两处表面(三栏列、对话/轨迹卡片)根本不读这些令牌,所以把同一个时长直接挂在 `background-color` 上。时长、缓动与两项否决都来自壁纸用的**同一个** `transitionPlan`,而且只在**调色板确实变了**的时候武装 —— 其它每一次 apply(拖滑块改的是 alpha)写入的样式表不带过渡、内联写 `none`,因为那里挂一条活着的过渡会让表面跟不上指针。
|
|
281
|
+
- **浅↔深互切是立即切换,壁纸照旧做动画** —— 在两套调色板之间插值会让界面走过中间调,而那里的墨色**两个方向都是错的**,所以浅↔深恰恰是渐变会让事情变糟的那一种情况。渐变只在**配色深浅不变**时武装(浅→浅、深→深);壁纸刻意不受这条规则约束:一张图淡入另一张图没有「深浅」可以搞错。
|
|
282
|
+
- **墨色现在是同步写入的,而那是一个真实存在、且只有一个写入者的 bug(修复)** —— 把一条规则换成深色主题色时,面板的文字可能留在**上一套**调色板上,而所有表面都已经跟到新的一套:深色面板上的深色墨色,读不出来,只有重新加载才能清掉,日志里什么都不会说。这是从报告里的截图**量出来**的:面板是插件自己的深色 `bg-layer-2`(`hsl(229,59%,27%)` = `rgb(28,43,109)`,在 87% 面板透明度下合成为采样到的 `rgb(23,36,91)`),而墨色是画在它上面的**浅色**分支令牌(`rgba(0,0,0,0.85)` 压在那块藏青上恰好就是字形核心采样到的 `rgb(3,6,14)`,`rgba(0,0,0,0.7)` 恰好是提示行采样到的 `rgb(7,10,27)`)。其它每个表面都有第二个同步的写入者,所以只有令牌样式表可能变旧 —— 而它唯一的写入者只能从一个包在 `catch {}` 里的 `requestAnimationFrame` 回调到达:不在渲染的窗口会吞掉那一帧,id 却仍然武装着、把此后每一次更新都堵在它后面,写入内部出错则什么都不说。现在调色板一旦移动,就在**与它必须保持一致的表面同一个任务里**同步写入;帧路径留给它本来要做的事(合并一次滑块拖动),积压超过一秒的帧会被丢掉而不是永久阻塞,写入者也会把失败记进日志而不是吞掉。
|
|
283
|
+
- **配置格式升到 5、导出格式升到 6** —— schema 4 的 sanitizer 会**按自己认识的键重建**配置,所以一个旧宿主进程会在刷新后的第一次写入时丢掉效果、缓动与时长,悄无声息,宿主日志里也什么都没有。公布的 `schema` 随之前进,「扣住写入」分支会拦住这样的宿主,`warnUnknownConfigKeys` 也出于同样的理由往深一层比较固定形状的 `transition` 块。
|
|
284
|
+
- **门禁** —— `check:transition` 加入 `pnpm test`,现在是**六道**;`check:repaint` 钉住这个墨色 bug 由之构成的三行(调色板变化时同步写入、不许吞掉失败、在令牌与内联两个调用点都把渐变问题的两半问全);`check:node` 覆盖出厂默认值、往返、恶意取值、被保留的 `0` 时长、漂移检查与旧值折算;`check:ui` 覆盖新文案与新类,并把胶囊标签登记进 `TABLE_KEYS`。
|
|
285
|
+
|
|
286
|
+
### v0.7.2
|
|
287
|
+
|
|
288
|
+
- **胶片条自己就是控件:拖拽排顺序,双击换画面** — 顺序以前只能靠下面那排 ↑ / ↓ 和「设为第一张」改,而五个缩略图本来就把顺序摆在那里。现在抓住任意一张拖到新位置即可,落点是一根 2px 竖条;而「哪张在屏上」交给了双击 —— 它不改顺序,也**不写任何配置**:在屏哪张是运行时状态而不是设置,所以下次启动照旧从第一张开始。
|
|
289
|
+
- **顺序和在屏的是哪张,从此互不干涉(修复)** — 旧的两个动作都以「把下标挪到刚被移动的那张图上」结尾,于是整理列表会把壁纸一起换掉(轮换关着、下标粘住时最容易撞见:「我调整顺序时,即便关了轮换,当前背景也可能会变」)。新的 `moveRuleImageTo` 取代了它们:摘出之前先记下**正在画的槽位**,插回去后把下标放回它身上;三个按钮和背后两个不再被调用的动作一起删掉。
|
|
290
|
+
- **在屏那张只留圆点,不再有边框** — 「正在编辑」已经有 `.is-sel` 的圈了,再给「正在显示」画一个,一排几乎一样的缩略图里就同时有两个「选中」;这是在屏状态唯一不能留给人猜的地方。
|
|
291
|
+
- **一次上传的多张图,每张都会各自取色** — 取色原本是**按下标**寻址的(只问 `rule.images[length - 1]`),对单张上传正确、对批量静默地错:最后一张之前的图没有自己的颜色、留在系统主题上。现在按**这一批自己刚追加的槽位**寻址,与它们落在哪个位置、卡片选中了哪张都无关。
|
|
292
|
+
- **卡片文案精简** — 「规则列表」标题、优先级那句、自动取色旁边那句解释都删了(面板标题与 `兜底` 徽章已经说过);`添加图片` / `替换这张` / `移除图片` 收成 `添加` / `替换` / `移除`;`编辑位置` 移到**布局模式那一行的末尾**(它编辑的是图片如何填满这块区域,仍只在「适应」下出现)。
|
|
283
293
|
|
|
284
294
|
## Star History
|
|
285
295
|
|